guide

Using Spindle.

The daily workflow is one command. Everything else — building your own skills, tuning per surface, and wiring Spindle into your own infrastructure — is optional depth you reach for when you need it.

On this page
  1. Install & activate
  2. Bind a repo (the daily step)
  3. Work ahead: advance & liaison
  4. Build your own
  5. Evaluate a skill
  6. Extending Spindle
  7. CLI reference

Install & activate

Spindle is a Python toolchain; skills arrive as pip-installable distributions. Install one, then mark it active so every later command reads its doctrine, channels, and profiles:

bash
$ uv sync --extra dev

# install the reference distribution and its packages, then activate it
$ spindle dist install examples/spindle-sample/distributions/spindle-sample
$ spindle dist activate spindle-sample

# confirm what's live
$ spindle dist list
$ spindle doctrine show
$ spindle skill list
$ spindle capability list

Installing a distribution links its skills into your global skills directory and records the event to Spindle's ledger. Activation writes the active distribution name into $SPINDLE_HOME; with a single distribution installed, Spindle will use it even before you activate.

Bind a repo — the daily step

This is the one command that stands between "skills installed" and "skills in use." It classifies the repo, resolves its blend, renders it for the harness and model, lints it, and materializes it:

bash
$ spindle bind /path/to/repo --harness claude

# target a model tier (changes rendering density), or preview first
$ spindle bind /path/to/repo --harness claude --model frontier
$ spindle bind /path/to/repo --harness claude --dry-run

# see how Spindle classifies a repo without binding
$ spindle appclass /path/to/repo

# remove exactly the skills Spindle materialized
$ spindle unbind /path/to/repo --harness claude

The materialized skills land in the harness's native directory — .claude/skills/ for Claude — so the agent discovers them with no extra configuration. Useful flags: --dry-run (compute, don't write), --no-render (select only, skip profiles), and --force (materialize despite lint problems — use sparingly; it's the escape hatch past the fail-closed gate).

Work ahead: advance & liaison

Because a bind is deterministic and cached, you can do it before anyone needs it. The advance team binds every configured surface up front — from an advance/surfaces.toml or a project registry — so the common case is a warm cache, and the "extra step" is already paid.

bash
# precompute blends for every known surface
$ spindle advance run --from-registry

# capture an ad-hoc need as a demand signal (optionally bind it now)
$ spindle liaison request /path/to/repo --intent "audit accessibility" --bind
$ spindle liaison log repo

The liaison records what a surface asked for, so a recurring one-off can graduate into a precomputed skill instead of being re-derived every time.

Build your own

A package is an ordinary pip project with a [tool.spindle.package] table; a distribution is one with [tool.spindle.distribution] that depends on packages. Scaffold both:

bash
$ spindle package new my-tools --dest ./packages/my-tools \
      --skill clarify --skill review --capabilities planning

$ spindle dist new my-dist --dest ./distributions/my-dist \
      --source-dir ../../ --package my-tools==0.1.0

Author a skill

A skill is a directory with a SKILL.md: YAML frontmatter plus a short, canonical body. Keep it small; put guardrails as ALWAYS/NEVER lines so rendering will protect them:

# packages/my-tools/my_tools/skills/design/SKILL.md
---
name: design
description: Convert a clarified request into an implementation design.
---

# design

Use after the request has a clear outcome and acceptance criteria.

1. Read the relevant code and existing conventions first.
2. Propose the smallest coherent change that satisfies the outcome.
3. Name the affected files, data contracts, and tests.

ALWAYS prefer existing local patterns over new abstractions.

Declare the package

# packages/my-tools/pyproject.toml
[tool.spindle.package]
name = "my-tools"
version = "0.1.0"
distribution = "my-dist"
skills = ["clarify", "design", "review"]
capabilities = ["technical-design", "plan-review"]

[[tool.spindle.package.sources]]        # provenance, optional
peer = "upstream-lib"
url  = "https://github.com/org/upstream-lib"
transposed_at = "2026-07-11"

Write a channel

A channel decides which of your installed skills a surface receives at a given scope and harness. Place it under the distribution's source_dir at channels/<scope>/<name>/<harness>/channel.toml. The system channel is the broad default; add cluster channels for app-types and a repo channel to override a single surface.

# channels/system/system/claude/channel.toml
version = "0.1.0"
absolutes = ["A1", "A2"]
skills = ["clarify", "design", "review"]

[tiers]
review = "judgment"

Tune per surface with profiles

Add a harness profile (dialect) and a model profile (density) to change how skills render without touching their source. See Rendering for the transforms.

# profiles/codex/profile.toml
version = "1"
transform = "terse"

# profiles/models/frontier/profile.toml
version = "1"
transform = "trim"
tier = "frontier"

Evaluate a skill

Before you trust a new or edited skill, prove it helps. Write an eval.toml, point it at a runner, and gate on held-out scores:

bash
$ spindle eval validate examples/evaluation-sample/eval.toml
$ spindle eval run      examples/evaluation-sample/eval.toml
$ spindle eval show     examples/evaluation-sample/receipts/<receipt>.json

The full contract — the runner's env vars, the result JSON, and the promotion gate — is on the Evaluation page. To improve a skill's prose against that same gate, use spindle optimize.

Extending Spindle

Spindle is open-core: the compose-render-materialize-evaluate machinery and the contracts are public; the infrastructure behind them is meant to be your own. The core ships local reference sinks (JSONL files under $SPINDLE_HOME) and swaps them out through environment variables and injectable callables. Private adapters depend on Spindle — never the reverse.

SeamEnv varDefault
State rootSPINDLE_HOME~/.spindle
Active distributionSPINDLE_ACTIVE_DIST_NAME / _DIRthe activated dist
Task sinkSPINDLE_TASK_QUEUE / SPINDLE_TASK_URLlocal JSONL queue
Gate sinkSPINDLE_GATE_QUEUElocal JSONL queue
Scout runnerSPINDLE_SCOUT_COMMANDillustrative default
LLM render / proposeANTHROPIC_API_KEYabsent → those steps skip, not fail

Around the core sits an optional learning & marketplace layer — scout, peers, verdict, roster, broker, gate, ingest, fleet. Treat it as a set of extension seams: discovery passes, a judgment store, a marketplace facade, decision-gate and task queues, and cross-machine ledger sync. The public base wires the contracts and offline defaults; the live providers are yours to supply.

CLI reference

The command surface, grouped by what it touches:

AreaCommands
Distributionsdist list · show · install · uninstall · activate · new
Packages / skillspackage list · show · new · skill list · show · capability list · show
Composeappclass · bind · unbind · advance run · liaison request · log
Doctrinedoctrine show · validate
Evaluate / tuneeval validate · run · show · optimize · rate
Learn / marketpeers · verdict · roster · broker · acquisitions · scout
Infra sinksingest · gate file · from-result · fleet status · sync
Housekeepingstatus · preempt · unpreempt · state show · rebuild

Run any command with --help for its flags, or read the bundle for the whole story in one file.