colonizer
install
docs Skill packs

#Skill packs

A skill pack is a directory that packages related agent skills (and optionally the tool servers those skills call) in the canonical Agent Plugins folder layout:

my-pack/
  plugin.json            manifest: what this pack is and what it ships
  mcp.json               tool servers the skills call (only when there are any)
  skills/<name>/SKILL.md one directory per skill

Settings → Skillsets switches packs on and off. It is the Claude Code module's plugins setting, a comma-separated list of names (default archify), and an org can switch individual packs on or off for its own colonies. The runner receives the list as COLONIZER_PLUGIN_DIRS (protocol.md). Each name resolves to <data>/plugins/<name>, else the vendored <COLONIZER_HOME>/plugins/<name> (a local copy wins, and the colony log says so), mounts read-only at /opt/colonizer/plugins/<name>, and loads into Claude Code as one {type: 'local', path} plugin entry. Skill packs are a Claude Code feature: the other agent modules have no plugins setting and load none.

One pack is also loaded by purpose: archify (vendored from tt-a1i/archify, MIT) is on by default, and it is added to every mapping colony's packs even where it has been switched off, because drawing the repository is that colony's whole task (docs/protocol.md, "Architecture maps"). It is a one-skill pack: upstream's skill directory, which reads its schemas and runs its own bin/archify.mjs by relative path, staged under skills/archify/ without its tests and its network update check.

#plugin.json

The manifest. Canonical readers (the validator, boot validation) prefer the root plugin.json; the Claude Code SDK reads the legacy .claude-plugin/plugin.json. superpowers and archify stage both (staging generates the root manifest), and so does the graft bundle; ecc and google-skills stage .claude-plugin/plugin.json alone, which the SDK and both checkers read as the manifest.

FieldMeaning
$schemaOptional. When present it must be an https:// URL string. Colonizer publishes no schema for this manifest, so staged packs leave it out.
namePlain, filesystem-safe pack name; conventionally the directory basename, but not required to match it.
versionSemver (1.2.3, with optional prerelease/build).
descriptionNon-empty: what the pack is for.
skillsArray of skill names the pack ships. Each must have a skills/<name>/SKILL.md. A legacy directory-style entry ("./skills/", as upstream ecc ships) means every skill under that directory; both checkers accept it, as is_skill_tree in crates/colonizer/src/plugins.rs does.

The boot checker requires only that the manifest parses as an object and that every listed skill exists; name, version and description are held to the rules above by scripts/validate-plugins.mjs (see Validation).

Version pins live in vendor/vendor.lock as the sha256 of the upstream archive. Nothing adopts a new pin on its own. The daily vendored-plugin updater (.github/workflows/vendored-plugin-updates.yml) proposes every upstream move, patch or not, as a pull request (or, where Actions may not open one, a stand-in issue) that lists the skills added, removed and changed, and a person merges it: new or changed skills change what a colony does.

#mcp.json

Only when the pack ships tool servers. An object of server entries (or one under mcpServers/servers); each entry is either local or remote:

The harness validates mcp.json but does not start its servers itself. superpowers, archify, google-skills and the graft bundle ship none (archify's and google-skills' staging fails if one appears).

#skills/<name>/SKILL.md

One directory per skill, named for the skill. SKILL.md is a Markdown file with a leading --- frontmatter block and a body:

Skill names must be unique across every enabled pack. A collision blocks the colony's boot with an error that names both packs: the model addresses a skill as <pack>:<name>, and two packs answering to the same name are ambiguous by construction.

#Validation

Two checkers, with different reach:

#Minimal example

plugin.json:

{
  "name": "my-pack",
  "version": "1.0.0",
  "description": "Example pack: one skill, one local tool server",
  "skills": ["my-skill"]
}

mcp.json:

{
  "mcpServers": {
    "my-server": {
      "command": "my-server",
      "args": ["--stdio"],
      "env": { "MY_SERVER_MODE": "readonly" }
    }
  }
}

skills/my-skill/SKILL.md:

---
name: my-skill
description: Use when the colony needs an example worked through.
---

# My skill

Do the example thing, then stop.

#Vendored packs

#ponytail

ponytail (v4.12.0, MIT) is a skill pack that makes an agent stop at the first rung that holds before it writes code: does this need to exist at all, is it already in the codebase, the standard library, a native platform feature, an installed dependency, one line — and only then the minimum that actually works. It does not ask for that discipline at the expense of correctness: validation, error handling, security and accessibility stay in, and a reviewer-flagged over-cut is the failure mode to watch for.

Skills only. Six skills are staged, each one SKILL.md: ponytail, ponytail-review, ponytail-audit, ponytail-debt, ponytail-gain, ponytail-help. Upstream's hooks/, ponytail-mcp/, commands/, the per-agent folders it ships for other harnesses and its benchmark assets are deliberately not staged. Staging is an allowlist rather than a copy with holes: the staged pack is asserted to hold only .claude-plugin/, LICENSE, plugin.json and skills/, so scripts/fetch-vendor.sh fails if any of them survives. The skills are instructions an agent reads; a hook or an MCP server would be third-party code executing inside a colony, which is the one thing no vendored pack is allowed to do. Neither upstream manifest is copied either — the root plugin.json is a bare {"name": "ponytail"} that fails validation for want of a version and a description, and .claude-plugin/plugin.json declares a hooks path to a hooks/ nothing stages — so staging generates both. The six names are an allowlist too: a seventh upstream skill fails staging until a maintainer adds it to scripts/fetch-vendor.sh and to the vendor.lock comment.

Off by default. An operator switches it on in Settings → Skillsets, by adding ponytail to the Claude Code module's plugins list (comma-separated names; archify is the default), or per-org with the workspace's agent.skillsets override. It is not in the default, and nothing turns it on by itself. In a colony the skills are addressed as ponytail:<name> — ponytail:ponytail, ponytail:ponytail-review and so on.

Its pin. The upstream release and archive sha256 live in vendor/vendor.lock as the ponytail plugin entry, like every other vendored pack, and the daily vendored-plugin updater proposes each move as a pull request; see Plugin directories. Its cost-saving claim is upstream's own benchmark, not Colonizer's: see Token savings and the bench protocol in The bench for what has and has not been measured here.

#Downloadable skillsets

Some skillsets are too big, or carry native code, to ship in every release. Those are downloadable: Settings → Skillsets lists them with a Download button, the mothership fetches a bundle pinned by sha256 and unpacks it to <data>/plugins/<name>, and from then on it is an ordinary local skillset — the same resolution, validation and read-only mount at /opt/colonizer/plugins/<name> as any other, switched on with the same toggle. Nothing is fetched until someone asks, and an operator's own plugins/<name> directory is never overwritten (the download refuses with 409, and the row says the local copy is used instead). The archive is checked against its sha256 and validated as a skillset before it replaces anything, so a colony booting during a download mounts either the whole old bundle or the whole new one.

#graft

graft (@nanonets/graft, MIT) builds a code map of a repository — files, symbols and call edges — and answers graft ask "<task>" --source, graft grep, graft callers <symbol> --depth all, graft skeleton <file> and graft map with exact file:line. The skillset's SKILL.md teaches the agent to reach for it before grepping and reading files.

Downloaded when asked. crates/colonizer/graft.lock pins the published graft-0.19.0-1 release for linux-x86_64 and linux-aarch64, one row per colony architecture. On an architecture no release pins, the Skillsets row says the skillset is not published for this machine yet, GET /api/plugins/graft reports unavailable, and a download request is refused with 409. An operator can still place their own plugins/graft directory.

Pincrates/colonizer/graft.lock, one row per colony architecture (linux-x86_64, linux-aarch64), compiled into the mothership
Bundlegraft/ with the plugin (.claude-plugin/plugin.json, plugin.json, skills/graft/SKILL.md), bin/graft, node/bin/node (Node 22, vendor/graft/node.lock), runtime/node_modules (npm ci from vendor/graft/package-lock.json), BUNDLE.json, LICENSE — about 80 MB compressed
Built by.github/workflows/graft-bundle.yml on a graft-<graft version>-<build> tag (native runners, node:22-bookworm, smoke-tested by scripts/graft-bundle/smoke.sh); workflow_dispatch is a dry run that only uploads artifacts. Locally: scripts/build-graft-bundle.sh in a microVM
APIGET /api/plugins/graft: {name, release, installed_release, state, bytes, total, started_at, finished_at, error}, where state is idle, downloading, unpacking, installed, failed, unavailable (nothing pinned for this architecture) or local (plugins/graft is the operator's own). POST /api/plugins/graft/download starts or joins the download and returns the same object at once; 409 for local and unavailable. GET /api/plugins carries the same object in its downloadable array. All three need an owner token

Why its own Node. graft 0.19 imports the native tree-sitter 0.21 core at startup, and that binding does not compile against Node 24's V8 headers — the major node-guest pins for colonies. Several grammars also publish no linux-arm64 prebuild (tree-sitter-kotlin none at all), so the bundle compiles them against the Node 22 it carries, inside Debian bookworm (the colony image's glibc), and bin/graft runs graft with that Node, never the colony's.

In the colony. The mount is read-only, so bin/graft keeps the graph under /var/tmp/colonizer-graft (COLONIZER_GRAFT_STATE overrides it) — outside the worktree, never in a commit — and builds it on the first query that finds no graph (graft build, the free pass: no model). Every later query keeps it in sync with the agent's edits. It runs offline by construction: ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, OPENAI_API_KEY, GRAFT_API_KEY and GRAFT_BASE_URL are unset for it (only graft build --deep would use a model), DO_NOT_TRACK=1 and CI=1 close its telemetry, and its background registry version check is answered from a cache that never expires.

Updating graft. Bump @nanonets/graft in vendor/graft/package.json, regenerate the lock (npm install --package-lock-only --ignore-scripts), push a graft-<version>-1 tag (the workflow refuses a tag whose version differs from vendor/graft/package.json), and pin the release's SHA256SUMS in graft.lock by pull request, one row per architecture. A pinned release newer than what is on disk shows the Download button again.

#understand-anything

understand-anything (MIT, by Egonex) is the second downloadable skillset: a knowledge graph of the repository that answers what a change would affect, shows a domain and business-flow view, and gives onboarding tours. Same shape as graft — crates/colonizer/understand-anything.lock pins one release by commit and sha256, Settings → Skillsets downloads it, a download is checked fail-closed before it unpacks anything, and the switch is the operator's. In a colony the full-repository pass and the two skills that need network or a writable plugin root are refused, so colonies use the file-scoped commands only. docs/understand-anything.md has the API, the pin and the cost guard.

#Reference migration: superpowers

superpowers (obra/superpowers, v6.4.2, MIT) is the first pack on the canonical layout, staged by scripts/fetch-vendor.sh at dist/plugins/superpowers:

To verify: validate the staged pack (node scripts/validate-plugins.mjs dist/plugins/superpowers), enable the pack, and check the agent lists its skills.