Authoring a Skill
Authoring a Skill for BoB
Section titled “Authoring a Skill for BoB”How to take a skill from idea to provisioned-into-a-project inside BoB. This guide is
the BoB-integration layer on top of the skill-creator
skill: skill-creator owns the build mechanics (scope → plan → scaffold → edit →
package → iterate); this guide owns the BoB-specific wrapper around it — which surface
to choose, where a finished skill lives, how it gets provisioned and promoted,
and what to check before it lands.
Don’t duplicate
skill-creator. When you reach the “write the SKILL.md” step, invoke/skill-creator(or read its SKILL.md) and follow it. This guide deliberately says nothing about anatomy, progressive disclosure, or degrees of freedom — those areskill-creator’s job.
1. First decide: skill, command, or agent?
Section titled “1. First decide: skill, command, or agent?”BoB extends Claude Code through three surfaces. Picking the wrong one is the most common authoring mistake, so resolve this before you build anything.
| Surface | What it is | Reach for it when… | Lives in |
|---|---|---|---|
| Skill | Auto-loaded procedural knowledge — a workflow, convention, or domain expertise Claude pulls in on its own when the description matches. | The capability is knowledge or a repeatable workflow that should activate implicitly whenever the task matches, with no explicit user invocation. | skills/ (universal) or registry/skills/ (provisioned) |
| Command | A user-invoked /slash entry point — an explicit, named action. |
The user should deliberately trigger a discrete operation by name (and it’s fine — or better — that nothing happens unless they ask). | commands/ (universal) or registry/commands/ (provisioned) |
| Agent | A specialized Task subagent with its own context, model, and tools. | The work needs isolated context or a distinct role/model — fan-out, an adversarial reviewer, a long search that shouldn’t pollute the main thread. | agents/ (universal) or registry/agents/ (provisioned) |
One concrete example of each:
- Skill —
timelord: whenever a doc references “today” or a relative date, Claude should automatically verify the real date. No one types/timelord; it activates on description match. → a skill. - Command —
/start-work: the user explicitly decides “begin a new branch for this issue now.” It’s a deliberate, named action with side effects. → a command. - Agent —
code-reviewer: a review should run in its own context with a critical role and its own model budget, returning only its verdict to the main loop. → an agent (authored via theagent-builderskill, the agent analogue ofskill-creator).
Rule of thumb: implicit knowledge → skill, explicit action → command, isolated role →
agent. When a feature wants both an implicit workflow and a /slash entry point, that’s
two artifacts (a skill plus a thin command that invokes it), not one overloaded skill.
2. Build it
Section titled “2. Build it”Author the skill with skill-creator — that is the entire build step, and this guide does
not repeat it:
Slash command only — runs inside a Claude Code session (skill-creator is a skill with no terminal entrypoint):
/skill-creatorBuild it in the registry (registry/skills/<name>/) unless it is genuinely universal
(needed by every project — like timelord or commit), in which case it goes in
skills/. When in doubt, author it in registry/ — promoting registry → universal later
is cheaper than discovering a “universal” skill only one project actually uses.
3. Register it into BoB
Section titled “3. Register it into BoB”A finished skill directory is just files until BoB knows about it. Registration is two moves, depending on where it lives:
Universal skill (skills/)
Section titled “Universal skill (skills/)”Universal skills auto-load globally — no manifest, no symlink. They reach the runtime
(BOB_HOME, ~/.claude/) when you deploy:
Terminal only — deploy.sh is a shell script with no slash equivalent:
scripts/deploy.sh --dry-run # preview the syncscripts/deploy.sh # sync BOB_SOURCE → BOB_HOMERegistry skill (registry/skills/)
Section titled “Registry skill (registry/skills/)”Registry skills are not auto-loaded — they are provisioned per project. Two steps:
- Sync the source into the runtime so the registry copy exists in
BOB_HOME(Terminal only —deploy.shis the shell sync mechanism, no slash equivalent):Terminal window scripts/deploy.sh - Add orchestrator metadata to the skill’s frontmatter so the recommendation engine
(
scripts/orchestrator/recommend.js) can suggest it to the right projects:A skill for a specific product or vendor (Eloqua, OpenReplay, a particular SaaS) must setapplies_to:stacks: [cloudflare-workers, d1] # stacks this skill is forproject_types: [api, backend]recommended_for: [development, ops]category: backend-runtimerequired_with: [skills/api-designing] # optional co-requisitesopt_in: true # only for product/vendor integrations — see belowopt_in: true(#1980): the stack cannot tell whether a project uses that product, so the engine never auto-recommends it and it is provisioned only by an explicitbob provision add(a declared entry is kept, never a would-remove). Leave the flag off for skills the stack genuinely implies (cloudflare-devfor a Workers project). This is what lets/provisionand the orchestrator answer “is this skill applicable to this project?” — see the Orchestrator Guide.
4. Provision it into a project
Section titled “4. Provision it into a project”Registry skills only become active in a project that declares them. Both paths manage the same manifest — pick whichever you’re in.
Slash command:
/provision add skill <name>Terminal: from the target project,
bob provision add skills/<name> # add to provisions/<project>.json + symlink it inbob provision status # confirm it's linked (and not duplicating a global item)bob provision refresh # reconcile all symlinks to match the manifestThe manifest at provisions/<project>.json is the single source of truth for which registry
items a project loads; bob provision only ever reconciles symlinks to match it (declarative,
idempotent — re-run freely).
Global-aware (#412): if the skill is already global for this machine,
bob provisionde-dupes it rather than double-provisioning; if a registry skill’s name collides with a global one but diverges,bob provision statusreports it as a conflict with a resolution hint.
5. Promote a project-local skill upward
Section titled “5. Promote a project-local skill upward”If you authored a skill inside one project and want it available to BoB globally, promote it into the source of truth rather than copying files by hand:
Terminal only — bob promote is a shell alias for promotion; there is no slash equivalent:
bob promote # promote a local item to the global source (BOB_SOURCE)After bob promote lands the skill in BOB_SOURCE (in skills/ or registry/skills/), run
scripts/deploy.sh to sync it to the runtime, then bob provision in any project that should load
it. (bob link does the inverse — link an existing global item into a project without promoting.)
The full path, end to end:
author (registry/skills/) → deploy.sh → cdprov add (in project)project-local skill → cdp (promote) → deploy.sh → cdprov add6. Pre-add validation checklist
Section titled “6. Pre-add validation checklist”Run this checklist before a skill is promoted into BoB (bob promote) or added to a manifest
(bob provision add). It is the gate the promotion flow references — a skill that fails any line
is not ready to land.
- Frontmatter complete —
nameand adescriptionthat states what the skill does and when to use it (the description is the only thing Claude sees when deciding to auto-load — vague descriptions never fire). - Triggers are concrete — the description names the situations/phrases that should activate the skill, not just its topic. “Use when X, Y, or Z” beats “knowledge about X.”
- Kebab-case naming — directory and
nameare lowercase kebab-case and match each other (skill-authoring, notSkillAuthoringorskill_authoring). - Idempotent — any bundled
scripts/are safe to re-run; the skill describes a repeatable workflow with no destructive side effects on a second invocation (the Prime Directive — seeskill-creator’s IaC principle). - No overlap — it doesn’t duplicate an existing skill. Check
skills/andregistry/skills/first; if an existing skill is close, extend it instead of forking. - Orchestrator metadata (registry skills only) —
applies_to/recommended_for/categoryset, so the recommendation engine can place it;opt_in: trueif it is a product/vendor integration the stack alone cannot justify (#1980). - Right surface — re-confirm §1: this is genuinely implicit knowledge, not a disguised command or agent.
Security gate
Section titled “Security gate”Every item that enters the registry is scanned by
NVIDIA SkillSpector (#1328), static-only
(--no-llm): deterministic, no API key, and no item content leaves the machine.
The gate augments human review at promotion — it never replaces it. A clean scan
does not mean the checklist above is done, and a reviewer still reads the item.
An item enters the registry one of two ways, and each is scanned:
bob promoterunsskillspector scan <item> --no-llmbefore it copies anything. A risk score over SkillSpector’s threshold refuses the promotion and leaves the local file in place;CAUTIONwarns and promotes; a machine without SkillSpector says so and promotes, with the CI gate as the backstop.- A PR runs the
skillspectorjob in CI on every changed item underskills/,commands/,agents/orregistry/. It fails only on a new finding: anything already reviewed is listed in the committed.skillspector-baseline.yaml. Until that file is committed, the job only reports.bob provision addlinks items that are already in the registry, so the CI gate covers it.
The engine is scripts/security/skillspector-gate.sh (check, items, scan,
baseline, bootstrap); it pins SkillSpector to one commit and uv to one checksummed
release. To scan an item yourself:
scripts/security/skillspector-gate.sh bootstrap ~/.cache/skillspector # prints the bin dirskillspector scan registry/skills/<name> --no-llmA new finding in your PR: fix the item, or — if the finding is a false positive or an
accepted risk — add an entry to .skillspector-baseline.yaml with a reason that says
why, in the same PR. BoB’s autonomous-loop skills legitimately trip the “excessive
agency” rules, which is what the baseline is for. Never add a broad glob rules: entry
to quiet a finding: a rule survives future edits and can hide new malicious content.
Regenerating the baseline: run the SkillSpector baseline sweep workflow
(gh workflow run skillspector-baseline.yml), download its skillspector-baseline
artifact, review every entry, and commit it. Do this whenever SKILLSPECTOR_REF is
bumped: exact fingerprints bind the scanner version and stop matching after a bump.
7. Retire, demote or promote an item
Section titled “7. Retire, demote or promote an item”Every project that linked an item keeps a symlink to it. Moving or deleting the
item in BOB_SOURCE therefore needs one more step, or the next bob ready in
every consuming project finds a dangling link it cannot explain (#2314):
- Move or delete the item —
registry/<type>/↔<type>/for a demotion or promotion; delete (and tag, per the archive procedure) for a retirement. - Record it in
scripts/lib/retired-items.json— one entry withtype,name,from(where it used to live),disposition(removed·superseded-by+replacement·moved-to-registry·moved-to-universal) andretired_by(the issue). This is required:make check-retired-itemsfails on any item path git history has seen that is absent without a record, and names the exact entry to add. make check— the gate is part of it.- Deploy. Consuming projects converge on their next
bob readyorbob provision refresh: a link to a promoted item is retargeted, one to a demoted item is retargeted when the project declares it and otherwise pruned with abob provision add <type> <name>hint, one to a retired item is pruned naming the disposition — each action named, nothing unknown deleted. The fleet snapshot’sbroken_linksper project confirms it per machine.
A promotion is the mirror of §5 and takes the same record (moved-to-universal).
The reference for the file, its gate and what derives from it is
Retired Items.
A promotion is a move, never a copy (#2735). Leaving the old file behind creates a name that exists on both sides — and the two sides resolve differently per project:
bob initlinks the universal copy,bob provisionlinks the registry one, and the registry link shadows the universal one where a manifest declares it./requirementsat like that for most of this repo’s life: the two files drifted into different commands, 15 projects ran one and 5 ran the other, and because_bob-home.farm-worker.jsonprovisions its commands, unattended farm runs and attended laptop sessions executed different instructions under the same name.make check-item-collisionsnow refuses the shape — delete the old file in the same commit as the move.
See also
Section titled “See also”skill-creator— the build mechanics this guide wraps.agent-builder— the agent analogue, when §1 points you to an agent.- Orchestrator Guide — how
applies_tometadata drives provisioning recommendations. - Project Setup Guide —
bob init/bob provisionwiring for a project.