Use SkillCatalog in scripts and CI
Some skc runs have nobody watching them. Sam has a script that tells him when his checkout needs skc update, and Acme has a CI job that checks every change to acme-skills. This page shows how such a run reads its results, fails on problems, avoids prompts, and stays out of your own setup.
Read JSON output
A script needs results it can parse, not text written for people. With --json, a command prints one JSON object instead of text: to standard output when it succeeds, and to standard error when it fails or finds a problem. Sam checks his delivered files, using jq to pick out a few fields:
skc deliver --check --json 2> result.json
jq '{command, status, outcome, message: .error.message}' result.json{
"command": "deliver",
"status": "error",
"outcome": "delivery-check-drift",
"message": "Drift check for all profiles: drift detected, 1 manually edited, 0 deleted, 0 unreadable."
}Every object has the same six keys, which Output and exit codes describes. data holds the details, here the files that were edited. A few commands, such as skc skill score, print their own JSON instead of this object.
Check results per command
A command that does not succeed ends with a non-zero exit code. Each command numbers its results in its own way, though, so the same code can mean different things in different commands. Branch on command and outcome instead.
After a teammate changed the team manifest, Sam's script asks whether his checkout needs an update. skc update --check answers with the outcome update-needed:
skc update --check --json 2>&1 | jq -r .outcomeupdate-neededCheck for problems without fixing them
Sam wants checks that find problems and leave the fixes to a person. These commands change nothing, and each one fails when it finds a problem:
skc validatechecks catalogs, profiles, settings, the team manifest in the current folder, and delivered files. Warnings, such as aSKILL.mdbody over 500 lines, fail it too, unless you add--warnings-ok.skc deliver --checkfails when a delivered file was edited, deleted, or cannot be read.skc update --checkfails whenskc updatewould create, update, or remove files in the checkout, or when something would stop that update, such as a file in the way or a catalog this machine has not added. It does not contact the Git host, so it misses catalog changes that nobody has pulled on this machine yet.skc statusfails when SkillCatalog cannot trust its own records, for example when a profile entry's catalog has no clone.
Only skc validate --path works without any SkillCatalog setup, because it checks the catalog folder you name. That is why a catalog repository's CI uses it, as the example at the end of this page shows. The other checks read the setup of the machine they run on.
Run without a terminal
Nobody answers prompts in a CI job, so the two commands that ask questions behave differently there. skc sync asks for a commit message when a catalog has changes. Without a terminal, it refuses to commit unless you pass --message:
skc sync --message "Require an owner for new endpoints"In a terminal, skc init asks for the catalog, the profile id, and the profile name when you leave them out. Without a terminal, it asks nothing and fails at once, naming the flags you left out. Pass --catalog-id, --id, and --display-name.
Isolate a run
A test or CI job needs a setup of its own, so that it neither reads nor changes your catalogs and settings. Pass --config-dir <folder> to every command, and skc keeps its catalogs, profiles, settings, and logs in that folder instead of ~/.skillcatalog. A new folder starts with no catalogs, no tools turned on, and usage metrics on.
--config-dir does not move delivery, which still writes into your tools' skills folders. For a run that delivers, point HOME at an empty folder instead, which moves both. A command that commits also needs a Git name and email in that new home folder:
export HOME="$(mktemp -d)"
git config --global user.name "Acme CI"
git config --global user.email ci@acme.testFind out what a command changes
An agent or script that must not change anything can look up each command in this table first:
| What the command does | Commands |
|---|---|
| Commits and pushes to a catalog's Git host | skc sync, skc skill publish-accepted, and skc catalog add on an empty repository. On a review-branch catalog, also scoring and applying an improvement |
| Writes files in your tools' skills folders | skc deliver, skc sync, skc update, skc install, skc uninstall, skc profile add, remove, reorder, and delete, skc settings enable and disable, skc skill publish-accepted |
| Deletes a catalog clone, with changes you have not synced | skc catalog remove, unless you pass --keep-clone |
| Writes files in a catalog clone, without committing | Scoring and applying an improvement on a direct catalog, and skc skill score-history <slug> --repair, which also keeps a backup in ~/.skillcatalog |
| Changes your setup | skc catalog add, skc catalog policy set, skc settings, skc scoring providers, skc profile create, skc proposal refresh and discard |
| Writes files in a repository | skc init, and profile changes on a project profile, which write your local manifest |
| Sends a skill to your AI provider | skc skill score, and skc skill improve without --apply-proposal. With --proposal-out, improve also writes that file |
| None of the above | The checks above, skc deliver --dry-run, skc catalog changes, skc skill score-history without --repair, and the list and show commands |
| Sends usage counts to the metrics addresses your catalogs name, while usage metrics are on | Every command except skc completions, skc help-json, skc hook run, and skc update --check, including the commands in the row above |
Even the commands in the row "None of the above" write logs and other files in the SkillCatalog folder, which lists the few exceptions.
skc help-json lists the side effects of every command as JSON, so a tool can look them up for itself:
skc help-json | jq -r '.command.commands[] | select(.name == "sync") | .behavior.side_effects[]'Successful syncs update the local clone of each requested catalog.
...Example: check a catalog in CI
Acme wants every change to acme-skills checked, wherever it was made. SkillCatalog checks each commit made in its own clone of a catalog, unless the catalog was added from a share link. It cannot check changes made on the Git host or in other clones of the repository. So Acme adds this workflow to acme-skills as .github/workflows/validate.yml, which checks every pull request and every push to main:
name: Validate skills
on:
pull_request:
push:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
container: rockylinux:9
steps:
- uses: actions/checkout@v7
- name: Install skc
run: |
cd "$(mktemp -d)"
file=skc-v0.9.0-x86_64-unknown-linux-gnu.tar.gz
url=https://github.com/humanfrontier/skillcatalog-releases/releases/download/v0.9.0
curl -fsSLO "$url/$file"
curl -fsSLO "$url/$file.sha256"
sha256sum -c "$file.sha256"
tar -xzf "$file"
install -m 755 skc /usr/local/bin/skc
- name: Validate the catalog
run: skc --config-dir "$(mktemp -d)" validate --path . --warnings-okThe job runs in a Rocky Linux 9 container, because the Linux build of skc runs on Rocky Linux and RHEL 9. Change 0.9.0 to the release you use. --config-dir "$(mktemp -d)" gives the check an empty SkillCatalog folder, and --warnings-ok lets warnings pass, as SkillCatalog's own check before a commit does.
For pull requests, Sam keeps a separate Git clone of acme-skills in ~/code/acme-skills, apart from SkillCatalog's clone. His pull request adds review-tests to the code-review stack, but not the skill itself. He runs the same check in ~/code/acme-skills before he pushes:
skc --config-dir "$(mktemp -d)" validate --path . --warnings-okError: validate
Selected checks: catalog, drift
Diagnostics: 1
- [catalog-integrity-error] catalog '~/code/acme-skills' stack:code-review: skill 'review-tests' not found
Path: ~/code/acme-skills
Hint: Fix the catalog content under '~/code/acme-skills' and rerun `skc validate`.He adds skills/review-tests/SKILL.md, and the same check passes:
Validate passed: catalog, drift.