-
Notifications
You must be signed in to change notification settings - Fork 2
Extending FeatherSpec
Adding to FeatherSpec means adding Markdown files. There is no registry, no manifest and no
build step — the only mutable fact is a single FeatherSpecVersion: line in AGENTS.md's
managed settings block (see Updating & Versioning).
A command needs two files that share the same stem:
1 · The body — .claude/commands/sdd-mytask.md
---
description: One-line summary shown in the / menu.
argument-hint: "[thing]"
disable-model-invocation: true
---
<!-- Single source for the /sdd-mytask workflow. Claude Code runs this file directly;
GitHub Copilot reaches it through the one-line loader in
.github/prompts/sdd-mytask.prompt.md. Deliberately no shell injection and no
argument-variable substitution: Copilot supports neither. -->
# /sdd-mytask
Do something. The user may name a thing after the command; if none is given, list the
candidates and ask.2 · The Copilot loader — .github/prompts/sdd-mytask.prompt.md
---
name: sdd-mytask
description: One-line summary shown in the / menu.
argument-hint: "[thing]"
---
# /sdd-mytask
Follow [`.claude/commands/sdd-mytask.md`](../../.claude/commands/sdd-mytask.md) exactly — read
that file first and treat it as your instructions for this turn. It is the single source for
this workflow; this file only points at it and holds no rules of its own.
Anything the user typed after the command is the argument the workflow refers to.The loader is the same three fields plus name:, and a single line pointing at the body. It
never holds a rule of its own.
Invoked as /sdd-mytask in both tools. Claude Code picks up new command files without a
restart; VS Code may need a full restart, not just a window reload.
One naming caveat: the template's shipped sdd-* filenames are reserved.
/sdd-featherspec-update never touches files it does not ship, so your own commands are
invisible to it — but if a future release ships a command with your chosen name, the update
surfaces a conflict with a rename mine option (renames body, loader and table row
together). A project-specific prefix sidesteps the collision entirely.
Because both tools execute the same text, the body is written to the lowest common denominator:
| Don't | Do instead |
|---|---|
!`git status` |
"Run git status --short and use the result." |
$ARGUMENTS, $0, $1
|
"The user may name X after the command; if none is given, ask." |
Naming a tool (WebFetch, #fetch) |
Name the capability: "your web search or fetch capability" — and say what to do when it is missing |
| Assuming a terminal tool name | State the command to run |
Keep the HTML comment at the top. It is what stops the next person reintroducing Claude-only syntax. See Design Decisions.
If your command changes DocLanguage, the architecture: snapshot or a style preference, it
must write to AGENTS.md — that is the only place those live. Never introduce a second store.
A rule needs two files, like a command: the rule itself and a thin Copilot loader. VS Code
cannot read instruction files from .claude/rules/, so the shipped .vscode/settings.json
points it at .github/instructions/ — a rule without its loader simply never reaches Copilot.
1 · The rule — .claude/rules/mytopic.md:
---
paths:
- src/**/*.ts
---
# My topic rules
- Keep it concise.2 · The Copilot loader — .github/instructions/mytopic.instructions.md, whose applyTo:
mirrors the rule's paths: globs (a declared exception in AGENTS.md; keep the two in sync):
---
applyTo: "src/**/*.ts"
description: "My topic rules (thin loader)."
---
Follow [`.claude/rules/mytopic.md`](../../.claude/rules/mytopic.md) — read that file now and
apply its rules to the matching files you are working on. It is the single source for these
rules; this loader only points at it and holds none of its own.paths: is a YAML list, even for a single entry. The rule loads when a matching file is
read — which also means it may not be loaded when a file is being created. If your rule
must apply at creation time, state the essentials inline in the command that creates the file,
the way /sdd-specify and /sdd-plan do, and note why.
Omitting paths: makes the rule always-on (applyTo: '**' is the loader's equivalent).
Prefer a scope: an always-on rule belongs in AGENTS.md instead.
| It is… | Put it in |
|---|---|
| policy — a lifecycle, a file set, a non-negotiable | AGENTS.md |
| craft — how to write files of a particular kind | .claude/rules/<topic>.md |
| mutable state — language, architecture, style preferences |
AGENTS.md, and nowhere else |
FeatherSpec ships no skills on purpose: both tools read every skill's name and description up front, so each skill costs context on every request.
Add one only when a workflow genuinely needs the model to trigger it on its own, without you
typing anything. Both tools read .claude/skills/<name>/SKILL.md, so a skill is shared without a
loader.
.github/agents/<Name>.agent.md is a Copilot-only persona you pick from the agent dropdown.
Claude Code has no persona picker — its equivalent is AGENTS.md plus /sdd-overview.
Do not move a persona file into .claude/agents/ expecting the same behaviour: in Claude Code
that folder defines an isolated subagent with its own context window, not a persona for your
conversation.
See Configuration — including the VS Code caveat that hook matchers are
ignored, so a shared hook must filter on tool_name inside the script.
Start here
Reference
Design & interop
- Interop Matrix
- Design Decisions
- Specify Method
- Committing to One Tool
- Migrating from the predecessor
- Releasing
Help