13 Commits
Author SHA1 Message Date
Defame1297 5b80f305e9 test(kyberforge): pin the provenance checker's three new behaviours
27a7669 changed validate-provenance.sh in three ways and tested none of
them: the bullet-form parser returning None rather than [] on unparsable
input, the source_keys: [] house-authored declaration, and the stripping
of section annotations off a Research doc path.

All three are checks that previously failed by staying silent, which is
the failure mode this repo keeps rediscovering. Shipping them untested
left nothing to catch a regression back to silence.

The 22 cases assert consequences rather than return values -- that check
8 runs on an unparsable block, that an unresolvable path emits an INFO
naming its slug -- and each was verified against a deliberate mutation
of the behaviour it covers.

Addresses #111.
2026-08-31 08:28:25 +00:00
Defame1297 8599b4a058 chore(release): bump the marketplace version to 0.5.0
0959291 bumped all six plugin versions for the ADR-0020 retrofit but left the
marketplace itself at 0.4.5, so consumers resolving against the marketplace
entry would see no change despite every packaged plugin having moved.
2026-08-31 08:02:49 +00:00
Defame1297 ee248ff5a5 docs: describe the boundary check's new outcomes and de-pin stale counts
The gate spec still described the routing-target check as pass/fail against a
single-arrow clause. It now documents the three outcomes, the hand-invocation
carve-out and the one-arrow rule, so a contributor hitting a SUGGESTION can
tell whether it is a real defect or accepted phrasing.

Both files cited skill counts and source line numbers that go stale on the
next edit and were already wrong; those citations are removed rather than
refreshed.
2026-08-31 08:02:44 +00:00
Defame1297 131b89733b fix(apm-orchestrate): correct the marketplace add-package direction and trim duplication
`apm marketplace package add` rejects a local path — it registers a remote
package reference — but the agent routed local-package registration to it, so
that dispatch could only fail. Local registration is a manifest edit and now
routes to `edit-config`.

The agent also carried its own copy of the `type:` guidance and a numbered
workflow that restated the dispatch procedure below it; both drift from
apm-workflow independently. `type:` correctness is delegated where it belongs,
and the description is rewritten to a trigger plus the apm-install boundary
rather than a restatement of the body.

Addresses #120.
2026-08-31 08:02:38 +00:00
Defame1297 dac9cad912 refactor(kyberforge): move audit guidance out of the per-run rubric load
skill-audit loaded roughly 4,268 words of rubric on every run, most of it
criteria for findings a clean skill never triggers. The auditing guidance moves
into finding-criteria.md, read only when a finding is actually raised, cutting
a clean audit to about 999 words. The named-skill exemption is replaced with
properties, so the rubric stops carrying a list that ages the moment a skill
is renamed.

apm-workflow's `type:` trap sat in one flow while biting several, so it is
promoted to a common gate reachable from all of them; its claim to be
self-contained was untrue once it started routing to apm-install. skill-author's
contract had drifted from body-discipline.md and is realigned, and agent-audit's
field inventory is brought in line with the same split.
2026-08-31 08:02:19 +00:00
Defame1297 e869912374 docs(bin): give every bin skill a README
The bin skills were the only plugin without per-skill READMEs, so a reader
had to open SKILL.md — an agent-facing contract, not an explainer — to learn
what a skill does and when it fires. Each README states purpose, triggers and
boundaries for a human audience, leaving SKILL.md free to stay terse.
2026-08-31 08:02:09 +00:00
Defame1297 03abcffb77 refactor(bin): cut per-invocation load and repair broken skill references
diagnose read its feedback-loops reference unconditionally, so every
invocation paid for guidance most runs never used; the read is conditional
again and the per-invocation cost drops from 1,278 to 831 words. Its HITL
template moves to assets/ because it is copied out, not read as reference.
prototype's two branch flows move into references/ for the same reason —
only one branch is ever taken.

research could not search the codebase it was asked to research without Grep
and Glob. caveman's description had grown into a paragraph where one sentence
carries the trigger. Four references pointed at things that do not exist: a
to-prd skill, a /setup-matt-pocock-skills command, two cross-skill ../ links
that only resolve in the source tree, and two places calling this project's
Gitea host GitHub.

Addresses #114.
2026-08-31 08:02:03 +00:00
Defame1297 1c3af75642 fix(core): make --no-import-syntax actually change the adapter validation
Both branches of the flag reduced to the same expression, so the option was
inert: a caller who asked for the no-import form got the import-form check
anyway and a passing result that meant nothing.

Two further defects in the same validator: --max-lines failed silently when
given a value it could not use, and the Fix text told the agent to edit
AGENTS.md when the offending content is the provider adapter's. The boundary
clauses now name the operation being routed rather than the file type, which
was ambiguous where both skills touch the same file.

Addresses #115.
2026-08-31 08:01:45 +00:00
Defame1297 14af50bc07 fix(git): scope git-workflow's trigger to ambiguity rather than to its domains
git-workflow's description enumerated the six domains it exists to route away
from, so it competed for selection with the very skills it should be handing
off to. It now triggers on the case it actually serves: an interactive request
whose domain is not yet clear.

Also clears frontmatter drift across the plugin and removes duplicated guidance
in pc-run that had diverged from its reference.
2026-08-31 08:01:39 +00:00
Defame1297 8680adf4c0 fix(gitea): make gitea-releases executable and correct misleading domain claims
gitea-releases was the weakest skill in the plugin: no allowed-tools, no
owner/repo resolution, and a checkbox list where a dispatch table belongs, so
an agent reaching it had to guess both its permissions and its inputs. The
id-vs-tag_name trap — deleting by tag name where the API wants the numeric id —
is restored as an explicit Gotcha because it destroys the wrong release
silently.

Elsewhere the `exclusive` flag was documented on the wrong side of the
read/write split, and label data from one instance was presented as though it
were universal, which invites an agent to assume a taxonomy that does not
exist on the target repo. rename_branch was missing from the branch surface.
Reference prose and fences are cleaned up in passing.
2026-08-31 08:01:33 +00:00
Defame1297 b07d54ad7a fix(lint): correct four Vale behaviours the skills described wrongly
Each of these would send a user down a path Vale does not support:

Core options placed under a glob header are not scoped to that glob — Vale
rejects them with E201, so the guidance to nest them produced a config that
will not load. The built-in `Vale` style is compiled in, but Vale still
requires StylesPath to exist on disk before it will run, so the "no StylesPath
needed" shortcut fails. The MDX guidance was inverted: under `[formats]
mdx = md` the mapping is what makes MDX lint at all, and it needs the mdx2vast
prerequisite that was never mentioned. And a spelling rule's `ignore` paths
resolve against StylesPath, not against the rule file's own directory, so the
documented relative paths silently matched nothing.
2026-08-31 08:01:26 +00:00
Defame1297 27a76692b0 fix(kyberforge): stop the provenance checker skipping check 8 on unparsed input
484357a taught the Contributing parser the bullet form, but a block it still
could not parse returned the same empty result as an explicit "(none)", so the
checker read "no contributing files" and skipped check 8 rather than reporting
that it could not tell. Checks 7 and 8 were consequently dead across the whole
git plugin without anything failing.

The parser now distinguishes "declared none" from "could not parse", which
wakes both checks. Because the parser is duplicated between the skill-audit and
agent-audit copies, it is fenced with BEGIN/END markers and a test hashes the
two regions so the copies cannot drift apart again silently.

Addresses #111.
2026-08-31 08:01:18 +00:00
Defame1297 db5a426416 fix(gates): make the ADR-0020 boundary check parse what skills actually write
The routing-target check understood only a single-arrow clause naming a bare
skill, so most real boundary prose was silently skipped rather than verified.
Two of those silences were fail-open: an unrecognised token following a target
dropped that target from the check entirely, and a skill directory with no
SKILL.md still resolved as a valid routing target, so a broken route passed.

Multi-target arrow clauses now draw a SUGGESTION instead of being ignored,
hand-invocation phrasing is carved out so it is not read as a route, and a
dotted filename parses into a new `unparsed` status rather than disappearing.
Three test fixtures had been relying on the SKILL.md-less directory resolving
as a target; they are corrected alongside the check.

Addresses #107, #108, #110.
2026-08-31 08:01:07 +00:00
210 changed files with 4954 additions and 1014 deletions

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "holocron", "name": "holocron",
"description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.", "description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.",
"version": "0.4.5", "version": "0.5.0",
"owner": { "owner": {
"name": "Defame1297", "name": "Defame1297",
"email": "[email protected]", "email": "[email protected]",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "holocron", "name": "holocron",
"description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.", "description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.",
"version": "0.4.5", "version": "0.5.0",
"owner": { "owner": {
"name": "Defame1297", "name": "Defame1297",
"email": "[email protected]", "email": "[email protected]",
+1 -1
View File
@@ -36,7 +36,7 @@ Fall back to raw shell only when no skill covers it.
- **Do not add repo-owned keys to `.claude/settings.json`.** apm treats it as its own deployed artifact and `apm audit --ci` replays the install and diffs, so anything apm would not have written is permanent drift that fails the `apm-audit-ci` pre-push hook. A hook you want here is authored in `plugins/<name>/.apm/hooks/` and deployed by apm, never hand-written into that file. The `SessionStart` entry already in it is exactly that: kyberforge authors it in `plugins/kyberforge/.apm/hooks/hooks.json` and apm merges it in, so it is apm's own output, it is what the replay expects, and it belongs in the commit — do not strip it (ADR-0019). Machine-specific settings go in the gitignored `.claude/settings.local.json`; shared enforcement goes in `.pre-commit-config.yaml`. - **Do not add repo-owned keys to `.claude/settings.json`.** apm treats it as its own deployed artifact and `apm audit --ci` replays the install and diffs, so anything apm would not have written is permanent drift that fails the `apm-audit-ci` pre-push hook. A hook you want here is authored in `plugins/<name>/.apm/hooks/` and deployed by apm, never hand-written into that file. The `SessionStart` entry already in it is exactly that: kyberforge authors it in `plugins/kyberforge/.apm/hooks/hooks.json` and apm merges it in, so it is apm's own output, it is what the replay expects, and it belongs in the commit — do not strip it (ADR-0019). Machine-specific settings go in the gitignored `.claude/settings.local.json`; shared enforcement goes in `.pre-commit-config.yaml`.
- **`apm.lock.yaml` turning up modified is expected, not a bug.** kyberforge's `SessionStart` hook runs `apm outdated` at startup and `apm update --yes` when something is behind, which rewrites the lock. Commit or discard it deliberately. - **`apm.lock.yaml` turning up modified is expected, not a bug.** kyberforge's `SessionStart` hook runs `apm outdated` at startup and `apm update --yes` when something is behind, which rewrites the lock. Commit or discard it deliberately.
- **A `.apm/` edit is not live in this session until it is pushed.** The six dependencies resolve from the holocron remote, unpinned against the default branch. `apm install` deploys from the lock; `apm update` is what re-resolves refs. - **A `.apm/` edit is not live in this session until it is pushed.** The six dependencies resolve from the holocron remote, unpinned against the default branch. `apm install` deploys from the lock; `apm update` is what re-resolves refs.
- **The ADR-0020 skill gates ship hot, with no baseline — and the corpus is now clean.** All 39 skills clear both FAIL tiers: no description over 400 characters, no body over 900 words (counted body-only). Issue #99 retrofitted them plugin by plugin — `kyberforge` was the last plugin wave, followed by corpus-wide passes and two rounds of independent audit fixes. Because nothing is grandfathered, the gates now bite on first commit — a new skill, or an edit that pushes a description past 400, is blocked until it complies. **No routing target dangles**, and `tests/test-adr0020-targets.sh` pins that set as empty, so a new boundary clause naming a non-existent skill fails the suite rather than joining a backlog. Two blind spots survive: `skill-size-check` does not cover the Vale half, so `Kyberforge.CompositionNote` fires nowhere today but any new description can reintroduce it; and every `references/` file is unlinted — which matters because the contract's own remedy is to move prose *into* `references/`, out of the prose gate's reach. That blind spot has **two** independent causes and closing either alone changes nothing: the `Kyberforge` style is scoped `[**/SKILL.md]`, *and* the `vale-audit-prefilter-skill` hook filters on `files: '^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$'`, so a reference file is never handed to Vale whatever the style says (#117). Check both gates: `pre-commit run --all-files`. - **The ADR-0020 skill gates ship hot, with no baseline — and the corpus is now clean.** All 39 skills clear both FAIL tiers: no description over 400 characters, no body over 900 words (counted body-only). Retrofitted plugin by plugin under #99 (see `docs/spec/gates.md`). Because nothing is grandfathered, the gates now bite on first commit — a new skill, or an edit that pushes a description past 400, is blocked until it complies. **No routing target dangles**, and `tests/test-adr0020-targets.sh` pins that set as empty, so a new boundary clause naming a non-existent skill fails the suite rather than joining a backlog. Two blind spots survive: `skill-size-check` does not cover the Vale half, so `Kyberforge.CompositionNote` fires nowhere today but any new description can reintroduce it; and every `references/` file is unlinted — which matters because the contract's own remedy is to move prose *into* `references/`, out of the prose gate's reach. That blind spot has **two** independent causes and closing either alone changes nothing: the `Kyberforge` style is scoped `[**/SKILL.md]` (the cause #117 records), *and* the `vale-audit-prefilter-skill` hook filters on `files: '^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$'`, so a reference file is never handed to Vale whatever the style says. Check both gates: `pre-commit run --all-files`.
- **Run `bash tests/run-tests.sh --strict` before considering any change done.** Keep the flag: without it a suite whose dependency is missing exits 77 and is counted SKIPPED rather than failed, so the run goes green having verified less than it claims. - **Run `bash tests/run-tests.sh --strict` before considering any change done.** Keep the flag: without it a suite whose dependency is missing exits 77 and is counted SKIPPED rather than failed, so the run goes green having verified less than it claims.
- **Before pushing, rehearse the gate locally:** `pre-commit run --hook-stage pre-push --all-files`. It runs the 14 pre-push hooks this repo authors itself plus pre-commit's 2 `meta` hooks, so it prints 16; `check-release-needed` passes without checking anything, because it needs a real push to `main`. `docs/spec/gates.md` reconciles both. - **Before pushing, rehearse the gate locally:** `pre-commit run --hook-stage pre-push --all-files`. It runs the 14 pre-push hooks this repo authors itself plus pre-commit's 2 `meta` hooks, so it prints 16; `check-release-needed` passes without checking anything, because it needs a real push to `main`. `docs/spec/gates.md` reconciles both.
- **Pushing without a network** needs `SKIP=apm-marketplace-check,apm-pack-check-clean git push` — those two resolve a remote marketplace entry via `git ls-remote`. Skip only those two; the rest are real local checks, and adding one to `SKIP` disarms it silently. - **Pushing without a network** needs `SKIP=apm-marketplace-check,apm-pack-check-clean git push` — those two resolve a remote marketplace entry via `git ls-remote`. Skip only those two; the rest are real local checks, and adding one to `SKIP` disarms it silently.
+2 -2
View File
@@ -1,5 +1,5 @@
name: holocron name: holocron
version: 0.4.5 version: 0.5.0
description: AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows. description: AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.
license: MIT license: MIT
@@ -52,7 +52,7 @@ marketplace:
# top-level apm.yml description:/version: above are NOT inherited into the # top-level apm.yml description:/version: above are NOT inherited into the
# compiled output despite being used elsewhere (e.g. by `apm audit`). # compiled output despite being used elsewhere (e.g. by `apm audit`).
description: AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows. description: AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.
version: 0.4.5 version: 0.5.0
owner: owner:
name: Defame1297 name: Defame1297
email: [email protected] email: [email protected]
+139 -17
View File
@@ -141,7 +141,7 @@ A boundary-clause target that resolves to no skill or agent has **three** possib
| Verdict | When | | Verdict | When |
|---|---| |---|---|
| **SUGGESTION** — the default | the target does not resolve and neither promotion condition below holds | | **SUGGESTION** — the default | the target does not resolve and neither promotion condition below holds |
| **blocking ERROR** | the target is **terminal** (not a compound modifier) **and** either written in route notation (`/name` for any name; `-> name` only when the name is hyphenated — see the gap below) **or** corroborated by another target in the same sentence that *does* resolve | | **blocking ERROR** | the target is **terminal** (not a compound modifier) **and** either written in route notation (`/name` for any name; a bare `-> name` only when the name is hyphenated, a backticked `` -> `name` `` for any — see the gap below) **or** corroborated by another target in the same sentence that *does* resolve |
| **INFO, "DID NOT RUN"** | no skill universe could be determined for the path at all — the targets are named and left unchecked, exit 0 | | **INFO, "DID NOT RUN"** | no skill universe could be determined for the path at all — the targets are named and left unchecked, exit 0 |
The default is deliberately soft because a hyphenated word in a boundary clause is as likely to be a The default is deliberately soft because a hyphenated word in a boundary clause is as likely to be a
@@ -149,14 +149,25 @@ tool, a file format or an English compound as a route: "pre-commit hooks" is pro
never reaches the check at all, being a compound modifier rather than a terminal name. The never reaches the check at all, being a compound modifier rather than a terminal name. The
SUGGESTION text says how to opt in — write it as `/name` or `-> name` and it gets checked properly. SUGGESTION text says how to opt in — write it as `/name` or `-> name` and it gets checked properly.
**Known gap: the arrow form only works for hyphenated names.** Target extraction is built on **Known gap: a BARE arrow target must be hyphenated.** Target extraction is built on `NAME_HYPH` in
`NAME_HYPH` (`scripts/skill-size-check.sh:543`), which requires at least one hyphen, and `scripts/skill-size-check.sh`, which requires at least one hyphen, and `ARROW_BOUNDARY` inherits
`ARROW_BOUNDARY` (`:561`) inherits that. So `-> gitea-prs` is extracted and checked, while that. So `Not X -> gitea-prs` is extracted and checked, while `Not X -> triage` yields no target.
`-> triage` is not extracted at all — no ERROR, no SUGGESTION, exit 0. The unicode arrow `→` is not The exclusion is deliberate, not an oversight: `research`, `triage`, `forge`, `prototype` and `tdd`
recognised in either case. This makes the SUGGESTION's own advice unsafe for a single-word skill: are all real skill names *and* ordinary English, so a bare single-word rule would flag most of the
taking it silences the finding rather than checking it. `/name` has no such restriction and is the corpus. The marked spellings carry no such restriction — `` `triage` `` and `/triage` are both
form to prefer. Tracked as a defect; `tests/test-adr0020-targets.sh` has one arrow case and its extracted — and are the forms to prefer. **Both arrow spellings are recognised:** `ARROW_MARKED`,
target happens to be hyphenated, so nothing currently covers this. `ARROW_BOUNDARY` and `BOUNDARY_ARROW` are each built from `(?:->|→)`, so the unicode arrow `→`
behaves exactly like `->` in every case below. Cite these constants by symbol name, never by line
number: the script moves often enough that a pinned line lands a reader in an unrelated comment
block and reads as plausible.
**The gap is no longer silent.** It used to be exactly that — no ERROR, no SUGGESTION, exit 0 — which
made the dangling-target SUGGESTION's own advice unsafe for a single-word skill: taking it silenced
the finding instead of checking it. `boundary_clause_status()` now separates the case out and
reports it as `unparsed` (see below), naming the parse failure and the two spellings that fix it.
The target is still not *resolved*; the author is now told so rather than left with a green gate.
`tests/test-adr0020-targets.sh` covers both directions (`arrow-single-word-target` and the silent
control `arrow-single-word-marked`).
Corroboration is what makes the soft default safe: a sentence whose *other* target resolves is Corroboration is what makes the soft default safe: a sentence whose *other* target resolves is
demonstrably a routing sentence, so a sibling that does not resolve is a typo rather than a noun, and demonstrably a routing sentence, so a sibling that does not resolve is a typo rather than a noun, and
@@ -200,17 +211,109 @@ through `.claude/skills/` alone, so **the same commit measured 2 dangling target
machine and 6 on a fresh clone**. A gate shipping hot with no baseline cannot give two answers. machine and 6 on a fresh clone**. A gate shipping hot with no baseline cannot give two answers.
Verified fixed: running the hook over a tree holding only `plugins/` and the root `apm.yml`, with no Verified fixed: running the hook over a tree holding only `plugins/` and the root `apm.yml`, with no
`.claude/` or `.agents/` anywhere, produces findings identical to the working tree — **26 description `.claude/` or `.agents/` anywhere, produced findings identical to the working tree. The figures that
FAILs, 9 body FAILs, 2 dangling targets, 0 missing references, 58 SUGGESTIONs**. reproduction recorded — 26 description FAILs, 9 body FAILs, 2 dangling targets, 0 missing references
— are the pre-retrofit corpus as it stood when the experiment was run, kept here as the evidence for
the install-independence claim. They are not current: the retrofit under #99 took the first three to
zero. What the experiment establishes is that the two trees agree, not what either measured.
### Boundary-clause detection: three outcomes, not two
`boundary_clause_status()` returns one of three values, and the two findings get separate messages:
| Status | When | Reported as |
|---|---|---|
| `present` | a prose marker (`do not`, `instead`, `rather than`, `not for`) or an arrow clause was found | nothing |
| `absent` | neither was found | SUGGESTION: add a boundary clause, in either form |
| `unparsed` | an arrow clause was found and **no target could be read out of it** | SUGGESTION: the clause is present — this is a *parse* failure, not a missing clause |
The third had to be split out. Collapsing it into `absent` is a **wrong** finding, not a strict one:
it sends the author to add a clause that is already there. Three of them instead reworded a correct
clause until the regex accepted it, one stripping the very filename that discriminates the skill
from its neighbour (**#110**).
`unparsed` is narrow and certain on purpose. It fires only on the arrow form, which *always* names a
target, so zero targets means the name is written in a shape the extractor cannot see — in practice
a bare single-word target, per the known gap above, and the message says to write it `` `name` `` or
`/name`. A **prose** clause yielding no target is not reported at all: "Do not use for anything else"
is a complete and legitimate boundary clause that names nowhere to go.
**One arrow, one target.** An arrow clause naming two or more targets draws its own SUGGESTION,
quoting both names and asking for a split, because only the first is ever resolved: the conjunction
continuation (`CONT_MARKED` / `CONT_ANY`) is wired to the prose route verbs and never to arrows. So
`Not X -> a or b` resolved `a`, left `b` resolved by nothing and reported by nothing, and then let
the audit print "1 of 1 boundary target(s) resolve" on a clause naming two — a gate under-reporting
its own coverage, which is the one failure mode ADR-0020 says a gate must not have (**#107**). The
clause is **rejected rather than the arrow scan extended**: extending it would widen the resolver's
deliberately conservative false-positive tuning across every arrow in the corpus, where splitting
costs the author one full stop. The convention is one arrow per target — `Not X -> a. Not Y -> b.` —
already what every retrofitted `gitea-*` skill does in practice, now stated in
`skill-author`'s `references/contract.md` instead of being folklore.
**Dotted filenames in a boundary clause now parse.** `CLAUSE_BODY` — what may sit between `Not` and
the arrow — used to be `[^.;]`, a class that cannot cross a `.`, so every clause naming a dotted
filename between the two (`AGENTS.md`, `.vale.ini`, `.pre-commit-config.yaml`) was invisible to both
`BOUNDARY_ARROW` and `ARROW_BOUNDARY`. The two resulting failures were different sizes (**#110**):
- with a **backticked** target the clause was *misdiagnosed*. The backtick sweep still extracted the
target, so the route was checked, but the gate reported "no boundary clause" on a clause that was
present and working. That is the misdiagnosis the three rewordings above came from.
- with a **bare** target the clause was *unchecked*. `ARROW_BOUNDARY` is the only extractor for a
bare arrow target, so `Not AGENTS.md -> no-such-skill` produced no target, no dangling report and
no missing-clause SUGGESTION. Silence, not noise — the worse of the two.
`CLAUSE_BODY` is now `(?:[^.;]|\.(?=\S))`: a dot inside a filename is followed by a non-space, a
sentence-ending dot by whitespace or end of string, so the class crosses `AGENTS.md` and still stops
at a real sentence end. **Read the second bullet forward as well as back:** a bare target sitting
after a dotted filename is now extracted, resolved, and a blocking ERROR when it dangles, where the
same clause used to pass unchecked in silence.
### SUGGESTION-only checks ### SUGGESTION-only checks
Three more, deterministic to measure but judgment to act on: Deterministic to measure, judgment to act on:
- a description with **no boundary clause at all**; - a description with **no boundary clause at all** (`absent`);
- an **arrow clause whose target could not be read** (`unparsed`);
- an **arrow clause naming more than one target**;
- a `## Gotchas` section with **more than five entries**; - a `## Gotchas` section with **more than five entries**;
- a `## Gotchas` section over **25% of the body**. - a `## Gotchas` section over **25% of the body**.
### Hand-invoked skills are exempt from the routing rules, and only those
A skill or agent whose frontmatter carries `disable-model-invocation: true` skips three checks:
- the boundary-clause check, `absent` and `unparsed` alike;
- the multi-target arrow check;
- the 250-character description **target** (`hand_invoked()` in `scripts/skill-size-check.sh`).
It keeps the 400-character description FAIL and **both** body word tiers, and if its description
does happen to name a target, that target is still resolved and can still dangle.
Why the exemption is right: `disable-model-invocation: true` removes the skill from the
model-visible listing entirely — it is not preloaded, and the Skill tool refuses to call it — so its
description is never matched against user intent. ADR-0020 and `skill-author`'s contract therefore
give such a skill **one plain human-facing sentence**: no trigger list, no boundary clause. No
validator knew the field existed (**#108**), so the boundary-clause SUGGESTION fired on exactly the
shape the contract mandates, and its remedy — "so the router knows where NOT to send this skill" —
was addressed to a router that cannot see the skill at all. An author who followed the advice made
the file worse. There is no router to inform.
The half that does **not** lift is the point. The body is still loaded on invocation and still
competes with the caller's live conversation, so neither body tier moves. The 400-character ceiling
stands too: a hand-invoked description is not preloaded, but it is still the one line the user reads
when choosing from the `/` menu, and that ceiling is an outlier stop rather than a routing-quality
budget — which is precisely why the 250-character target is the tier that lifts.
The field is read as a **boolean**, not as a mention of the key. PyYAML already resolves the
unquoted YAML 1.1 booleans, so the extra handling catches a quoted `"true"`, which a host reads as
truthy; `disable-model-invocation: false` is the model-invoked case written out longhand and buys
nothing. A frontmatter parse failure returns false rather than raising — the flag is a *modifier* on
other checks, and `description_value()` on the same text already reports the broken frontmatter, so
raising here would diagnose one file twice two different ways.
`caveman` and `zoom-out` are the two carriers here. `tests/test-skill-size-check.sh` pins both
halves — what the carve-out lifts, each with a flag-removed control, and what it must not.
### `verbose: true` is load-bearing ### `verbose: true` is load-bearing
The hook is declared `verbose: true` so the SUGGESTION tier is audible. pre-commit prints nothing at The hook is declared `verbose: true` so the SUGGESTION tier is audible. pre-commit prints nothing at
@@ -281,8 +384,10 @@ script.** `scripts/skill-size-check.sh` applies its body gate to whatever path i
bash scripts/skill-size-check.sh plugins/*/.apm/agents/*.agent.md bash scripts/skill-size-check.sh plugins/*/.apm/agents/*.agent.md
``` ```
exits 1 today with 900-word body FAILs on `git-orchestrate` (933), `gitea-orchestrate` (1,199) and exits 1 today with 900-word body FAILs on `git-orchestrate` and `gitea-orchestrate`. (Counts are
`apm-orchestrate` (1,113). Agent files escape only because the hook definitions filter on `SKILL.md` deliberately not pinned here — agent bodies are edited like any other file, and a figure in this
paragraph goes stale the moment one is trimmed. Run the command.) Agent files escape only because
the hook definitions filter on `SKILL.md`
— a file-pattern accident that happens to implement the design, not the design itself. **Do not — a file-pattern accident that happens to implement the design, not the design itself. **Do not
"extend" that hook's `files:` pattern to cover agents** on the assumption that the script already "extend" that hook's `files:` pattern to cover agents** on the assumption that the script already
knows the difference; doing so silently enforces a gate ADR-0020 declines to set. knows the difference; doing so silently enforces a gate ADR-0020 declines to set.
@@ -293,13 +398,30 @@ knows the difference; doing so silently enforces a gate ADR-0020 declines to set
non-compliant skill's current numbers was considered and rejected in favour of hot gates. non-compliant skill's current numbers was considered and rejected in favour of hot gates.
**The corpus is now clean on both gates.** Issue **#99** retrofitted all 39 skills plugin by plugin; **The corpus is now clean on both gates.** Issue **#99** retrofitted all 39 skills plugin by plugin;
`kyberforge` was the last wave, followed by two corpus-wide passes. `kyberforge` was the last wave, after which the corpus was swept as a whole rather than per plugin.
Each sweep is followed by an **independent review round**: a fresh agent with no memory of the
retrofit re-measures the corpus and files what it finds, and the round repeats until one lands no
findings. The rounds are recorded as comments on **#99** — read the current state off that thread,
which is why no round count is pinned here.
| Gate | Current findings | | Gate | Current findings |
|---|---| |---|---|
| `skill-size-check` | **0 of 39** descriptions and **0 of 39** bodies exceed their FAIL tier; 0 dangling targets; 31 SUGGESTIONs | | `skill-size-check` | **0 of 39** descriptions and **0 of 39** bodies exceed their FAIL tier; 0 dangling targets; SUGGESTIONs outstanding (count not pinned — see below) |
| `Kyberforge.CompositionNote` (Vale) | **0 errors** — the four `gitea-*` carriers were all retrofitted | | `Kyberforge.CompositionNote` (Vale) | **0 errors** — the four `gitea-*` carriers were all retrofitted |
**The SUGGESTION count is deliberately not recorded here.** It moves with every skill edit *and*
with every change to the gate's own tiering, so any figure written down is stale by the next commit.
Measure it instead:
```
bash scripts/skill-size-check.sh plugins/*/.apm/skills/*/SKILL.md | grep -c '^SUGGESTION'
pre-commit run skill-size-check --all-files # same findings, via the hook
```
A non-zero count is the expected steady state, not a regression. SUGGESTIONs exit 0 and block
nothing; only the two FAIL tiers, the dangling-target ERROR and the missing-`references/` ERROR do.
Read the count as a work queue, and the FAIL columns above as the gate.
`Kyberforge.CompositionNote` is the ADR-0020 Vale rule banning composition and architecture prose `Kyberforge.CompositionNote` is the ADR-0020 Vale rule banning composition and architecture prose
from a description. Every Vale rule here is `level: error` with no ignorable tier, so a description from a description. Every Vale rule here is `level: error` with no ignorable tier, so a description
that reintroduces one blocks the commit even though no skill carries one today. that reintroduces one blocks the commit even though no skill carries one today.
+29
View File
@@ -0,0 +1,29 @@
# caveman
Ultra-compressed output mode: drop articles, filler and pleasantries, keep the technical substance exact.
## What it does
Switches the agent into a terse register — no articles, no hedging, no pleasantries, fragments allowed, arrows for causality — while leaving technical terms, code blocks and quoted error strings untouched. The mode is *sticky*: once turned on it stays on for every subsequent response until the user says "stop caveman" or "normal mode", rather than decaying back to normal prose after a few turns.
It carries one built-in escape hatch. Security warnings, confirmations for irreversible actions, multi-step sequences where fragment order could be misread, and any request to clarify are answered in normal prose, then the compressed register resumes.
## Hand-invoked only
`SKILL.md` sets `disable-model-invocation: true`. This is the single most important thing to know about this skill: **the model cannot route to it.** No other skill can hand off to it, and no phrasing in a user's request will cause it to be selected automatically. The only way in is the human typing `/caveman`.
That is deliberate — output style is the user's choice, not an inference the router should make on their behalf. It is also why the description reads as one plain human-facing sentence rather than carrying the trigger phrasing and boundary clause a routable skill needs.
## Usage
```text
/caveman
```
Then keep working normally. To leave the mode, say "stop caveman" or "normal mode".
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The whole skill — persistence rule, compression rules, worked examples, and the auto-clarity exception |
+3 -5
View File
@@ -2,17 +2,15 @@
name: caveman name: caveman
disable-model-invocation: true disable-model-invocation: true
description: > description: >
Ultra-compressed output mode: drops articles, filler and pleasantries while Ultra-compressed output mode that drops articles, filler and pleasantries while
keeping technical substance exact. Cuts token usage by roughly 75%. Hand-invoked keeping technical substance exact, cutting token usage by roughly 75%.
only — type /caveman to turn it on, "stop caveman" or "normal mode" to turn it
off. Stays active across turns until you do.
--- ---
Respond terse like smart caveman. All technical substance stay. Only fluff die. Respond terse like smart caveman. All technical substance stay. Only fluff die.
## Persistence ## Persistence
ACTIVE EVERY RESPONSE once triggered. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode". ACTIVE EVERY RESPONSE once user type `/caveman`. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode".
## Rules ## Rules
@@ -0,0 +1,35 @@
# diagnose
A six-phase discipline for hard bugs and performance regressions: feedback loop → reproduce → hypothesise → instrument → fix with a regression test → clean up.
## What it does
Imposes an order of operations on debugging so the agent cannot skip to guessing. The load-bearing phase is the first one: build a fast, deterministic, agent-runnable pass/fail signal for the bug. Everything downstream — bisection, hypothesis testing, instrumentation — just consumes that signal, so the skill refuses to advance to Phase 2 without one, and says so explicitly rather than hypothesising blind.
The remaining phases each carry a constraint worth knowing about: hypotheses are generated 3–5 at a time and must be falsifiable, so the first plausible idea cannot anchor the whole investigation; every debug log is tagged with a unique prefix (`[DEBUG-a4f2]`) so cleanup is a single grep; the regression test is written before the fix and only at a seam that exercises the real bug pattern; and the run closes by asking what would have prevented the bug, handing off to `improve-codebase-architecture` when the answer is architectural.
Performance regressions take a branch of their own inside Phase 4 — baseline measurement and bisection, not logs.
## Conditional reading
Neither reference file is read on every run; `SKILL.md` names the condition for each.
- `references/feedback-loops.md` is read when Phase 1 has no signal yet, or when the loop you have is slow or intermittent.
- `references/regression-seams.md` is read when Phase 5 leaves you unsure whether the available seam is deep enough — or whether one exists at all.
## Usage
```text
/diagnose
```
Describe the bug or the regression. For filing and triaging a reported bug rather than diagnosing it, use `triage`; for test-first feature work, use `tdd`.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The six phases and their gates — what must be true before each one ends |
| `references/feedback-loops.md` | Loaded when Phase 1 has no loop or the loop is too weak: ten ways to construct one ordered by cost, how to sharpen an existing loop, handling intermittent bugs, and what to ask the user for when the bug resists reproduction |
| `references/regression-seams.md` | Loaded when Phase 5 is unsure about the seam: what makes a seam correct, the four shapes of a too-shallow seam, and what to do when no correct seam exists |
| `assets/hitl-loop.template.sh` | Copy-and-edit bash template for the last-resort human-in-the-loop feedback loop, cited by `references/feedback-loops.md`. Provides `step` and `capture` helpers and prints captured values as `KEY=VALUE` for the agent to parse |
+6 -6
View File
@@ -18,7 +18,9 @@ When exploring the codebase, use the project's domain glossary to get a clear me
Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.** Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.**
Read `references/feedback-loops.md` — even if you already have a signal. Ten ways to build a loop ordered by cost, how to sharpen the one you have, and what to do when the bug resists reproduction. An unsharpened loop is usually not good enough yet. **If you do not yet have such a signal, read `references/feedback-loops.md`** — ten ways to build one ordered by cost, and what to ask the user for when the bug resists reproduction entirely.
**If you do have one, it is probably not sharp enough yet.** Make it faster and more deterministic, and make it assert on the exact symptom rather than "didn't crash" — a 30-second flaky loop is barely better than no loop. If it stays slow or intermittent after that, read that file's "Iterate on the loop itself" and "Intermittent bugs" sections.
Do not proceed to Phase 2 until you have a loop you believe in. If you cannot build one, stop and say so explicitly, listing what you tried — never hypothesise without a signal. Do not proceed to Phase 2 until you have a loop you believe in. If you cannot build one, stop and say so explicitly, listing what you tried — never hypothesise without a signal.
@@ -62,13 +64,11 @@ Tool preference:
## Phase 5 — Fix + regression test ## Phase 5 — Fix + regression test
Write the regression test **before the fix** — but only if there is a **correct seam** for it. Write the regression test **before the fix** — but only at a **correct seam**: one where the test exercises the real bug pattern as it occurs at the call site. If the available seam looks too shallow, or you cannot tell whether it is, read `references/regression-seams.md`.
A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence. **If no correct seam exists, that itself is the finding.** Note it and carry it into Phase 6 — the architecture is preventing the bug from being locked down.
**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase. At a correct seam:
If a correct seam exists:
1. Turn the Phase 1 loop into a failing test at that seam, narrowed to the symptom captured in Phase 2. 1. Turn the Phase 1 loop into a failing test at that seam, narrowed to the symptom captured in Phase 2.
2. Watch it fail. 2. Watch it fail.
@@ -13,7 +13,7 @@ A feedback loop is a fast, deterministic, agent-runnable pass/fail signal for th
7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode. 7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode.
8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it. 8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it.
9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs. 9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs.
10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `scripts/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you. 10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `assets/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you.
## Iterate on the loop itself ## Iterate on the loop itself
@@ -0,0 +1,24 @@
# Judging a regression-test seam
Read this when Phase 5 leaves you unsure whether the seam available for the regression test is the correct one — either because the obvious seam looks shallow, or because there appears to be no seam at all.
## What makes a seam correct
A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site: the same entry point, the same participants, the same ordering, and the same state the real caller holds when it goes wrong.
## Seams that are too shallow
- A single-caller test when the bug only appears with multiple callers.
- A unit test that cannot replicate the chain of calls that triggered the bug.
- A test that reproduces the symptom by construction — asserting on a value the test itself set — rather than by driving the code path that produces it.
- A test that mocks out the collaborator the bug actually lives in.
A regression test at a shallow seam gives false confidence. It passes forever, including after a change reintroduces the bug at the real call site, and it will be read by the next maintainer as proof the bug is locked down.
## When there is no correct seam
Do not force one, and do not settle for a shallow seam to have something green. Instead:
1. Apply the fix and verify it against the Phase 1 loop directly.
2. Write down which seams you considered and why each was too shallow.
3. Carry that into Phase 6's "what would have prevented this bug" question. A missing seam is an architecture finding — tangled callers, hidden coupling, or a module with no testable boundary — and the handoff is the `improve-codebase-architecture` skill, with those specifics attached.
@@ -0,0 +1,27 @@
# grill-me
Interview the user relentlessly about a plan or design until the decision tree is fully resolved.
## What it does
Turns the agent into an interviewer rather than an implementer. It walks the design tree branch by branch, resolving dependencies between decisions one at a time, and offers its own recommended answer alongside each question so the user has something concrete to push against. Two rules give it its shape: **one question at a time**, and **never ask what the codebase can answer** — if a question is settleable by reading the code, the agent goes and reads the code instead of spending the user's attention on it.
## Composition
This is the plain grilling loop, with no documentation side effects. The sibling `grill-with-docs` skill runs the same interview but additionally challenges answers against the project's `CONTEXT.md` glossary and existing ADRs, and writes decisions back into those files as they crystallise. Reach for that one when the project has a domain model worth defending; reach for this one when it does not, or when nothing should be written down yet.
`triage` composes the documented variant, not this one, when an issue needs fleshing out.
## Usage
```text
/grill-me
```
Describe the plan or design to be stress-tested. Expect questions one at a time, each with a recommended answer.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The whole skill — the interview instruction, the one-question-at-a-time rule, and the explore-instead-of-asking rule |
@@ -0,0 +1,36 @@
# grill-with-docs
The grilling interview, run against the project's domain model — and writing decisions back into `CONTEXT.md` and ADRs as they land.
## What it does
Runs the same relentless one-question-at-a-time interview as `grill-me`, with the project's own documentation as an active participant. During codebase exploration it also locates the domain documentation — a root `CONTEXT.md` and `docs/adr/`, or a `CONTEXT-MAP.md` pointing at per-context glossaries and ADR directories in a multi-context repo — and then uses it four ways:
- **Challenges terms against the glossary.** When the user's usage conflicts with what `CONTEXT.md` already defines, that is raised immediately rather than absorbed.
- **Sharpens fuzzy language** by proposing a precise canonical term ("you're saying 'account' — do you mean the Customer or the User?").
- **Cross-references claims against the code**, and surfaces contradictions between what the user says happens and what the code does.
- **Updates `CONTEXT.md` inline**, the moment a term is resolved, rather than batching changes to the end of the session where they get lost.
Files are created lazily — only when there is something real to write.
ADRs are offered *sparingly*, and only when all three tests pass: the decision is hard to reverse, it would surprise a future reader without the context, and it was a genuine trade-off with real alternatives. Missing any one of the three means no ADR.
## Composition
`grill-me` is the same interview without the documentation side effects — use it when there is no domain model to defend or nothing should be written down yet. `triage` composes this skill (not `grill-me`) at step 4 when an issue needs fleshing out. `improve-codebase-architecture` runs its own grilling loop and borrows this skill's `CONTEXT.md` and ADR discipline for the decisions that come out of it.
## Usage
```text
/grill-with-docs
```
Describe the plan or design. Expect questions one at a time, each with a recommended answer, and expect `CONTEXT.md` to be edited during the session rather than after it.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The interview instruction plus the domain-awareness rules: file layout discovery, the four during-session behaviours, and the three-part ADR test |
| `CONTEXT-FORMAT.md` | Skill-root document, cited when a term is resolved: the structure of a `CONTEXT.md` and how to write a Language entry |
| `ADR-FORMAT.md` | Skill-root document, cited when an ADR is offered: `docs/adr/` naming, sequential numbering, and the ADR template |
@@ -0,0 +1,36 @@
# improve-codebase-architecture
Surface architectural friction and propose deepening opportunities — refactors that turn shallow modules into deep ones.
## What it does
Looks for places where a codebase is hard to understand, hard to test, or hard for an agent to navigate, and proposes refactors that concentrate behaviour behind smaller interfaces. It runs in three stages:
1. **Explore.** Reads the domain glossary and any ADRs in the area first, then walks the codebase with an `Explore` sub-agent — organically, noting friction rather than applying fixed heuristics. The **deletion test** is the filter: imagine deleting the module; if complexity vanishes it was a pass-through, if complexity reappears across N callers it was earning its keep.
2. **Present candidates.** A numbered list, each with files, problem, solution and benefits — benefits stated in terms of *locality* and *leverage* and of how tests would improve. No interfaces are proposed yet; the user picks one.
3. **Grilling loop.** Walks the design tree for the chosen candidate, with documentation side effects landing inline as decisions crystallise.
The skill is opinionated about vocabulary, and that is the point: **module, interface, implementation, depth, seam, adapter, leverage, locality**, used exactly, with no drift into "component", "service", "API" or "boundary". Domain nouns come from `CONTEXT.md`, architecture nouns from `LANGUAGE.md` — so a proposal reads as "the Order intake module", never "the FooBarHandler".
ADRs are treated as decisions not to be re-litigated. A candidate that contradicts one is surfaced only when the friction is real enough to warrant reopening it, and is marked as such.
## Composition
`diagnose` hands off here when a bug's post-mortem concludes that no correct test seam exists, or that callers are tangled — the recommendation is made after the fix is in, not before. The grilling loop follows `grill-with-docs`'s discipline for `CONTEXT.md` entries and ADR offers, and `SKILL.md` names that skill's format documents directly.
## Usage
```text
/improve-codebase-architecture
```
Point at a codebase or an area of one. Expect a numbered candidate list and a "which of these would you like to explore?" before any interface design happens.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Condensed glossary, key principles, and the three-stage process |
| `LANGUAGE.md` | Skill-root document, cited throughout `SKILL.md`: full definitions of every term, the words each one replaces, and the full principle list |
| `INTERFACE-DESIGN.md` | Skill-root document, read at stage 3 when the user wants alternative interfaces explored: the parallel sub-agent "Design It Twice" pattern, framing the problem space, and the per-agent design constraints |
| `DEEPENING.md` | Skill-root document, cited from `INTERFACE-DESIGN.md`: how to deepen a cluster of shallow modules safely, the four dependency categories (in-process, local-substitutable, remote-but-owned, true external), seam discipline, and the replace-don't-layer testing strategy |
@@ -65,7 +65,7 @@ Once the user picks a candidate, drop into a grilling conversation. Walk the des
Side effects happen inline as decisions crystallize: Side effects happen inline as decisions crystallize:
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `/grill-with-docs` (see [CONTEXT-FORMAT.md](../grill-with-docs/CONTEXT-FORMAT.md)). Create the file lazily if it doesn't exist. - **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `grill-with-docs`, in the format `grill-with-docs`'s `CONTEXT-FORMAT.md` defines. Create the file lazily if it doesn't exist.
- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. - **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [ADR-FORMAT.md](../grill-with-docs/ADR-FORMAT.md). - **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See `grill-with-docs`'s `ADR-FORMAT.md`.
- **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md). - **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md).
@@ -0,0 +1,32 @@
# prototype
Build a throwaway prototype that answers one design question — either a runnable terminal app or several UI variations.
## What it does
Treats a prototype as **throwaway code that answers a question**, and lets the question decide the artifact. `SKILL.md` opens with a two-row dispatch table and the run resolves exactly one row before doing anything else:
- *"Does this logic / state model feel right?"* → a tiny interactive terminal app that pushes the state machine through the cases that are hard to reason about on paper.
- *"What should this look like?"* → several radically different UI variations on one route, switchable from a floating bottom bar via a URL search param.
The two branches produce fundamentally different artifacts, so picking wrong wastes the whole prototype. When the question is genuinely ambiguous and the user is unreachable, the skill defaults on the shape of the surrounding code (backend module → logic, page or component → UI) and states the assumption at the top of the prototype rather than silently choosing.
Six rules apply to both branches: throwaway and visibly named as such, one command to run, no persistence by default, no polish, surface the full state after every action or variant switch, and delete or absorb the prototype when it is done. The *answer* is the only durable output — the skill captures it in a commit message, ADR, issue or `NOTES.md` before the code is deleted.
## Usage
```text
/prototype
```
State the design question. For production code, use `tdd`; for talking a design through without building anything, use `grill-me`.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The branch dispatch table and the rules that apply to both branches |
| `references/logic.md` | The logic branch, read only when that row is selected: when it is the right shape, and how to build the interactive terminal app |
| `references/ui.md` | The UI branch, read only when that row is selected: when it is the right shape, and how to build and switch between the variations |
Each reference is self-contained — a run reads one of the two, never both.
+7 -5
View File
@@ -3,8 +3,8 @@ name: prototype
description: > description: >
Use when the user wants a throwaway prototype to answer a design question about Use when the user wants a throwaway prototype to answer a design question about
a data model, state machine or business logic, or to mock up a UI in several a data model, state machine or business logic, or to mock up a UI in several
variations, as in "try a few designs". Not production code -> `tdd`. Not variations. Not production code -> `tdd`. Not talking a design through ->
talking a design through without building -> `grill-me`. `grill-me`.
--- ---
# Prototype # Prototype
@@ -13,10 +13,12 @@ A prototype is **throwaway code that answers a question**. The question decides
## Pick a branch ## Pick a branch
Identify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around: | Question being answered | Build | Reference |
|---|---|---|
| "Does this logic / state model feel right?" | A tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper | `references/logic.md` |
| "What should this look like?" | Several radically different UI variations on one route, switchable via a URL search param and a floating bottom bar | `references/ui.md` |
- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md). Build a tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper. Resolve the row from the user's prompt, the surrounding code, or by asking if the user is around, then read only that reference — each is self-contained.
- **"What should this look like?"** → [UI.md](UI.md). Generate several radically different UI variations on a single route, switchable via a URL search param and a floating bottom bar.
The two branches produce fundamentally different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype. The two branches produce fundamentally different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype.
@@ -9,7 +9,7 @@ A tiny interactive terminal app that lets the user drive a state model by hand.
- "I want to feel out what the API should look like before writing it." - "I want to feel out what the API should look like before writing it."
- Anything where the user wants to **press buttons and watch state change**. - Anything where the user wants to **press buttons and watch state change**.
If the question is "what should this look like" — wrong branch. Use [UI.md](UI.md). If the question is "what should this look like" — wrong branch. Read `references/ui.md`.
## Process ## Process
@@ -72,8 +72,5 @@ When the prototype has done its job, the answer to the question is the only thin
## Anti-patterns ## Anti-patterns
- **Don't add tests.** A prototype that needs tests is no longer a prototype.
- **Don't wire it to the real database.** Use an in-memory store unless the question is specifically about persistence.
- **Don't generalise.** No "what if we wanted to support X later." The prototype answers one question.
- **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module. - **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module.
- **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping. - **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping.
@@ -2,7 +2,7 @@
Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away. Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away.
If the question is about logic/state rather than what something looks like — wrong branch. Use [LOGIC.md](LOGIC.md). If the question is about logic/state rather than what something looks like — wrong branch. Read `references/logic.md`.
## When this is the right shape ## When this is the right shape
@@ -109,4 +109,3 @@ Don't leave variant components or the switcher lying around. They rot fast and c
- **Variants that differ only in colour or copy.** That's a tweak, not a prototype. Real variants disagree about structure. - **Variants that differ only in colour or copy.** That's a tweak, not a prototype. Real variants disagree about structure.
- **Sharing too much code between variants.** A shared `<Header>` is fine; a shared `<Layout>` defeats the point. Each variant should be free to throw out the layout. - **Sharing too much code between variants.** A shared `<Header>` is fine; a shared `<Layout>` defeats the point. Each variant should be free to throw out the layout.
- **Wiring variants to real mutations.** Read-only prototypes are fine. If a variant needs to mutate, point it at a stub — the question is "what should this look like", not "does the backend work". - **Wiring variants to real mutations.** Read-only prototypes are fine. If a variant needs to mutate, point it at a stub — the question is "what should this look like", not "does the backend work".
- **Promoting the prototype directly to production.** The variant code was written under prototype constraints (no tests, minimal error handling). Rewrite it properly when you fold it in.
@@ -0,0 +1,31 @@
# research
Research a tool, library or API from canonical documentation into a directory of structured per-topic reference files.
## What it does
Runs a six-step pipeline: scope against the working directory (what version is actually in use, what is already documented), resolve the topic through Context7, websearch for canonical docs covering whatever Context7 missed, read those sources, deepen one level into the links worth following, then write one markdown file per topic area plus a `sources.md` provenance record.
Four gotchas at the top of `SKILL.md` shape the whole run, and each exists because of a specific failure: the output path is never inferred (a guessed destination scatters a directory's worth of files through someone's source tree); nothing is written outside that path; no empty topic file is ever written (a stub `troubleshooting.md` reads downstream as researched and closed); and a Context7 "no results", redirect or header-only response does not count as coverage. If no topic area has content, the run writes nothing at all — `sources.md` included — and reports what it searched.
The frontmatter pins `model: sonnet` and a closed `allowed-tools` list. Notably it grants no subagent tool, so every `WebFetch` is serial and each fetched page lands in the run's own context — which is why steps 4 and 5 insist on reducing each page to notes before fetching the next, and cap deepening at roughly ten extra pages.
## Composition
`references/file-format.md` is not optional reading before the write step: the `sources.md` field names it defines are matched literally by the downstream provenance validator. Prose written in their place parses as nothing and the check passes having verified nothing.
## Usage
```text
/research
```
Name the topic and the output path — the skill will stop and ask if the path is missing. Supplying starting URLs is treated as a deliberate source choice and skips Context7 resolution and discovery. For documentation derived from existing code or specs, use `write-docs`; for a bug or incident, use `diagnose`.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The four gotchas and the six research steps |
| `references/topics.md` | Read at Step 1 before narrowing scope: the default topic list (`overview`, `installation`, `configuration`, `cli-reference`, `api-reference`, `examples`, and more) and what each file covers |
| `references/file-format.md` | Read at Step 6 before writing: the frontmatter schema for a topic file and the exact `sources.md` field names the provenance validator matches |
+5 -3
View File
@@ -8,10 +8,12 @@ description: >-
metadata: metadata:
category: research category: research
allowed-tools: allowed-tools:
- WebSearch - Grep
- WebFetch - Glob
- Read - Read
- Write - Write
- WebSearch
- WebFetch
- mcp__context7__resolve-library-id - mcp__context7__resolve-library-id
- mcp__context7__query-docs - mcp__context7__query-docs
model: sonnet model: sonnet
@@ -61,4 +63,4 @@ Merge every set of notes, Context7 and web alike, by topic area. Read `reference
Spell the `sources.md` field names exactly as `references/file-format.md` gives them. The downstream provenance validator matches them literally; prose in their place parses as nothing, and the check passes having verified nothing. Spell the `sources.md` field names exactly as `references/file-format.md` gives them. The downstream provenance validator matches them literally; prose in their place parses as nothing, and the check passes having verified nothing.
If no topic area has content, write nothing at all, `sources.md` included, and report what was searched. A directory of empty files is worse than an honest miss. If no topic area has content, write nothing at all, `sources.md` included, and report what was searched.
+32
View File
@@ -0,0 +1,32 @@
# tdd
Test-driven development as a strict red-green-refactor loop, one behaviour at a time.
## What it does
Two convictions drive this skill. The first is about what a test is for: tests verify behaviour through public interfaces, not implementation details. A good test reads like a specification ("user can checkout with valid cart") and survives refactors because it does not care about internal structure. The warning sign for a bad one is precise — the test breaks when you refactor but behaviour has not changed.
The second is an explicit anti-pattern: **do not write all the tests first, then all the implementation.** Horizontal slicing treats RED as "write every test" and GREEN as "write every implementation", and it produces tests of *imagined* behaviour — tests of the shape of things, insensitive to real change, committed to before the implementation was understood. The correct shape is vertical: one test → one implementation → repeat, each cycle informed by what the last one taught you.
The workflow is four stages: plan (confirm the interface and which behaviours matter, with the user — you cannot test everything), fire a tracer bullet (one test proving the path works end to end), loop incrementally one behaviour at a time, then refactor once everything is green. Refactoring while RED is forbidden.
Codebase exploration uses the project's domain glossary, so test names and interface vocabulary match the project's language, and ADRs in the area are respected.
## Usage
```text
/tdd
```
Describe the feature or bug. Expect the skill to ask what the public interface should look like and which behaviours matter most before any code is written. For diagnosing an existing bug rather than building test-first, use `diagnose`; for throwaway exploratory code, use `prototype`.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Philosophy, the horizontal-slicing anti-pattern, the four-stage workflow, and the per-cycle checklist |
| `tests.md` | Skill-root document, cited from Philosophy: worked good and bad test examples |
| `mocking.md` | Skill-root document, cited from Philosophy: mock at system boundaries only, and what not to mock |
| `deep-modules.md` | Skill-root document, cited from stage 1: what a deep module is (small interface, large implementation) and why it is the design to aim for |
| `interface-design.md` | Skill-root document, cited from stage 1: designing interfaces for testability, starting with accepting dependencies rather than creating them |
| `refactoring.md` | Skill-root document, cited from stage 4: the refactor-candidate checklist — duplication, long methods, shallow modules, feature envy, primitive obsession |
@@ -1,6 +1,6 @@
# Writing Agent Briefs # Writing Agent Briefs
An agent brief is a structured comment posted on a GitHub issue when it moves to `ready-for-agent`. It is the authoritative specification that an AFK agent will work from. The original issue body and discussion are context — the agent brief is the contract. An agent brief is a structured comment posted on an issue in the issue tracker when it moves to `ready-for-agent`. It is the authoritative specification that an AFK agent will work from. The original issue body and discussion are context — the agent brief is the contract.
## Principles ## Principles
@@ -27,7 +27,7 @@ Describe **what** the system should do, not **how** to implement it. The agent w
The agent needs to know when it's done. Every agent brief must have concrete, testable acceptance criteria. Each criterion should be independently verifiable. The agent needs to know when it's done. Every agent brief must have concrete, testable acceptance criteria. Each criterion should be independently verifiable.
- **Good:** "Running `gh issue list --label needs-triage` returns issues that have been through initial classification" - **Good:** "Querying the issue tracker for the `needs-triage` label returns issues that have been through initial classification"
- **Bad:** "Triage should work correctly" - **Bad:** "Triage should work correctly"
### Explicit scope boundaries ### Explicit scope boundaries
+35
View File
@@ -0,0 +1,35 @@
# triage
Move issues on the project issue tracker through a small state machine of triage roles.
## What it does
Gives issue triage an explicit state model and a fixed set of moves. Every issue carries exactly one **category** role (`bug`, `enhancement`) and one **state** role (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`); conflicting state roles are flagged to the maintainer before anything else happens. Unlabeled issues normally enter at `needs-triage`; `needs-info` returns there once the reporter replies. The maintainer can override at any point, and unusual transitions are questioned rather than executed silently.
A run does one of three things depending on what the maintainer asks for:
- **Show what needs attention** — three buckets, oldest first: unlabeled, `needs-triage`, and `needs-info` with reporter activity since the last triage notes.
- **Triage a specific issue** — gather context (including prior triage notes, so resolved questions are not re-asked, and `.out-of-scope/` records that resemble the issue), recommend a category and state with reasoning, attempt reproduction for bugs *before* any grilling, run a `grill-with-docs` session if the issue needs fleshing out, then apply the outcome.
- **Quick state override** — "move #42 to ready-for-agent" is trusted and applied directly, skipping grilling, after confirming the exact changes.
Two hard rules: every comment or issue the skill posts during triage must open with the AI-generated disclaimer, and the canonical role names above are *not* necessarily the label strings in the tracker — the mapping has to be supplied to the run.
## Composition
`grill-with-docs` is invoked at step 4 when an issue needs fleshing out; whatever that session establishes is carried into the triage notes so the work is not lost. The reverse direction also exists: `diagnose` names this skill as the place to send a *reported* bug that needs filing rather than debugging.
## Usage
```text
/triage
```
Then describe what you want in natural language — "show me anything that needs my attention", "let's look at #42", "move #42 to ready-for-agent", "what's ready for agents to pick up?".
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The roles and state machine, the three invocation modes, the needs-info template, and how to resume a prior session |
| `AGENT-BRIEF.md` | Skill-root document, cited when an issue moves to `ready-for-agent` (and reused for `ready-for-human`): how to write a brief that stays durable for weeks while the codebase moves under it — describe interfaces and behavioural contracts, not line numbers |
| `OUT-OF-SCOPE.md` | Skill-root document, cited when an enhancement is closed `wontfix` and when checking for prior rejections: how the `.out-of-scope/` knowledge base is laid out and what it is for — institutional memory, and deduplication against re-litigated requests |
+1 -1
View File
@@ -35,7 +35,7 @@ Five **state** roles:
Every triaged issue should carry exactly one category role and one state role. If state roles conflict, flag it and ask the maintainer before doing anything else. Every triaged issue should carry exactly one category role and one state role. If state roles conflict, flag it and ask the maintainer before doing anything else.
These are canonical role names — the actual label strings used in the issue tracker may differ. The mapping should have been provided to you - run `/setup-matt-pocock-skills` if not. These are canonical role names — the actual label strings used in the issue tracker may differ. Resolve each canonical name against the tracker's live label set before applying it, using whichever tracker skill this install provides. If a name has no counterpart there, report the gap and ask the maintainer for the mapping — never substitute a guess.
State transitions: an unlabeled issue normally goes to `needs-triage` first; from there it moves to `needs-info`, `ready-for-agent`, `ready-for-human`, or `wontfix`. `needs-info` returns to `needs-triage` once the reporter replies. The maintainer can override at any time — flag transitions that look unusual and ask before proceeding. State transitions: an unlabeled issue normally goes to `needs-triage` first; from there it moves to `needs-info`, `ready-for-agent`, `ready-for-human`, or `wontfix`. `needs-info` returns to `needs-triage` once the reporter replies. The maintainer can override at any time — flag transitions that look unusual and ask before proceeding.
@@ -0,0 +1,30 @@
# write-docs
Produce technical documentation derived from code and spec, one section at a time, with a confirmation gate on every section.
## What it does
Casts the agent as a technical writer with one non-negotiable constraint: **every claim must be traceable to a source file line, a spec section, or an explicit user statement.** Nothing is invented, and behaviour that genuinely cannot be documented from the available sources is marked out-of-scope rather than explained away.
The process is eight steps — identify scope, read and extract, gap check, draft section by section, confirmation gate, delta summary, reader testing, finalise — and several of them are deliberately gated on the human:
- Files are read only after the user approves them by name. The skill may propose candidates; it waits.
- The **gap check** presents what the code does say and asks the user to fill only what it does not: caller intent, error-handling rationale, non-obvious side effects.
- No section is finalised until the full revised text has been shown. The skill never gates on output the user has not seen, and never reprints the whole document — all edits are surgical.
- **Reader testing** predicts 5–10 questions a target reader would ask, then spawns a sub-agent that receives only the finished doc and the questions — no source files. If the doc cannot answer them, neither can the sub-agent, and the run loops back to drafting.
Summary and overview sections are written last, once the detail sections are stable.
## Usage
```text
/write-docs
```
Name the files or modules to document, the target audience (developer / user / contributor / internal), and the documentation type (reference, guide, README section, inline comment, changelog entry). For a PRD, ADR or decision doc, use `grill-me` or `grill-with-docs` instead — those have dedicated handling.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The whole skill — role, use/do-not-use boundaries, required inputs, constraints, the eight-step process, output format, failure handling, and a nine-item self-check |
+2 -2
View File
@@ -35,7 +35,7 @@ You are a technical writer that produces documentation by reading code and spec
- User says "write docs for X", "document this", "create docs for this feature", "write a README for this" - User says "write docs for X", "document this", "create docs for this feature", "write a README for this"
**Do not use when:** **Do not use when:**
- User wants a PRD, decision doc, or architecture proposal → `to-prd` or `grill-me` - User wants a PRD, decision doc, or architecture proposal → `grill-me` or `grill-with-docs`
- User wants to document a skill file (skill files are self-describing) - User wants to document a skill file (skill files are self-describing)
- User wants marketing or blog copy - User wants marketing or blog copy
- Documentation requires tacit organisational knowledge that cannot be read from code or spec - Documentation requires tacit organisational knowledge that cannot be read from code or spec
@@ -88,7 +88,7 @@ You are a technical writer that produces documentation by reading code and spec
- Stage skipped without a logged reason → flag and require the one-sentence log before continuing - Stage skipped without a logged reason → flag and require the one-sentence log before continuing
- Code behaviour is undocumentable (internal implementation detail, no public spec) → note as out-of-scope in the doc; do not invent an explanation - Code behaviour is undocumentable (internal implementation detail, no public spec) → note as out-of-scope in the doc; do not invent an explanation
- Reader Testing sub-agent fails on multiple questions → surface the failures, return to step 4; do not mark complete - Reader Testing sub-agent fails on multiple questions → surface the failures, return to step 4; do not mark complete
- Requested output is a PRD, decision doc, or architecture proposal → redirect to `to-prd`, `grill-me`, or `grill-with-docs` - Requested output is a PRD, decision doc, or architecture proposal → redirect to `grill-me` or `grill-with-docs`
## Self-check ## Self-check
@@ -0,0 +1,25 @@
# zoom-out
Ask the agent to go up a layer of abstraction and map the modules and callers around unfamiliar code.
## What it does
A single-purpose prompt for the moment you land in a part of the codebase you do not know. Instead of answering at the level of the file in front of it, the agent climbs one layer and produces a map of the relevant modules and their callers — and names them using the project's own domain glossary vocabulary, so the map lines up with the language the rest of the repo already uses.
## Hand-invoked only
`SKILL.md` sets `disable-model-invocation: true`, so the router never selects this skill on its own and no other skill can hand off to it. It runs when the human asks for it. That also means its description is written as one plain human-facing sentence — it carries no trigger phrasing or boundary clause, because nothing routes on it.
## Usage
```text
/zoom-out
```
Best used with the unfamiliar code already in context — the skill widens the view around what you are looking at rather than picking a starting point for you.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The whole skill — a single instruction, no supporting files |
+29
View File
@@ -0,0 +1,29 @@
# caveman
Ultra-compressed output mode: drop articles, filler and pleasantries, keep the technical substance exact.
## What it does
Switches the agent into a terse register — no articles, no hedging, no pleasantries, fragments allowed, arrows for causality — while leaving technical terms, code blocks and quoted error strings untouched. The mode is *sticky*: once turned on it stays on for every subsequent response until the user says "stop caveman" or "normal mode", rather than decaying back to normal prose after a few turns.
It carries one built-in escape hatch. Security warnings, confirmations for irreversible actions, multi-step sequences where fragment order could be misread, and any request to clarify are answered in normal prose, then the compressed register resumes.
## Hand-invoked only
`SKILL.md` sets `disable-model-invocation: true`. This is the single most important thing to know about this skill: **the model cannot route to it.** No other skill can hand off to it, and no phrasing in a user's request will cause it to be selected automatically. The only way in is the human typing `/caveman`.
That is deliberate — output style is the user's choice, not an inference the router should make on their behalf. It is also why the description reads as one plain human-facing sentence rather than carrying the trigger phrasing and boundary clause a routable skill needs.
## Usage
```text
/caveman
```
Then keep working normally. To leave the mode, say "stop caveman" or "normal mode".
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The whole skill — persistence rule, compression rules, worked examples, and the auto-clarity exception |
+3 -5
View File
@@ -2,17 +2,15 @@
name: caveman name: caveman
disable-model-invocation: true disable-model-invocation: true
description: > description: >
Ultra-compressed output mode: drops articles, filler and pleasantries while Ultra-compressed output mode that drops articles, filler and pleasantries while
keeping technical substance exact. Cuts token usage by roughly 75%. Hand-invoked keeping technical substance exact, cutting token usage by roughly 75%.
only — type /caveman to turn it on, "stop caveman" or "normal mode" to turn it
off. Stays active across turns until you do.
--- ---
Respond terse like smart caveman. All technical substance stay. Only fluff die. Respond terse like smart caveman. All technical substance stay. Only fluff die.
## Persistence ## Persistence
ACTIVE EVERY RESPONSE once triggered. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode". ACTIVE EVERY RESPONSE once user type `/caveman`. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode".
## Rules ## Rules
+35
View File
@@ -0,0 +1,35 @@
# diagnose
A six-phase discipline for hard bugs and performance regressions: feedback loop → reproduce → hypothesise → instrument → fix with a regression test → clean up.
## What it does
Imposes an order of operations on debugging so the agent cannot skip to guessing. The load-bearing phase is the first one: build a fast, deterministic, agent-runnable pass/fail signal for the bug. Everything downstream — bisection, hypothesis testing, instrumentation — just consumes that signal, so the skill refuses to advance to Phase 2 without one, and says so explicitly rather than hypothesising blind.
The remaining phases each carry a constraint worth knowing about: hypotheses are generated 3–5 at a time and must be falsifiable, so the first plausible idea cannot anchor the whole investigation; every debug log is tagged with a unique prefix (`[DEBUG-a4f2]`) so cleanup is a single grep; the regression test is written before the fix and only at a seam that exercises the real bug pattern; and the run closes by asking what would have prevented the bug, handing off to `improve-codebase-architecture` when the answer is architectural.
Performance regressions take a branch of their own inside Phase 4 — baseline measurement and bisection, not logs.
## Conditional reading
Neither reference file is read on every run; `SKILL.md` names the condition for each.
- `references/feedback-loops.md` is read when Phase 1 has no signal yet, or when the loop you have is slow or intermittent.
- `references/regression-seams.md` is read when Phase 5 leaves you unsure whether the available seam is deep enough — or whether one exists at all.
## Usage
```text
/diagnose
```
Describe the bug or the regression. For filing and triaging a reported bug rather than diagnosing it, use `triage`; for test-first feature work, use `tdd`.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The six phases and their gates — what must be true before each one ends |
| `references/feedback-loops.md` | Loaded when Phase 1 has no loop or the loop is too weak: ten ways to construct one ordered by cost, how to sharpen an existing loop, handling intermittent bugs, and what to ask the user for when the bug resists reproduction |
| `references/regression-seams.md` | Loaded when Phase 5 is unsure about the seam: what makes a seam correct, the four shapes of a too-shallow seam, and what to do when no correct seam exists |
| `assets/hitl-loop.template.sh` | Copy-and-edit bash template for the last-resort human-in-the-loop feedback loop, cited by `references/feedback-loops.md`. Provides `step` and `capture` helpers and prints captured values as `KEY=VALUE` for the agent to parse |
+6 -6
View File
@@ -18,7 +18,9 @@ When exploring the codebase, use the project's domain glossary to get a clear me
Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.** Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.**
Read `references/feedback-loops.md` — even if you already have a signal. Ten ways to build a loop ordered by cost, how to sharpen the one you have, and what to do when the bug resists reproduction. An unsharpened loop is usually not good enough yet. **If you do not yet have such a signal, read `references/feedback-loops.md`** — ten ways to build one ordered by cost, and what to ask the user for when the bug resists reproduction entirely.
**If you do have one, it is probably not sharp enough yet.** Make it faster and more deterministic, and make it assert on the exact symptom rather than "didn't crash" — a 30-second flaky loop is barely better than no loop. If it stays slow or intermittent after that, read that file's "Iterate on the loop itself" and "Intermittent bugs" sections.
Do not proceed to Phase 2 until you have a loop you believe in. If you cannot build one, stop and say so explicitly, listing what you tried — never hypothesise without a signal. Do not proceed to Phase 2 until you have a loop you believe in. If you cannot build one, stop and say so explicitly, listing what you tried — never hypothesise without a signal.
@@ -62,13 +64,11 @@ Tool preference:
## Phase 5 — Fix + regression test ## Phase 5 — Fix + regression test
Write the regression test **before the fix** — but only if there is a **correct seam** for it. Write the regression test **before the fix** — but only at a **correct seam**: one where the test exercises the real bug pattern as it occurs at the call site. If the available seam looks too shallow, or you cannot tell whether it is, read `references/regression-seams.md`.
A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence. **If no correct seam exists, that itself is the finding.** Note it and carry it into Phase 6 — the architecture is preventing the bug from being locked down.
**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase. At a correct seam:
If a correct seam exists:
1. Turn the Phase 1 loop into a failing test at that seam, narrowed to the symptom captured in Phase 2. 1. Turn the Phase 1 loop into a failing test at that seam, narrowed to the symptom captured in Phase 2.
2. Watch it fail. 2. Watch it fail.
@@ -13,7 +13,7 @@ A feedback loop is a fast, deterministic, agent-runnable pass/fail signal for th
7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode. 7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode.
8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it. 8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it.
9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs. 9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs.
10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `scripts/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you. 10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `assets/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you.
## Iterate on the loop itself ## Iterate on the loop itself
@@ -0,0 +1,24 @@
# Judging a regression-test seam
Read this when Phase 5 leaves you unsure whether the seam available for the regression test is the correct one — either because the obvious seam looks shallow, or because there appears to be no seam at all.
## What makes a seam correct
A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site: the same entry point, the same participants, the same ordering, and the same state the real caller holds when it goes wrong.
## Seams that are too shallow
- A single-caller test when the bug only appears with multiple callers.
- A unit test that cannot replicate the chain of calls that triggered the bug.
- A test that reproduces the symptom by construction — asserting on a value the test itself set — rather than by driving the code path that produces it.
- A test that mocks out the collaborator the bug actually lives in.
A regression test at a shallow seam gives false confidence. It passes forever, including after a change reintroduces the bug at the real call site, and it will be read by the next maintainer as proof the bug is locked down.
## When there is no correct seam
Do not force one, and do not settle for a shallow seam to have something green. Instead:
1. Apply the fix and verify it against the Phase 1 loop directly.
2. Write down which seams you considered and why each was too shallow.
3. Carry that into Phase 6's "what would have prevented this bug" question. A missing seam is an architecture finding — tangled callers, hidden coupling, or a module with no testable boundary — and the handoff is the `improve-codebase-architecture` skill, with those specifics attached.
+27
View File
@@ -0,0 +1,27 @@
# grill-me
Interview the user relentlessly about a plan or design until the decision tree is fully resolved.
## What it does
Turns the agent into an interviewer rather than an implementer. It walks the design tree branch by branch, resolving dependencies between decisions one at a time, and offers its own recommended answer alongside each question so the user has something concrete to push against. Two rules give it its shape: **one question at a time**, and **never ask what the codebase can answer** — if a question is settleable by reading the code, the agent goes and reads the code instead of spending the user's attention on it.
## Composition
This is the plain grilling loop, with no documentation side effects. The sibling `grill-with-docs` skill runs the same interview but additionally challenges answers against the project's `CONTEXT.md` glossary and existing ADRs, and writes decisions back into those files as they crystallise. Reach for that one when the project has a domain model worth defending; reach for this one when it does not, or when nothing should be written down yet.
`triage` composes the documented variant, not this one, when an issue needs fleshing out.
## Usage
```text
/grill-me
```
Describe the plan or design to be stress-tested. Expect questions one at a time, each with a recommended answer.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The whole skill — the interview instruction, the one-question-at-a-time rule, and the explore-instead-of-asking rule |
@@ -0,0 +1,36 @@
# grill-with-docs
The grilling interview, run against the project's domain model — and writing decisions back into `CONTEXT.md` and ADRs as they land.
## What it does
Runs the same relentless one-question-at-a-time interview as `grill-me`, with the project's own documentation as an active participant. During codebase exploration it also locates the domain documentation — a root `CONTEXT.md` and `docs/adr/`, or a `CONTEXT-MAP.md` pointing at per-context glossaries and ADR directories in a multi-context repo — and then uses it four ways:
- **Challenges terms against the glossary.** When the user's usage conflicts with what `CONTEXT.md` already defines, that is raised immediately rather than absorbed.
- **Sharpens fuzzy language** by proposing a precise canonical term ("you're saying 'account' — do you mean the Customer or the User?").
- **Cross-references claims against the code**, and surfaces contradictions between what the user says happens and what the code does.
- **Updates `CONTEXT.md` inline**, the moment a term is resolved, rather than batching changes to the end of the session where they get lost.
Files are created lazily — only when there is something real to write.
ADRs are offered *sparingly*, and only when all three tests pass: the decision is hard to reverse, it would surprise a future reader without the context, and it was a genuine trade-off with real alternatives. Missing any one of the three means no ADR.
## Composition
`grill-me` is the same interview without the documentation side effects — use it when there is no domain model to defend or nothing should be written down yet. `triage` composes this skill (not `grill-me`) at step 4 when an issue needs fleshing out. `improve-codebase-architecture` runs its own grilling loop and borrows this skill's `CONTEXT.md` and ADR discipline for the decisions that come out of it.
## Usage
```text
/grill-with-docs
```
Describe the plan or design. Expect questions one at a time, each with a recommended answer, and expect `CONTEXT.md` to be edited during the session rather than after it.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The interview instruction plus the domain-awareness rules: file layout discovery, the four during-session behaviours, and the three-part ADR test |
| `CONTEXT-FORMAT.md` | Skill-root document, cited when a term is resolved: the structure of a `CONTEXT.md` and how to write a Language entry |
| `ADR-FORMAT.md` | Skill-root document, cited when an ADR is offered: `docs/adr/` naming, sequential numbering, and the ADR template |
@@ -0,0 +1,36 @@
# improve-codebase-architecture
Surface architectural friction and propose deepening opportunities — refactors that turn shallow modules into deep ones.
## What it does
Looks for places where a codebase is hard to understand, hard to test, or hard for an agent to navigate, and proposes refactors that concentrate behaviour behind smaller interfaces. It runs in three stages:
1. **Explore.** Reads the domain glossary and any ADRs in the area first, then walks the codebase with an `Explore` sub-agent — organically, noting friction rather than applying fixed heuristics. The **deletion test** is the filter: imagine deleting the module; if complexity vanishes it was a pass-through, if complexity reappears across N callers it was earning its keep.
2. **Present candidates.** A numbered list, each with files, problem, solution and benefits — benefits stated in terms of *locality* and *leverage* and of how tests would improve. No interfaces are proposed yet; the user picks one.
3. **Grilling loop.** Walks the design tree for the chosen candidate, with documentation side effects landing inline as decisions crystallise.
The skill is opinionated about vocabulary, and that is the point: **module, interface, implementation, depth, seam, adapter, leverage, locality**, used exactly, with no drift into "component", "service", "API" or "boundary". Domain nouns come from `CONTEXT.md`, architecture nouns from `LANGUAGE.md` — so a proposal reads as "the Order intake module", never "the FooBarHandler".
ADRs are treated as decisions not to be re-litigated. A candidate that contradicts one is surfaced only when the friction is real enough to warrant reopening it, and is marked as such.
## Composition
`diagnose` hands off here when a bug's post-mortem concludes that no correct test seam exists, or that callers are tangled — the recommendation is made after the fix is in, not before. The grilling loop follows `grill-with-docs`'s discipline for `CONTEXT.md` entries and ADR offers, and `SKILL.md` names that skill's format documents directly.
## Usage
```text
/improve-codebase-architecture
```
Point at a codebase or an area of one. Expect a numbered candidate list and a "which of these would you like to explore?" before any interface design happens.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Condensed glossary, key principles, and the three-stage process |
| `LANGUAGE.md` | Skill-root document, cited throughout `SKILL.md`: full definitions of every term, the words each one replaces, and the full principle list |
| `INTERFACE-DESIGN.md` | Skill-root document, read at stage 3 when the user wants alternative interfaces explored: the parallel sub-agent "Design It Twice" pattern, framing the problem space, and the per-agent design constraints |
| `DEEPENING.md` | Skill-root document, cited from `INTERFACE-DESIGN.md`: how to deepen a cluster of shallow modules safely, the four dependency categories (in-process, local-substitutable, remote-but-owned, true external), seam discipline, and the replace-don't-layer testing strategy |
@@ -65,7 +65,7 @@ Once the user picks a candidate, drop into a grilling conversation. Walk the des
Side effects happen inline as decisions crystallize: Side effects happen inline as decisions crystallize:
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `/grill-with-docs` (see [CONTEXT-FORMAT.md](../grill-with-docs/CONTEXT-FORMAT.md)). Create the file lazily if it doesn't exist. - **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `grill-with-docs`, in the format `grill-with-docs`'s `CONTEXT-FORMAT.md` defines. Create the file lazily if it doesn't exist.
- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. - **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [ADR-FORMAT.md](../grill-with-docs/ADR-FORMAT.md). - **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See `grill-with-docs`'s `ADR-FORMAT.md`.
- **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md). - **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md).
+32
View File
@@ -0,0 +1,32 @@
# prototype
Build a throwaway prototype that answers one design question — either a runnable terminal app or several UI variations.
## What it does
Treats a prototype as **throwaway code that answers a question**, and lets the question decide the artifact. `SKILL.md` opens with a two-row dispatch table and the run resolves exactly one row before doing anything else:
- *"Does this logic / state model feel right?"* → a tiny interactive terminal app that pushes the state machine through the cases that are hard to reason about on paper.
- *"What should this look like?"* → several radically different UI variations on one route, switchable from a floating bottom bar via a URL search param.
The two branches produce fundamentally different artifacts, so picking wrong wastes the whole prototype. When the question is genuinely ambiguous and the user is unreachable, the skill defaults on the shape of the surrounding code (backend module → logic, page or component → UI) and states the assumption at the top of the prototype rather than silently choosing.
Six rules apply to both branches: throwaway and visibly named as such, one command to run, no persistence by default, no polish, surface the full state after every action or variant switch, and delete or absorb the prototype when it is done. The *answer* is the only durable output — the skill captures it in a commit message, ADR, issue or `NOTES.md` before the code is deleted.
## Usage
```text
/prototype
```
State the design question. For production code, use `tdd`; for talking a design through without building anything, use `grill-me`.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The branch dispatch table and the rules that apply to both branches |
| `references/logic.md` | The logic branch, read only when that row is selected: when it is the right shape, and how to build the interactive terminal app |
| `references/ui.md` | The UI branch, read only when that row is selected: when it is the right shape, and how to build and switch between the variations |
Each reference is self-contained — a run reads one of the two, never both.
+7 -5
View File
@@ -3,8 +3,8 @@ name: prototype
description: > description: >
Use when the user wants a throwaway prototype to answer a design question about Use when the user wants a throwaway prototype to answer a design question about
a data model, state machine or business logic, or to mock up a UI in several a data model, state machine or business logic, or to mock up a UI in several
variations, as in "try a few designs". Not production code -> `tdd`. Not variations. Not production code -> `tdd`. Not talking a design through ->
talking a design through without building -> `grill-me`. `grill-me`.
--- ---
# Prototype # Prototype
@@ -13,10 +13,12 @@ A prototype is **throwaway code that answers a question**. The question decides
## Pick a branch ## Pick a branch
Identify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around: | Question being answered | Build | Reference |
|---|---|---|
| "Does this logic / state model feel right?" | A tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper | `references/logic.md` |
| "What should this look like?" | Several radically different UI variations on one route, switchable via a URL search param and a floating bottom bar | `references/ui.md` |
- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md). Build a tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper. Resolve the row from the user's prompt, the surrounding code, or by asking if the user is around, then read only that reference — each is self-contained.
- **"What should this look like?"** → [UI.md](UI.md). Generate several radically different UI variations on a single route, switchable via a URL search param and a floating bottom bar.
The two branches produce fundamentally different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype. The two branches produce fundamentally different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype.
@@ -9,7 +9,7 @@ A tiny interactive terminal app that lets the user drive a state model by hand.
- "I want to feel out what the API should look like before writing it." - "I want to feel out what the API should look like before writing it."
- Anything where the user wants to **press buttons and watch state change**. - Anything where the user wants to **press buttons and watch state change**.
If the question is "what should this look like" — wrong branch. Use [UI.md](UI.md). If the question is "what should this look like" — wrong branch. Read `references/ui.md`.
## Process ## Process
@@ -72,8 +72,5 @@ When the prototype has done its job, the answer to the question is the only thin
## Anti-patterns ## Anti-patterns
- **Don't add tests.** A prototype that needs tests is no longer a prototype.
- **Don't wire it to the real database.** Use an in-memory store unless the question is specifically about persistence.
- **Don't generalise.** No "what if we wanted to support X later." The prototype answers one question.
- **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module. - **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module.
- **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping. - **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping.
@@ -2,7 +2,7 @@
Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away. Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away.
If the question is about logic/state rather than what something looks like — wrong branch. Use [LOGIC.md](LOGIC.md). If the question is about logic/state rather than what something looks like — wrong branch. Read `references/logic.md`.
## When this is the right shape ## When this is the right shape
@@ -109,4 +109,3 @@ Don't leave variant components or the switcher lying around. They rot fast and c
- **Variants that differ only in colour or copy.** That's a tweak, not a prototype. Real variants disagree about structure. - **Variants that differ only in colour or copy.** That's a tweak, not a prototype. Real variants disagree about structure.
- **Sharing too much code between variants.** A shared `<Header>` is fine; a shared `<Layout>` defeats the point. Each variant should be free to throw out the layout. - **Sharing too much code between variants.** A shared `<Header>` is fine; a shared `<Layout>` defeats the point. Each variant should be free to throw out the layout.
- **Wiring variants to real mutations.** Read-only prototypes are fine. If a variant needs to mutate, point it at a stub — the question is "what should this look like", not "does the backend work". - **Wiring variants to real mutations.** Read-only prototypes are fine. If a variant needs to mutate, point it at a stub — the question is "what should this look like", not "does the backend work".
- **Promoting the prototype directly to production.** The variant code was written under prototype constraints (no tests, minimal error handling). Rewrite it properly when you fold it in.
+31
View File
@@ -0,0 +1,31 @@
# research
Research a tool, library or API from canonical documentation into a directory of structured per-topic reference files.
## What it does
Runs a six-step pipeline: scope against the working directory (what version is actually in use, what is already documented), resolve the topic through Context7, websearch for canonical docs covering whatever Context7 missed, read those sources, deepen one level into the links worth following, then write one markdown file per topic area plus a `sources.md` provenance record.
Four gotchas at the top of `SKILL.md` shape the whole run, and each exists because of a specific failure: the output path is never inferred (a guessed destination scatters a directory's worth of files through someone's source tree); nothing is written outside that path; no empty topic file is ever written (a stub `troubleshooting.md` reads downstream as researched and closed); and a Context7 "no results", redirect or header-only response does not count as coverage. If no topic area has content, the run writes nothing at all — `sources.md` included — and reports what it searched.
The frontmatter pins `model: sonnet` and a closed `allowed-tools` list. Notably it grants no subagent tool, so every `WebFetch` is serial and each fetched page lands in the run's own context — which is why steps 4 and 5 insist on reducing each page to notes before fetching the next, and cap deepening at roughly ten extra pages.
## Composition
`references/file-format.md` is not optional reading before the write step: the `sources.md` field names it defines are matched literally by the downstream provenance validator. Prose written in their place parses as nothing and the check passes having verified nothing.
## Usage
```text
/research
```
Name the topic and the output path — the skill will stop and ask if the path is missing. Supplying starting URLs is treated as a deliberate source choice and skips Context7 resolution and discovery. For documentation derived from existing code or specs, use `write-docs`; for a bug or incident, use `diagnose`.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The four gotchas and the six research steps |
| `references/topics.md` | Read at Step 1 before narrowing scope: the default topic list (`overview`, `installation`, `configuration`, `cli-reference`, `api-reference`, `examples`, and more) and what each file covers |
| `references/file-format.md` | Read at Step 6 before writing: the frontmatter schema for a topic file and the exact `sources.md` field names the provenance validator matches |
+5 -3
View File
@@ -8,10 +8,12 @@ description: >-
metadata: metadata:
category: research category: research
allowed-tools: allowed-tools:
- WebSearch - Grep
- WebFetch - Glob
- Read - Read
- Write - Write
- WebSearch
- WebFetch
- mcp__context7__resolve-library-id - mcp__context7__resolve-library-id
- mcp__context7__query-docs - mcp__context7__query-docs
model: sonnet model: sonnet
@@ -61,4 +63,4 @@ Merge every set of notes, Context7 and web alike, by topic area. Read `reference
Spell the `sources.md` field names exactly as `references/file-format.md` gives them. The downstream provenance validator matches them literally; prose in their place parses as nothing, and the check passes having verified nothing. Spell the `sources.md` field names exactly as `references/file-format.md` gives them. The downstream provenance validator matches them literally; prose in their place parses as nothing, and the check passes having verified nothing.
If no topic area has content, write nothing at all, `sources.md` included, and report what was searched. A directory of empty files is worse than an honest miss. If no topic area has content, write nothing at all, `sources.md` included, and report what was searched.
+32
View File
@@ -0,0 +1,32 @@
# tdd
Test-driven development as a strict red-green-refactor loop, one behaviour at a time.
## What it does
Two convictions drive this skill. The first is about what a test is for: tests verify behaviour through public interfaces, not implementation details. A good test reads like a specification ("user can checkout with valid cart") and survives refactors because it does not care about internal structure. The warning sign for a bad one is precise — the test breaks when you refactor but behaviour has not changed.
The second is an explicit anti-pattern: **do not write all the tests first, then all the implementation.** Horizontal slicing treats RED as "write every test" and GREEN as "write every implementation", and it produces tests of *imagined* behaviour — tests of the shape of things, insensitive to real change, committed to before the implementation was understood. The correct shape is vertical: one test → one implementation → repeat, each cycle informed by what the last one taught you.
The workflow is four stages: plan (confirm the interface and which behaviours matter, with the user — you cannot test everything), fire a tracer bullet (one test proving the path works end to end), loop incrementally one behaviour at a time, then refactor once everything is green. Refactoring while RED is forbidden.
Codebase exploration uses the project's domain glossary, so test names and interface vocabulary match the project's language, and ADRs in the area are respected.
## Usage
```text
/tdd
```
Describe the feature or bug. Expect the skill to ask what the public interface should look like and which behaviours matter most before any code is written. For diagnosing an existing bug rather than building test-first, use `diagnose`; for throwaway exploratory code, use `prototype`.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Philosophy, the horizontal-slicing anti-pattern, the four-stage workflow, and the per-cycle checklist |
| `tests.md` | Skill-root document, cited from Philosophy: worked good and bad test examples |
| `mocking.md` | Skill-root document, cited from Philosophy: mock at system boundaries only, and what not to mock |
| `deep-modules.md` | Skill-root document, cited from stage 1: what a deep module is (small interface, large implementation) and why it is the design to aim for |
| `interface-design.md` | Skill-root document, cited from stage 1: designing interfaces for testability, starting with accepting dependencies rather than creating them |
| `refactoring.md` | Skill-root document, cited from stage 4: the refactor-candidate checklist — duplication, long methods, shallow modules, feature envy, primitive obsession |
+2 -2
View File
@@ -1,6 +1,6 @@
# Writing Agent Briefs # Writing Agent Briefs
An agent brief is a structured comment posted on a GitHub issue when it moves to `ready-for-agent`. It is the authoritative specification that an AFK agent will work from. The original issue body and discussion are context — the agent brief is the contract. An agent brief is a structured comment posted on an issue in the issue tracker when it moves to `ready-for-agent`. It is the authoritative specification that an AFK agent will work from. The original issue body and discussion are context — the agent brief is the contract.
## Principles ## Principles
@@ -27,7 +27,7 @@ Describe **what** the system should do, not **how** to implement it. The agent w
The agent needs to know when it's done. Every agent brief must have concrete, testable acceptance criteria. Each criterion should be independently verifiable. The agent needs to know when it's done. Every agent brief must have concrete, testable acceptance criteria. Each criterion should be independently verifiable.
- **Good:** "Running `gh issue list --label needs-triage` returns issues that have been through initial classification" - **Good:** "Querying the issue tracker for the `needs-triage` label returns issues that have been through initial classification"
- **Bad:** "Triage should work correctly" - **Bad:** "Triage should work correctly"
### Explicit scope boundaries ### Explicit scope boundaries
+35
View File
@@ -0,0 +1,35 @@
# triage
Move issues on the project issue tracker through a small state machine of triage roles.
## What it does
Gives issue triage an explicit state model and a fixed set of moves. Every issue carries exactly one **category** role (`bug`, `enhancement`) and one **state** role (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`); conflicting state roles are flagged to the maintainer before anything else happens. Unlabeled issues normally enter at `needs-triage`; `needs-info` returns there once the reporter replies. The maintainer can override at any point, and unusual transitions are questioned rather than executed silently.
A run does one of three things depending on what the maintainer asks for:
- **Show what needs attention** — three buckets, oldest first: unlabeled, `needs-triage`, and `needs-info` with reporter activity since the last triage notes.
- **Triage a specific issue** — gather context (including prior triage notes, so resolved questions are not re-asked, and `.out-of-scope/` records that resemble the issue), recommend a category and state with reasoning, attempt reproduction for bugs *before* any grilling, run a `grill-with-docs` session if the issue needs fleshing out, then apply the outcome.
- **Quick state override** — "move #42 to ready-for-agent" is trusted and applied directly, skipping grilling, after confirming the exact changes.
Two hard rules: every comment or issue the skill posts during triage must open with the AI-generated disclaimer, and the canonical role names above are *not* necessarily the label strings in the tracker — the mapping has to be supplied to the run.
## Composition
`grill-with-docs` is invoked at step 4 when an issue needs fleshing out; whatever that session establishes is carried into the triage notes so the work is not lost. The reverse direction also exists: `diagnose` names this skill as the place to send a *reported* bug that needs filing rather than debugging.
## Usage
```text
/triage
```
Then describe what you want in natural language — "show me anything that needs my attention", "let's look at #42", "move #42 to ready-for-agent", "what's ready for agents to pick up?".
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The roles and state machine, the three invocation modes, the needs-info template, and how to resume a prior session |
| `AGENT-BRIEF.md` | Skill-root document, cited when an issue moves to `ready-for-agent` (and reused for `ready-for-human`): how to write a brief that stays durable for weeks while the codebase moves under it — describe interfaces and behavioural contracts, not line numbers |
| `OUT-OF-SCOPE.md` | Skill-root document, cited when an enhancement is closed `wontfix` and when checking for prior rejections: how the `.out-of-scope/` knowledge base is laid out and what it is for — institutional memory, and deduplication against re-litigated requests |
+1 -1
View File
@@ -35,7 +35,7 @@ Five **state** roles:
Every triaged issue should carry exactly one category role and one state role. If state roles conflict, flag it and ask the maintainer before doing anything else. Every triaged issue should carry exactly one category role and one state role. If state roles conflict, flag it and ask the maintainer before doing anything else.
These are canonical role names — the actual label strings used in the issue tracker may differ. The mapping should have been provided to you - run `/setup-matt-pocock-skills` if not. These are canonical role names — the actual label strings used in the issue tracker may differ. Resolve each canonical name against the tracker's live label set before applying it, using whichever tracker skill this install provides. If a name has no counterpart there, report the gap and ask the maintainer for the mapping — never substitute a guess.
State transitions: an unlabeled issue normally goes to `needs-triage` first; from there it moves to `needs-info`, `ready-for-agent`, `ready-for-human`, or `wontfix`. `needs-info` returns to `needs-triage` once the reporter replies. The maintainer can override at any time — flag transitions that look unusual and ask before proceeding. State transitions: an unlabeled issue normally goes to `needs-triage` first; from there it moves to `needs-info`, `ready-for-agent`, `ready-for-human`, or `wontfix`. `needs-info` returns to `needs-triage` once the reporter replies. The maintainer can override at any time — flag transitions that look unusual and ask before proceeding.
+30
View File
@@ -0,0 +1,30 @@
# write-docs
Produce technical documentation derived from code and spec, one section at a time, with a confirmation gate on every section.
## What it does
Casts the agent as a technical writer with one non-negotiable constraint: **every claim must be traceable to a source file line, a spec section, or an explicit user statement.** Nothing is invented, and behaviour that genuinely cannot be documented from the available sources is marked out-of-scope rather than explained away.
The process is eight steps — identify scope, read and extract, gap check, draft section by section, confirmation gate, delta summary, reader testing, finalise — and several of them are deliberately gated on the human:
- Files are read only after the user approves them by name. The skill may propose candidates; it waits.
- The **gap check** presents what the code does say and asks the user to fill only what it does not: caller intent, error-handling rationale, non-obvious side effects.
- No section is finalised until the full revised text has been shown. The skill never gates on output the user has not seen, and never reprints the whole document — all edits are surgical.
- **Reader testing** predicts 5–10 questions a target reader would ask, then spawns a sub-agent that receives only the finished doc and the questions — no source files. If the doc cannot answer them, neither can the sub-agent, and the run loops back to drafting.
Summary and overview sections are written last, once the detail sections are stable.
## Usage
```text
/write-docs
```
Name the files or modules to document, the target audience (developer / user / contributor / internal), and the documentation type (reference, guide, README section, inline comment, changelog entry). For a PRD, ADR or decision doc, use `grill-me` or `grill-with-docs` instead — those have dedicated handling.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The whole skill — role, use/do-not-use boundaries, required inputs, constraints, the eight-step process, output format, failure handling, and a nine-item self-check |
+2 -2
View File
@@ -35,7 +35,7 @@ You are a technical writer that produces documentation by reading code and spec
- User says "write docs for X", "document this", "create docs for this feature", "write a README for this" - User says "write docs for X", "document this", "create docs for this feature", "write a README for this"
**Do not use when:** **Do not use when:**
- User wants a PRD, decision doc, or architecture proposal → `to-prd` or `grill-me` - User wants a PRD, decision doc, or architecture proposal → `grill-me` or `grill-with-docs`
- User wants to document a skill file (skill files are self-describing) - User wants to document a skill file (skill files are self-describing)
- User wants marketing or blog copy - User wants marketing or blog copy
- Documentation requires tacit organisational knowledge that cannot be read from code or spec - Documentation requires tacit organisational knowledge that cannot be read from code or spec
@@ -88,7 +88,7 @@ You are a technical writer that produces documentation by reading code and spec
- Stage skipped without a logged reason → flag and require the one-sentence log before continuing - Stage skipped without a logged reason → flag and require the one-sentence log before continuing
- Code behaviour is undocumentable (internal implementation detail, no public spec) → note as out-of-scope in the doc; do not invent an explanation - Code behaviour is undocumentable (internal implementation detail, no public spec) → note as out-of-scope in the doc; do not invent an explanation
- Reader Testing sub-agent fails on multiple questions → surface the failures, return to step 4; do not mark complete - Reader Testing sub-agent fails on multiple questions → surface the failures, return to step 4; do not mark complete
- Requested output is a PRD, decision doc, or architecture proposal → redirect to `to-prd`, `grill-me`, or `grill-with-docs` - Requested output is a PRD, decision doc, or architecture proposal → redirect to `grill-me` or `grill-with-docs`
## Self-check ## Self-check
+25
View File
@@ -0,0 +1,25 @@
# zoom-out
Ask the agent to go up a layer of abstraction and map the modules and callers around unfamiliar code.
## What it does
A single-purpose prompt for the moment you land in a part of the codebase you do not know. Instead of answering at the level of the file in front of it, the agent climbs one layer and produces a map of the relevant modules and their callers — and names them using the project's own domain glossary vocabulary, so the map lines up with the language the rest of the repo already uses.
## Hand-invoked only
`SKILL.md` sets `disable-model-invocation: true`, so the router never selects this skill on its own and no other skill can hand off to it. It runs when the human asks for it. That also means its description is written as one plain human-facing sentence — it carries no trigger phrasing or boundary clause, because nothing routes on it.
## Usage
```text
/zoom-out
```
Best used with the unfamiliar code already in context — the skill widens the view around what you are looking at rather than picking a starting point for you.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | The whole skill — a single instruction, no supporting files |
@@ -33,6 +33,6 @@ hand-edit made outside `agentsmd-author` — the audit is what confirms the resu
| `tests/validate-drift.bats` | (source-only) Bats test suite for `scripts/validate-drift.sh` | | `tests/validate-drift.bats` | (source-only) Bats test suite for `scripts/validate-drift.sh` |
Rows marked **(source-only)** exist in the authoring source (`.apm/skills/agentsmd-audit/`) but are Rows marked **(source-only)** exist in the authoring source (`.apm/skills/agentsmd-audit/`) but are
not present in an installed plugin: `scripts/sync-plugin-content.sh` strips `<category>/<name>/tests` not present in an installed plugin: the repo's `scripts/sync-plugin-content.sh` strips
when it generates the flat mirror, because these are dev-time fixtures no plugin host needs to `<category>/<name>/tests` when it generates the flat mirror, because these are dev-time fixtures no
discover (ADR-0017). Run them from a repo checkout, not from an install. plugin host needs to discover (ADR-0017). Run them from a repo checkout, not from an install.
@@ -1,10 +1,10 @@
--- ---
name: agentsmd-audit name: agentsmd-audit
description: > description: >
Use when the user wants a repo's AGENTS.md audited — "audit this AGENTS.md", Use when the user wants a repo's AGENTS.md audited for secrets, structure
"is this AGENTS.md safe to commit" — or after a hand-edit outside and drift — "is this AGENTS.md safe to commit" — or after a hand-edit
`agentsmd-author`. Reports secrets, structure and drift; never edits. outside `agentsmd-author`.
Not for CLAUDE.md or provider files -> `provider-adapter-author`. Not converting a provider file -> `provider-adapter-author`.
Not writing AGENTS.md -> `agentsmd-author`. Not writing AGENTS.md -> `agentsmd-author`.
allowed-tools: Bash Read allowed-tools: Bash Read
metadata: metadata:
@@ -31,7 +31,7 @@ bash scripts/validate-structure.sh <repo-root>
bash scripts/validate-drift.sh <repo-root> bash scripts/validate-drift.sh <repo-root>
``` ```
Each script walks the repo for every `AGENTS.md` file (root and nested, excluding `.git`, `node_modules`, `vendor`, and similar) and prints `FAIL`/`INFO`/`SUGGESTION` lines with `Why`/`Fix` (or `Note`) per finding. A nonzero exit means at least one FAIL was found in that dimension. If a script cannot execute (`python3` unavailable, Bash denied), fall back to manual review: scan for real-looking credentials, check common sections are present, and spot-check a few referenced commands/paths by hand. Grade a manual finding the way the scripts grade theirs: a missing common section (e.g. no "Security" heading) is informational, not a failure — not every repo needs every section from the checklist. Only flag a FAIL when the file is empty, entirely unfilled placeholder text, or contains a real embedded secret/stale reference. Each script walks the repo for every `AGENTS.md` file (root and nested, excluding `.git`, `node_modules`, `vendor`, and similar) and prints `FAIL` lines, plus `INFO`/`SUGGESTION` where applicable, with `Why`/`Fix` (or `Note`) per finding. A nonzero exit means at least one FAIL was found in that dimension. If a script cannot execute (`python3` unavailable, Bash denied), fall back to manual review: scan for real-looking credentials, check common sections are present, and spot-check a few referenced commands/paths by hand. Grade a manual finding the way the scripts grade theirs: a missing common section (e.g. no "Security" heading) is informational, not a failure — not every repo needs every section from the checklist. Only flag a FAIL when the file is empty, entirely unfilled placeholder text, or contains a real embedded secret/stale reference.
## Step 2 — Report ## Step 2 — Report
@@ -43,16 +43,4 @@ Checked: secrets · structure · drift
Then output only findings that were found, in this order within a repo: `### Secrets`, `### Structure`, `### Drift`. Omit a dimension heading entirely if it produced nothing — its absence confirms it passed. Report each finding verbatim as emitted by the scripts (they already carry file:line, Why/Fix or Note). Then output only findings that were found, in this order within a repo: `### Secrets`, `### Structure`, `### Drift`. Omit a dimension heading entirely if it produced nothing — its absence confirms it passed. Report each finding verbatim as emitted by the scripts (they already carry file:line, Why/Fix or Note).
Close with a result block: Close with a `## Result` block holding one line: `PASS`, `PASS (N suggestions)`, or `FAIL (N fails · M suggestions)`, each optionally followed by ` · P info`. Omit the suggestion count when there are none, and omit `· P info` when there are none. INFO and SUGGESTION findings are observational — they never flip PASS to FAIL. Do not fix anything — this skill reports and proposes only. Point the user to `agentsmd-author` to apply fixes.
```text
## Result
PASS
PASS · P info
PASS (N suggestions) · P info
FAIL (N fails)
FAIL (N fails) · P info
```
INFO and SUGGESTION findings are observational — they never flip PASS to FAIL. Do not fix anything — this skill reports and proposes only. Point the user to `agentsmd-author` to apply fixes.
@@ -3,7 +3,7 @@ name: agentsmd-author
description: > description: >
Use when the user wants a repo's AGENTS.md written or updated, root or Use when the user wants a repo's AGENTS.md written or updated, root or
nested, including "document this for AI coding tools". Writes only verified nested, including "document this for AI coding tools". Writes only verified
conventions. Not review-only -> `agentsmd-audit`. Not for CLAUDE.md -> conventions. Not review-only -> `agentsmd-audit`. Not converting CLAUDE.md ->
`provider-adapter-author`. `provider-adapter-author`.
allowed-tools: Bash Read Write Edit allowed-tools: Bash Read Write Edit
metadata: metadata:
@@ -36,8 +36,8 @@ AGENTS.md has no required schema. Use only sections that reflect something real
## Step 4 — Check for an existing provider file ## Step 4 — Check for an existing provider file
Look for `CLAUDE.md`, `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, or similar in the target repo. If one exists and now duplicates content the AGENTS.md you just wrote/updated already owns, invoke the `provider-adapter-author` skill on it to reconcile. Look for `CLAUDE.md`, `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, or similar in the target repo. If one exists, invoke the `provider-adapter-author` skill on it to reconcile — whether it duplicates content the AGENTS.md you just wrote/updated now owns, or is merely stale or missing a pointer to it. Never edit it yourself in either case.
## Step 5 — Audit and report ## Step 5 — Audit and report
Invoke the `agentsmd-audit` skill directly on the AGENTS.md file(s) you just wrote or updated. This closeout is mandatory, not optional, even when the change looks trivial — never sign the work off on your own judgment. Resolve any FAIL findings before considering the work done — re-invoke this skill's own writing steps to fix them, then re-run the audit, same as any other close-the-loop check. Report what was created/changed, whether a provider file was reconciled, and the audit's final result. Invoke the `agentsmd-audit` skill on the target repo root — its validators take a `<repo-root>` and walk the tree for every AGENTS.md themselves; there is no per-file entry point. This closeout is mandatory, not optional, even when the change looks trivial — never sign the work off on your own judgment. Resolve any FAIL findings before considering the work done — re-invoke this skill's own writing steps to fix them, then re-run the audit, same as any other close-the-loop check. Report what was created/changed, whether a provider file was reconciled, and the audit's final result.
@@ -31,6 +31,6 @@ Provide the path to the provider-specific file to convert (and the target repo r
| `tests/validate-adapter.bats` | (source-only) Bats test suite for `scripts/validate-adapter.sh` | | `tests/validate-adapter.bats` | (source-only) Bats test suite for `scripts/validate-adapter.sh` |
Rows marked **(source-only)** exist in the authoring source (`.apm/skills/provider-adapter-author/`) Rows marked **(source-only)** exist in the authoring source (`.apm/skills/provider-adapter-author/`)
but are not present in an installed plugin: `scripts/sync-plugin-content.sh` strips but are not present in an installed plugin: the repo's `scripts/sync-plugin-content.sh` strips
`<category>/<name>/tests` when it generates the flat mirror, because these are dev-time fixtures no `<category>/<name>/tests` when it generates the flat mirror, because these are dev-time fixtures no
plugin host needs to discover (ADR-0017). Run them from a repo checkout, not from an install. plugin host needs to discover (ADR-0017). Run them from a repo checkout, not from an install.
@@ -2,7 +2,8 @@
name: provider-adapter-author name: provider-adapter-author
description: > description: >
Use when a provider file (CLAUDE.md, .cursor rules, copilot-instructions) Use when a provider file (CLAUDE.md, .cursor rules, copilot-instructions)
duplicating the repo's AGENTS.md should be cut to a thin adapter. duplicating the repo's AGENTS.md should be cut to a thin adapter — "make
CLAUDE.md just import AGENTS.md".
Not writing the AGENTS file -> `agentsmd-author`. Not writing the AGENTS file -> `agentsmd-author`.
Not auditing the AGENTS file -> `agentsmd-audit`. Not auditing the AGENTS file -> `agentsmd-audit`.
allowed-tools: Bash Read Edit Write allowed-tools: Bash Read Edit Write
@@ -16,7 +17,6 @@ metadata:
## Gotchas ## Gotchas
- Assume a provider has no cross-file import mechanism until you have confirmed it has one. Claude Code is the exception, not the rule: a `CLAUDE.md` may consist of nothing but `@path` lines, while the same `@AGENTS.md` line in a Cursor rule or a Copilot instructions file is inert text no tool resolves. Pass `--no-import-syntax` to `scripts/validate-adapter.sh` for those providers. - Assume a provider has no cross-file import mechanism until you have confirmed it has one. Claude Code is the exception, not the rule: a `CLAUDE.md` may consist of nothing but `@path` lines, while the same `@AGENTS.md` line in a Cursor rule or a Copilot instructions file is inert text no tool resolves. Pass `--no-import-syntax` to `scripts/validate-adapter.sh` for those providers.
- Works standalone or composed-into by `agentsmd-author` — behave identically either way; don't assume a caller skill exists.
## Step 1 — Detect ## Step 1 — Detect
@@ -43,7 +43,7 @@ Run the bundled check before finishing — this is the skill's own closeout gate
bash scripts/validate-adapter.sh [--no-import-syntax] [--max-lines N] <adapter-file> <agents-md-file> bash scripts/validate-adapter.sh [--no-import-syntax] [--max-lines N] <adapter-file> <agents-md-file>
``` ```
Fix any `FAIL` by editing the provider file, and re-run until it exits `0`. The size `FAIL` advises moving provider-agnostic content into `AGENTS.md`; disregard that half of its wording and delete the redundant lines instead. Fix any `FAIL` by editing the provider file, and re-run until it exits `0`.
## Step 4 — Report ## Step 4 — Report
@@ -16,10 +16,15 @@ Arguments:
Options: Options:
--no-import-syntax The target provider has no native cross-file import --no-import-syntax The target provider has no native cross-file import
mechanism. Accept a plain-text pointer mention of mechanism. Require a plain-text pointer line naming
"AGENTS.md" instead of requiring an @import-style line. "AGENTS.md" instead of an @import-style line; an
@AGENTS.md line alone does not satisfy it, because
such a provider never resolves it. Without this flag
an actual @import line is required, and naming
AGENTS.md in prose alone does not satisfy it.
--max-lines N Max non-blank lines allowed in the adapter file before --max-lines N Max non-blank lines allowed in the adapter file before
it's considered no longer "thin". Default: 60. it's considered no longer "thin". Must be a
non-negative integer. Default: 60.
--help, -h Show this help and exit 0. --help, -h Show this help and exit 0.
Exit codes: Exit codes:
@@ -44,7 +49,15 @@ while [[ $# -gt 0 ]]; do
shift shift
;; ;;
--max-lines) --max-lines)
MAX_LINES="${2:-}" if [[ $# -lt 2 ]]; then
echo "Error: --max-lines requires a value (a non-negative integer)." >&2
exit 1
fi
MAX_LINES="$2"
if [[ ! "$MAX_LINES" =~ ^[0-9]+$ ]]; then
echo "Error: --max-lines expects a non-negative integer, got '$MAX_LINES'." >&2
exit 1
fi
shift 2 shift 2
;; ;;
*) *)
@@ -94,21 +107,25 @@ if not adapter_content.strip():
IMPORT_RE = re.compile(r'(?m)^\s*@\S*AGENTS\.md\s*$') IMPORT_RE = re.compile(r'(?m)^\s*@\S*AGENTS\.md\s*$')
lines = adapter_content.splitlines() lines = adapter_content.splitlines()
import_lines = [ln for ln in lines if IMPORT_RE.match(ln)] import_lines = [ln for ln in lines if IMPORT_RE.match(ln)]
# A prose pointer is any line naming AGENTS.md that is not itself an import
# line — an inert `@AGENTS.md` in a provider that resolves no imports points
# a reader at nothing.
pointer_lines = [ln for ln in lines if not IMPORT_RE.match(ln) and "AGENTS.md" in ln]
if no_import_syntax: if no_import_syntax:
has_reference = "AGENTS.md" in adapter_content has_reference = bool(pointer_lines)
else: else:
has_reference = bool(import_lines) or "AGENTS.md" in adapter_content has_reference = bool(import_lines)
if not has_reference: if not has_reference:
has_fail = True has_fail = True
print(f"FAIL Adapter has no reference to AGENTS.md — {adapter_path}") print(f"FAIL Adapter has no reference to AGENTS.md — {adapter_path}")
if no_import_syntax: if no_import_syntax:
print(" Why: This provider has no import syntax, so the adapter must at least mention AGENTS.md as a text pointer.") print(" Why: This provider resolves no cross-file import, so the adapter must point at AGENTS.md in prose; an `@AGENTS.md` line here is inert text.")
print(" Fix: Add a sentence like \"See AGENTS.md at the repo root for shared conventions.\"") print(" Fix: Add a sentence like \"See AGENTS.md at the repo root for shared conventions.\"")
else: else:
print(" Why: A thin adapter must import AGENTS.md (e.g. `@AGENTS.md`) rather than silently omitting it.") print(" Why: A thin adapter must import AGENTS.md with an `@AGENTS.md` line; merely naming the file in prose defers nothing.")
print(" Fix: Add an `@AGENTS.md` (or equivalent relative path) import line.") print(" Fix: Add an `@AGENTS.md` (or equivalent relative path) import line, or pass --no-import-syntax if this provider resolves no imports.")
print() print()
# --- Duplication check --- # --- Duplication check ---
@@ -132,7 +149,7 @@ if non_blank_count > max_lines:
has_fail = True has_fail = True
print(f"FAIL Adapter is not thin — {adapter_path}") print(f"FAIL Adapter is not thin — {adapter_path}")
print(f" Why: {non_blank_count} non-blank lines exceeds the {max_lines}-line threshold for a thin adapter.") print(f" Why: {non_blank_count} non-blank lines exceeds the {max_lines}-line threshold for a thin adapter.")
print(" Fix: Move provider-agnostic content into AGENTS.md; keep only genuinely provider-specific additions here.") print(" Fix: Delete the lines already covered by AGENTS.md; keep only genuinely provider-specific additions here.")
print() print()
if has_fail: if has_fail:
@@ -115,6 +115,51 @@ EOF
assert_output --partial "no reference" assert_output --partial "no reference"
} }
@test "the two --no-import-syntax branches disagree: a text-pointer-only adapter fails in default mode" {
ADAPTER="$TMPDIR/copilot-instructions.md"
cat > "$ADAPTER" <<'EOF'
See AGENTS.md at the repo root for setup, style, and testing conventions.
## Copilot-specific
Prefer inline suggestions over chat for one-line edits.
EOF
run bash "$SCRIPT" "$ADAPTER" "$AGENTS_MD"
assert_failure
assert_output --partial "no reference"
run bash "$SCRIPT" --no-import-syntax "$ADAPTER" "$AGENTS_MD"
assert_success
}
@test "with --no-import-syntax, an inert @AGENTS.md line alone is not a prose pointer" {
ADAPTER="$TMPDIR/copilot-instructions.md"
cat > "$ADAPTER" <<'EOF'
@AGENTS.md
## Copilot-specific
Prefer inline suggestions over chat for one-line edits.
EOF
run bash "$SCRIPT" --no-import-syntax "$ADAPTER" "$AGENTS_MD"
assert_failure
assert_output --partial "no reference"
}
@test "--max-lines as the final argument reports a real error instead of failing silently" {
ADAPTER="$TMPDIR/CLAUDE.md"
echo "@AGENTS.md" > "$ADAPTER"
run bash "$SCRIPT" "$ADAPTER" "$AGENTS_MD" --max-lines
assert_failure
assert_output --partial "--max-lines requires a value"
}
@test "--max-lines rejects a non-numeric value with a real error" {
ADAPTER="$TMPDIR/CLAUDE.md"
echo "@AGENTS.md" > "$ADAPTER"
run bash "$SCRIPT" --max-lines abc "$ADAPTER" "$AGENTS_MD"
assert_failure
assert_output --partial "non-negative integer"
}
@test "--help exits 0 and documents usage" { @test "--help exits 0 and documents usage" {
run bash "$SCRIPT" --help run bash "$SCRIPT" --help
assert_success assert_success
+3 -3
View File
@@ -33,6 +33,6 @@ hand-edit made outside `agentsmd-author` — the audit is what confirms the resu
| `tests/validate-drift.bats` | (source-only) Bats test suite for `scripts/validate-drift.sh` | | `tests/validate-drift.bats` | (source-only) Bats test suite for `scripts/validate-drift.sh` |
Rows marked **(source-only)** exist in the authoring source (`.apm/skills/agentsmd-audit/`) but are Rows marked **(source-only)** exist in the authoring source (`.apm/skills/agentsmd-audit/`) but are
not present in an installed plugin: `scripts/sync-plugin-content.sh` strips `<category>/<name>/tests` not present in an installed plugin: the repo's `scripts/sync-plugin-content.sh` strips
when it generates the flat mirror, because these are dev-time fixtures no plugin host needs to `<category>/<name>/tests` when it generates the flat mirror, because these are dev-time fixtures no
discover (ADR-0017). Run them from a repo checkout, not from an install. plugin host needs to discover (ADR-0017). Run them from a repo checkout, not from an install.
+6 -18
View File
@@ -1,10 +1,10 @@
--- ---
name: agentsmd-audit name: agentsmd-audit
description: > description: >
Use when the user wants a repo's AGENTS.md audited — "audit this AGENTS.md", Use when the user wants a repo's AGENTS.md audited for secrets, structure
"is this AGENTS.md safe to commit" — or after a hand-edit outside and drift — "is this AGENTS.md safe to commit" — or after a hand-edit
`agentsmd-author`. Reports secrets, structure and drift; never edits. outside `agentsmd-author`.
Not for CLAUDE.md or provider files -> `provider-adapter-author`. Not converting a provider file -> `provider-adapter-author`.
Not writing AGENTS.md -> `agentsmd-author`. Not writing AGENTS.md -> `agentsmd-author`.
allowed-tools: Bash Read allowed-tools: Bash Read
metadata: metadata:
@@ -31,7 +31,7 @@ bash scripts/validate-structure.sh <repo-root>
bash scripts/validate-drift.sh <repo-root> bash scripts/validate-drift.sh <repo-root>
``` ```
Each script walks the repo for every `AGENTS.md` file (root and nested, excluding `.git`, `node_modules`, `vendor`, and similar) and prints `FAIL`/`INFO`/`SUGGESTION` lines with `Why`/`Fix` (or `Note`) per finding. A nonzero exit means at least one FAIL was found in that dimension. If a script cannot execute (`python3` unavailable, Bash denied), fall back to manual review: scan for real-looking credentials, check common sections are present, and spot-check a few referenced commands/paths by hand. Grade a manual finding the way the scripts grade theirs: a missing common section (e.g. no "Security" heading) is informational, not a failure — not every repo needs every section from the checklist. Only flag a FAIL when the file is empty, entirely unfilled placeholder text, or contains a real embedded secret/stale reference. Each script walks the repo for every `AGENTS.md` file (root and nested, excluding `.git`, `node_modules`, `vendor`, and similar) and prints `FAIL` lines, plus `INFO`/`SUGGESTION` where applicable, with `Why`/`Fix` (or `Note`) per finding. A nonzero exit means at least one FAIL was found in that dimension. If a script cannot execute (`python3` unavailable, Bash denied), fall back to manual review: scan for real-looking credentials, check common sections are present, and spot-check a few referenced commands/paths by hand. Grade a manual finding the way the scripts grade theirs: a missing common section (e.g. no "Security" heading) is informational, not a failure — not every repo needs every section from the checklist. Only flag a FAIL when the file is empty, entirely unfilled placeholder text, or contains a real embedded secret/stale reference.
## Step 2 — Report ## Step 2 — Report
@@ -43,16 +43,4 @@ Checked: secrets · structure · drift
Then output only findings that were found, in this order within a repo: `### Secrets`, `### Structure`, `### Drift`. Omit a dimension heading entirely if it produced nothing — its absence confirms it passed. Report each finding verbatim as emitted by the scripts (they already carry file:line, Why/Fix or Note). Then output only findings that were found, in this order within a repo: `### Secrets`, `### Structure`, `### Drift`. Omit a dimension heading entirely if it produced nothing — its absence confirms it passed. Report each finding verbatim as emitted by the scripts (they already carry file:line, Why/Fix or Note).
Close with a result block: Close with a `## Result` block holding one line: `PASS`, `PASS (N suggestions)`, or `FAIL (N fails · M suggestions)`, each optionally followed by ` · P info`. Omit the suggestion count when there are none, and omit `· P info` when there are none. INFO and SUGGESTION findings are observational — they never flip PASS to FAIL. Do not fix anything — this skill reports and proposes only. Point the user to `agentsmd-author` to apply fixes.
```text
## Result
PASS
PASS · P info
PASS (N suggestions) · P info
FAIL (N fails)
FAIL (N fails) · P info
```
INFO and SUGGESTION findings are observational — they never flip PASS to FAIL. Do not fix anything — this skill reports and proposes only. Point the user to `agentsmd-author` to apply fixes.
+3 -3
View File
@@ -3,7 +3,7 @@ name: agentsmd-author
description: > description: >
Use when the user wants a repo's AGENTS.md written or updated, root or Use when the user wants a repo's AGENTS.md written or updated, root or
nested, including "document this for AI coding tools". Writes only verified nested, including "document this for AI coding tools". Writes only verified
conventions. Not review-only -> `agentsmd-audit`. Not for CLAUDE.md -> conventions. Not review-only -> `agentsmd-audit`. Not converting CLAUDE.md ->
`provider-adapter-author`. `provider-adapter-author`.
allowed-tools: Bash Read Write Edit allowed-tools: Bash Read Write Edit
metadata: metadata:
@@ -36,8 +36,8 @@ AGENTS.md has no required schema. Use only sections that reflect something real
## Step 4 — Check for an existing provider file ## Step 4 — Check for an existing provider file
Look for `CLAUDE.md`, `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, or similar in the target repo. If one exists and now duplicates content the AGENTS.md you just wrote/updated already owns, invoke the `provider-adapter-author` skill on it to reconcile. Look for `CLAUDE.md`, `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, or similar in the target repo. If one exists, invoke the `provider-adapter-author` skill on it to reconcile — whether it duplicates content the AGENTS.md you just wrote/updated now owns, or is merely stale or missing a pointer to it. Never edit it yourself in either case.
## Step 5 — Audit and report ## Step 5 — Audit and report
Invoke the `agentsmd-audit` skill directly on the AGENTS.md file(s) you just wrote or updated. This closeout is mandatory, not optional, even when the change looks trivial — never sign the work off on your own judgment. Resolve any FAIL findings before considering the work done — re-invoke this skill's own writing steps to fix them, then re-run the audit, same as any other close-the-loop check. Report what was created/changed, whether a provider file was reconciled, and the audit's final result. Invoke the `agentsmd-audit` skill on the target repo root — its validators take a `<repo-root>` and walk the tree for every AGENTS.md themselves; there is no per-file entry point. This closeout is mandatory, not optional, even when the change looks trivial — never sign the work off on your own judgment. Resolve any FAIL findings before considering the work done — re-invoke this skill's own writing steps to fix them, then re-run the audit, same as any other close-the-loop check. Report what was created/changed, whether a provider file was reconciled, and the audit's final result.
@@ -31,6 +31,6 @@ Provide the path to the provider-specific file to convert (and the target repo r
| `tests/validate-adapter.bats` | (source-only) Bats test suite for `scripts/validate-adapter.sh` | | `tests/validate-adapter.bats` | (source-only) Bats test suite for `scripts/validate-adapter.sh` |
Rows marked **(source-only)** exist in the authoring source (`.apm/skills/provider-adapter-author/`) Rows marked **(source-only)** exist in the authoring source (`.apm/skills/provider-adapter-author/`)
but are not present in an installed plugin: `scripts/sync-plugin-content.sh` strips but are not present in an installed plugin: the repo's `scripts/sync-plugin-content.sh` strips
`<category>/<name>/tests` when it generates the flat mirror, because these are dev-time fixtures no `<category>/<name>/tests` when it generates the flat mirror, because these are dev-time fixtures no
plugin host needs to discover (ADR-0017). Run them from a repo checkout, not from an install. plugin host needs to discover (ADR-0017). Run them from a repo checkout, not from an install.
@@ -2,7 +2,8 @@
name: provider-adapter-author name: provider-adapter-author
description: > description: >
Use when a provider file (CLAUDE.md, .cursor rules, copilot-instructions) Use when a provider file (CLAUDE.md, .cursor rules, copilot-instructions)
duplicating the repo's AGENTS.md should be cut to a thin adapter. duplicating the repo's AGENTS.md should be cut to a thin adapter — "make
CLAUDE.md just import AGENTS.md".
Not writing the AGENTS file -> `agentsmd-author`. Not writing the AGENTS file -> `agentsmd-author`.
Not auditing the AGENTS file -> `agentsmd-audit`. Not auditing the AGENTS file -> `agentsmd-audit`.
allowed-tools: Bash Read Edit Write allowed-tools: Bash Read Edit Write
@@ -16,7 +17,6 @@ metadata:
## Gotchas ## Gotchas
- Assume a provider has no cross-file import mechanism until you have confirmed it has one. Claude Code is the exception, not the rule: a `CLAUDE.md` may consist of nothing but `@path` lines, while the same `@AGENTS.md` line in a Cursor rule or a Copilot instructions file is inert text no tool resolves. Pass `--no-import-syntax` to `scripts/validate-adapter.sh` for those providers. - Assume a provider has no cross-file import mechanism until you have confirmed it has one. Claude Code is the exception, not the rule: a `CLAUDE.md` may consist of nothing but `@path` lines, while the same `@AGENTS.md` line in a Cursor rule or a Copilot instructions file is inert text no tool resolves. Pass `--no-import-syntax` to `scripts/validate-adapter.sh` for those providers.
- Works standalone or composed-into by `agentsmd-author` — behave identically either way; don't assume a caller skill exists.
## Step 1 — Detect ## Step 1 — Detect
@@ -43,7 +43,7 @@ Run the bundled check before finishing — this is the skill's own closeout gate
bash scripts/validate-adapter.sh [--no-import-syntax] [--max-lines N] <adapter-file> <agents-md-file> bash scripts/validate-adapter.sh [--no-import-syntax] [--max-lines N] <adapter-file> <agents-md-file>
``` ```
Fix any `FAIL` by editing the provider file, and re-run until it exits `0`. The size `FAIL` advises moving provider-agnostic content into `AGENTS.md`; disregard that half of its wording and delete the redundant lines instead. Fix any `FAIL` by editing the provider file, and re-run until it exits `0`.
## Step 4 — Report ## Step 4 — Report
@@ -16,10 +16,15 @@ Arguments:
Options: Options:
--no-import-syntax The target provider has no native cross-file import --no-import-syntax The target provider has no native cross-file import
mechanism. Accept a plain-text pointer mention of mechanism. Require a plain-text pointer line naming
"AGENTS.md" instead of requiring an @import-style line. "AGENTS.md" instead of an @import-style line; an
@AGENTS.md line alone does not satisfy it, because
such a provider never resolves it. Without this flag
an actual @import line is required, and naming
AGENTS.md in prose alone does not satisfy it.
--max-lines N Max non-blank lines allowed in the adapter file before --max-lines N Max non-blank lines allowed in the adapter file before
it's considered no longer "thin". Default: 60. it's considered no longer "thin". Must be a
non-negative integer. Default: 60.
--help, -h Show this help and exit 0. --help, -h Show this help and exit 0.
Exit codes: Exit codes:
@@ -44,7 +49,15 @@ while [[ $# -gt 0 ]]; do
shift shift
;; ;;
--max-lines) --max-lines)
MAX_LINES="${2:-}" if [[ $# -lt 2 ]]; then
echo "Error: --max-lines requires a value (a non-negative integer)." >&2
exit 1
fi
MAX_LINES="$2"
if [[ ! "$MAX_LINES" =~ ^[0-9]+$ ]]; then
echo "Error: --max-lines expects a non-negative integer, got '$MAX_LINES'." >&2
exit 1
fi
shift 2 shift 2
;; ;;
*) *)
@@ -94,21 +107,25 @@ if not adapter_content.strip():
IMPORT_RE = re.compile(r'(?m)^\s*@\S*AGENTS\.md\s*$') IMPORT_RE = re.compile(r'(?m)^\s*@\S*AGENTS\.md\s*$')
lines = adapter_content.splitlines() lines = adapter_content.splitlines()
import_lines = [ln for ln in lines if IMPORT_RE.match(ln)] import_lines = [ln for ln in lines if IMPORT_RE.match(ln)]
# A prose pointer is any line naming AGENTS.md that is not itself an import
# line — an inert `@AGENTS.md` in a provider that resolves no imports points
# a reader at nothing.
pointer_lines = [ln for ln in lines if not IMPORT_RE.match(ln) and "AGENTS.md" in ln]
if no_import_syntax: if no_import_syntax:
has_reference = "AGENTS.md" in adapter_content has_reference = bool(pointer_lines)
else: else:
has_reference = bool(import_lines) or "AGENTS.md" in adapter_content has_reference = bool(import_lines)
if not has_reference: if not has_reference:
has_fail = True has_fail = True
print(f"FAIL Adapter has no reference to AGENTS.md — {adapter_path}") print(f"FAIL Adapter has no reference to AGENTS.md — {adapter_path}")
if no_import_syntax: if no_import_syntax:
print(" Why: This provider has no import syntax, so the adapter must at least mention AGENTS.md as a text pointer.") print(" Why: This provider resolves no cross-file import, so the adapter must point at AGENTS.md in prose; an `@AGENTS.md` line here is inert text.")
print(" Fix: Add a sentence like \"See AGENTS.md at the repo root for shared conventions.\"") print(" Fix: Add a sentence like \"See AGENTS.md at the repo root for shared conventions.\"")
else: else:
print(" Why: A thin adapter must import AGENTS.md (e.g. `@AGENTS.md`) rather than silently omitting it.") print(" Why: A thin adapter must import AGENTS.md with an `@AGENTS.md` line; merely naming the file in prose defers nothing.")
print(" Fix: Add an `@AGENTS.md` (or equivalent relative path) import line.") print(" Fix: Add an `@AGENTS.md` (or equivalent relative path) import line, or pass --no-import-syntax if this provider resolves no imports.")
print() print()
# --- Duplication check --- # --- Duplication check ---
@@ -132,7 +149,7 @@ if non_blank_count > max_lines:
has_fail = True has_fail = True
print(f"FAIL Adapter is not thin — {adapter_path}") print(f"FAIL Adapter is not thin — {adapter_path}")
print(f" Why: {non_blank_count} non-blank lines exceeds the {max_lines}-line threshold for a thin adapter.") print(f" Why: {non_blank_count} non-blank lines exceeds the {max_lines}-line threshold for a thin adapter.")
print(" Fix: Move provider-agnostic content into AGENTS.md; keep only genuinely provider-specific additions here.") print(" Fix: Delete the lines already covered by AGENTS.md; keep only genuinely provider-specific additions here.")
print() print()
if has_fail: if has_fail:
@@ -1,3 +1,7 @@
---
source_keys: []
---
# Orchestrator request contract # Orchestrator request contract
`git-orchestrate` and other calling agents send this shape. The result shape they parse back is in `git-orchestrate` and other calling agents send this shape. The result shape they parse back is in
@@ -8,7 +8,6 @@ description: >
Not branch lifecycle -> `git-branches`. Not branch lifecycle -> `git-branches`.
metadata: metadata:
version: "0.1.3"
category: git category: git
source_keys: source_keys:
- conventional-commits-spec - conventional-commits-spec
+2 -3
View File
@@ -9,21 +9,20 @@ description: >
Not submodule pointers -> `git-submodules`. Not submodule pointers -> `git-submodules`.
metadata: metadata:
category: git-workflow category: git
source_keys: source_keys:
- git-scm-remote-docs - git-scm-remote-docs
- git-scm-fetch-docs - git-scm-fetch-docs
- git-scm-push-docs - git-scm-push-docs
- git-scm-pull-docs - git-scm-pull-docs
- context7-git-htmldocs - context7-git-htmldocs
--- ---
## Gotchas ## Gotchas
- **`--force-with-lease` alone is not safe** — background processes (IDE plugins, cron jobs) running `git fetch` silently defeat the protection. Combine it with `--force-if-includes`, or pin the explicit `--force-with-lease=<ref>:<sha>` form. - **`--force-with-lease` alone is not safe** — background processes (IDE plugins, cron jobs) running `git fetch` silently defeat the protection. Combine it with `--force-if-includes`, or pin the explicit `--force-with-lease=<ref>:<sha>` form.
- **Prune does not touch tags by default** — `git fetch --prune` leaves orphaned tags behind. Use `--prune --prune-tags`, or set `fetch.pruneTags true`. - **Prune does not touch tags by default** — `git fetch --prune` leaves orphaned tags behind. Use `--prune --prune-tags`, or set `fetch.pruneTags true`.
- **Pull defaults shift between Git versions** — older ones default to merge, newer to `--ff-only`. Set `pull.ff only` explicitly rather than trusting the installed default. - **Set `pull.ff only` explicitly** — do not trust the installed default.
## Step 1 — Clear the force-push gate ## Step 1 — Clear the force-push gate
@@ -23,6 +23,10 @@ A pull that diverges with no strategy configured fails, and that failure is the
## Config precedence ## Config precedence
The installed default varies by Git version — older versions merge on divergence, newer ones
default to `--ff-only` — so an unset `pull.ff` means the same pull behaves differently on different
machines. Set it explicitly.
Highest wins: Highest wins:
1. Command-line flag (`--ff-only` / `--rebase` / `--no-rebase`) 1. Command-line flag (`--ff-only` / `--rebase` / `--no-rebase`)
@@ -51,7 +51,7 @@ executing.
## Output format ## Output format
``` ```yaml
operation: <clone|add|init|update|status|sync|set-url|set-branch|absorbgitdirs|deinit|remove> operation: <clone|add|init|update|status|sync|set-url|set-branch|absorbgitdirs|deinit|remove>
status: <success|error|partial> status: <success|error|partial>
message: <one line; include git's own output on error> message: <one line; include git's own output on error>
@@ -1,7 +1,6 @@
--- ---
metadata: source_keys:
source_keys: - git-scm-submodule-docs
- git-scm-submodule-docs
--- ---
# References # References
@@ -2,9 +2,9 @@
name: git-workflow name: git-workflow
description: > description: >
Use when a human wants to work through local git interactively — commits, branches, history, Use when a human's local git request is general or ambiguous — it routes to the owning
submodules, worktrees, or remotes. Not an agent caller needing deterministic execution -> domain skill. Not an unambiguous commit -> `git-commits`. Not an unambiguous branch ->
`git-orchestrate`. Not server-side Gitea work -> `gitea-workflow`. `git-branches`. Not an agent caller -> `git-orchestrate`. Not Gitea -> `gitea-workflow`.
metadata: metadata:
category: git category: git
@@ -28,7 +28,7 @@ metadata:
| Create on a new branch | `git worktree add -b <branch> <path>` | | Create on a new branch | `git worktree add -b <branch> <path>` |
| Create on the branch named after the path basename | `git worktree add <path>` — checks that branch out if it exists, else creates it from HEAD | | Create on the branch named after the path basename | `git worktree add <path>` — checks that branch out if it exists, else creates it from HEAD |
| Create and reset an existing branch to HEAD — discards its commits | `git worktree add -B <branch> <path>` | | Create and reset an existing branch to HEAD — discards its commits | `git worktree add -B <branch> <path>` |
| Create a local branch tracking a remote one | `git worktree add --track -b <branch> <path> <remote>/<branch>` — always correct. `git worktree add <path> <branch>` expands to exactly this, but **only** when `<branch>` has no local copy (gate below) | | Create a local branch tracking a remote one | `git worktree add --track -b <branch> <path> <remote>/<branch>` — always correct. `git worktree add <path> <branch>` expands to exactly this, but **only** under the conditions in `references/worktrees.md` |
| Throwaway experiment, no branch | `git worktree add -d <path>` — detached HEAD | | Throwaway experiment, no branch | `git worktree add -d <path>` — detached HEAD |
| **Never** `git worktree add <path> <remote>/<branch>` | That ref resolves, so the shortcut never fires and you get **a detached HEAD, no branch, no upstream**. Commits there go unreachable once HEAD moves, and `git push` needs an explicit refspec. Use the tracking row above | | **Never** `git worktree add <path> <remote>/<branch>` | That ref resolves, so the shortcut never fires and you get **a detached HEAD, no branch, no upstream**. Commits there go unreachable once HEAD moves, and `git push` needs an explicit refspec. Use the tracking row above |
| List | `git worktree list -v`, or `--porcelain -z` to parse | | List | `git worktree list -v`, or `--porcelain -z` to parse |
@@ -40,15 +40,14 @@ metadata:
If the operation needs anything the table does not carry — the full `add` flag If the operation needs anything the table does not carry — the full `add` flag
table, orphan branches, sparse-checkout, locking for removable media, remote table, orphan branches, sparse-checkout, locking for removable media, remote
disambiguation across several remotes, worktree config keys, or the worked disambiguation across several remotes, how to name a worktree unambiguously,
emergency-fix and PR-review patterns — read `references/worktrees.md`. worktree config keys, or the worked emergency-fix and PR-review patterns — read
`references/worktrees.md`.
Gates: Gates:
- **`move`, `remove` — the main worktree cannot be moved or removed.** Only linked worktrees, the ones `git worktree add` created, are candidates. - **`move`, `remove` — the main worktree cannot be moved or removed.** Only linked worktrees, the ones `git worktree add` created, are candidates.
- **`add`, `move`, `remove` — escalate force flags one step at a time.** `-f` overrides a safeguard such as an unclean tree; `move` and `remove` need `-ff` on top of that when the worktree is locked. Confirm with the user before either — both discard state. - **`add`, `move`, `remove` — escalate force flags one step at a time.** `-f` overrides a safeguard such as an unclean tree; `move` and `remove` need `-ff` on top of that when the worktree is locked. Confirm with the user before either — both discard state.
- **`lock`, `move`, `remove`, `repair` — identify a worktree by full path, unique basename, or unique partial path.** An ambiguous name errors rather than picking; `git worktree list` shows the usable identifiers.
- **`add` — the bare-name tracking shortcut needs exactly one remote.** `git worktree add <path> <branch>` sets up tracking only when `<branch>` is absent locally, no `-b`/`-B`/`-d` is given, and exactly one remote carries the name. With several, it fires only if `checkout.defaultRemote` names one. When the remote is ambiguous or unknown, use `--track -b`.
- **`add` — lock at creation, not after.** `git worktree add --lock` is atomic, where add-then-`lock` leaves a window in which the worktree is unprotected. - **`add` — lock at creation, not after.** `git worktree add --lock` is atomic, where add-then-`lock` leaves a window in which the worktree is unprotected.
## Step 2 — Report ## Step 2 — Report
@@ -4,6 +4,8 @@ source_keys:
- git-scm-worktree-docs - git-scm-worktree-docs
--- ---
# Git worktrees
## Shared vs. per-worktree state ## Shared vs. per-worktree state
All worktrees share one object store, one config, and most refs under `refs/`. Each worktree keeps All worktrees share one object store, one config, and most refs under `refs/`. Each worktree keeps
@@ -12,6 +14,12 @@ its own `HEAD`, index, and per-worktree metadata (`ORIG_HEAD`, `MERGE_HEAD`, `re
worktree** exists per repo — the one `git init` or `git clone` produced — and it cannot be removed worktree** exists per repo — the one `git init` or `git clone` produced — and it cannot be removed
or moved. Every other worktree is a **linked worktree** created by `git worktree add`. or moved. Every other worktree is a **linked worktree** created by `git worktree add`.
## Identifying a worktree
`lock`, `move`, `remove` and `repair` accept a full path, a unique basename, or a unique partial
path. An ambiguous name errors rather than picking one; `git worktree list` shows the identifiers
that are usable.
## `add` forms ## `add` forms
```bash ```bash
@@ -85,10 +93,11 @@ git worktree unlock <path> # when reconnected
git worktree add --track -b <branch> <path> <remote>/<branch> # explicit: no guessing at all git worktree add --track -b <branch> <path> <remote>/<branch> # explicit: no guessing at all
git worktree add <path> <branch> # shortcut: needs one clear remote git worktree add <path> <branch> # shortcut: needs one clear remote
``` ```
The shortcut fires only when `<branch>` is not found locally, none of `-b`/`-B`/`--detach` were **The bare-name shortcut needs exactly one remote.** It fires only when `<branch>` is not found
given, and a tracking branch of that name exists in exactly one remote. When several remotes carry locally, none of `-b`/`-B`/`--detach` were given, and a tracking branch of that name exists in
the name, `checkout.defaultRemote` picks one for disambiguation purposes; with no such setting the exactly one remote. When several remotes carry the name, `checkout.defaultRemote` picks one for
shortcut has no single remote to resolve against and does not apply. disambiguation purposes; with no such setting the shortcut has no single remote to resolve against
and does not apply. When the remote is ambiguous or unknown, use the explicit `--track -b` form.
`--guess-remote` covers the *other* spelling — `git worktree add <path>` with no `<commit-ish>` at `--guess-remote` covers the *other* spelling — `git worktree add <path>` with no `<commit-ish>` at
all. It bases the new branch on the remote-tracking branch matching `$(basename <path>)` when all. It bases the new branch on the remote-tracking branch matching `$(basename <path>)` when
+1 -3
View File
@@ -20,7 +20,7 @@ allowed-tools: Bash Read
- The `SKIP` env var takes exact hook `id` values, comma-separated with no spaces: `SKIP=check-yaml,gitleaks git commit -m "msg"`. A space after a comma silently skips nothing instead of erroring. - The `SKIP` env var takes exact hook `id` values, comma-separated with no spaces: `SKIP=check-yaml,gitleaks git commit -m "msg"`. A space after a comma silently skips nothing instead of erroring.
- Never bypass a failing hook with `git commit --no-verify` (or `-n`). Hooks are the automated QA gate, so a bypassed commit pushes the failure downstream where it costs more — diagnose it instead. - Never bypass a failing hook with `git commit --no-verify` (or `-n`). Hooks are the automated QA gate, so a bypassed commit pushes the failure downstream where it costs more — diagnose it instead.
- `- files were modified by this hook` is not a bug. A fixer hook (`trailing-whitespace`, `end-of-file-fixer`, `pretty-format-json --autofix`) rewrote a staged file, so the staged snapshot is stale and the commit is blocked on purpose. The fix is to re-stage and re-run the same commit: `git add -u && git commit`. Do NOT reach for `pre-commit install -f` here — that flag overwrites hook files in `.git/hooks/` and has nothing to do with re-staging. - `- files were modified by this hook` is not a bug. A fixer hook rewrote a staged file, so the staged snapshot is stale and the commit is blocked on purpose. Re-stage and re-run the same commit: `git add -u && git commit`. Do NOT reach for `pre-commit install -f` here — it overwrites `.git/hooks/` and has nothing to do with re-staging.
## Gate — `pre-commit clean` ## Gate — `pre-commit clean`
@@ -47,8 +47,6 @@ Determine intent from the user's request, then execute the matching operation. W
| "clean", "wipe cache", "rebuild from scratch" | `pre-commit clean` — read `references/clean.md` | | "clean", "wipe cache", "rebuild from scratch" | `pre-commit clean` — read `references/clean.md` |
| "hooks aren't running", "hook never fires", "why did a hook fail", a hook failure whose cause is unclear | Diagnose — read `references/failure-patterns.md` | | "hooks aren't running", "hook never fires", "why did a hook fail", a hook failure whose cause is unclear | Diagnose — read `references/failure-patterns.md` |
If the intent is ambiguous, default to `pre-commit run --all-files`.
## Run ## Run
Default to `pre-commit run --all-files`; never silently narrow to staged files. Run `pre-commit run` (staged only) or `pre-commit run <hook-id>` (one named hook) when the user asks for it. Default to `pre-commit run --all-files`; never silently narrow to staged files. Run `pre-commit run` (staged only) or `pre-commit run <hook-id>` (one named hook) when the user asks for it.
@@ -6,16 +6,12 @@ source_keys:
# Wiping the pre-commit cache # Wiping the pre-commit cache
Reached from `SKILL.md`'s Route table when the user asks to clean the cache or rebuild environments from scratch. Self-contained. Reached from `SKILL.md`'s Route table when the user asks to clean the cache or rebuild environments from scratch. The confirmation gate on `pre-commit clean` stays in `SKILL.md`, because it must fire on every path that reaches this command, not only this one.
## Gate — confirm first ## Gate
`pre-commit clean` wipes the whole cache at `~/.cache/pre-commit`, forcing every hook environment to be re-downloaded on the next run. Require explicit confirmation before executing it: `pre-commit clean` runs only after the confirmation gate in `SKILL.md` clears — that gate, its exact wording, and the `pre-commit gc` alternative live there and are not restated here.
> "This will wipe the entire pre-commit cache. All hook environments will be re-downloaded on next run. Proceed?"
```bash ```bash
pre-commit clean pre-commit clean
``` ```
Prefer `pre-commit gc` when the goal is only to reclaim disk — it drops unused environments and leaves the ones in use intact, so it needs no confirmation.
@@ -1,3 +1,7 @@
---
source_keys: []
---
# Orchestrator request contract # Orchestrator request contract
`git-orchestrate` and other calling agents send this shape. The result shape they parse back is in `git-orchestrate` and other calling agents send this shape. The result shape they parse back is in
-1
View File
@@ -8,7 +8,6 @@ description: >
Not branch lifecycle -> `git-branches`. Not branch lifecycle -> `git-branches`.
metadata: metadata:
version: "0.1.3"
category: git category: git
source_keys: source_keys:
- conventional-commits-spec - conventional-commits-spec
+2 -3
View File
@@ -9,21 +9,20 @@ description: >
Not submodule pointers -> `git-submodules`. Not submodule pointers -> `git-submodules`.
metadata: metadata:
category: git-workflow category: git
source_keys: source_keys:
- git-scm-remote-docs - git-scm-remote-docs
- git-scm-fetch-docs - git-scm-fetch-docs
- git-scm-push-docs - git-scm-push-docs
- git-scm-pull-docs - git-scm-pull-docs
- context7-git-htmldocs - context7-git-htmldocs
--- ---
## Gotchas ## Gotchas
- **`--force-with-lease` alone is not safe** — background processes (IDE plugins, cron jobs) running `git fetch` silently defeat the protection. Combine it with `--force-if-includes`, or pin the explicit `--force-with-lease=<ref>:<sha>` form. - **`--force-with-lease` alone is not safe** — background processes (IDE plugins, cron jobs) running `git fetch` silently defeat the protection. Combine it with `--force-if-includes`, or pin the explicit `--force-with-lease=<ref>:<sha>` form.
- **Prune does not touch tags by default** — `git fetch --prune` leaves orphaned tags behind. Use `--prune --prune-tags`, or set `fetch.pruneTags true`. - **Prune does not touch tags by default** — `git fetch --prune` leaves orphaned tags behind. Use `--prune --prune-tags`, or set `fetch.pruneTags true`.
- **Pull defaults shift between Git versions** — older ones default to merge, newer to `--ff-only`. Set `pull.ff only` explicitly rather than trusting the installed default. - **Set `pull.ff only` explicitly** — do not trust the installed default.
## Step 1 — Clear the force-push gate ## Step 1 — Clear the force-push gate
@@ -23,6 +23,10 @@ A pull that diverges with no strategy configured fails, and that failure is the
## Config precedence ## Config precedence
The installed default varies by Git version — older versions merge on divergence, newer ones
default to `--ff-only` — so an unset `pull.ff` means the same pull behaves differently on different
machines. Set it explicitly.
Highest wins: Highest wins:
1. Command-line flag (`--ff-only` / `--rebase` / `--no-rebase`) 1. Command-line flag (`--ff-only` / `--rebase` / `--no-rebase`)
+1 -1
View File
@@ -51,7 +51,7 @@ executing.
## Output format ## Output format
``` ```yaml
operation: <clone|add|init|update|status|sync|set-url|set-branch|absorbgitdirs|deinit|remove> operation: <clone|add|init|update|status|sync|set-url|set-branch|absorbgitdirs|deinit|remove>
status: <success|error|partial> status: <success|error|partial>
message: <one line; include git's own output on error> message: <one line; include git's own output on error>
@@ -1,7 +1,6 @@
--- ---
metadata: source_keys:
source_keys: - git-scm-submodule-docs
- git-scm-submodule-docs
--- ---
# References # References
+3 -3
View File
@@ -2,9 +2,9 @@
name: git-workflow name: git-workflow
description: > description: >
Use when a human wants to work through local git interactively — commits, branches, history, Use when a human's local git request is general or ambiguous — it routes to the owning
submodules, worktrees, or remotes. Not an agent caller needing deterministic execution -> domain skill. Not an unambiguous commit -> `git-commits`. Not an unambiguous branch ->
`git-orchestrate`. Not server-side Gitea work -> `gitea-workflow`. `git-branches`. Not an agent caller -> `git-orchestrate`. Not Gitea -> `gitea-workflow`.
metadata: metadata:
category: git category: git
+4 -5
View File
@@ -28,7 +28,7 @@ metadata:
| Create on a new branch | `git worktree add -b <branch> <path>` | | Create on a new branch | `git worktree add -b <branch> <path>` |
| Create on the branch named after the path basename | `git worktree add <path>` — checks that branch out if it exists, else creates it from HEAD | | Create on the branch named after the path basename | `git worktree add <path>` — checks that branch out if it exists, else creates it from HEAD |
| Create and reset an existing branch to HEAD — discards its commits | `git worktree add -B <branch> <path>` | | Create and reset an existing branch to HEAD — discards its commits | `git worktree add -B <branch> <path>` |
| Create a local branch tracking a remote one | `git worktree add --track -b <branch> <path> <remote>/<branch>` — always correct. `git worktree add <path> <branch>` expands to exactly this, but **only** when `<branch>` has no local copy (gate below) | | Create a local branch tracking a remote one | `git worktree add --track -b <branch> <path> <remote>/<branch>` — always correct. `git worktree add <path> <branch>` expands to exactly this, but **only** under the conditions in `references/worktrees.md` |
| Throwaway experiment, no branch | `git worktree add -d <path>` — detached HEAD | | Throwaway experiment, no branch | `git worktree add -d <path>` — detached HEAD |
| **Never** `git worktree add <path> <remote>/<branch>` | That ref resolves, so the shortcut never fires and you get **a detached HEAD, no branch, no upstream**. Commits there go unreachable once HEAD moves, and `git push` needs an explicit refspec. Use the tracking row above | | **Never** `git worktree add <path> <remote>/<branch>` | That ref resolves, so the shortcut never fires and you get **a detached HEAD, no branch, no upstream**. Commits there go unreachable once HEAD moves, and `git push` needs an explicit refspec. Use the tracking row above |
| List | `git worktree list -v`, or `--porcelain -z` to parse | | List | `git worktree list -v`, or `--porcelain -z` to parse |
@@ -40,15 +40,14 @@ metadata:
If the operation needs anything the table does not carry — the full `add` flag If the operation needs anything the table does not carry — the full `add` flag
table, orphan branches, sparse-checkout, locking for removable media, remote table, orphan branches, sparse-checkout, locking for removable media, remote
disambiguation across several remotes, worktree config keys, or the worked disambiguation across several remotes, how to name a worktree unambiguously,
emergency-fix and PR-review patterns — read `references/worktrees.md`. worktree config keys, or the worked emergency-fix and PR-review patterns — read
`references/worktrees.md`.
Gates: Gates:
- **`move`, `remove` — the main worktree cannot be moved or removed.** Only linked worktrees, the ones `git worktree add` created, are candidates. - **`move`, `remove` — the main worktree cannot be moved or removed.** Only linked worktrees, the ones `git worktree add` created, are candidates.
- **`add`, `move`, `remove` — escalate force flags one step at a time.** `-f` overrides a safeguard such as an unclean tree; `move` and `remove` need `-ff` on top of that when the worktree is locked. Confirm with the user before either — both discard state. - **`add`, `move`, `remove` — escalate force flags one step at a time.** `-f` overrides a safeguard such as an unclean tree; `move` and `remove` need `-ff` on top of that when the worktree is locked. Confirm with the user before either — both discard state.
- **`lock`, `move`, `remove`, `repair` — identify a worktree by full path, unique basename, or unique partial path.** An ambiguous name errors rather than picking; `git worktree list` shows the usable identifiers.
- **`add` — the bare-name tracking shortcut needs exactly one remote.** `git worktree add <path> <branch>` sets up tracking only when `<branch>` is absent locally, no `-b`/`-B`/`-d` is given, and exactly one remote carries the name. With several, it fires only if `checkout.defaultRemote` names one. When the remote is ambiguous or unknown, use `--track -b`.
- **`add` — lock at creation, not after.** `git worktree add --lock` is atomic, where add-then-`lock` leaves a window in which the worktree is unprotected. - **`add` — lock at creation, not after.** `git worktree add --lock` is atomic, where add-then-`lock` leaves a window in which the worktree is unprotected.
## Step 2 — Report ## Step 2 — Report
@@ -4,6 +4,8 @@ source_keys:
- git-scm-worktree-docs - git-scm-worktree-docs
--- ---
# Git worktrees
## Shared vs. per-worktree state ## Shared vs. per-worktree state
All worktrees share one object store, one config, and most refs under `refs/`. Each worktree keeps All worktrees share one object store, one config, and most refs under `refs/`. Each worktree keeps
@@ -12,6 +14,12 @@ its own `HEAD`, index, and per-worktree metadata (`ORIG_HEAD`, `MERGE_HEAD`, `re
worktree** exists per repo — the one `git init` or `git clone` produced — and it cannot be removed worktree** exists per repo — the one `git init` or `git clone` produced — and it cannot be removed
or moved. Every other worktree is a **linked worktree** created by `git worktree add`. or moved. Every other worktree is a **linked worktree** created by `git worktree add`.
## Identifying a worktree
`lock`, `move`, `remove` and `repair` accept a full path, a unique basename, or a unique partial
path. An ambiguous name errors rather than picking one; `git worktree list` shows the identifiers
that are usable.
## `add` forms ## `add` forms
```bash ```bash
@@ -85,10 +93,11 @@ git worktree unlock <path> # when reconnected
git worktree add --track -b <branch> <path> <remote>/<branch> # explicit: no guessing at all git worktree add --track -b <branch> <path> <remote>/<branch> # explicit: no guessing at all
git worktree add <path> <branch> # shortcut: needs one clear remote git worktree add <path> <branch> # shortcut: needs one clear remote
``` ```
The shortcut fires only when `<branch>` is not found locally, none of `-b`/`-B`/`--detach` were **The bare-name shortcut needs exactly one remote.** It fires only when `<branch>` is not found
given, and a tracking branch of that name exists in exactly one remote. When several remotes carry locally, none of `-b`/`-B`/`--detach` were given, and a tracking branch of that name exists in
the name, `checkout.defaultRemote` picks one for disambiguation purposes; with no such setting the exactly one remote. When several remotes carry the name, `checkout.defaultRemote` picks one for
shortcut has no single remote to resolve against and does not apply. disambiguation purposes; with no such setting the shortcut has no single remote to resolve against
and does not apply. When the remote is ambiguous or unknown, use the explicit `--track -b` form.
`--guess-remote` covers the *other* spelling — `git worktree add <path>` with no `<commit-ish>` at `--guess-remote` covers the *other* spelling — `git worktree add <path>` with no `<commit-ish>` at
all. It bases the new branch on the remote-tracking branch matching `$(basename <path>)` when all. It bases the new branch on the remote-tracking branch matching `$(basename <path>)` when
+1 -3
View File
@@ -20,7 +20,7 @@ allowed-tools: Bash Read
- The `SKIP` env var takes exact hook `id` values, comma-separated with no spaces: `SKIP=check-yaml,gitleaks git commit -m "msg"`. A space after a comma silently skips nothing instead of erroring. - The `SKIP` env var takes exact hook `id` values, comma-separated with no spaces: `SKIP=check-yaml,gitleaks git commit -m "msg"`. A space after a comma silently skips nothing instead of erroring.
- Never bypass a failing hook with `git commit --no-verify` (or `-n`). Hooks are the automated QA gate, so a bypassed commit pushes the failure downstream where it costs more — diagnose it instead. - Never bypass a failing hook with `git commit --no-verify` (or `-n`). Hooks are the automated QA gate, so a bypassed commit pushes the failure downstream where it costs more — diagnose it instead.
- `- files were modified by this hook` is not a bug. A fixer hook (`trailing-whitespace`, `end-of-file-fixer`, `pretty-format-json --autofix`) rewrote a staged file, so the staged snapshot is stale and the commit is blocked on purpose. The fix is to re-stage and re-run the same commit: `git add -u && git commit`. Do NOT reach for `pre-commit install -f` here — that flag overwrites hook files in `.git/hooks/` and has nothing to do with re-staging. - `- files were modified by this hook` is not a bug. A fixer hook rewrote a staged file, so the staged snapshot is stale and the commit is blocked on purpose. Re-stage and re-run the same commit: `git add -u && git commit`. Do NOT reach for `pre-commit install -f` here — it overwrites `.git/hooks/` and has nothing to do with re-staging.
## Gate — `pre-commit clean` ## Gate — `pre-commit clean`
@@ -47,8 +47,6 @@ Determine intent from the user's request, then execute the matching operation. W
| "clean", "wipe cache", "rebuild from scratch" | `pre-commit clean` — read `references/clean.md` | | "clean", "wipe cache", "rebuild from scratch" | `pre-commit clean` — read `references/clean.md` |
| "hooks aren't running", "hook never fires", "why did a hook fail", a hook failure whose cause is unclear | Diagnose — read `references/failure-patterns.md` | | "hooks aren't running", "hook never fires", "why did a hook fail", a hook failure whose cause is unclear | Diagnose — read `references/failure-patterns.md` |
If the intent is ambiguous, default to `pre-commit run --all-files`.
## Run ## Run
Default to `pre-commit run --all-files`; never silently narrow to staged files. Run `pre-commit run` (staged only) or `pre-commit run <hook-id>` (one named hook) when the user asks for it. Default to `pre-commit run --all-files`; never silently narrow to staged files. Run `pre-commit run` (staged only) or `pre-commit run <hook-id>` (one named hook) when the user asks for it.
@@ -6,16 +6,12 @@ source_keys:
# Wiping the pre-commit cache # Wiping the pre-commit cache
Reached from `SKILL.md`'s Route table when the user asks to clean the cache or rebuild environments from scratch. Self-contained. Reached from `SKILL.md`'s Route table when the user asks to clean the cache or rebuild environments from scratch. The confirmation gate on `pre-commit clean` stays in `SKILL.md`, because it must fire on every path that reaches this command, not only this one.
## Gate — confirm first ## Gate
`pre-commit clean` wipes the whole cache at `~/.cache/pre-commit`, forcing every hook environment to be re-downloaded on the next run. Require explicit confirmation before executing it: `pre-commit clean` runs only after the confirmation gate in `SKILL.md` clears — that gate, its exact wording, and the `pre-commit gc` alternative live there and are not restated here.
> "This will wipe the entire pre-commit cache. All hook environments will be re-downloaded on next run. Proceed?"
```bash ```bash
pre-commit clean pre-commit clean
``` ```
Prefer `pre-commit gc` when the goal is only to reclaim disk — it drops unused environments and leaves the ones in use intact, so it needs no confirmation.
@@ -4,7 +4,7 @@ Manage Gitea repository branches and inspect commit history via the Gitea MCP se
## What it does ## What it does
This skill handles branch lifecycle operations (list, create, delete) and read-only commit This skill handles branch lifecycle operations (list, create, rename, delete) and read-only commit
history (list commits, get a single commit's full detail) against a Gitea repository. It resolves history (list commits, get a single commit's full detail) against a Gitea repository. It resolves
`owner`/`repo` from the git remote, dispatches to the right MCP tool, and applies safety and `owner`/`repo` from the git remote, dispatches to the right MCP tool, and applies safety and
pagination conventions specific to Gitea's API (e.g. refusing to delete a protected branch without pagination conventions specific to Gitea's API (e.g. refusing to delete a protected branch without
@@ -18,25 +18,26 @@ Branch references that only exist relative to a pull request — a PR's head or
cross-repo fork PR heads in particular — belong to `gitea-prs`; `list_branches` cannot see a fork's cross-repo fork PR heads in particular — belong to `gitea-prs`; `list_branches` cannot see a fork's
head at all. head at all.
The skill triggers on phrasings like "list branches", "create a branch", "delete a branch", The skill triggers on phrasings like "list branches", "create a branch", "rename a branch", "delete a branch",
"what commits are on this branch", "show commit <sha>", and "what changed in that commit", even "what commits are on this branch", "show commit <sha>", and "what changed in that commit", even
when the user does not say "Gitea", as long as the repo's remote is a Gitea instance. when the user does not say "Gitea", as long as the repo's remote is a Gitea instance.
## Before you start ## Before you start
Requires a Gitea MCP server configured with a token that has `write:repository` scope. This is Requires a Gitea MCP server configured with a token that has `write:repository` scope. This is
confirmed for `list_branches`, `create_branch`, and `delete_branch` (Gitea gates reads behind write confirmed for `list_branches`, `create_branch`, and `delete_branch` (and inferred for
`rename_branch`) (Gitea gates reads behind write
scope for repo-scoped operations); `list_commits` and `get_commit` are inferred to need the same scope for repo-scoped operations); `list_commits` and `get_commit` are inferred to need the same
scope by analogy, not explicitly confirmed — see `references/commits.md`. Requires a git remote scope by analogy, not explicitly confirmed — see `references/commits.md`. Requires a git remote
named `origin` pointing at the Gitea instance. named `origin` pointing at the Gitea instance.
## Usage ## Usage
``` ```text
/gitea-branches /gitea-branches
``` ```
Describe your task: list/create/delete a branch, or list/inspect commits. See `SKILL.md`'s Describe your task: list/create/rename/delete a branch, or list/inspect commits. See `SKILL.md`'s
dispatch table for the full set of recognized invocations. dispatch table for the full set of recognized invocations.
## Files ## Files
@@ -44,6 +45,6 @@ dispatch table for the full set of recognized invocations.
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `SKILL.md` | Skill instructions for agents — dispatch table, gotchas | | `SKILL.md` | Skill instructions for agents — dispatch table, gotchas |
| `references/branches.md` | Verified call signatures and mechanics for list/create/delete branch | | `references/branches.md` | Verified call signatures and mechanics for list/create/rename/delete branch |
| `references/commits.md` | Verified call signatures and mechanics for list/get commit | | `references/commits.md` | Verified call signatures and mechanics for list/get commit |
| `references/sources.md` | Research sources backing the branch/commit guidance | | `references/sources.md` | Research sources backing the branch/commit guidance |
@@ -2,10 +2,10 @@
name: gitea-branches name: gitea-branches
description: > description: >
Use when listing, creating, or deleting branches in a Gitea repository, or Use when listing, creating, renaming, or deleting branches in a Gitea repository,
reading its commit history — even when the user does not say "Gitea". Not a or reading its commit history — even when the user does not say "Gitea". Not a
local working copy's branches -> `git-branches`. Not local history -> local checkout's branches -> `git-branches`. Not local history ->
`git-history`. Not a PR's head or base branch -> `gitea-prs`. `git-history`. Not a PR's head or base -> `gitea-prs`.
compatibility: Requires Gitea MCP server configured with a token with write:repository scope; this is confirmed to gate list_branches, create_branch, and delete_branch (Gitea gates reads behind write scope for repo-scoped operations), and is inferred by analogy (not explicitly confirmed by source docs) to also gate list_commits and get_commit. Requires git remote "origin" pointing to the Gitea instance. compatibility: Requires Gitea MCP server configured with a token with write:repository scope; this is confirmed to gate list_branches, create_branch, and delete_branch (Gitea gates reads behind write scope for repo-scoped operations), and is inferred by analogy (not explicitly confirmed by source docs) to also gate list_commits and get_commit. Requires git remote "origin" pointing to the Gitea instance.
@@ -17,7 +17,7 @@ metadata:
- gitea-mcp-slim-go - gitea-mcp-slim-go
- context7-websites-gitea - context7-websites-gitea
allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__rename_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
--- ---
## Gotchas ## Gotchas
@@ -42,17 +42,18 @@ git remote get-url origin
|---|---| |---|---|
| `/gitea-branches` or `/gitea-branches list` | List branches | | `/gitea-branches` or `/gitea-branches list` | List branches |
| `/gitea-branches create <name> [from <base>]` | Create branch | | `/gitea-branches create <name> [from <base>]` | Create branch |
| `/gitea-branches rename <name> to <new-name>` | Rename branch |
| `/gitea-branches delete <name>` | Delete branch | | `/gitea-branches delete <name>` | Delete branch |
| `/gitea-branches commits [on <branch>] [touching <path>]` | List commit history | | `/gitea-branches commits [on <branch>] [touching <path>]` | List commit history |
| `/gitea-branches commit <sha>` | Get full detail for one commit | | `/gitea-branches commit <sha>` | Get full detail for one commit |
For branch operations (list/create/delete), read `references/branches.md` — it carries the call signatures, the `old_branch` source rule, and the protected-branch refusal in full. For branch operations (list/create/rename/delete), read `references/branches.md` — it carries the call signatures, the `old_branch` source rule, and the protected-branch refusal in full.
For commit operations (list/get), read `references/commits.md`. For commit operations (list/get), read `references/commits.md`.
## Step 3 — Report ## Step 3 — Report
For reads: display branches as name + protected flag; display commits as SHA (short), message summary, author, date. For reads: display branches as name + protected flag; display commits as SHA (short), message summary, author, date.
For writes (create/delete): confirm the action taken, the branch name, and (for create) the base it forked from. For writes (create/rename/delete): confirm the action taken, the branch name, and (for create) the base it forked from or (for rename) the name it had before.
For errors: surface the HTTP code and message, applying the 404 gotcha above before reporting "not found" to the user. For errors: surface the HTTP code and message, applying the 404 gotcha above before reporting "not found" to the user.
@@ -7,10 +7,11 @@ source_keys:
# Branch operations # Branch operations
Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch` Call signatures below were verified live against the deployed `gitea-mcp` server via `ToolSearch`,
at authoring time, not copied from research docs — this is deliberate: research docs are generated not copied from research docs — this is deliberate: research docs are generated from source code at
from source code at a point in time and can drift from the server actually deployed. Re-verify a point in time and can drift from the server actually deployed. **Last verified against gitea-mcp
against the live schema if these tools appear to behave differently than documented here. v1.7.0**, as reported by `get_gitea_mcp_server_version`. Re-verify against the live schema if the
deployed version differs or these tools behave differently than documented here.
## `list_branches` ## `list_branches`
@@ -21,7 +22,7 @@ against the live schema if these tools appear to behave differently than documen
- `per_page` (number, optional, default: `30`) - `per_page` (number, optional, default: `30`)
**Call:** **Call:**
``` ```text
list_branches owner: <owner> repo: <repo> list_branches owner: <owner> repo: <repo>
``` ```
@@ -41,7 +42,7 @@ count is less than `per_page`.
branch server-side (not necessarily your current local checkout) branch server-side (not necessarily your current local checkout)
**Call:** **Call:**
``` ```text
create_branch owner: <owner> repo: <repo> branch: <new-name> old_branch: <source-branch> create_branch owner: <owner> repo: <repo> branch: <new-name> old_branch: <source-branch>
``` ```
@@ -53,6 +54,29 @@ top-level request with no working branch context), omit `old_branch` and let it
A branch name collision returns `409 Conflict`. A branch name collision returns `409 Conflict`.
## `rename_branch`
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `branch` (string, required) — the branch's current name
- `new_name` (string, required) — the name to move it to
**Call:**
```text
rename_branch owner: <owner> repo: <repo> branch: <current-name> new_name: <new-name>
```
A rename moves the ref server-side; it is not a delete-plus-create, and no commit history is
rewritten. What it does to things *pointing at* the old name — open pull requests using it as head or
base, a branch protection rule matching it, CI config, and tracking branches on every other clone —
is **not confirmed** by this skill's sources: the deployed tool describes itself only as "Rename an
existing branch in a repository". Treat a rename of a branch with open PRs or a protection rule as a
change needing verification afterward (`list_branches`, plus `gitea-prs` for the PR side), and
confirm with the user first, exactly as for `delete_branch` below. A collision with an existing
branch name is expected to return `409 Conflict` by analogy with `create_branch`, not separately
confirmed.
## `delete_branch` ## `delete_branch`
**Parameters:** **Parameters:**
@@ -61,7 +85,7 @@ A branch name collision returns `409 Conflict`.
- `branch` (string, required) - `branch` (string, required)
**Call:** **Call:**
``` ```text
delete_branch owner: <owner> repo: <repo> branch: <name> delete_branch owner: <owner> repo: <repo> branch: <name>
``` ```
@@ -73,9 +97,12 @@ protected, every time, regardless of how the request is phrased.
## Token scope ## Token scope
All three — `list_branches`, `create_branch`, `delete_branch` — require `write:repository`. Gitea `list_branches`, `create_branch` and `delete_branch` all require `write:repository`. Gitea
gates reads behind write scope for repo-scoped operations, so `list_branches` needs the same scope gates reads behind write scope for repo-scoped operations, so `list_branches` needs the same scope
as the write operations, not `write:issue` alone. An earlier version of this doc claimed as the write operations, not `write:issue` alone. An earlier version of this doc claimed
`write:issue` alone was sufficient for `list_branches`, based on empirical testing under a token `write:issue` alone was sufficient for `list_branches`, based on empirical testing under a token
that held both `write:issue` and `write:repository` simultaneously — that test didn't isolate the that held both `write:issue` and `write:repository` simultaneously — that test didn't isolate the
variable, so it couldn't actually establish `write:issue` alone as sufficient. variable, so it couldn't actually establish `write:issue` alone as sufficient.
`rename_branch` is a write on the same repo-scoped surface and is inferred to need `write:repository`
too — inferred by analogy, not separately confirmed.
@@ -8,8 +8,9 @@ source_keys:
# Commit operations # Commit operations
Read-only commit history, scoped to a repo (optionally to one branch or one path). Call signatures Read-only commit history, scoped to a repo (optionally to one branch or one path). Call signatures
below were verified live against the deployed `gitea-mcp` server via `ToolSearch` at authoring time, below were verified live against the deployed `gitea-mcp` server via `ToolSearch`, not copied from
not copied from research docs, for the same drift-avoidance reason noted in `references/branches.md`. research docs, for the same drift-avoidance reason noted in `references/branches.md`. **Last verified
against gitea-mcp v1.7.0**, as reported by `get_gitea_mcp_server_version`.
This domain has no prior skill precedent — it's new coverage added alongside branches because commit This domain has no prior skill precedent — it's new coverage added alongside branches because commit
history is naturally scoped to a branch (a "what happened on this branch" question), not because it history is naturally scoped to a branch (a "what happened on this branch" question), not because it
@@ -27,7 +28,7 @@ shares any tool family with branch create/delete.
- `per_page` (number, optional, default: `30`, minimum: `1`) - `per_page` (number, optional, default: `30`, minimum: `1`)
**Call:** **Call:**
``` ```text
list_commits owner: <owner> repo: <repo> sha: <branch-or-sha> path: <optional-path> list_commits owner: <owner> repo: <repo> sha: <branch-or-sha> path: <optional-path>
``` ```
@@ -52,7 +53,7 @@ Paginate per the pagination Gotcha in SKILL.md if you need more than one page of
- `sha` (string, required) - `sha` (string, required)
**Call:** **Call:**
``` ```text
get_commit owner: <owner> repo: <repo> sha: <commit-sha> get_commit owner: <owner> repo: <repo> sha: <commit-sha>
``` ```
@@ -2,8 +2,8 @@
**Note on call signatures:** per `docs/adr/0011-gitea-skill-deep-modules.md`, the tool parameter **Note on call signatures:** per `docs/adr/0011-gitea-skill-deep-modules.md`, the tool parameter
signatures in `references/branches.md` and `references/commits.md` were re-verified live via signatures in `references/branches.md` and `references/commits.md` were re-verified live via
`ToolSearch` against the deployed `gitea-mcp` server at authoring time — they are not copied `ToolSearch` against the deployed `gitea-mcp` server — **last verified against v1.7.0**, as reported
verbatim from `api-reference.md` below. This resolves issue #6 comment #849's root-cause finding by `get_gitea_mcp_server_version` — rather than copied verbatim from `api-reference.md` below. This resolves issue #6 comment #849's root-cause finding
that a prior skill was authored from API docs that had drifted from the actual MCP tool schema. that a prior skill was authored from API docs that had drifted from the actual MCP tool schema.
The research docs cited here informed gotchas, response shapes, and workflow context, not the The research docs cited here informed gotchas, response shapes, and workflow context, not the
parameter lists themselves. parameter lists themselves.
@@ -11,7 +11,7 @@ parameter lists themselves.
## gitea-mcp-repo ## gitea-mcp-repo
- **URL:** https://gitea.com/gitea/gitea-mcp - **URL:** https://gitea.com/gitea/gitea-mcp
- **Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md. - **Description:** Official gitea-mcp repository; operation/*.go source files documenting the MCP tools, their parameters, and CLI flags. Extracted at v1.3.0; the parameter lists carried into this skill are re-verified live against the deployed server, last at v1.7.0. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md, references/branches.md, references/commits.md - **Contributing files:** SKILL.md, references/branches.md, references/commits.md
- **Status:** `extracted` - **Status:** `extracted`
@@ -19,7 +19,7 @@ parameter lists themselves.
## gitea-mcp-slim-go ## gitea-mcp-slim-go
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/repo/slim.go - **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/repo/slim.go
- **Description:** Slim response shape structs from gitea-mcp source; defines exactly which fields the MCP server returns for branches (name, protected, commit_sha) and commits (sha, html_url, created, message, author), and informed get_commit's always-populated guarantee vs. list_commits' conditional fields. - **Description:** Slim response shape structs from gitea-mcp source, extracted at v1.3.0; defines exactly which fields the MCP server returns for branches (name, protected, commit_sha) and commits (sha, html_url, created, message, author), and informed get_commit's always-populated guarantee vs. list_commits' conditional fields.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md - **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** references/branches.md, references/commits.md - **Contributing files:** references/branches.md, references/commits.md
- **Status:** `extracted` - **Status:** `extracted`
@@ -8,7 +8,7 @@ This skill handles file-domain operations within the Gitea integration suite: re
## Usage ## Usage
``` ```text
/gitea-files /gitea-files
``` ```
@@ -24,7 +24,7 @@ allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__g
- **A 404 may mean an under-scoped token, not a missing path.** Every tool here gates on `write:repository`, and Gitea masks insufficient scope as 404. Check scopes first. - **A 404 may mean an under-scoped token, not a missing path.** Every tool here gates on `write:repository`, and Gitea masks insufficient scope as 404. Check scopes first.
- **Reads take `ref`, writes take `branch_name`.** One concept, two parameter names — chaining a read into a write drops the branch if you carry the wrong key. - **Reads take `ref`, writes take `branch_name`.** One concept, two parameter names — chaining a read into a write drops the branch if you carry the wrong key.
- **`content` is base64 both ways.** Encode before a write, decode after a read; `withLines: true` returns numbered lines. - **`content` is base64 both ways.** Encode before a write, decode after a read.
## Inputs ## Inputs
@@ -32,14 +32,12 @@ allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__g
## Dispatch ## Dispatch
Read the reference for the row you land on before making the call.
| Condition | Flow | Reference | | Condition | Flow | Reference |
|---|---|---| |---|---|---|
| Read one file, list one directory level, or walk the repository tree | Read | `references/reading.md` | | Read one file, list one directory level, or walk the repository tree | Read | `references/reading.md` — the three read tools, their pagination behaviour, and why neither a directory listing nor a tree entry supplies the SHA a write needs |
| Create, update, or delete a file | Write | `references/writing.md` | | Create, update, or delete a file | Write | `references/writing.md` — the SHA-first sequence every update and delete depends on, the worked multi-call sequence, and how to triage a write that fails |
If the request only inspects repository contents, read `references/reading.md` — it carries the three read tools, their pagination behaviour, and why neither a directory listing nor a tree entry supplies the SHA a write needs.
If the request creates, updates or deletes a file, read `references/writing.md` — it carries the SHA-first sequence every update and delete depends on, the worked multi-call sequence, and how to triage a write that fails.
A request that reads and then writes runs both flows in that order: fetch the file first, then write with the SHA that call returned. A request that reads and then writes runs both flows in that order: fetch the file first, then write with the SHA that call returned.
@@ -29,15 +29,17 @@ commit lands on it in one call, replacing a separate branch-creation step.
## Delete a file ## Delete a file
Same SHA-first pattern, with no create-style fallback — `delete_file` without `sha` returns Same SHA-first pattern, with no create-style fallback. `sha` is schema-**required** on
HTTP 422. `delete_file`, unlike `create_or_update_file` where omitting it means *create* — so an omitted `sha`
is rejected client-side by input validation and the call never reaches Gitea. The HTTP 422 that is
actually reachable here is the stale-`sha` case.
1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`. 1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`.
2. `delete_file(owner, repo, path, message, branch_name, sha: <that value>)`. 2. `delete_file(owner, repo, path, message, branch_name, sha: <that value>)`.
## Worked sequence — new file on a new branch, then a PR ## Worked sequence — new file on a new branch, then a PR
``` ```text
1. create_or_update_file 1. create_or_update_file
owner, repo owner, repo
path: "docs/example.md" path: "docs/example.md"
@@ -58,9 +60,10 @@ when the write replaces an existing one.
| Symptom | Cause | Action | | Symptom | Cause | Action |
|---|---|---| |---|---|---|
| HTTP 409 | `sha` omitted on a path that already exists | Fetch the current SHA, retry as an update | | HTTP 409 | `sha` omitted on a path that already exists | Fetch the current SHA, retry as an update |
| HTTP 422 | `sha` missing or stale | Re-fetch the SHA immediately before the write | | HTTP 422 | Stale `sha` — the file changed between the read and the write | Re-fetch the SHA immediately before the write |
| 403 or 422 with no SHA explanation | Branch protection requires signed commits | Stop and report | | 403 or 422 with no SHA explanation | Branch protection requires signed commits | Stop and report |
| HTTP 413 | Reverse-proxy body limit in front of Gitea | Report; retrying cannot fix it | | HTTP 413 | Reverse-proxy body limit in front of Gitea | Report; retrying cannot fix it |
| Client-side input-validation error naming `sha` | `sha` omitted on `delete_file`, where it is schema-required | Fetch the current SHA and retry — nothing was sent to Gitea |
| HTTP 404 | Wrong path, or a token without `write:repository` | Verify the path, then the token's scopes | | HTTP 404 | Wrong path, or a token without `write:repository` | Verify the path, then the token's scopes |
**Signed commits.** These writes create commits server-side from a bare API token with no 2FA or **Signed commits.** These writes create commits server-side from a bare API token with no 2FA or
@@ -10,9 +10,8 @@ its state, adding/editing comments, applying labels, and searching issues/PRs ac
The create flow closes out four enrichments deferred from issue #6 comment #848: label inference The create flow closes out four enrichments deferred from issue #6 comment #848: label inference
and milestone assignment (both by composing `gitea-labels-milestones`), an assignee workaround for and milestone assignment (both by composing `gitea-labels-milestones`), an assignee workaround for
the blocked `get_me` scope, and the "Depends on #N" dependency-linking convention. It supersedes the the blocked `get_me` scope, and the "Depends on #N" dependency-linking convention. It supersedes the
`issue`/`issue <N>`/`issue close <N>`/`issue comment <N>` dispatch in the old flat `issue`/`issue <N>`/`issue close <N>`/`issue comment <N>` dispatch this plugin's old single flat
`plugins/bin/skills/gitea/SKILL.md`, removed per Gitea skill carried, retired when the plugin was split into per-domain deep modules.
`docs/adr/0011-gitea-skill-deep-modules.md`.
## Before you start ## Before you start
@@ -34,7 +33,7 @@ issue request: local git branch or commit work belongs to `gitea-branches` (Gite
## Usage ## Usage
``` ```text
/gitea-issues /gitea-issues
``` ```
@@ -84,8 +84,8 @@ repo-scoped number space. Use the bare `#N` form for same-repo dependencies; use
a dependency in a different repo. a dependency in a different repo.
When creating an issue that depends on another, append a line like: When creating an issue that depends on another, append a line like:
``` ```text
Depends on #42 Depends on #42
``` ```
to the body before calling `issue_write method: "create"`. There is no separate field or follow-up to the body before calling `issue_write method: "create"`. No separate field or follow-up call is
call — the rendering happens automatically once the body is saved. involved — the rendering happens automatically once the body is saved.
@@ -37,7 +37,7 @@ item is the `html_url` path segment (`/issues/` vs `/pulls/`), since `is_pull` i
list items — see the Gotchas section of SKILL.md. list items — see the Gotchas section of SKILL.md.
**Call:** **Call:**
``` ```text
list_issues owner: <owner> repo: <repo> state: "open" type: "issues" list_issues owner: <owner> repo: <repo> state: "open" type: "issues"
``` ```
@@ -66,7 +66,7 @@ number is backed by a pull request — absent, not `false`, on true issues).
to name strings, unlike the labels array on `get`). to name strings, unlike the labels array on `get`).
**Call:** **Call:**
``` ```text
issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N> issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N>
``` ```
@@ -95,7 +95,7 @@ gotcha in SKILL.md.
- `remove_deadline` (boolean, optional) - `remove_deadline` (boolean, optional)
**Create:** **Create:**
``` ```text
issue_write method: "create" issue_write method: "create"
owner: <owner> repo: <repo> owner: <owner> repo: <repo>
title: <title> body: <body> title: <title> body: <body>
@@ -105,19 +105,19 @@ issue_write method: "create"
``` ```
**Close:** **Close:**
``` ```text
issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed" issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed"
``` ```
There is no `method: "close"` — using one will error. No `method: "close"` exists — using one errors.
**Comment:** **Comment:**
``` ```text
issue_write method: "add_comment" owner: <owner> repo: <repo> issue_number: <N> body: <text> issue_write method: "add_comment" owner: <owner> repo: <repo> issue_number: <N> body: <text>
``` ```
**Apply resolved label IDs directly** (bypassing `references/enrichments.md`'s inference step, e.g. **Apply resolved label IDs directly** (bypassing `references/enrichments.md`'s inference step, e.g.
when the caller already named exact labels): when the caller already named exact labels):
``` ```text
issue_write method: "add_labels" owner: <owner> repo: <repo> issue_number: <N> labels: [<IDs>] issue_write method: "add_labels" owner: <owner> repo: <repo> issue_number: <N> labels: [<IDs>]
``` ```
To replace all labels atomically instead of adding: `method: "replace_labels"`. To replace all labels atomically instead of adding: `method: "replace_labels"`.
@@ -22,12 +22,12 @@ time (see `references/sources.md`) — confirmed to match `api-reference.md`.
- `per_page` (number, optional, default `30`) - `per_page` (number, optional, default `30`)
**Call:** **Call:**
``` ```text
search_issues query: <text> search_issues query: <text>
``` ```
**Narrowing the search:** **Narrowing the search:**
``` ```text
search_issues query: <text> owner: <owner> state: "open" type: "pulls" labels: "bug,urgent" search_issues query: <text> owner: <owner> state: "open" type: "pulls" labels: "bug,urgent"
``` ```
Loaded 100 of 210 files, more files were not shown because too many files have changed in this diff. Show more