the catalog

Hundreds of skills available. None of them in the way.

The catalog lists skill sources pinned to exact commits. Nothing in it is installed. An agent or a job runner asks for help with a task and gets back the few skills a judge thinks would matter, as files to read.

On this page
  1. Sources and pins
  2. Find, use, report
  3. How a choice is made
  4. Feedback
  5. For job runtimes
  6. What Spindle will not do

Sources and pins

catalog.toml
[[source]]
slug = "ours"
path = "../packages"                 # a local directory, read in place

[[source]]
slug = "upstream"
ref = "github:owner/skills@<40-character commit>"
track = "main"
include = ["plugins/upstream/skills/*"]   # optional: index only these
exclude = ["plugins/upstream/skills/setup-*"]
notes = "Shown to agents beside each skill from this source."
bash
$ spindle catalog sync               # export pinned sources, rebuild the index
$ spindle catalog sync upstream      # or one source
$ spindle catalog list
$ spindle catalog update             # what would a newer upstream commit change?
$ spindle catalog update upstream --apply

A Git source must pin a full commit. update compares the pin with the tracked branch and lists the skills added, changed, and removed, with any new static indicators. --apply rewrites the pin, and you review it as an ordinary diff to the house. include and exclude take globs over each skill's directory, relative to the source root, so one plugin can be taken from a repository of many and its harness-specific setup skills left out.

For every skill the index records its digest, the commit it came from, whether it hard-codes its own install location (and so cannot be used from a snapshot), and static indicators of network, credential, or shell use.

Find, use, report

bash
$ spindle find "agents keep making the same mistake in this repo"
* 0.80 1.00  upstream/correct    Find the mistakes agents keep repeating and make each impossible
  0.45 0.92  other/retro         Conduct a retrospective on a coding session
$ spindle use upstream/correct              # the SKILL.md path; --print for the text
$ spindle report --outcome accepted --helped upstream/correct
$ spindle usage                             # counts and the prior per skill

The two numbers are the judge's probability and the text-match score relative to the best match. use hands over a path, which works in any harness that can read files and needs no install, lease, or reload. Use a lease (try or borrow) only when a skill has to appear in a harness's native skill list.

How a choice is made

01

Expand

An optional command rewrites the task as phrases a skill description would use.

[sourcing] expand
02

Recall

Full-text search ranks the catalog per phrase and for the task, then fuses the rankings.

BM25 + RRF
03

Weigh

Ranks are weighted by each skill's record. Kernel skills are left out.

usage prior
04

Judge

Would this skill materially help? At or above 0.6 is selected. None is a fine answer.

--judge auto|jev|none
05

Receipt

The selection is saved with its candidates, scores, and skill digests.

~/.spindle/selections

The expander is any executable named in house.toml that reads the task on stdin and prints short search phrases, one per line. It is usually a small model call. A missing command, a non-zero exit, a timeout, or empty output means no expansion. --expand none skips it.

The judge is the Jev decision model: one call, a calibrated probability per candidate. Enable it with TYPESAFE_API_KEY, or name a key file with SPINDLE_JEV_KEY_FILE or jev_key_file in ~/.spindle/house.toml. It is advisory and fail-soft: a missing key, a timeout, or a malformed answer falls back to the text-match shortlist, marked lexical, and says so.

Feedback

Every step appends to ~/.spindle/usage.jsonl: what was offered and selected, what was read, and how the work was judged. report --helped and --hurt are direct verdicts. An outcome on a run that merely had a skill loaded counts as weak evidence. The resulting prior lifts skills that keep helping and lowers skills that keep being rejected, and spindle usage shows both.

For job runtimes

A runner that dispatches agent jobs needs one call before dispatch and one after grading:

bash
$ spindle brief --json --task-file - --repo "$WORKTREE" < commission.md
$ spindle report --selection "$SELECTION" --job "$JOB_ID" \
    --outcome accepted --loaded upstream/systematic-debugging
brief --json fieldWhat the runtime does with it
prompt_blockAppend to the agent's prompt. It lists each offered skill with the path to read, so the agent pays for a skill only if it uses it. Empty means nothing was offered.
skillsStore beside the job's own receipt: selection id, how it was chosen, delivery mode, and each skill's ref, digest, and judged probability. A reviewer then sees what guidance the producer had.
selectionPass to report after grading.
read_rootsAdd to the agent's read allow-list in a sandbox, so it can open an offered skill, or one it finds itself, by path.

What Spindle will not do

Run a source
Sources are exported through the same non-executing snapshot path as spindle inspect: no hooks, no install scripts, no package managers.
Float a version
A Git source without a full commit is refused. Upstream work arrives only through catalog update --apply.
Let a skill outrank the task
The handover tells the agent a skill is guidance. It never overrides the task, its acceptance criteria, or the house rules.
Hide what was offered
Every selection has a receipt, and every offered skill a digest, so an evaluator can pin the exact bytes in play.