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 .outcome
update-needed

Check 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 validate checks catalogs, profiles, settings, the team manifest in the current folder, and delivered files. Warnings, such as a SKILL.md body over 500 lines, fail it too, unless you add --warnings-ok.
  • skc deliver --check fails when a delivered file was edited, deleted, or cannot be read.
  • skc update --check fails when skc update would 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 status fails 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.test

Find 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 doesCommands
Commits and pushes to a catalog's Git hostskc 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 foldersskc 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 syncedskc catalog remove, unless you pass --keep-clone
Writes files in a catalog clone, without committingScoring and applying an improvement on a direct catalog, and skc skill score-history <slug> --repair, which also keeps a backup in ~/.skillcatalog
Changes your setupskc catalog add, skc catalog policy set, skc settings, skc scoring providers, skc profile create, skc proposal refresh and discard
Writes files in a repositoryskc init, and profile changes on a project profile, which write your local manifest
Sends a skill to your AI providerskc skill score, and skc skill improve without --apply-proposal. With --proposal-out, improve also writes that file
None of the aboveThe 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 onEvery 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-ok

The 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-ok
Error: 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.