mirror of
https://github.com/mattpocock/skills.git
synced 2026-07-29 02:52:42 +07:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41283677d6 | ||
|
|
d4e8664be2 | ||
|
|
6e742d6441 | ||
|
|
ad5ee1e12f | ||
|
|
fa3b2a6b35 | ||
|
|
17f22a371b | ||
|
|
9c32629965 | ||
|
|
b8fd9afa42 | ||
|
|
9603c1cc81 | ||
|
|
fde4cd58cf | ||
|
|
c70cb09193 | ||
|
|
e9fcdf95b4 | ||
|
|
7f68c06dcb | ||
|
|
bbce2f91d6 | ||
|
|
66898f60e8 | ||
|
|
0ac3f3fbf1 | ||
|
|
697d4ce974 | ||
|
|
8f4952b6a3 | ||
|
|
d3275456a4 | ||
|
|
04fee67571 | ||
|
|
33dfc9b28f | ||
|
|
8eb8f58f0f | ||
|
|
3bb587fa5c | ||
|
|
170ad48655 | ||
|
|
2602257241 | ||
|
|
45afd8074a | ||
|
|
6704a52ac4 | ||
|
|
7d694b7ae9 | ||
|
|
f15b4dca01 | ||
|
|
e74fee89fe | ||
|
|
42a5b70fca | ||
|
|
391a2701dd | ||
|
|
2ee14df19c | ||
|
|
fa460cbf09 | ||
|
|
3687fb4f38 | ||
|
|
3ea30afc68 | ||
|
|
f947f93b92 | ||
|
|
d80ff7a840 | ||
|
|
0375c88bfb | ||
|
|
30a9c7447f | ||
|
|
89d370d181 | ||
|
|
19c50d50fd | ||
|
|
e4722b8e65 | ||
|
|
371b9c9c94 | ||
|
|
cdec9f6eb2 | ||
|
|
d6274600ab |
@@ -0,0 +1,28 @@
|
||||
# Ship the skill set as a native Claude Code plugin; defer a native Codex plugin
|
||||
|
||||
These skills have always been installable via [skills.sh](https://skills.sh/mattpocock/skills) (`npx skills add mattpocock/skills`), which copies editable skill files into a user's project across Claude Code, Codex, and other Agent-Skills-standard harnesses. A recurring request is a **plug-and-play** distribution: subscribe to the set as a read-only, always-current bundle you don't edit, rather than a fork you own. That is exactly what native plugin systems provide.
|
||||
|
||||
We ship a native **Claude Code plugin** and, for now, **defer** a native **Codex plugin**. The split is forced by how each ecosystem's plugin manifest selects skills, against this repo's bucketed layout.
|
||||
|
||||
## The constraint: bucketed skills vs. single-path selection
|
||||
|
||||
Skills live in bucket folders under `skills/` — `engineering/` and `productivity/` are **promoted** (shipped); `misc/`, `personal/`, `in-progress/`, and `deprecated/` are **not**. A plugin must expose only the promoted set, which spans two of those bucket folders.
|
||||
|
||||
- **Claude Code** — `.claude-plugin/plugin.json` accepts `skills` as an **array of explicit skill-directory paths**. We list the promoted skills one by one, exclude everything else with zero ambiguity, and add `.claude-plugin/marketplace.json` so the repo is its own single-plugin marketplace. Verified end to end: `claude plugin validate . --strict` passes, and `marketplace add` → `install` resolves all promoted skills.
|
||||
|
||||
- **Codex** — `.codex-plugin/plugin.json` accepts `skills` only as a **single path string** (arrays are rejected with `missing or invalid plugin.json`), and Codex discovers `SKILL.md` files recursively under it. There is no way to name two bucket folders, or to curate a subset, from one path. Two escape hatches were tested and rejected:
|
||||
- Pointing at `./skills/` would also ship `deprecated/`, `in-progress/`, `personal/`, and `misc/` — retired, draft, and personal skills we deliberately don't promote.
|
||||
- A curated flat directory of **symlinks** into the buckets does not survive install: Codex copies the plugin tree into its cache and **drops symlinks**, so the skills arrive empty.
|
||||
|
||||
The only robust ways to give Codex a single promoted-only path are (a) **restructure** so `skills/` contains only promoted skills (moving the non-promoted buckets out — a large blast radius across `CLAUDE.md`, `scripts/link-skills.sh`, the bucket READMEs, and the local dev workflow that relies on `in-progress/` and `personal/`), or (b) **commit duplicate copies** of promoted skills into a flat directory (a sync burden and a second source of truth). Both are structural decisions, not something to bundle into shipping the Claude plugin. This is very likely the original, half-remembered reason a plugin wasn't shipped earlier: the manifest formats didn't cleanly express a curated subset of a bucketed repo.
|
||||
|
||||
## Decision
|
||||
|
||||
- Ship the **Claude Code plugin** now (`.claude-plugin/plugin.json` + `.claude-plugin/marketplace.json`), curated to the promoted set, as the headline v1.2 deliverable.
|
||||
- Keep **skills.sh** as the universal installer — it already serves Codex and other harnesses today, so no Codex user is left without an install path.
|
||||
- **Defer** the native Codex plugin until we decide between restructuring `skills/` to promoted-only vs. committing a generated flat copy. Revisit when Codex either supports a `skills` array / include-list or preserves symlinks on install.
|
||||
|
||||
## Invariants this creates
|
||||
|
||||
- Every promoted skill has an entry in `.claude-plugin/plugin.json`'s `skills` array (this already stood as a `CLAUDE.md` rule; it now also gates the plugin's contents).
|
||||
- `.claude-plugin/plugin.json`'s `version` tracks `package.json`'s version — bump both together on release. Claude uses the plugin `version` to decide when installed users see an update.
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
Every `SKILL.md` in this repo is a skill. The one axis that splits them is **invocation** — who can reach it:
|
||||
|
||||
- **User-invoked** — reachable **only by the human typing its name**. Set `disable-model-invocation: true` in the frontmatter. The `description` is **human-facing**: a one-line summary read by a person browsing slash-commands. Strip trigger lists ("Use when the user says…").
|
||||
- **Model-invoked** — reachable by **model or user**. The default: omit `disable-model-invocation`. The `description` is **model-facing** and keeps rich trigger phrasing ("Use when the user wants…, mentions…, asks for…") so auto-invocation fires. The test for whether a skill should stay model-invoked: _could the model usefully reach for this autonomously?_ (Reuse is the reason to extract a skill, not the test.)
|
||||
- **User-invoked** — reachable **only by the human typing its name**. Set `disable-model-invocation: true` in the frontmatter (Claude Code) and `policy.allow_implicit_invocation: false` in `agents/openai.yaml` (Codex). The `description` is **human-facing**: a one-line summary read by a person browsing slash-commands. Strip trigger lists ("Use when the user says…").
|
||||
- **Model-invoked** — reachable by **model or user**. The default: omit `disable-model-invocation` and the `policy` block from `agents/openai.yaml`. The `description` is **model-facing** and keeps rich trigger phrasing ("Use when the user wants…, mentions…, asks for…") so auto-invocation fires. The test for whether a skill should stay model-invoked: _could the model usefully reach for this autonomously?_ (Reuse is the reason to extract a skill, not the test.)
|
||||
|
||||
Because a user-invoked skill has no description, nothing but the human can reach it — no other skill can fire it. So a user-invoked skill may invoke model-invoked skills, but it can never reach another user-invoked skill.
|
||||
Each harness excludes a user-invoked skill from the model's reach in its own way, so nothing but the human can fire it — no other skill can. A user-invoked skill may invoke model-invoked skills, but it can never reach another user-invoked skill.
|
||||
|
||||
Every skill also carries an `agents/openai.yaml` beside its `SKILL.md`. It holds Codex UI metadata — `interface.display_name` and `interface.short_description` for the skill picker — and, for user-invoked skills, the `policy.allow_implicit_invocation: false` that pairs with `disable-model-invocation`. Keep the two in sync: a skill is user-invoked in both harnesses or neither.
|
||||
|
||||
Bucket `README.md`s and the top-level `README.md` group entries into **User-invoked** and **Model-invoked**.
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
"mattpocock-skills": patch
|
||||
---
|
||||
|
||||
Make `/ask-matt` clued-up about `/wayfinder` — the heaviest, most cognitively demanding flow.
|
||||
|
||||
The router now sharpens the two routing mistakes people most often make with wayfinder:
|
||||
|
||||
- **Over-reaching for it.** It's slower and denser than a single grill, so it's flagged as the heaviest flow and reserved for the idea that genuinely won't fit one session — a well-scoped feature belongs on `/grill-with-docs`, not here.
|
||||
- **Losing the way at the handoff.** When the map clears, wayfinder hands off, it doesn't build: merge onto the main flow at `/to-spec` (which collapses the map's linked decisions into a buildable plan) rather than looping the map straight into `/implement`. Straight-to-`/implement` is only for efforts that turned out genuinely small.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Add Codex metadata alongside each skill's Claude Code frontmatter so the set works in both harnesses without generated copies.
|
||||
|
||||
- Add an `agents/openai.yaml` beside every `SKILL.md` with Codex UI metadata (`interface.display_name`, `interface.short_description`).
|
||||
- Mark every user-invoked skill with `policy.allow_implicit_invocation: false`, the Codex analog of `disable-model-invocation: true`, so Codex excludes it from implicit invocation while explicit `$skill` invocation still works.
|
||||
- Document the dual-harness invocation model in `.agents/invocation.md`, `CLAUDE.md`, and the promoted-bucket READMEs.
|
||||
- Add `AGENTS.md` as a symlink to `CLAUDE.md` so Codex reads the same repo instructions.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Graduate **`to-questionnaire`** out of `in-progress/` into the **Productivity** bucket, so it ships in the plugin. It turns a decision you can't answer alone into a Markdown questionnaire for the one person who can — filled in async, or worked through together in a meeting.
|
||||
|
||||
Its defining move is that it grills you about the **send**, not the subject: a normal grilling session interrogates the topic, which is exactly what you can't answer here, so the interview asks only who the questionnaire is going to and what you need back, then aims every question at the gap between the two.
|
||||
|
||||
Now wired as a promoted skill — plugin entry, top-level + Productivity READMEs under **User-invoked**, a docs page at `docs/productivity/to-questionnaire.md`, and a Standalone route in `ask-matt` framing it as the inverse of `/grill-me` (mine someone else, not yourself).
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"mattpocock-skills": patch
|
||||
---
|
||||
|
||||
Reword **`grilling`** for general use. Its description and body no longer scope the interview to a software plan: "this plan" → "this", "enact the plan" → "act on it", and "exploring the codebase" → "exploring the environment". The technique is unchanged; it now reads as a stress-test of any plan, decision, or idea.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"mattpocock-skills": patch
|
||||
---
|
||||
|
||||
Rework **`grilling`** from one-question-at-a-time to round-by-round. It now maps the decision tree and asks the whole **frontier** — every question whose prerequisites are already settled — in a single numbered round, then recomputes the frontier from the user's answers and asks the next round. Same 13 questions land in ~3 rounds instead of 13. Facts the environment can answer are dispatched to background sub-agents so research never blocks the round: only questions downstream of a running exploration wait for it. The session ends when the frontier is empty.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Reshape the **`prototype`** skill's logic branch to produce a **single shareable HTML file** instead of a terminal app. The demo is one self-contained file (plain HTML/CSS/JS, no build, no server) a non-developer can open by double-click and drive in their own domain language: a labelled state panel, always-available free-play buttons, and a set of tabbed **guided walkthroughs** — each a scenario with the ordered buttons to press underneath it. The portable pure-logic module still lifts into the real code; the HTML shell is the throwaway primary source.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Reword how the **`prototype`** skill handles its artifacts around a single idea: **the prototype is a primary source**. Rather than being deleted once it's answered its question, the prototype is captured as runnable evidence on a throwaway branch (`prototype/<name>`) out of main, with a context pointer to it left on the implementation issue — so the main branch keeps only the validated decision while the exploration stays findable. The answer (verdict + question) is still captured durably in an issue/ADR/commit.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Ship the skill set as a native **Claude Code plugin**. The repo is now its own single-plugin marketplace, so you can subscribe to the promoted skills as a managed, read-only bundle instead of copying editable files:
|
||||
|
||||
```
|
||||
/plugin marketplace add mattpocock/skills
|
||||
/plugin install mattpocock-skills@mattpocock
|
||||
```
|
||||
|
||||
`.claude-plugin/plugin.json` gains full marketplace metadata (version, description, author, license, keywords) and a sibling `.claude-plugin/marketplace.json` lists the plugin. `skills.sh` remains the universal installer (and the path for Codex and other harnesses today); a native Codex plugin is deferred — see `.agents/adr/0002-ship-as-a-claude-code-plugin.md` for why.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"mattpocock-skills": patch
|
||||
---
|
||||
|
||||
Name the `/wayfinder` unit a **decision ticket**.
|
||||
|
||||
People kept reading a wayfinder ticket as an ordinary *implementation* ticket — a slice of a build to execute — when wayfinder uses them as **decision tickets**: questions whose resolution is a decision. The skill description and its opening line now introduce "decision ticket" (and say what makes it one), and the `ask-matt` / engineering README wayfinder blurbs and the docs page match — while "ticket" stays the everyday word once the term is established. `CONTEXT.md` records **Decision ticket** as a domain term so the "avoid: ticket" guidance no longer contradicts wayfinder's deliberate use of the word.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Wayfinder now burns research tickets down with subagents instead of leaving them parked for a separately-launched session.
|
||||
|
||||
Research stays a real ticket type — it's a genuine shared blocker that downstream decisions hang on, and that dependency is exactly what the frontier's blocking edges exist to render. What changes is how it's resolved: because research is AFK, charting doesn't stop and read it. After creating the tickets, the charting session fires a `/research` subagent for each research ticket to burn it down in parallel, capturing the findings on a throwaway `research/<name>` branch with a context pointer. Research tickets are the one exception to *one ticket per session*.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Extend **`writing-for-agents`**' pruning section with a new leading word: **cache**. Single source of truth now reaches past the document into the environment — `package.json` scripts, config files, directory layout, `--help` output are themselves authoritative, so a doc that restates them is a cache of a lookup, earning its load only when the lookup is expensive. The positive target: cache what the agent cannot find by looking (unwritten conventions, the reason behind a choice, gotchas no config confesses), and leave one-file, one-command lookups to the environment, where they cannot go stale.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
**Breaking:** rename **`writing-great-skills`** → **`writing-for-agents`** and restructure it. The reference now covers any document an agent consumes — skills, `AGENTS.md` / `CLAUDE.md`, docs reached by a pointer — not just skills. `GLOSSARY.md` is merged into `SKILL.md` (one authoritative treatment per term; the `_Avoid_` synonym lists and the standalone Predictability definition are gone); the skill-only mechanics (frontmatter, model- vs user-invoked, router skills, the invocation cut of splitting) are disclosed to a new `SKILL-MECHANICS.md`. The skill is now **model-invoked**: it fires when creating or editing skills or modifying `AGENTS.md`/`CLAUDE.md`. `ask-matt`'s pointer updated. Reinstall under the new name; the old name is gone (no alias).
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Add a YAGNI scoping filter to the **`improve-codebase-architecture`** skill's Explore step. Instead of scanning the whole repo evenly, it now scopes to where change is actually landing: if you name a direction it takes it, otherwise it reads the last ~20 commit messages to bias exploration toward actively-developed paths. A deepening opportunity in code nobody touches is a refactor you'll never cash in — the leverage only pays off where you keep editing — so the report stops tidying dormant corners of the repo.
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "mattpocock",
|
||||
"owner": {
|
||||
"name": "Matt Pocock",
|
||||
"url": "https://www.aihero.dev"
|
||||
},
|
||||
"description": "Matt Pocock's skills for real engineering, as an installable Claude Code plugin.",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "mattpocock-skills",
|
||||
"source": "./",
|
||||
"description": "Matt Pocock's agent skills for real engineering — grilling, spec/ticket flows, TDD, code review, domain modelling and more.",
|
||||
"category": "engineering",
|
||||
"keywords": [
|
||||
"engineering",
|
||||
"skills",
|
||||
"tdd",
|
||||
"code-review",
|
||||
"grilling"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,23 @@
|
||||
{
|
||||
"name": "mattpocock-skills",
|
||||
"version": "1.2.0",
|
||||
"description": "Matt Pocock's agent skills for real engineering — grilling, spec/ticket flows, TDD, code review, domain modelling and more. Plug-and-play, not vibe coding.",
|
||||
"author": {
|
||||
"name": "Matt Pocock",
|
||||
"url": "https://www.aihero.dev"
|
||||
},
|
||||
"homepage": "https://www.aihero.dev/s/skills-newsletter",
|
||||
"repository": "https://github.com/mattpocock/skills",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"engineering",
|
||||
"skills",
|
||||
"tdd",
|
||||
"code-review",
|
||||
"grilling",
|
||||
"domain-modeling",
|
||||
"productivity"
|
||||
],
|
||||
"skills": [
|
||||
"./skills/engineering/ask-matt",
|
||||
"./skills/engineering/diagnosing-bugs",
|
||||
@@ -17,10 +35,12 @@
|
||||
"./skills/engineering/domain-modeling",
|
||||
"./skills/engineering/codebase-design",
|
||||
"./skills/engineering/code-review",
|
||||
"./skills/engineering/resolving-merge-conflicts",
|
||||
"./skills/productivity/grill-me",
|
||||
"./skills/productivity/grilling",
|
||||
"./skills/productivity/handoff",
|
||||
"./skills/productivity/teach",
|
||||
"./skills/productivity/writing-great-skills"
|
||||
"./skills/productivity/to-questionnaire",
|
||||
"./skills/productivity/writing-for-agents"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ Skills are organized into bucket folders under `skills/`:
|
||||
- `in-progress/` — drafts not yet ready to ship
|
||||
- `deprecated/` — no longer used
|
||||
|
||||
Every skill in `engineering/` or `productivity/` (the **promoted** buckets) must have a reference in the top-level `README.md` and an entry in `.claude-plugin/plugin.json`. Skills in `misc/`, `personal/`, `in-progress/`, and `deprecated/` must not appear in either.
|
||||
Every skill in `engineering/` or `productivity/` (the **promoted** buckets) must have a reference in the top-level `README.md` and an entry in `.claude-plugin/plugin.json`'s `skills` array (the Claude Code plugin ships exactly the promoted set). Skills in `misc/`, `personal/`, `in-progress/`, and `deprecated/` must not appear in either.
|
||||
|
||||
The repo is also its own single-plugin Claude Code marketplace: `.claude-plugin/marketplace.json` lists the one `mattpocock-skills` plugin. When bumping the release version, keep `.claude-plugin/plugin.json`'s `version` in sync with `package.json`'s — Claude uses the plugin `version` to decide when installed users see an update. Run `claude plugin validate . --strict` after touching either manifest. Why a Claude plugin but not (yet) a Codex one lives in [.agents/adr/0002-ship-as-a-claude-code-plugin.md](./.agents/adr/0002-ship-as-a-claude-code-plugin.md).
|
||||
|
||||
Each skill entry in the top-level `README.md` must link the skill name to its `SKILL.md`.
|
||||
|
||||
@@ -15,7 +17,7 @@ Each bucket folder has a `README.md` that lists every skill in the bucket with a
|
||||
|
||||
Skills in `engineering/` and `productivity/` also have a human-facing docs page at `docs/<bucket>/<skill-name>.md` (the docs tree mirrors those two bucket folders under `skills/`). The published URL is `https://aihero.dev/skills-<skill-name>` regardless of bucket — the docs path is repo organisation only. When you add, rename, or change the behaviour of a skill in `engineering/` or `productivity/`, create or re-sync its docs page following [.agents/writing-docs.md](./.agents/writing-docs.md). Skills in the non-promoted buckets (`misc/`, `personal/`, `in-progress/`, `deprecated/`) get **no** docs page.
|
||||
|
||||
Every `SKILL.md` is either user-invoked (`disable-model-invocation: true`, reachable only by the human) or model-invoked (model- or user-reachable). See [.agents/invocation.md](./.agents/invocation.md).
|
||||
Every `SKILL.md` is either user-invoked (`disable-model-invocation: true` plus `policy.allow_implicit_invocation: false` in `agents/openai.yaml`, reachable only by the human) or model-invoked (model- or user-reachable). See [.agents/invocation.md](./.agents/invocation.md).
|
||||
|
||||
[`ask-matt`](./skills/engineering/ask-matt/SKILL.md) is the router that maps every user-reachable skill and how they relate. The same trigger that re-syncs a docs page applies to it: whenever you add, rename, remove, or change how a user-reachable skill fits the flows, re-read `ask-matt`'s `SKILL.md` and update it so the map stays accurate — a new skill it never mentions, or a stale one it still routes to, is a router that lies.
|
||||
|
||||
|
||||
+5
-1
@@ -10,7 +10,10 @@ _Avoid_: backlog manager, backlog backend, issue host
|
||||
|
||||
**Issue**:
|
||||
A single tracked unit of work inside an **Issue tracker** — a bug, task, spec, or slice produced by `to-tickets`.
|
||||
_Avoid_: ticket (use only when quoting external systems that call them tickets)
|
||||
_Avoid_: ticket (use only when quoting external systems that call them tickets, or for a **Decision ticket** — see below)
|
||||
|
||||
**Decision ticket**:
|
||||
A `wayfinder` unit — a child **Issue** of a `wayfinder:map` holding a *question* whose resolution is a decision, not a slice of a build to execute. The **decision** qualifier is what keeps it distinct from an implementation ticket; `wayfinder` introduces the term, then uses "ticket".
|
||||
|
||||
**Triage role**:
|
||||
A canonical state-machine label applied to an **Issue** during triage (e.g. `needs-triage`, `ready-for-afk`). Each role maps to a real label string in the **Issue tracker** via `docs/agents/triage-labels.md`.
|
||||
@@ -19,6 +22,7 @@ A canonical state-machine label applied to an **Issue** during triage (e.g. `nee
|
||||
|
||||
- An **Issue tracker** holds many **Issues**
|
||||
- An **Issue** carries one **Triage role** at a time
|
||||
- A **Decision ticket** is an **Issue** (a child of a `wayfinder:map`)
|
||||
|
||||
## Flagged ambiguities
|
||||
|
||||
|
||||
@@ -39,6 +39,33 @@ npx skills@latest add mattpocock/skills
|
||||
|
||||
4. Bam - you're ready to go.
|
||||
|
||||
## Install as a Claude Code plugin
|
||||
|
||||
Prefer a plug-and-play install you don't maintain by hand? These skills also ship as a native [Claude Code plugin](https://code.claude.com/docs/en/plugins). Instead of copying editable files into your repo, the plugin installs the whole skill set as a managed bundle that updates when I ship a new version — you subscribe rather than fork.
|
||||
|
||||
Inside Claude Code:
|
||||
|
||||
```
|
||||
/plugin marketplace add mattpocock/skills
|
||||
/plugin install mattpocock-skills@mattpocock
|
||||
```
|
||||
|
||||
Or from your shell:
|
||||
|
||||
```bash
|
||||
claude plugin marketplace add mattpocock/skills
|
||||
claude plugin install mattpocock-skills@mattpocock
|
||||
```
|
||||
|
||||
Then run `/setup-matt-pocock-skills` once per repo, exactly as in the quickstart above.
|
||||
|
||||
Two ways to install, two philosophies:
|
||||
|
||||
- **[skills.sh](https://skills.sh/mattpocock/skills)** copies the skills into your project so you can hack on them and make them your own.
|
||||
- **The plugin** keeps them as a read-only, always-current bundle you don't edit — best when you just want my set to work and follow along as it evolves.
|
||||
|
||||
> Using Codex or another agent? The [skills.sh installer](https://skills.sh/mattpocock/skills) already installs these skills into Codex and other Agent-Skills-standard harnesses today. A native Codex plugin is on the roadmap — see [`.agents/adr/0002-ship-as-a-claude-code-plugin.md`](./.agents/adr/0002-ship-as-a-claude-code-plugin.md).
|
||||
|
||||
## Why These Skills Exist
|
||||
|
||||
I built these skills as a way to fix common failure modes I see with Claude Code, Codex, and other coding agents.
|
||||
@@ -168,6 +195,7 @@ Skills I use daily for code work.
|
||||
- **[domain-modeling](./skills/engineering/domain-modeling/SKILL.md)** — Actively build and sharpen a project's domain model — challenge terms against the glossary, stress-test with edge-case scenarios, and update `CONTEXT.md` and ADRs inline.
|
||||
- **[codebase-design](./skills/engineering/codebase-design/SKILL.md)** — Shared discipline and vocabulary for designing deep modules: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface.
|
||||
- **[code-review](./skills/engineering/code-review/SKILL.md)** — Two-axis review of the diff since a fixed point: **Standards** (does it follow the repo's coding standards, plus a Fowler smell baseline?) and **Spec** (does it faithfully implement the originating issue/PRD?), run as parallel sub-agents so neither pollutes the other.
|
||||
- **[resolving-merge-conflicts](./skills/engineering/resolving-merge-conflicts/SKILL.md)** — Work through an in-progress git merge or rebase conflict hunk by hunk, resolving by intent traced to each side's primary source, then finish the operation — never `--abort`.
|
||||
|
||||
### Productivity
|
||||
|
||||
@@ -178,8 +206,9 @@ General workflow tools, not code-specific.
|
||||
- **[grill-me](./skills/productivity/grill-me/SKILL.md)** — Get relentlessly interviewed about a plan or design until every branch of the decision tree is resolved.
|
||||
- **[handoff](./skills/productivity/handoff/SKILL.md)** — Compact the current conversation into a handoff document so another agent can continue the work.
|
||||
- **[teach](./skills/productivity/teach/SKILL.md)** — Teach the user a new skill or concept over multiple sessions, using the current directory as a stateful teaching workspace.
|
||||
- **[writing-great-skills](./skills/productivity/writing-great-skills/SKILL.md)** — Reference for writing and editing skills well: the vocabulary and principles that make a skill predictable.
|
||||
- **[to-questionnaire](./skills/productivity/to-questionnaire/SKILL.md)** — Turn a decision you can't answer alone into a Markdown questionnaire for the one person who can — filled in async, or together over a meeting. It grills you about the send (who it's for, what you need back), not the subject.
|
||||
- **[writing-for-agents](./skills/productivity/writing-for-agents/SKILL.md)** — Writing documents for agents: skills, AGENTS.md/CLAUDE.md, and any doc an agent reaches by a pointer.
|
||||
|
||||
**Model-invoked**
|
||||
|
||||
- **[grilling](./skills/productivity/grilling/SKILL.md)** — Interview the user relentlessly about a plan or design until every branch of the decision tree is resolved. The reusable loop behind `grill-me` and `grill-with-docs`.
|
||||
- **[grilling](./skills/productivity/grilling/SKILL.md)** — Interview the user relentlessly about a plan, decision, or idea until every branch of the decision tree is resolved. The reusable loop behind `grill-me` and `grill-with-docs`.
|
||||
|
||||
@@ -28,7 +28,7 @@ This skill is stateful — it writes into your repo as it grills. Resolved terms
|
||||
|
||||
## The grill
|
||||
|
||||
The engine is a **grill**: a relentless, one-question-at-a-time walk down the design tree, resolving dependencies between decisions before moving on, with a recommended answer offered for every question. Questions the codebase can answer are answered by reading the codebase, not by asking you.
|
||||
The engine is a **grill**: a relentless, one-question-at-a-time walk down the decision tree, resolving dependencies between decisions before moving on, with a recommended answer offered for every question. Questions the codebase can answer are answered by reading the codebase, not by asking you.
|
||||
|
||||
What makes this variant its own skill is where the answers go. As the grill runs, fuzzy language gets sharpened into canonical terms and written to the glossary inline — not batched at the end. The glossary stays a glossary: pure vocabulary, no implementation details, no spec. ADRs are offered sparingly, only when a decision is hard to reverse, surprising without context, and the result of a real trade-off. Most sessions produce a sharper glossary and few or no ADRs, and that's the intended shape.
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ npx skills update improve-codebase-architecture
|
||||
|
||||
It does **not** hand you a flat list of refactors. Every candidate has to pass the **deletion test** — would removing this module *concentrate* complexity behind a smaller interface, or just move it around? Only the "concentrates" cases earn a card. That filter is what stops the report from becoming generic cleanup advice.
|
||||
|
||||
Unless you point it at a specific area, it also scopes itself to where development is actually landing — reading the recent commits to bias toward the code you're still changing. Deepening a module pays off by making future changes to it easier, so it puts extra weight on the parts of the repo that have recently changed.
|
||||
|
||||
## When to reach for it
|
||||
|
||||
You invoke this by typing `/improve-codebase-architecture` — the agent won't reach for it on its own.
|
||||
@@ -36,4 +38,4 @@ Then it stops and asks which one you want to explore. Pick one and it runs the [
|
||||
|
||||
## Where it fits
|
||||
|
||||
`improve-codebase-architecture` is **periodic maintenance** — run it every few days, not as a step in a chain. Its neighbours are [codebase-design](https://aihero.dev/skills-codebase-design), which owns the depth-and-seam vocabulary every candidate is written in, [grilling](https://aihero.dev/skills-grilling), which walks the design tree once you've chosen a candidate, and [domain-modeling](https://aihero.dev/skills-domain-modeling), which keeps `CONTEXT.md` and the ADRs current as the redesign settles. When you're unsure which skill or flow fits, [ask-matt](https://aihero.dev/skills-ask-matt) routes you.
|
||||
`improve-codebase-architecture` is **periodic maintenance** — run it every few days, not as a step in a chain. Its neighbours are [codebase-design](https://aihero.dev/skills-codebase-design), which owns the depth-and-seam vocabulary every candidate is written in, [grilling](https://aihero.dev/skills-grilling), which walks the decision tree once you've chosen a candidate, and [domain-modeling](https://aihero.dev/skills-domain-modeling), which keeps `CONTEXT.md` and the ADRs current as the redesign settles. When you're unsure which skill or flow fits, [ask-matt](https://aihero.dev/skills-ask-matt) routes you.
|
||||
|
||||
@@ -26,14 +26,16 @@ Reach for it when you have a design question that's hard to settle on paper —
|
||||
|
||||
The question decides the shape, and there are two shapes:
|
||||
|
||||
- **"Does this logic / state model feel right?"** — a tiny interactive terminal app that pushes the state machine through the awkward cases, printing the full state after every action so you can watch what changes.
|
||||
- **"Does this logic / state model feel right?"** — a single shareable HTML file, free-play buttons plus tabbed guided walkthroughs, that pushes the state machine through the awkward cases and shows the full state after every click, so anyone — including a non-developer — can watch what changes.
|
||||
- **"What should this look like?"** — several radically different UI variations on one route, switchable from a floating bar, so you compare real renders instead of imagining them.
|
||||
|
||||
Picking the wrong branch wastes the whole prototype, so the question comes first. Both branches keep state in memory, run from one command, and surface the full state on every step.
|
||||
Picking the wrong branch wastes the whole prototype, so the question comes first. Both branches keep state in memory, are trivial to run, and surface the full state on every step.
|
||||
|
||||
## The answer is the artifact
|
||||
## Keep the prototype as a primary source
|
||||
|
||||
The code is disposable; the **answer** is the only thing worth keeping. When the prototype has settled its question, capture the verdict somewhere durable — a commit message, an ADR, an issue, or a `NOTES.md` next to it — alongside the question it answered, then delete or absorb the code. A prototype left rotting in the repo has outlived its purpose.
|
||||
A finished prototype leaves two things. The **answer** — the verdict plus the question it settled — is what you capture durably (a commit message, an ADR, an issue). The **prototype itself is a primary source** — the runnable evidence the answer came from.
|
||||
|
||||
The prototype doesn't belong in the main branch: no tests, no error handling, nothing to maintain. But that's not a reason to destroy it. Once the answer is captured, fold any validated decision into the real code, then capture the prototype on a throwaway branch — out of main, never merged — and leave a context pointer to it on the implementation issue. The main branch stays clean; the raw exploration stays one click away for anyone who wants to re-run it. A prototype left rotting in the main branch has outlived its purpose — a prototype captured as a primary source on a side branch hasn't.
|
||||
|
||||
## Where it fits
|
||||
|
||||
|
||||
@@ -57,4 +57,3 @@ grill-with-docs → to-spec → to-tickets → implement → code-review
|
||||
```
|
||||
|
||||
Reach for it after the plan and domain language are resolved, and before you break the work into implementation tickets. Its key neighbours are [grill-with-docs](https://aihero.dev/skills-grill-with-docs), which sharpens the context so the spec is precise, and [to-tickets](https://aihero.dev/skills-to-tickets), which turns the spec into a set of tickets for [implement](https://aihero.dev/skills-implement) to build. When you're unsure which skill or flow fits, [ask-matt](https://aihero.dev/skills-ask-matt) routes you.
|
||||
</content>
|
||||
|
||||
@@ -54,4 +54,3 @@ grill-with-docs → to-spec → to-tickets → implement → code-review
|
||||
```
|
||||
|
||||
It sits between [to-spec](https://aihero.dev/skills-to-spec), which hands it a settled spec with user stories to slice against, and [implement](https://aihero.dev/skills-implement), which builds each ticket, driving [tdd](https://aihero.dev/skills-tdd) internally to write the tests test-first, before its [code-review](https://aihero.dev/skills-code-review) pass. Work the frontier one ticket per fresh context, clearing between them. When you're unsure which skill or flow fits, [ask-matt](https://aihero.dev/skills-ask-matt) routes you.
|
||||
</content>
|
||||
|
||||
@@ -12,7 +12,7 @@ npx skills update wayfinder
|
||||
|
||||
## What it does
|
||||
|
||||
`wayfinder` takes an effort too big for one agent session — wrapped in fog, where the way from here to the goal isn't visible yet — and charts it as a **shared map** of investigation tickets on your issue tracker, then resolves them one at a time until the way is clear. It **plans, it doesn't do**: every ticket resolves a decision, and the map is done when nothing is left to decide before someone goes and builds the thing — so it produces decisions, not deliverables.
|
||||
`wayfinder` takes an effort too big for one agent session — wrapped in fog, where the way from here to the goal isn't visible yet — and charts it as a **shared map** of **decision tickets** on your issue tracker, then resolves them one at a time until the way is clear. It **plans, it doesn't do**: every ticket resolves a decision — a question to settle, not a slice of a build to execute — and the map is done when nothing is left to decide before someone goes and builds the thing — so it produces decisions, not deliverables.
|
||||
|
||||
## When to reach for it
|
||||
|
||||
@@ -30,13 +30,13 @@ The **map** is a single `wayfinder:map` issue whose tickets are its child issues
|
||||
|
||||
Beyond the live tickets lies the **fog of war** — decisions you can tell are coming but can't yet pin down. The test for whether something is a ticket or still fog is whether you can *state the question precisely now*, not whether you can answer it. Resolving a ticket clears the fog ahead of it, **graduating** whatever's now specifiable into fresh tickets. The **frontier** is the open, unblocked, unclaimed tickets — the edge of the known — and it's what the tracker's native blocking renders visually, so you see what's takeable without opening the map. Fog only gathers *toward* the **destination**; work past it is ruled **out of scope**, closed, never graduating.
|
||||
|
||||
Every ticket is **HITL** (human in the loop — grilling, prototype) or **AFK** (agent alone — research); a HITL ticket only resolves through a live exchange, so the agent never answers its own questions.
|
||||
Every ticket is **HITL** (human in the loop — grilling, prototype) or **AFK** (agent alone — research); a HITL ticket only resolves through a live exchange, so the agent never answers its own questions. Research stays a real ticket — a shared blocker downstream decisions hang on — but because it's AFK, a session doesn't stop and read: it fires a `/research` **subagent** to burn the ticket down in parallel, keeping the frontier fast, and captures the findings on a throwaway `research/<name>` branch.
|
||||
|
||||
## It's working if
|
||||
|
||||
- Naming the **destination** is the first act — before any ticket exists — because it fixes the scope every ticket is measured against.
|
||||
- One map is one `wayfinder:map` issue; tickets are its child issues, referred to by **name**, never a bare `#42`.
|
||||
- A session resolves **at most one ticket**, records the answer as a resolution comment, closes the ticket, and appends a one-line pointer to *Decisions so far*.
|
||||
- A session resolves **at most one ticket** (research tickets excepted), records the answer as a resolution comment, closes the ticket, and appends a one-line pointer to *Decisions so far*.
|
||||
- If the opening grill surfaces **no fog**, it stops and tells you the journey is small enough to skip the map.
|
||||
|
||||
## Where it fits
|
||||
|
||||
@@ -22,7 +22,7 @@ You invoke this by typing `/grill-me` — the agent won't reach for it on its ow
|
||||
|
||||
Reach for it before you build, when a plan feels roughly right but you can sense unresolved decisions hiding in it — the moment you want the soft spots found and forced into the open. If you want that same interrogation to also leave a paper trail of ADRs and a glossary behind, use [grill-with-docs](https://aihero.dev/skills-grill-with-docs) instead. And if the effort is too big to hold in one session and the route to the goal is still foggy — a greenfield project, a huge feature build — start further upstream with [wayfinder](https://aihero.dev/skills-wayfinder), which charts it as a map of decisions first and then merges back into this flow.
|
||||
|
||||
## The design tree
|
||||
## The decision tree
|
||||
|
||||
The session walks the plan as a tree of decisions, resolving dependencies between them one by one — a parent decision settled before the choices that hang off it. The point is not to reach agreement quickly; it is to make every implicit call explicit, so nothing important is left silently assumed. You come out the other side with a plan whose branches have all been visited.
|
||||
|
||||
|
||||
@@ -12,9 +12,17 @@ npx skills update grilling
|
||||
|
||||
## What it does
|
||||
|
||||
`grilling` is the relentless interview that stress-tests a plan or design before you build it. It walks down the design tree branch by branch, resolving the dependencies between decisions one at a time until you and the agent share the same understanding.
|
||||
`grilling` is the relentless interview that stress-tests a plan or design before you build it. It maps the plan as a **design tree** — every decision branches into the decisions that hang off it — and works that tree in **rounds** until you and the agent share the same understanding.
|
||||
|
||||
It asks **one question at a time** and waits for your answer before the next — never a bulk list, which is bewildering. Each question comes with the agent's own recommended answer, and any question the codebase can settle it explores instead of asking you. It won't start enacting the plan until you confirm the shared understanding has been reached.
|
||||
Each round asks the whole **frontier**: every decision whose prerequisites are already settled — the questions it can put to you *now* without guessing at answers it hasn't heard yet. Your answers reshape the tree, pushing the frontier outward, and the next round asks whatever that unblocks. Thirteen questions land in a handful of rounds instead of thirteen. Every question comes with the agent's own recommended answer; any *fact* the environment can settle it dispatches to a background sub-agent rather than asking you — and it doesn't block on that research, only the questions downstream of it wait. It won't act on the plan until you confirm the shared understanding has been reached.
|
||||
|
||||
### Prefer one question at a time?
|
||||
|
||||
If the old one-at-a-time rhythm suited you better, keep it. Add a line to your global `CLAUDE.md`:
|
||||
|
||||
```
|
||||
When grilling, ask one question at a time.
|
||||
```
|
||||
|
||||
## When to reach for it
|
||||
|
||||
@@ -22,9 +30,9 @@ Type `/grilling`, or the agent reaches for it automatically when a task fits —
|
||||
|
||||
Reach for it when a plan or design still has soft spots and you want them surfaced before code is written. In practice you usually invoke it through one of its two wrappers rather than by name: for a plain grilling session use [grill-me](https://aihero.dev/skills-grill-me); to have the session also write ADRs and a glossary as it goes, use [grill-with-docs](https://aihero.dev/skills-grill-with-docs).
|
||||
|
||||
## The design tree
|
||||
## The decision tree
|
||||
|
||||
The mental model is a **design tree**: every plan branches into decisions, and decisions depend on each other. `grilling` descends that tree one node at a time, so an early answer can reshape which questions come next. That is why the questions arrive singly and in dependency order — a firehose of parallel questions loses the structure that makes the interview converge on a shared understanding.
|
||||
The mental model is a **design tree**: every plan branches into decisions, and decisions depend on each other. The **frontier** is the set of decisions whose prerequisites are all settled — the only questions that can be asked without guessing. `grilling` asks the whole frontier at once, then recomputes it from your answers, so a round is exactly the batch of questions that *don't* depend on each other. A question whose answer hinges on another still open this round waits for a later round. Asking in frontier-sized rounds keeps the dependency structure intact while sparing you a question-at-a-time drip.
|
||||
|
||||
## Pulled out on purpose
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
Quickstart:
|
||||
|
||||
```bash
|
||||
npx skills add mattpocock/skills --skill=to-questionnaire
|
||||
```
|
||||
|
||||
```bash
|
||||
npx skills update to-questionnaire
|
||||
```
|
||||
|
||||
[Source](https://github.com/mattpocock/skills/tree/main/skills/productivity/to-questionnaire)
|
||||
|
||||
## What it does
|
||||
|
||||
`to-questionnaire` turns a decision you can't settle on your own into a **questionnaire** — a Markdown document you hand to the one person who holds what you're missing, to fill in async or work through together in a meeting.
|
||||
|
||||
It grills you about the **send**, never the subject. A normal grilling session interrogates the topic, which is exactly what you can't answer here — that's why you're writing to someone else. So the interview asks only the two things you can always answer: who this is going to, and what you need back from them. The questions in the document are then aimed at the **gap** between the two.
|
||||
|
||||
## When to reach for it
|
||||
|
||||
You invoke this by typing `/to-questionnaire` — the agent won't reach for it on its own.
|
||||
|
||||
Reach for this when a decision is blocked on knowledge that lives in someone else's head — a client, a domain expert, a colleague on another team — and you want a document that pulls it out of them in one pass. When the knowledge is in *your* head and just needs sharpening, use [grill-me](https://aihero.dev/skills-grill-me) instead; when it's in the codebase, use [grill-with-docs](https://aihero.dev/skills-grill-with-docs).
|
||||
|
||||
## The send, not the subject
|
||||
|
||||
The interview is two exchanges, and it stops there:
|
||||
|
||||
- **Who is it going to?** Their role, expertise, and relationship to you. This fixes the tone and how much context the document has to carry — an outside client needs orienting; a teammate does not.
|
||||
- **What do you need back?** The concrete decisions or facts you can't resolve alone. This becomes the checklist the finished questionnaire is measured against: every item you named has a question aimed at it.
|
||||
|
||||
Everything after that is drafting. The output is written to `to-questionnaire-<slug>.md` in the current directory.
|
||||
|
||||
## The document
|
||||
|
||||
It's a **discovery questionnaire**: you lack the context, the recipient holds it. That framing drives its shape — a purpose line and the decision riding on it, a short context section for a recipient who wasn't in your head, and questions ordered **most-important-first**, because async means you may only get one pass. Each question is one idea, never compound, with an answer stub beneath it and a *why this matters* line only where the question could be misread. It closes with a catch-all: anything we didn't ask that we should know?
|
||||
|
||||
## It's working if
|
||||
|
||||
- It asks you about the recipient and the deliverable, then stops asking — no interrogation of the topic itself.
|
||||
- Every item you named as "what I need back" is traceable to a question in the file.
|
||||
- The questions read as aimed at what the *recipient* knows, not as your own open questions copied down.
|
||||
|
||||
## Where it fits
|
||||
|
||||
`to-questionnaire` is a reach-for-it-anytime standalone — it sits at the boundary of your own knowledge, where the next step is another person rather than another skill. Its neighbour is [grill-me](https://aihero.dev/skills-grill-me), because the two split on where the answers live: grilling mines you, a questionnaire mines someone else. What comes back typically feeds the main flow at [grill-with-docs](https://aihero.dev/skills-grill-with-docs) or [to-spec](https://aihero.dev/skills-to-spec). When you're unsure which skill fits the moment, [ask-matt](https://aihero.dev/skills-ask-matt) routes you.
|
||||
@@ -0,0 +1,42 @@
|
||||
Quickstart:
|
||||
|
||||
```bash
|
||||
npx skills add mattpocock/skills --skill=writing-for-agents
|
||||
```
|
||||
|
||||
```bash
|
||||
npx skills update writing-for-agents
|
||||
```
|
||||
|
||||
[Source](https://github.com/mattpocock/skills/tree/main/skills/productivity/writing-for-agents)
|
||||
|
||||
## What it does
|
||||
|
||||
`writing-for-agents` is the reference you write agent-facing documents against — skills, `AGENTS.md` / `CLAUDE.md`, and any doc an agent reaches by a pointer. The packaging differs; the writing does not: the same levers make each one predictable, so the agent takes the same *process* every run rather than producing the same output.
|
||||
|
||||
Formerly `writing-great-skills`. The rename tracks what the reference always was underneath: almost none of it is skill-specific. The universal core — context pointers, the two loads, the information hierarchy, completion criteria, leading words, pruning — applies to any document an agent consumes; the genuinely skill-only mechanics (frontmatter, the model- vs user-invoked choice, router skills) are disclosed to a linked `SKILL-MECHANICS.md` you read only when the document you're writing is a skill.
|
||||
|
||||
## When to reach for it
|
||||
|
||||
The agent reaches for it on its own whenever you're creating or editing a skill, or modifying `AGENTS.md` or `CLAUDE.md` — and you can still type `/writing-for-agents` to pull it up directly.
|
||||
|
||||
## The two loads
|
||||
|
||||
The concept the whole reference turns on is a pair of budgets every document and pointer spends:
|
||||
|
||||
- **Context load** — the cost of always-loaded material on the agent's window: an `AGENTS.md` line, a skill description, anything sitting in context every turn whether or not it fires.
|
||||
- **Cognitive load** — the cost on the human: which documents exist and when to reach for each. You are the index. Not a cost to minimise — it's the price of human agency.
|
||||
|
||||
Once you're thinking in these two loads, most authoring decisions — split or don't, inline or disclose, point or push — become the same trade made in different places.
|
||||
|
||||
## The other levers
|
||||
|
||||
- **Context pointers** — the reference held in context that names out-of-context material and encodes when to reach it. A skill description and an `AGENTS.md` line pointing at a doc are the same object; the pointer's *wording*, not its target, decides when and how reliably the agent reaches through it.
|
||||
- **Information hierarchy** — the ladder from in-file step, to in-file reference, to disclosed reference behind a pointer. **Progressive disclosure** is the move down that ladder so the top stays legible; **co-location** decides what sits beside each piece once placed.
|
||||
- **Completion criteria** — the clarity and demand of each step's done-condition, and the **legwork** it drives; the defence against **premature completion**.
|
||||
- **Leading words** — a compact concept already in the model's pretraining (*tight*, *red*, *tracer bullet*) that the agent thinks with while running the document; hunt restatements a single word can retire.
|
||||
- **Pruning** — single source of truth, relevance, and the no-op test applied sentence by sentence, against **sediment** and **sprawl**. Single source of truth reaches past the document into the environment: a doc restating what's already in `package.json`, a config file, or `--help` output is a **cache** of a lookup that was never expensive, and it's the copy that goes stale. Cache what the agent can't find by looking — the unwritten convention, the reason behind a choice.
|
||||
|
||||
## Where it fits
|
||||
|
||||
This is a reach-for-it-anytime standalone reference — the meta-skill you consult while building the rest of the set, not a step in a chain. When you're unsure which skill or flow fits a task, [ask-matt](https://aihero.dev/skills-ask-matt) routes you over the whole set.
|
||||
@@ -1,45 +0,0 @@
|
||||
Quickstart:
|
||||
|
||||
```bash
|
||||
npx skills add mattpocock/skills --skill=writing-great-skills
|
||||
```
|
||||
|
||||
```bash
|
||||
npx skills update writing-great-skills
|
||||
```
|
||||
|
||||
[Source](https://github.com/mattpocock/skills/tree/main/skills/productivity/writing-great-skills)
|
||||
|
||||
## What it does
|
||||
|
||||
`writing-great-skills` is the reference you write and edit skills against — the shared vocabulary and principles that make a skill predictable.
|
||||
|
||||
A skill's job is to wrangle determinism out of a stochastic system, so the goal is not the same *output* every run but the same *process*. **Predictability** is the root virtue, and every design choice is judged against it — not against how clever, complete, or exhaustive the skill reads.
|
||||
|
||||
## When to reach for it
|
||||
|
||||
You invoke this by typing `/writing-great-skills` — the agent won't reach for it on its own.
|
||||
|
||||
Reach for it whenever you're authoring a new skill or editing an existing one and want it to behave the same way every time: deciding invocation mode, writing a description, choosing what lives in `SKILL.md` versus a linked file, or diagnosing why a skill misfires.
|
||||
|
||||
## Cognitive load
|
||||
|
||||
The concept the whole reference turns on is **cognitive load** — and its counterpart, **context load**. Every skill spends one or the other:
|
||||
|
||||
- A **model-invoked** skill keeps a description in the window every turn, so it costs **context load** but fires on its own.
|
||||
- A **user-invoked** skill strips that description; it costs zero context load, but now *you* are the index that has to remember it exists — that's **cognitive load**.
|
||||
|
||||
Most of these skills are user-invoked, which is why cognitive load is the pressure the whole system is built to manage: when user-invoked skills multiply past what you can hold in your head, the cure is a **router skill** that names the others and when to reach for each. Once you're thinking in these two loads, most authoring decisions — split or don't, inline or disclose, model- or user-invoked — become the same trade made in different places.
|
||||
|
||||
## The other levers
|
||||
|
||||
The rest of the reference is the toolkit for spending those loads well:
|
||||
|
||||
- **Leading words** — a compact concept already in the model's pretraining (_tight_, _red_, _tracer bullet_) that the agent thinks with while running the skill. It anchors execution *and* invocation in the fewest tokens; hunt restatements that a single word can retire.
|
||||
- **Information hierarchy** — the ladder from in-skill step, to in-skill reference, to external reference behind a **context pointer**. **Progressive disclosure** is the move down that ladder so the top stays legible.
|
||||
- **Pruning** — single source of truth, relevance, and the no-op test applied sentence by sentence, against **sediment** and **sprawl**.
|
||||
- **Failure modes** — **premature completion**, **duplication**, **sediment**, **sprawl**, **no-op** — to diagnose a skill that isn't behaving.
|
||||
|
||||
## Where it fits
|
||||
|
||||
This is a reach-for-it-anytime standalone reference — the meta-skill you consult while building the rest of the set, not a step in a chain. Its natural neighbour is any router you maintain, because a router is the direct cure for the cognitive load that user-invoked skills pile up; when you're unsure which skill or flow fits a task, [ask-matt](https://aihero.dev/skills-ask-matt) routes you over the whole set.
|
||||
@@ -8,7 +8,7 @@ set -euo pipefail
|
||||
# Links all skills in the repository into the local skill directories used by
|
||||
# each agent harness:
|
||||
# - ~/.claude/skills — Claude Code
|
||||
# - ~/.agents/skills — pi and other Agent-Skills-standard harnesses
|
||||
# - ~/.agents/skills — Codex and other Agent Skills-compatible harnesses
|
||||
# Each entry is a symlink into this repo, so a `git pull` is all that's needed
|
||||
# to keep installed skills up to date.
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Design an Interface"
|
||||
short_description: "Explore alternative module interfaces"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "QA"
|
||||
short_description: "Conversational QA that files issues"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Request Refactor Plan"
|
||||
short_description: "Plan a safe incremental refactor"
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Ubiquitous Language"
|
||||
short_description: "Build a shared domain glossary"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -4,7 +4,7 @@ Skills I use daily for code work.
|
||||
|
||||
## User-invoked
|
||||
|
||||
Reachable only when you type them (`disable-model-invocation: true`).
|
||||
Reachable only when you type them (Claude Code: `disable-model-invocation: true`; Codex: `policy.allow_implicit_invocation: false` in `agents/openai.yaml`).
|
||||
|
||||
- **[ask-matt](./ask-matt/SKILL.md)** — Ask which skill or flow fits your situation. A router over the user-invoked skills in this repo.
|
||||
- **[grill-with-docs](./grill-with-docs/SKILL.md)** — Grilling session that also builds your project's domain model, sharpening terminology and updating `CONTEXT.md` and ADRs inline.
|
||||
@@ -13,7 +13,8 @@ Reachable only when you type them (`disable-model-invocation: true`).
|
||||
- **[setup-matt-pocock-skills](./setup-matt-pocock-skills/SKILL.md)** — Configure this repo for the engineering skills (issue tracker, triage labels, domain doc layout). Run once per repo.
|
||||
- **[to-spec](./to-spec/SKILL.md)** — Turn the current conversation into a spec and publish it to the issue tracker.
|
||||
- **[to-tickets](./to-tickets/SKILL.md)** — Break any plan, spec, or conversation into a set of tracer-bullet tickets, each declaring its blocking edges — text in a local file, or native blocking links on a real tracker.
|
||||
- **[wayfinder](./wayfinder/SKILL.md)** — Plan a huge chunk of work — more than one agent session can hold — as a shared map of investigation tickets on the issue tracker, resolved one at a time until the way to the destination is clear.
|
||||
- **[implement](./implement/SKILL.md)** — Build the work described by a spec or set of tickets, driving `/tdd` at pre-agreed seams and closing out with `/code-review` before committing.
|
||||
- **[wayfinder](./wayfinder/SKILL.md)** — Plan a huge chunk of work — more than one agent session can hold — as a shared map of decision tickets on the issue tracker, resolved one at a time until the way to the destination is clear.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
@@ -27,3 +28,4 @@ Model- or user-reachable (rich trigger phrasing so the model can reach for them)
|
||||
- **[domain-modeling](./domain-modeling/SKILL.md)** — Actively build and sharpen a project's domain model — challenge terms, stress-test with scenarios, update `CONTEXT.md` and ADRs inline.
|
||||
- **[codebase-design](./codebase-design/SKILL.md)** — Shared discipline and vocabulary for designing deep modules: small interfaces, clean seams, testable through the interface.
|
||||
- **[code-review](./code-review/SKILL.md)** — Two-axis review of the diff since a fixed point: **Standards** (does it follow the repo's coding standards, plus a Fowler smell baseline?) and **Spec** (does it faithfully implement the originating issue/PRD?), run as parallel sub-agents.
|
||||
- **[resolving-merge-conflicts](./resolving-merge-conflicts/SKILL.md)** — Work through an in-progress git merge or rebase conflict hunk by hunk, resolving by intent traced to each side's primary source, then finish the operation — never `--abort`.
|
||||
|
||||
@@ -41,7 +41,9 @@ A starting situation that generates work, then merges onto the main flow.
|
||||
|
||||
- **Something's broken** → **`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** — one command that already goes red on *this* bug — then fixes with a regression test. Its post-mortem hands off to **`/improve-codebase-architecture`** when the real finding is that there's no good seam to lock the bug down.
|
||||
|
||||
- **A huge, foggy effort — a greenfield project or a huge feature build, too big for one session** → **`/wayfinder`**. When the way from here to the destination isn't visible yet, it charts a **shared map** of investigation tickets on the issue tracker and resolves them one at a time — producing **decisions, not deliverables** — until the fog is pushed back and the way is clear. Then it merges onto the main flow at **`/to-spec`** (or, if the effort turned out small enough, straight to **`/implement`**). Where **`/grill-with-docs`** sharpens an idea you can hold in one session, wayfinder is for the idea you can't.
|
||||
- **A huge, foggy effort — a greenfield project or a huge feature build, too big for one session** → **`/wayfinder`**, the most cognitively demanding flow here. When the way from here to the destination isn't visible yet, it charts a **shared map** of **decision tickets** on the issue tracker and resolves them one at a time — producing **decisions, not deliverables** — until the fog is pushed back and the way is clear. Where **`/grill-with-docs`** sharpens an idea you can hold in one session, wayfinder is for the idea you can't — and it's slower and denser, so save it for exactly that, never a well-scoped feature.
|
||||
|
||||
When the map clears, **it hands off, it doesn't build**: merge onto the main flow at **`/to-spec`**, which collapses the map's linked decisions into a buildable plan, then `/to-tickets` and `/implement` as usual. Looping the map straight into `/implement` skips that collapse and throws the linked detail away — go straight to `/implement` only when the effort turned out genuinely small.
|
||||
|
||||
## Codebase health
|
||||
|
||||
@@ -68,8 +70,9 @@ Off the main flow entirely.
|
||||
- **`/grill-me`** — the same relentless interview as `/grill-with-docs`, but for when you have **no codebase**. Stateless: it saves nothing locally, builds no `CONTEXT.md`. Reach for it to sharpen any plan or design that doesn't live in a repo.
|
||||
- **`/prototype`** — a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway from day one — keep the answer, delete the code. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper.
|
||||
- **`/research`** — delegate reading legwork to a **background agent**: it investigates a question against **primary sources**, then leaves a cited Markdown file in the repo. Keep working while it reads. The file it produces is something to take *into* the main flow at `/grill-with-docs` — research feeds the thinking, it doesn't replace it.
|
||||
- **`/to-questionnaire`** — when the thing blocking you isn't in your head or the codebase but in **someone else's**, this writes them a questionnaire to fill in. It's the inverse of `/grill-me`: instead of interviewing you about the subject, it interviews you about the **send** — who it's going to, what you need back — and aims the questions at the gap. What comes back is material for `/grill-with-docs` or `/to-spec`.
|
||||
- **`/teach`** — learn a concept over multiple sessions, using the current directory as a stateful workspace.
|
||||
- **`/writing-great-skills`** — reference for writing and editing skills well.
|
||||
- **`/writing-for-agents`** — reference for writing documents agents consume: skills, AGENTS.md, pointed-at docs.
|
||||
|
||||
## Precondition
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Ask Matt"
|
||||
short_description: "Find the right skill or workflow"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Code Review"
|
||||
short_description: "Review a diff on standards and spec"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Codebase Design"
|
||||
short_description: "Vocabulary for deep-module design"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Diagnosing Bugs"
|
||||
short_description: "Diagnose hard bugs and regressions"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Domain Modeling"
|
||||
short_description: "Build and sharpen a domain model"
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Grill with Docs"
|
||||
short_description: "Grill a design and write its docs"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Implement"
|
||||
short_description: "Build work from a spec or tickets"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -17,6 +17,11 @@ This command is _informed_ by the project's domain model and built on a shared d
|
||||
|
||||
### 1. Explore
|
||||
|
||||
**Scope before you scan — YAGNI.** Deepening a module pays off by making future changes to it easier, so put extra weight on the parts of the codebase that have recently changed. Decide *where* to look before you look:
|
||||
|
||||
- If the user named a direction — a module, a subsystem, a pain point — take it, and skip the inference below.
|
||||
- Otherwise, walk back a good stretch of the commit history (`git log --oneline`) to find the codebase's hot spots — the files and areas that keep coming up — and let those paths pull your attention first. If the changes are scattered with no clear hot spot, widen the net.
|
||||
|
||||
Read the project's domain glossary (`CONTEXT.md`) and any ADRs in the area you're touching first.
|
||||
|
||||
Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
|
||||
@@ -56,7 +61,7 @@ Do NOT propose interfaces yet. After the file is written, ask the user: "Which o
|
||||
|
||||
### 3. Grilling loop
|
||||
|
||||
Once the user picks a candidate, run the `/grilling` skill to walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive.
|
||||
Once the user picks a candidate, run the `/grilling` skill to walk the decision tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive.
|
||||
|
||||
Side effects happen inline as decisions crystallize — run the `/domain-modeling` skill to keep the domain model current as you go:
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Improve Codebase Architecture"
|
||||
short_description: "Find and grill architecture improvements"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,13 +1,15 @@
|
||||
# Logic Prototype
|
||||
|
||||
A tiny interactive terminal app that lets the user drive a state model by hand. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases.
|
||||
A single, self-contained HTML file — a **shareable demo** — that lets anyone drive a state model by clicking buttons. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases.
|
||||
|
||||
Because it's one file with nothing to install, you can hand it to a non-developer — a designer, a PM, a domain expert — and let them feel the model for themselves. So it speaks their language, not the code's.
|
||||
|
||||
## When this is the right shape
|
||||
|
||||
- "I'm not sure if this state machine handles the edge case where X then Y."
|
||||
- "Does this data model actually let me represent the case where..."
|
||||
- "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 someone wants to **press buttons and watch state change**.
|
||||
|
||||
If the question is "what should this look like" — wrong branch. Use [UI.md](UI.md).
|
||||
|
||||
@@ -15,17 +17,11 @@ If the question is "what should this look like" — wrong branch. Use [UI.md](UI
|
||||
|
||||
### 1. State the question
|
||||
|
||||
Before writing code, write down what state model and what question you're prototyping. One paragraph, in the prototype's README or a comment at the top of the file. A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK.
|
||||
Before writing code, write down what state model and what question you're prototyping. One paragraph, at the top of the demo (in a visible intro, not just a comment). A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK.
|
||||
|
||||
### 2. Pick the language
|
||||
### 2. Isolate the logic in a portable module
|
||||
|
||||
Use whatever the host project uses. If the project has no obvious runtime (e.g. a docs repo), ask.
|
||||
|
||||
Match the project's existing conventions for tooling — don't add a new package manager or runtime just for the prototype.
|
||||
|
||||
### 3. Isolate the logic in a portable module
|
||||
|
||||
Put the actual logic — the bit that's answering the question — behind a small, pure interface that could be lifted out and dropped into the real codebase later. The TUI around it is throwaway; the logic module shouldn't be.
|
||||
Put the actual logic — the bit that's answering the question — in a single `<script>` block written as a small, pure module that could be lifted out and dropped into the real codebase later. The page around it is throwaway; this module isn't.
|
||||
|
||||
The right shape depends on the question:
|
||||
|
||||
@@ -34,46 +30,38 @@ The right shape depends on the question:
|
||||
- **A small set of pure functions** over a plain data type. Good when there's no implicit current state — just transformations.
|
||||
- **A class or module with a clear method surface** when the logic genuinely owns ongoing internal state.
|
||||
|
||||
Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a TUI. Keep it pure: no I/O, no terminal code, no `console.log` for control flow. The TUI imports it and calls into it; nothing flows the other direction.
|
||||
Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a page. Keep it pure: no DOM, no `document`, no button handlers reaching inside it. The page calls into it; nothing flows the other direction. This is what makes the prototype useful past its own lifetime: once the question's answered, the validated reducer / machine / function set lifts into the real module on its own.
|
||||
|
||||
This is what makes the prototype useful past its own lifetime. When the question's been answered, the validated reducer / machine / function set can be lifted into the real module — the TUI shell gets deleted.
|
||||
### 3. Build the shareable HTML file
|
||||
|
||||
### 4. Build the smallest TUI that exposes the state
|
||||
One file, plain HTML/CSS/JS — no framework, no bundler, no server, everything inline so it opens by double-click and survives being emailed around. Anyone should be able to run it by opening it.
|
||||
|
||||
Build it as a **lightweight TUI** — on every tick, clear the screen (`console.clear()` / `print("\033[2J\033[H")` / equivalent) and re-render the whole frame. The user should always see one stable view, not an ever-growing scrollback.
|
||||
Write it for a non-developer. Every label is in **domain language**, not code — buttons and state read like the business, not the reducer. Explain in plain words what's happening.
|
||||
|
||||
Each frame has two parts, in this order:
|
||||
Lay it out with a clean hierarchy, top to bottom:
|
||||
|
||||
1. **Current state**, pretty-printed and diff-friendly (one field per line, or formatted JSON). Use **bold** for field names or section headers and **dim** for less important context (timestamps, IDs, derived values). Native ANSI escape codes are fine — `\x1b[1m` bold, `\x1b[2m` dim, `\x1b[0m` reset. No need to pull in a styling library unless one is already in the project.
|
||||
2. **Keyboard shortcuts**, listed at the bottom: `[a] add user [d] delete user [t] tick clock [q] quit`. Bold the key, dim the description, or vice-versa — whatever reads cleanly.
|
||||
1. **Title and one-line explanation** of what this demo lets you explore (the question from step 1).
|
||||
2. **Current state** — the full relevant state, rendered as a readable panel (labelled fields, not a raw JSON dump), re-rendered after every click so the change is visible. Where it helps a non-developer follow, call out what just changed.
|
||||
3. **Free-play buttons** — one button per action, always available, so anyone can poke at the model in any order. Each click dispatches its action and re-renders the state.
|
||||
4. **Guided walkthroughs** — a set of **scenarios**, one per tab. Each tab holds a short plain-language description of the scenario — the situation it sets up and what to watch for — and underneath it, the ordered **buttons to press** for that scenario. Each step is a real button: clicking it performs that action and moves to the next step. Starting a walkthrough resets to a known initial state so the scenario runs the same way every time.
|
||||
|
||||
Behaviour:
|
||||
Choose scenarios that demonstrate the awkward cases — the happy path, a tricky edge case, an attempt at something that should be illegal — the ones hard to reason about on paper.
|
||||
|
||||
1. **Initialise state** — a single in-memory object/struct. Render the first frame on start.
|
||||
2. **Read one keystroke (or one line)** at a time, dispatch to a handler that mutates state.
|
||||
3. **Re-render** the full frame after every action — don't append, replace.
|
||||
4. **Loop until quit.**
|
||||
Keep it beautiful but restrained: clean typography, generous spacing, one accent colour. No animations, no gimmicks — nothing that competes with the state and the buttons.
|
||||
|
||||
The whole frame should fit on one screen.
|
||||
### 4. Hand it over
|
||||
|
||||
### 5. Make it runnable in one command
|
||||
Send them the file, or open it for them. They'll click through the walkthroughs and free-play whenever they get to it; the interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point. If they want new actions or a new scenario, add them. Prototypes evolve.
|
||||
|
||||
Add a script to the project's existing task runner (`package.json` scripts, `Makefile`, `justfile`, `pyproject.toml`). The user should run `pnpm run <prototype-name>` or equivalent — never need to remember a path.
|
||||
### 5. Capture the answer and the prototype
|
||||
|
||||
If the host project has no task runner, just put the command at the top of the prototype's README.
|
||||
|
||||
### 6. Hand it over
|
||||
|
||||
Give the user the run command. They'll drive it themselves; the interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point. If they want new actions added, add them. Prototypes evolve.
|
||||
|
||||
### 7. Capture the answer
|
||||
|
||||
When the prototype has done its job, the answer to the question is the only thing worth keeping. If the user is around, ask what it taught them. If not, leave a `NOTES.md` next to the prototype so the answer can be filled in (or filled in by you, if you've watched the session) before the prototype gets deleted.
|
||||
Once the prototype has answered its question, capture the answer, then capture the prototype the way the [SKILL](SKILL.md) describes. The logic-specific mapping: the validated reducer / machine / function set lifts into the real module (the decision, absorbed); the HTML shell rides along to the throwaway branch that keeps the prototype as a primary source — and being one self-contained file, it stays trivially re-runnable there.
|
||||
|
||||
## 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 wire it to the real database.** Use in-memory state 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 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 blur the logic and the page together.** If the pure module references the DOM, `document`, or button handlers, it's no longer liftable. Keep the page as a thin shell over a pure module.
|
||||
- **Don't reach for a framework, bundler, or server.** One file the recipient double-clicks; a React app or a dev server defeats "shareable".
|
||||
- **Don't ship the HTML shell into production.** The page is optimised for being clicked through by hand. The logic module behind it is the bit worth keeping.
|
||||
|
||||
@@ -11,7 +11,7 @@ A prototype is **throwaway code that answers a question**. The question decides
|
||||
|
||||
Identify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around:
|
||||
|
||||
- **"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.
|
||||
- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md). Build a single shareable HTML file — free-play buttons plus tabbed guided walkthroughs — that pushes the state machine through cases that are hard to reason about on paper, and that a non-developer can drive.
|
||||
- **"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 very 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.
|
||||
@@ -19,12 +19,8 @@ The two branches produce very different artifacts — getting this wrong wastes
|
||||
## Rules that apply to both
|
||||
|
||||
1. **Throwaway from day one, and clearly marked as such.** Locate the prototype code close to where it will actually be used (next to the module or page it's prototyping for) so context is obvious — but name it so a casual reader can see it's a prototype, not production. For throwaway UI routes, obey whatever routing convention the project already uses; don't invent a new top-level structure.
|
||||
2. **One command to run.** Whatever the project's existing task runner supports — `pnpm <name>`, `python <path>`, `bun <path>`, etc. The user must be able to start it without thinking.
|
||||
2. **Trivial to run.** A UI prototype starts from one command in the project's task runner — `pnpm <name>`, `python <path>`, `bun <path>`, etc. A logic demo is a single HTML file the user double-clicks. Either way, no thinking required to start it.
|
||||
3. **No persistence by default.** State lives in memory. Persistence is the thing the prototype is _checking_, not something it should depend on. If the question explicitly involves a database, hit a scratch DB or a local file with a clear "PROTOTYPE — wipe me" name.
|
||||
4. **Skip the polish.** No tests, no error handling beyond what makes the prototype _runnable_, no abstractions. The point is to learn something fast and then delete it.
|
||||
4. **Skip the polish.** No tests, no error handling beyond what makes the prototype _runnable_, no abstractions. The point is to learn something fast.
|
||||
5. **Surface the state.** After every action (logic) or on every variant switch (UI), print or render the full relevant state so the user can see what changed.
|
||||
6. **Delete or absorb when done.** When the prototype has answered its question, either delete it or fold the validated decision into the real code — don't leave it rotting in the repo.
|
||||
|
||||
## When done
|
||||
|
||||
The _answer_ is the only thing worth keeping from a prototype. Capture it somewhere durable (commit message, ADR, issue, or a `NOTES.md` next to the prototype) along with the question it was answering. If the user is around, that capture is a quick conversation; if not, leave the placeholder so they (or you, on the next pass) can fill in the verdict before deleting the prototype.
|
||||
6. **Capture it when done.** Fold any validated decision into the real code, then capture the prototype itself as a **primary source**: commit it to a throwaway branch, out of main, and leave a context pointer to that branch on the implementation issue. Capture the answer too — the verdict and the question it settled — in the issue or a commit. The main branch keeps only the validated decision.
|
||||
|
||||
@@ -97,12 +97,12 @@ Surface the URL (and the `?variant=` keys). The user will flip through whenever
|
||||
|
||||
### 6. Capture the answer and clean up
|
||||
|
||||
Once a variant has won, write down which one and why (commit message, ADR, issue, or a `NOTES.md` next to the prototype if running AFK and the user hasn't responded yet). Then:
|
||||
Once a variant has won, capture the answer — which variant and why — then capture the prototype the way the [SKILL](SKILL.md) describes. Fold the winner into the real code and move the rest onto the throwaway branch, not into main:
|
||||
|
||||
- **Sub-shape A** — delete the losing variants and the switcher; fold the winner into the existing page.
|
||||
- **Sub-shape B** — promote the winning variant to a real route, delete the throwaway route and the switcher.
|
||||
- **Sub-shape A** — fold the winner into the existing page; drop the losing variants and the switcher from main.
|
||||
- **Sub-shape B** — promote the winning variant to a real route; drop the throwaway route and the switcher from main.
|
||||
|
||||
Don't leave variant components or the switcher lying around. They rot fast and confuse the next reader.
|
||||
The full set of variants is the primary source, so it lands on the throwaway branch, not the bin — variant components and the switcher left in the main branch rot fast and confuse the next reader.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Prototype"
|
||||
short_description: "Prototype to answer a design question"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Research"
|
||||
short_description: "Research from high-trust sources"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Resolving Merge Conflicts"
|
||||
short_description: "Resolve merge and rebase conflicts"
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Setup Matt Pocock Skills"
|
||||
short_description: "Configure a repo for the skills"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "TDD"
|
||||
short_description: "Test-driven red-green-refactor"
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "To Spec"
|
||||
short_description: "Turn a conversation into a spec"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -105,4 +105,3 @@ The end-to-end behaviour this ticket makes work, from the user's perspective —
|
||||
In either form, avoid specific file paths or code snippets — they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
||||
|
||||
Work the frontier one ticket at a time with `/implement`, clearing context between tickets.
|
||||
</content>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "To Tickets"
|
||||
short_description: "Split a plan into tracer-bullet tickets"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Triage"
|
||||
short_description: "Move issues through triage roles"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: wayfinder
|
||||
description: Plan a huge chunk of work — more than one agent session can hold — as a shared map of investigation tickets on your issue tracker, and resolve them one at a time until the way to the destination is clear.
|
||||
description: Plan a huge chunk of work — more than one agent session can hold — as a shared map of decision tickets on your issue tracker, and resolve them one at a time until the way to the destination is clear.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
A loose idea has arrived — too big for one agent session, and wrapped in fog: the way from here to the **destination** isn't visible yet. Wayfinding is about finding that way, not charging at the destination. This skill charts the way as a **shared map** on the repo's issue tracker, then works its tickets one at a time until the route is clear.
|
||||
A loose idea has arrived — too big for one agent session, and wrapped in fog: the way from here to the **destination** isn't visible yet. Wayfinding is about finding that way, not charging at the destination. This skill charts the way as a **shared map** on the repo's issue tracker, then works its **decision tickets** — questions whose resolution is a decision, not slices of a build to execute — one at a time until the route is clear.
|
||||
|
||||
The destination varies per effort, and naming it is the first act of charting — it shapes every ticket. It might be a spec to hand off and iterate on, a decision to lock before planning starts, or a change made in place like a data-structure migration. The map is domain-agnostic — engineering work, course content, whatever fits the shape.
|
||||
|
||||
@@ -74,7 +74,7 @@ The answer isn't part of the body — it's recorded on resolution (see [Work thr
|
||||
|
||||
Every ticket is either **HITL** — human in the loop, worked *with* a human who speaks for themselves — or **AFK**, driven by the agent alone. A HITL ticket only resolves through that live exchange; the agent never stands in for the human's side of it (a grilling agent that answers its own questions has broken this).
|
||||
|
||||
- **Research** (AFK): Reading documentation, third-party APIs, or local resources like knowledge bases. Creates a markdown summary as a linked asset. Use when knowledge outside the current working directory is required.
|
||||
- **Research** (AFK): Reading documentation, third-party APIs, or local resources like knowledge bases to surface a fact a decision waits on. Resolved by a `/research` **subagent**. Use when knowledge outside the current working directory is required.
|
||||
- **Prototype** (HITL): Raise the fidelity of the discussion by making a cheap, rough, concrete artifact to react to — an outline, a rough take, a stub, or UI/logic code via the /prototype skill. Links the prototype as an asset. Use when "how should it look" or "how should it behave" is the key question.
|
||||
- **Grilling** (HITL): Conversation via the /grilling and /domain-modeling skills, one question at a time. The default case.
|
||||
- **Task** (HITL or AFK): Manual work that must happen before a *decision* can be made — nothing to decide, prototype, or research, but the discussion is blocked until it's done. Signing up for a service so its API can be judged, provisioning access, moving data so its shape can be seen. This is the one type that *does* rather than decides — and it earns its place by unblocking a decision, not by delivering the destination. The agent drives it alone where it can (AFK); otherwise it hands the human a precise checklist (HITL). Resolved when the work is done; the answer records what was done and any resulting facts (credentials location, new URLs, row counts) later tickets depend on.
|
||||
@@ -102,7 +102,7 @@ Ruling something out of scope is a scoping act, not a step on the route. When a
|
||||
|
||||
## Invocation
|
||||
|
||||
Two modes. Either way, **never resolve more than one ticket per session.**
|
||||
Two modes. Either way, **never resolve more than one ticket per session** — with the exception of research tickets.
|
||||
|
||||
### Chart the map
|
||||
|
||||
@@ -112,7 +112,8 @@ User invokes with a loose idea.
|
||||
2. **Map the frontier.** Grill again, **breadth-first** this time: fan out across the whole space rather than deep on any one thread, surfacing the open decisions and the first steps takeable now. **If this surfaces no fog** — the way to the destination is already clear, the whole journey small enough for one session — you don't need a map. Stop and ask the user how they'd like to proceed.
|
||||
3. **Create the map** (label `wayfinder:map`): Destination and Notes filled in, Decisions-so-far empty, the fog sketched into **Not yet specified**.
|
||||
4. **Create the tickets you can specify now** as child issues of the map — then wire blocking edges in a **second pass** (issues need ids before they can reference each other). Wiring sorts them into the frontier and the blocked; everything you can't yet specify stays in the fog — the **Not yet specified** section.
|
||||
5. Stop — charting the map is one session's work; do not also resolve tickets.
|
||||
5. **Fire the research subagents.** For each `research` ticket you just created, spin up a `/research` subagent to resolve it in parallel, capturing its findings on a throwaway `research/<name>` branch with a context pointer from the ticket.
|
||||
6. Stop — charting is one session's work; it hand-resolves nothing.
|
||||
|
||||
### Work through the map
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Wayfinder"
|
||||
short_description: "Map a large effort as decision tickets"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -8,3 +8,4 @@ Skills that are still being developed. They're not ready to ship — expect roug
|
||||
- **[writing-fragments](./writing-fragments/SKILL.md)** — Grilling session that mines you for fragments — heterogeneous nuggets of writing — and appends them to a single document as raw material for a future article.
|
||||
- **[writing-shape](./writing-shape/SKILL.md)** — Take a markdown file of raw material and shape it into an article paragraph by paragraph, arguing format choices at each step.
|
||||
- **[claude-handoff](./claude-handoff/SKILL.md)** — Hand the current conversation off to a fresh background agent that picks up the work immediately, seeded with a handoff summary via `claude --bg`. User-invoked.
|
||||
- **[setup-ts-deep-modules](./setup-ts-deep-modules/SKILL.md)** — Wire dependency-cruiser into a TypeScript repo so each package is a deep module — implementation hidden in subfolders, reachable only through its entry-point files, tests exercising it through those. User-invoked.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Claude Handoff"
|
||||
short_description: "Hand off to a background agent"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Loop Me"
|
||||
short_description: "Spec the workflows you want to build"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
name: setup-ts-deep-modules
|
||||
description: Wire dependency-cruiser into a TypeScript repo so each package is a deep module — implementation hidden in subfolders, reachable only through its entry-point files. User-invoked.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Setup TS Deep Modules
|
||||
|
||||
Make every package in this repo a **deep module**: a lot of behaviour behind a small interface. A package's public surface is its **entry points** — the files at the package root — and everything in its subfolders is hidden. This skill installs [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) and the rules that make the entry points the only way in, then proves the rules bite.
|
||||
|
||||
For the vocabulary (deep module, interface, seam, depth), run the `/codebase-design` skill — use its language throughout.
|
||||
|
||||
## The shape this enforces
|
||||
|
||||
```
|
||||
src/packages/
|
||||
<name>/
|
||||
index.ts ← an entry point (public). Import this from outside.
|
||||
client.ts ← another entry point. Packages may expose SEVERAL.
|
||||
lib/ ← implementation: hidden from outside, free to import each other.
|
||||
tests/ ← co-located tests + fixtures (a subfolder, so private).
|
||||
```
|
||||
|
||||
The public surface is the package's **root files** — not one designated `index.ts`. By convention implementation lives in `lib/` and tests in `tests/`, giving every package the same two-folder shape. The rule itself is general, though: *anything* in *any* subfolder is private, so you never extend the config to add a folder.
|
||||
|
||||
Four rules, all `error`:
|
||||
|
||||
1. **Entry-point boundary** — code outside a package (app code or another package) may import only that package's entry points (its root files), never anything in its subfolders.
|
||||
2. **Intra-package freedom** — a package's own files import each other freely.
|
||||
3. **Tests through the entry points** — files under `<pkg>/tests/` may import any package's entry points and their own `tests/` fixtures, but never any package's subfolder internals (not even their own). Integration tests across packages are fine; deep imports are not.
|
||||
4. **No cycles** — no dependency cycles.
|
||||
|
||||
**Entry points, not a barrel.** Because the public surface is *every* root file, a package can expose several small entry points (`index.ts`, `client.ts`, `server.ts`) instead of funnelling everything through one giant `index.ts`. Barrel files that re-export a whole subtree are discouraged — keep entry points small and hide implementation in subfolders.
|
||||
|
||||
Layering (which packages may depend on which) is a *different* concern and is left as a commented stub in the config for this repo to fill in.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Detect the environment
|
||||
|
||||
- **Package manager** — `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `bun.lockb` → bun, else npm. Use it for every command below (`pnpm`/`yarn`/`npm run`/`bunx`).
|
||||
- **Packages root** — if `src/` exists use `src/packages`, else `packages`. Confirm the choice with the user if the repo already has a different obvious convention.
|
||||
- **Existing config** — check for a `.dependency-cruiser.*` file. If one exists, do **not** overwrite it: merge the four rules and the options in, and tell the user what you added.
|
||||
|
||||
**Done when:** package manager, packages root, and existing-config status are all known.
|
||||
|
||||
### 2. Install dependency-cruiser
|
||||
|
||||
Install `dependency-cruiser` as a devDependency with the detected package manager.
|
||||
|
||||
**Done when:** `dependency-cruiser` is in `devDependencies`.
|
||||
|
||||
### 3. Write the config
|
||||
|
||||
Copy [`dependency-cruiser.config.cjs`](./dependency-cruiser.config.cjs) to the repo root as `.dependency-cruiser.cjs`. Set `PACKAGES_ROOT` to the root detected in step 1. The rules are path-depth based and extension-agnostic, so nothing else needs adapting.
|
||||
|
||||
**Done when:** `.dependency-cruiser.cjs` exists with the correct `PACKAGES_ROOT`, and the four forbidden rules are present.
|
||||
|
||||
### 4. Wire it into the checks
|
||||
|
||||
- Add a `lint:boundaries` script: `depcruise <packages-root>` (or `depcruise src`).
|
||||
- Fold it into the repo's umbrella check command — the one that already runs typecheck (e.g. a `check` / `ci` / `validate` script). Do **not** touch `tsconfig` or add path aliases.
|
||||
- If there is no umbrella script, add `lint:boundaries` and tell the user to include it in CI.
|
||||
|
||||
**Done when:** `lint:boundaries` exists and runs as part of the same command as typecheck.
|
||||
|
||||
### 5. Scaffold the example package
|
||||
|
||||
Create a committed `<packages-root>/example/` as a copy-me template:
|
||||
|
||||
- `index.ts` — an entry point. Export one function that delegates to an internal file (so the package is visibly *deep*, not a pass-through).
|
||||
- `lib/impl.ts` — an internal file in a **subfolder**, imported by `index.ts`, not reachable from outside.
|
||||
- `tests/example.test.ts` — imports **only** `../index` (an entry point), and asserts against the public function.
|
||||
|
||||
Tell the user this is a starter template to copy or delete.
|
||||
|
||||
**Done when:** the example package exists, exposes its behaviour through a root entry point, and hides `impl` in a subfolder.
|
||||
|
||||
### 6. Prove the rules bite
|
||||
|
||||
This is the completion criterion for the whole skill — a config that doesn't fail on a violation is worthless.
|
||||
|
||||
1. Run `lint:boundaries`. It must **pass** on the clean example.
|
||||
2. Temporarily add a deep import to `tests/example.test.ts` (e.g. `import { thing } from "../lib/impl"`). Run `lint:boundaries` again — it must **fail** with `tests-through-entrypoints`.
|
||||
3. Revert the deep import. Run once more — it must **pass**.
|
||||
|
||||
**Done when:** you have observed a pass, then a fail on the deep import, then a pass again. If step 2 does not fail, the rules are not wired correctly — fix before finishing.
|
||||
|
||||
### 7. Document the convention
|
||||
|
||||
Write a `README.md` **in the packages folder** (`<packages-root>/README.md`) — next to the packages it governs — covering: the `src/packages/<name>/` layout (entry points at the root, `lib/` for implementation, `tests/` for tests), "import only through a package's entry points (its root files)", and how to run `lint:boundaries`. **Discourage barrel files** explicitly — expose several small entry points instead of re-exporting a whole subtree through one index. Keep it to the copy-me snippet plus the four rules in one paragraph each.
|
||||
|
||||
Then add a **context pointer** to it from the repo's agent-instructions file — `CLAUDE.md` if present, else `AGENTS.md` (create `AGENTS.md` if neither exists). One line is enough, e.g. `Packages are deep modules — see [src/packages/README.md](./src/packages/README.md) before adding or importing one.` This is what makes an agent discover the boundary rule instead of tripping over it.
|
||||
|
||||
**Done when:** `<packages-root>/README.md` exists and discourages barrels, and the repo's `CLAUDE.md`/`AGENTS.md` links to it.
|
||||
|
||||
## Notes
|
||||
|
||||
- The config's `$1` back-references (dependency-cruiser's group matching) are what let a package reach its own internals while outsiders can't — don't flatten them into separate per-package rules.
|
||||
- Public vs private is decided by **depth**: a package's root files are entry points; anything in a subfolder is private. The conventional subfolders are `lib/` (implementation) and `tests/`, but the rule doesn't hardcode them — any subfolder is private, so a new folder never needs a config change. Adding an entry point is just adding a root file — no barrel.
|
||||
- Packages are **flat**: one tier of immediate children under the root. A package's internals may nest as deep as you like; a package may not contain another package.
|
||||
- Use `.cjs` (not `.js`) so the config's `module.exports` works even in `"type": "module"` repos.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Setup TS Deep Modules"
|
||||
short_description: "Enforce deep TypeScript modules"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,95 @@
|
||||
// @ts-check
|
||||
// Deep-module enforcement for dependency-cruiser.
|
||||
//
|
||||
// Each package under the packages root is a DEEP MODULE: a lot of behaviour
|
||||
// behind a small interface. A package's PUBLIC SURFACE is its ENTRY POINTS —
|
||||
// the files at the package root. Implementation lives in SUBFOLDERS and is
|
||||
// private — by convention `lib/` for implementation and `tests/` for tests,
|
||||
// though any subfolder is private. A package may expose several small entry
|
||||
// points (index.ts, client.ts, server.ts, …) — prefer that over one giant
|
||||
// barrel index.
|
||||
//
|
||||
// The only thing you should ever need to edit here is PACKAGES_ROOT.
|
||||
|
||||
/** Where packages live. One immediate child dir per package (flat, no nesting). */
|
||||
const PACKAGES_ROOT = "src/packages";
|
||||
|
||||
// --- derived patterns (no need to edit) -------------------------------------
|
||||
const R = PACKAGES_ROOT;
|
||||
/**
|
||||
* A package's private internals: anything nested inside a package subfolder.
|
||||
* The package's root files are its entry points and are NOT matched here —
|
||||
* they stay importable from outside.
|
||||
*/
|
||||
const PACKAGE_INTERNALS = `^${R}/[^/]+/[^/]+/`;
|
||||
|
||||
/** @type {import('dependency-cruiser').IConfiguration} */
|
||||
module.exports = {
|
||||
forbidden: [
|
||||
{
|
||||
name: "entrypoint-boundary-from-app",
|
||||
comment:
|
||||
"App/root code may import a package's entry points (its root files), but nothing inside its subfolders.",
|
||||
severity: "error",
|
||||
from: { pathNot: `^${R}/` }, // importer is NOT inside any package
|
||||
to: { path: PACKAGE_INTERNALS },
|
||||
},
|
||||
{
|
||||
name: "entrypoint-boundary-across-packages",
|
||||
comment:
|
||||
"A package's own files import each other freely, but may reach OTHER packages only through their entry points — never their internals.",
|
||||
severity: "error",
|
||||
// importer is inside a package ($1), but is not a test file
|
||||
from: { path: `^${R}/([^/]+)/`, pathNot: `^${R}/[^/]+/tests/` },
|
||||
to: {
|
||||
path: PACKAGE_INTERNALS,
|
||||
pathNot: `^${R}/$1/`, // same package → intra-package freedom
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tests-through-entrypoints",
|
||||
comment:
|
||||
"A package's tests exercise it through its entry points like everyone else: they may import any package's entry points and their own tests/ fixtures, but never any package's internals — not even their own.",
|
||||
severity: "error",
|
||||
from: { path: `^${R}/([^/]+)/tests/` }, // a test file, in package $1
|
||||
to: {
|
||||
path: PACKAGE_INTERNALS,
|
||||
pathNot: `^${R}/$1/tests/`, // own tests/ fixtures → allowed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tests-folder-is-private",
|
||||
comment:
|
||||
"A package's tests/ folder is reachable only from tests — nothing else may import fixtures.",
|
||||
severity: "error",
|
||||
from: { pathNot: `^${R}/[^/]+/tests/` }, // importer is not itself a test
|
||||
to: { path: `^${R}/[^/]+/tests/` },
|
||||
},
|
||||
{
|
||||
name: "no-circular",
|
||||
comment: "No dependency cycles. Scope to `^${R}/` if you want to allow cycles outside packages.",
|
||||
severity: "error",
|
||||
from: {},
|
||||
to: { circular: true },
|
||||
},
|
||||
|
||||
// --- Layering (optional, off by default) ----------------------------------
|
||||
// Interface-hiding controls HOW you import (through the entry points).
|
||||
// Layering controls WHICH packages may depend on which. Add your own rules
|
||||
// here, e.g.:
|
||||
//
|
||||
// {
|
||||
// name: "ui-may-not-depend-on-billing",
|
||||
// severity: "error",
|
||||
// from: { path: `^${R}/ui/` },
|
||||
// to: { path: `^${R}/billing/` },
|
||||
// },
|
||||
],
|
||||
options: {
|
||||
doNotFollow: { path: "node_modules" },
|
||||
tsConfig: { fileName: "tsconfig.json" },
|
||||
enhancedResolveOptions: {
|
||||
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Wizard"
|
||||
short_description: "Generate an interactive setup wizard"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Writing Beats"
|
||||
short_description: "Assemble raw material into beats"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Writing Fragments"
|
||||
short_description: "Mine raw writing fragments"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Writing Shape"
|
||||
short_description: "Shape raw material into an article"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Git Guardrails for Claude Code"
|
||||
short_description: "Block dangerous git commands"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Migrate to Shoehorn"
|
||||
short_description: "Replace test assertions with shoehorn"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Scaffold Exercises"
|
||||
short_description: "Scaffold lint-ready course exercises"
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Setup Pre-Commit"
|
||||
short_description: "Add pre-commit quality checks"
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Edit Article"
|
||||
short_description: "Restructure and tighten a draft"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Obsidian Vault"
|
||||
short_description: "Manage linked notes in Obsidian"
|
||||
@@ -4,15 +4,16 @@ General workflow tools, not code-specific.
|
||||
|
||||
## User-invoked
|
||||
|
||||
Reachable only when you type them (`disable-model-invocation: true`).
|
||||
Reachable only when you type them (Claude Code: `disable-model-invocation: true`; Codex: `policy.allow_implicit_invocation: false` in `agents/openai.yaml`).
|
||||
|
||||
- **[grill-me](./grill-me/SKILL.md)** — Get relentlessly interviewed about a plan or design until every branch of the decision tree is resolved.
|
||||
- **[handoff](./handoff/SKILL.md)** — Compact the current conversation into a handoff document so another agent can continue the work.
|
||||
- **[teach](./teach/SKILL.md)** — Teach the user a new skill or concept over multiple sessions, using the current directory as a stateful teaching workspace.
|
||||
- **[writing-great-skills](./writing-great-skills/SKILL.md)** — Reference for writing and editing skills well: the vocabulary and principles that make a skill predictable.
|
||||
- **[to-questionnaire](./to-questionnaire/SKILL.md)** — Turn a decision you can't answer alone into a Markdown questionnaire for the one person who can — filled in async, or together over a meeting.
|
||||
- **[writing-for-agents](./writing-for-agents/SKILL.md)** — Writing documents for agents: skills, AGENTS.md/CLAUDE.md, and any doc an agent reaches by a pointer.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
Model- or user-reachable (rich trigger phrasing so the model can reach for them).
|
||||
|
||||
- **[grilling](./grilling/SKILL.md)** — Interview the user relentlessly about a plan or design until every branch of the decision tree is resolved.
|
||||
- **[grilling](./grilling/SKILL.md)** — Interview the user relentlessly about a plan, decision, or idea until every branch of the decision tree is resolved.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Grill Me"
|
||||
short_description: "Sharpen a plan through interview"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,12 +1,14 @@
|
||||
---
|
||||
name: grilling
|
||||
description: Grill the user relentlessly about a plan or design. Use when the user wants to stress-test a plan before building, or uses any 'grill' trigger phrases.
|
||||
description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.
|
||||
---
|
||||
|
||||
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
|
||||
Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it.
|
||||
|
||||
Ask the questions one at a time, waiting for feedback on each question before continuing. Asking multiple questions at once is bewildering.
|
||||
Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled — the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round.
|
||||
|
||||
If a *fact* can be found by exploring the codebase, look it up rather than asking me. The *decisions*, though, are mine — put each one to me and wait for my answer.
|
||||
Each round the user answers reshapes the tree — settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one.
|
||||
|
||||
Do not enact the plan until I confirm we have reached a shared understanding.
|
||||
Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it — don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report — ask the rest of the frontier now. The _decisions_ are the user's — put each to them and wait.
|
||||
|
||||
The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Grilling"
|
||||
short_description: "Stress-test thinking one question at a time"
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Handoff"
|
||||
short_description: "Compact a conversation into a handoff"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Teach"
|
||||
short_description: "Learn a concept in a guided workspace"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: to-questionnaire
|
||||
description: Turn a decision you can't fully answer into a questionnaire for someone else to fill in.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Turn something the user can't answer alone into a **questionnaire** — a Markdown document they hand to one person to fill in async, or fill out together over a meeting. The recipient holds knowledge the user lacks; the questionnaire pulls it out of them.
|
||||
|
||||
**Grill the send, not the subject.** Interview the user only about the _send_, which they can always answer: who it goes to, and what they need back. The questions in the document then target the **gap** between what the recipient knows and what the user needs.
|
||||
|
||||
1. **Who is it going to?** Ask, in one exchange, the recipient's role, expertise, and relationship to the user. This fixes the questionnaire's tone and how much context it must carry. Done when you know who the recipient is and what they know that the user doesn't.
|
||||
|
||||
2. **What do you need back?** Ask, in one exchange, the specific decisions or facts the user can't resolve alone and needs from this person. Done when you have a concrete list of what the user must walk away able to do or decide.
|
||||
|
||||
3. **Write the questionnaire.** Draft questions aimed at the gap from steps 1–2, following the Document structure below. Write it to `to-questionnaire-<slug>.md` in the current directory (slug from the topic) and report the path. Done when the file exists and every item the user named in step 2 is covered by a question.
|
||||
|
||||
## Document structure
|
||||
|
||||
Frame the document as a **discovery questionnaire**: the user lacks context, the recipient holds it. Order questions most-important-first — async means you may only get one pass — and group them under `##` headings by theme once there are more than a handful. Write it using the template below.
|
||||
|
||||
<questionnaire-template>
|
||||
|
||||
# <Questionnaire title>
|
||||
|
||||
**Purpose:** why this questionnaire exists and the decision riding on it.
|
||||
|
||||
**From:** <the user> — **To:** <the recipient> — **How your answers will be used:** <where they go>
|
||||
|
||||
## Context
|
||||
|
||||
One paragraph orienting a recipient who wasn't in the user's head. Enough to answer well, not a page.
|
||||
|
||||
## How to answer
|
||||
|
||||
Deadline and rough effort. Partial answers and "I don't know" are useful — flag anything you're unsure of rather than skipping it.
|
||||
|
||||
## <Theme heading>
|
||||
|
||||
One `##` section per theme. Under each, its questions, most-important-first. Every question is one idea — never compound — with an answer stub directly beneath, and a one-line _why this matters_ only where the question could be misread or invite a throwaway answer.
|
||||
|
||||
<question-example>
|
||||
### What load is the system expected to handle at launch?
|
||||
|
||||
_Why this matters: it decides whether we provision for burst traffic now or defer it._
|
||||
|
||||
>
|
||||
</question-example>
|
||||
|
||||
## Anything else?
|
||||
|
||||
A closing catch-all: anything we didn't ask that we should know?
|
||||
|
||||
</questionnaire-template>
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "To Questionnaire"
|
||||
short_description: "Front-load questions into a doc for someone to answer"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,22 @@
|
||||
# Skill mechanics
|
||||
|
||||
The skill-specific branch of [`writing-for-agents`](SKILL.md): what changes when the document is a skill — frontmatter, the invocation choice, and router skills. Everything else about writing it is the universal reference in `SKILL.md`.
|
||||
|
||||
## Invocation
|
||||
|
||||
Two choices, trading the two loads:
|
||||
|
||||
- A **model-invoked** skill keeps a `description`, so the agent can fire it autonomously — and other skills can reach it. You can still type its name: model-invocation always _includes_ user reach; a description only ever adds agent discovery, never removes the human's. The description is the skill's top-level context pointer, forced to stay loaded at all times — permanent context load in exchange for discoverability. A model-invoked skill whose content is all reference is also one home for shared reference: another skill can invoke it, so reference needed by several skills lives in one place. Mechanics: omit `disable-model-invocation`, and write a model-facing description carrying the trigger branches (the pointer-writing rules in `SKILL.md` apply in full).
|
||||
- A **user-invoked** skill strips the description from the agent's reach: only the human typing its name can invoke it, and no other skill can. Zero context load, but it spends cognitive load — you are the index that must remember it exists. Mechanics: set `disable-model-invocation: true`; the `description` becomes human-facing — a one-line summary, trigger lists stripped.
|
||||
|
||||
Pick model-invocation only when the agent must reach the skill on its own, or another skill must. If it only ever fires by hand, make it user-invoked and pay no context load.
|
||||
|
||||
Shared reference that two user-invoked skills both need can live in neither — with no descriptions, neither can fire the other. Push it to a plain file outside the skill system: external reference any skill can point at.
|
||||
|
||||
## Splitting by invocation
|
||||
|
||||
The invocation cut of splitting (the sequence cut lives in `SKILL.md`): split off a model-invoked skill when you have a distinct leading word that should trigger it on its own — a trigger word you actually use in your prompts — or another skill must reach it. You pay context load for the new always-loaded description, so that independent reach has to be worth it.
|
||||
|
||||
## Router skills
|
||||
|
||||
When user-invoked skills multiply past what you can remember, that piled-up cognitive load is cured by a **router skill**: one user-invoked skill that names the others and when to reach for each, so the human has one skill to remember instead of many. It can only hint, never fire them: user-invoked skills have no description, so nothing but the human can reach them.
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
name: writing-for-agents
|
||||
description: Writing documents for agents. Use when creating or editing skills, or modifying AGENTS.md or CLAUDE.md.
|
||||
---
|
||||
|
||||
Reference for writing any document an agent consumes — a skill, an `AGENTS.md` / `CLAUDE.md`, a doc reached by a pointer. The packaging differs; the writing does not: the same levers make each one predictable — the agent taking the same _process_ every run, not producing the same output.
|
||||
|
||||
When the document you're writing is a skill, read [`SKILL-MECHANICS.md`](SKILL-MECHANICS.md) for frontmatter, invocation choice, and router skills.
|
||||
|
||||
## Context pointers
|
||||
|
||||
A **context pointer** is a reference held in the agent's context that names some out-of-context material and encodes the condition for reaching it. A skill's description is one; a line in `AGENTS.md` naming a doc is the same object. The pointer's _wording_, not its target, decides when the agent reaches the material — and how reliably. A must-have target behind a weakly worded pointer is a variance bug: sharpen the wording first, and inline the material only if sharpening fails.
|
||||
|
||||
A pointer does two jobs — state what the material is, and list the **branches** that should trigger reaching it (a branch is a distinct case the document handles, so different runs take different paths through it). Every word of an always-loaded pointer costs on every turn, so it earns even harder pruning than the body:
|
||||
|
||||
- **Front-load the leading word** — the pointer is where it does its triggering work.
|
||||
- **One trigger per branch.** Synonyms that rename a single branch are one branch written twice; collapse them and keep only genuinely distinct branches.
|
||||
- **Cut identity the body already carries.**
|
||||
|
||||
## The two loads
|
||||
|
||||
Every document and pointer you add spends one of two budgets:
|
||||
|
||||
- **Context load** — the cost of always-loaded material on the agent's window: an `AGENTS.md` line, a skill description, anything sitting in context every turn, spending tokens and attention whether or not it fires.
|
||||
- **Cognitive load** — the cost on the human: which documents exist and when to reach for each. The human is the index. Not a cost to minimise — it is the price of human agency; spend it where human judgement matters, remove it where it does not.
|
||||
|
||||
Material reached only through a pointer escapes context load at the price of the pointer's own line; material with no pointer at all rides entirely on cognitive load.
|
||||
|
||||
## Information hierarchy
|
||||
|
||||
A document is built from two content types — **steps** (the ordered actions the agent performs) and **reference** (definitions, rules, facts consulted on demand) — that mix freely: all steps (a recipe), all reference (a review's rules, this skill), or both. The core decision is where each piece sits on the **information hierarchy**, a ladder ranked by how immediately the agent needs the material:
|
||||
|
||||
1. **In-file step** — the primary tier: what the agent does, in order.
|
||||
2. **In-file reference** — consulted on demand. Often a legitimately flat peer-set (every rule of a review on one rung) — a fine arrangement, not a smell.
|
||||
3. **Disclosed reference** — pushed out into a separate file, reached by a context pointer, loaded only when the pointer fires. Spans a sibling file in the same folder through fully external reference that lives anywhere and any document can point at.
|
||||
|
||||
Push too little down and the top bloats; push too much and you hide material the agent actually needs. That tension is the whole decision.
|
||||
|
||||
**Progressive disclosure** is the move down the ladder — out of the main file and behind a pointer — so the top stays legible. Not primarily a token optimisation: it is how the hierarchy is protected. Branching is the cleanest disclosure test: inline what every branch needs, and push behind a pointer what only some branches reach. When a document has steps, in-file reference that should be disclosed buries them and turns attending to them into a coin-flip — a variance lever, not just a legibility one.
|
||||
|
||||
**Co-location** is the within-file companion: where the ladder decides _how far down_ a piece sits, co-location decides _what sits beside it_ once there. Keep a concept's definition, rules, and caveats under one heading rather than scattered, so reading one part brings its neighbours with it. The test: the document should read like documentation written for the agent — grouped material reads that way; scattered material does not. (Distinct from duplication: that repeats one meaning in two places; scattering fragments one meaning across many.)
|
||||
|
||||
**Sprawl** is the failure mode here: a document simply too long, even when every line is live and unique. Attention thins across the excess, and every extra line is one more to keep relevant. The cure is the ladder: disclose reference behind pointers, and split by branch or sequence so each path carries only what it needs.
|
||||
|
||||
## Steps and completion criteria
|
||||
|
||||
Every step ends on a **completion criterion** — the condition that tells the agent the work is done. Two properties make it a lever:
|
||||
|
||||
- **Clarity** — can the agent tell done from not-done? A vague bound ("understanding reached") invites **premature completion**: ending the step before it is genuinely done, attention slipping to _being done_. The visible steps still ahead — the **post-completion steps** — supply the pull; the criterion's clarity is the resistance. Defend in order: **sharpen the bound first** (local and cheap); only if it is irreducibly fuzzy _and_ you observe the rush, hide the later steps by splitting the sequence — and hiding only works across a real context boundary (a hand-off or a subagent dispatch; an inline call leaves the later steps in context and clears nothing).
|
||||
- **Demand** — how much it requires. "Every modified model accounted for" forces thorough work where "produce a change list" does not. Demand drives **legwork** — the digging the agent does within the work, latent in the wording rather than written as its own step — and it is not step-bound: "every rule applied" binds a body of flat reference just as "every step done" binds a sequence, which is how an all-reference document still carries an exhaustiveness bar.
|
||||
|
||||
The strongest criteria are both checkable and exhaustive.
|
||||
|
||||
## When to split
|
||||
|
||||
Splitting one document into two spends one of the two loads, so split only when the cut earns it:
|
||||
|
||||
- **By sequence** — split a run of steps where the post-completion steps tempt the agent to rush the one in front of it. Keeping them out of view drives more legwork on the current task. Beware the reverse: merging sequences exposes each step's later steps to what follows, inviting premature completion.
|
||||
- **By invocation** — skill-specific: see [`SKILL-MECHANICS.md`](SKILL-MECHANICS.md).
|
||||
|
||||
## Leading words
|
||||
|
||||
A **leading word** is a compact concept already living in the model's pretraining that the agent thinks with while running the document (_lesson_, _fog of war_, _tracer bullets_). Repeated as a token, never as a sentence, it accumulates a distributed definition and anchors a whole region of behaviour in the fewest tokens, by recruiting priors the model already holds. Coining your own works if you define it clearly, but a made-up word recruits no priors — you pay in definition tokens what a pretrained word gives free; reach for an existing word first.
|
||||
|
||||
It anchors twice. In the body, _execution_: the agent reaches for the same behaviour every time the word appears, and inside flat reference it focuses attention on a class of thing to look for. In a pointer, _invocation_: when the same word lives in your prompts, your docs, and your codebase, the agent links that shared language to the material and reaches it more reliably.
|
||||
|
||||
Hunt for opportunities to refactor with leading words. A triad spelled out at three sites, a pointer spending a sentence to gesture at one idea — each is a passage begging to collapse into a single token:
|
||||
|
||||
- "fast, deterministic, low-overhead" → _tight_ (a _tight_ loop).
|
||||
- "a loop you believe in" → _red_ — a fuzzy gate becomes a binary observable state (the loop goes _red_ on the bug, or it doesn't).
|
||||
|
||||
You win twice: fewer tokens, and a sharper hook for the agent to hang its thinking on. Assume every document is carrying restatements that leading words retire — go find them.
|
||||
|
||||
**Negation** is the failure mode beside this lever: steering by prohibition drags the forbidden behaviour into context and makes it _more_ available, not less. _Don't think of an elephant_, and the elephant is all there is; the negation is a weak modifier the strongly-activated concept overruns, so the ban half-reads as an instruction to do the thing. Prompt the **positive** — state the target behaviour ("write one-line comments") so the banned one is never spoken. A prohibition earns its place only as a hard guardrail you cannot phrase positively; even then, pair it with the positive target so attention lands on what to do.
|
||||
|
||||
## Pruning
|
||||
|
||||
- Keep each meaning in a **single source of truth**: one authoritative place, so changing the behaviour is a one-place edit. **Duplication** — the same meaning in more than one place — costs maintenance and tokens, and inflates a meaning's prominence on the ladder past its real rank. (The accidental inverse of a leading word, which repeats a token on purpose, never the meaning.)
|
||||
- The **environment** is a source of truth too — `package.json` scripts, config files, the directory layout, `--help` output — and a document that restates it is a **cache**: a copy of a lookup, earning its load only when the lookup is expensive. Cache what the agent cannot find by looking: the unwritten convention, the reason behind a choice, the gotcha no config confesses. Leave the one-file, one-command lookups to the environment, where they cannot go stale.
|
||||
- Check every line for **relevance**: does it still bear on what the document does? A line loses relevance by never bearing on the task (mere exposition, or a branch that should be disclosed) or by going stale as the behaviour or world it describes changes. Shorter documents are easier to keep relevant. Without a pruning discipline the default fate is **sediment**: stale layers that settle because adding feels safe and removing feels risky, until you must core down through them to find what is still live.
|
||||
- Hunt **no-ops** sentence by sentence: an instruction the model already obeys by default pays load to say nothing. The test — does it change behaviour versus the default? — is model-relative, not reader-relative: two people disagreeing about a no-op disagree about the default, and settle it by running the document, not by debate. When a sentence fails, delete the whole sentence rather than trim words from it. The test also grades leading words: a word too weak to beat the default (_be thorough_ when the agent is already thorough-ish) is a no-op, and the fix is a stronger word (_relentless_), not a different technique.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Writing Great Skills"
|
||||
short_description: "Principles for predictable skills"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,201 +0,0 @@
|
||||
# Glossary — Building Great Skills
|
||||
|
||||
The domain model for what makes a skill great. A skill exists to wrangle determinism out of a stochastic system; the root virtue is **Predictability**, and every term below is a lever on it. This is the disclosed reference for [`writing-great-skills`](SKILL.md).
|
||||
|
||||
The terms are grouped by axis: **Invocation** (how a skill is reached), **Information Hierarchy** (how its content is arranged), **Steering** (how the agent's runtime behaviour is shaped), and **Pruning** (how it is kept lean). Each **failure mode** lives beside the lever that cures it, tagged _failure mode_.
|
||||
|
||||
**Bold terms** in any definition are themselves defined in this glossary; find them by their heading.
|
||||
|
||||
## Predictability
|
||||
|
||||
The degree to which a skill makes the agent behave the same _way_ on every run — the same process, not the same output (a brainstorming skill should _predictably_ diverge; its tokens vary, its behaviour doesn't). The root virtue every other term serves — cost and maintainability are symptoms of it, not rivals.
|
||||
|
||||
_Avoid_: consistency, reliability, robustness, output-determinism
|
||||
|
||||
## Invocation
|
||||
|
||||
How a skill is reached — and the two loads you pay for the choice.
|
||||
|
||||
### Model-Invoked
|
||||
|
||||
A skill that keeps its **description** field, so the agent can see it and fire it autonomously — and the human can still type its name, so model-invocation always _includes_ user reach. There is no model-only state: a description only ever _adds_ agent discovery, never removes the human's. Pays a permanent **context load** on every turn in exchange for that discoverability. Reachable by other skills, because the description that makes it agent-discoverable makes it invocable. A model-invoked skill whose content is all **reference** is also one home for shared reference: another skill can invoke it, so reference needed by several skills lives in one place. Pick model-invocation only when the agent must reach the skill on its own; if it never fires except by hand, drop the description and pay no context load.
|
||||
|
||||
_Avoid_: ability, tool, capability
|
||||
|
||||
### User-Invoked
|
||||
|
||||
A skill with its **description** stripped — invisible to the agent and reachable only by the human typing its name (user-_only_, where **model-invoked** is user-_and-agent_). Trades agent-discoverability for zero **context load**. Because it has no description, nothing but the human can reach it: no other skill can fire it.
|
||||
|
||||
_Avoid_: procedure, workflow, command
|
||||
|
||||
### Description
|
||||
|
||||
The skill's machine-readable trigger, and the one **context pointer** a **model-invoked** skill is forced to keep loaded at all times. Its mere presence _is_ the invocation axis: keep it and the skill is model-invoked (and reachable by other skills); delete it and the skill is **user-invoked**, reachable only by the human. The source of a model-invoked skill's **context load**.
|
||||
|
||||
_Avoid_: frontmatter, summary
|
||||
|
||||
### Context Pointer
|
||||
|
||||
A reference held in the agent's context that names some out-of-context material and encodes the condition for reaching it. The **description** is the top-level context pointer (context window → skill); pointers to disclosed files are the same object one level down. Its wording, not the target, decides _when_ the agent reaches — and _how reliably_. A must-have target behind a weakly worded pointer is a variance bug: fix the wording first, and inline the material only if sharpening fails.
|
||||
|
||||
_Avoid_: link, reference, import
|
||||
|
||||
### Context Load
|
||||
|
||||
The cost a **model-invoked** skill imposes on the agent's context window — its **description**, always loaded, spending both tokens and attention. What **user-invoked** skills escape by having no description, and the brake on splitting into more model-invoked skills.
|
||||
|
||||
_Avoid_: token cost, context bloat
|
||||
|
||||
### Cognitive Load
|
||||
|
||||
The cost a **user-invoked** skill imposes on the human — what they must hold in their head: which skills exist and when to reach for each (the human is the index). What **model-invocation** removes by being agent-discoverable, and the brake on splitting into more user-invoked skills. Not a cost to minimise: it is the price of human agency, the reason some skills stay user-invoked. Spend it where human judgement matters; remove it where it does not.
|
||||
|
||||
_Avoid_: human index, burden, overhead
|
||||
|
||||
### Router Skill
|
||||
|
||||
A **user-invoked** skill whose job is to point at your other user-invoked skills — naming each and when to reach for it — so the human has one skill to remember instead of many. It can only hint, never fire them: user-invoked skills have no **description**, so nothing but the human can reach them. The cure for **cognitive load** when user-invoked skills multiply.
|
||||
|
||||
_Avoid_: dispatcher, menu, registry, index, router procedure
|
||||
|
||||
### Granularity
|
||||
|
||||
How finely you divide skills. Finer division spends one of the two loads: more **model-invoked** skills spend **context load** (more descriptions crowding the window and competing for attention); more **user-invoked** skills spend **cognitive load** (more for the human to remember and reach for). Two cuts guide the division. By **invocation**, split off a model-invoked skill where you have a distinct **leading word** to trigger it — a trigger word you actually use in your prompts. By **sequence**, split a run of **steps** where a step's **post-completion steps** need hiding, since isolating it in its own context clears what follows. Beware the reverse: merging sequences exposes each step's post-completion steps to what follows, inviting premature completion.
|
||||
|
||||
_Avoid_: chunking, modularity
|
||||
|
||||
## Information Hierarchy
|
||||
|
||||
How a skill's content is arranged, and how far down the ladder each piece sits.
|
||||
|
||||
### Information Hierarchy
|
||||
|
||||
A skill's content ranked by how immediately the agent needs it — a single ladder, produced by two cuts: in-file or behind a pointer, and step or reference. The rungs:
|
||||
|
||||
- **Steps** — in-file, primary
|
||||
- **Reference**, in-file — secondary
|
||||
- **Reference**, disclosed — behind a **context pointer**
|
||||
|
||||
A skill with no **steps** uses just the bottom two rungs — often a legitimately flat peer-set (e.g. every rule of a review on one rung), which is a fine arrangement, not a smell. The hierarchy is independent of invocation: a skill can be model- or user-invoked whether it is all steps, all reference, or both. When a skill has steps, in-file reference that should be disclosed buries them and turns attending to them into a coin-flip — a variance lever, not just a legibility one. Keep the top of the ladder legible; push down it whatever you can.
|
||||
|
||||
_Avoid_: structure, organization, layout
|
||||
|
||||
### Steps
|
||||
|
||||
The ordered actions the agent performs — when a skill has them, the primary tier of its content, and the part that earns its place in SKILL.md. Not every skill has steps: a skill can be all steps (`tdd`), all **reference** (a review), or both, independent of invocation. Every step ends on a **completion criterion**, clear or vague.
|
||||
|
||||
_Avoid_: workflow, instructions, choreography
|
||||
|
||||
### Reference
|
||||
|
||||
Material the agent refers to on demand — definitions, facts, parameters, examples, conditional instructions. When a skill has **steps** it is secondary to them; when a skill has none it is the entire content; or it lives outside any skill entirely — see **External Reference**. Reached via **context pointers**, and the prime candidate for **progressive disclosure**.
|
||||
|
||||
_Avoid_: supporting material, docs, background
|
||||
|
||||
### External Reference
|
||||
|
||||
**Reference** that lives outside the skill system — a plain file, no **description**, no **steps**, not invocable — that any skill can point at. The home for shared reference that needn't fire on its own, and the only shared home two **user-invoked** skills can use, since neither has a description and so neither can fire the other.
|
||||
|
||||
_Avoid_: doc, resource, knowledge base
|
||||
|
||||
### Progressive Disclosure
|
||||
|
||||
Moving **reference** down the ladder — out of SKILL.md and behind a **context pointer** — so the top stays legible. Not primarily a token optimisation; it is how the **information hierarchy** is protected. Licensed by **branching**: disclose what only some branches need, inline what every path needs, and if a pointer fires unreliably on must-have material, sharpen its wording, and pull it back inline only if that fails.
|
||||
|
||||
_Avoid_: lazy loading, chunking
|
||||
|
||||
### Co-location
|
||||
|
||||
Keeping the material an agent needs at once in one place — a concept's definition, rules, and caveats under a single heading, not scattered across the file — so reading one part brings its neighbours with it. The within-file companion to the **Information Hierarchy**: the hierarchy ranks _how far down_ a piece sits; co-location decides _what sits beside it_ once there. There is no formula for the right format of a body of **reference**; the test is that a skill should read like documentation written for the agent, and grouped material reads that way where scattered material does not. Distinct from **Duplication**: that repeats one meaning in two places, where scattering fragments a single meaning across many.
|
||||
|
||||
_Avoid_: grouping, clustering, cohesion
|
||||
|
||||
### Sprawl
|
||||
|
||||
_Failure mode._ A skill that is simply too long — too many lines in SKILL.md — independent of whether they are stale or repeated. Even an all-live, all-unique skill can sprawl. It costs readability (the agent wades through more before it can act, and attention thins across the excess), maintainability (every extra line is one more to keep **relevant**), and tokens. The cure is the **information hierarchy**: push **reference** down behind **context pointers**, and split by **branch** or sequence so each path carries only what it needs. Distinct from **sediment** (length from stale accumulation) and **duplication** (length from repeated meaning) — sprawl is length itself, whatever its cause.
|
||||
|
||||
_Avoid_: bloat, length, size, verbosity
|
||||
|
||||
## Steering
|
||||
|
||||
The levers that shape the agent's runtime behaviour toward **Predictability**.
|
||||
|
||||
### Branch
|
||||
|
||||
A distinct way a skill can be invoked — a case the skill handles — so different runs take different paths through it. A skill with many steps may carry many branches; a linear one has none.
|
||||
|
||||
_Avoid_: path, case, fork
|
||||
|
||||
### Leading Word
|
||||
|
||||
A compact concept — also called a _Leitwort_ — already living in the model's pretraining, that the agent thinks with while running the skill. It encodes a behavioural principle in the fewest possible tokens by invoking priors the model already holds (e.g. _lesson_, _proximal zone of development_, _fog of war_, _tracer bullets_). Repeated as a token, never as a sentence, it accumulates a distributed definition across the skill and anchors a whole region of behaviour. Coining your own works if you define it clearly, but a made-up word recruits no priors — you pay in definition tokens what a pretrained word gives free. Reach for an existing word first.
|
||||
|
||||
A leading word serves **predictability** twice. In the body it anchors **execution** — the agent reaches for the same behaviour every time the concept appears, and inside flat reference it focuses attention on a class of thing to look for, recruiting the right checks each run. In the **description** it anchors **invocation** — and not only within the skill: when the same word lives in your prompts, your docs, and your codebase, the agent links that shared language to the skill and fires it more reliably. Word a description with the leading words you actually use when you want the skill.
|
||||
|
||||
_Avoid_: keyword, term, motif
|
||||
|
||||
### Completion Criterion
|
||||
|
||||
The condition that tells the agent a unit of work is done — the target it judges against. Two properties make it a lever, not just a quality. Its **clarity** (can the agent tell done from not-done?) resists **premature completion** — a vague bound ("understanding reached") lets the agent declare done and slip to the next step; this axis needs _steps_ to bite, since premature completion is a between-steps failure. Its **demand** (how much it requires) sets **legwork** — "every modified model accounted for" forces thorough work where "produce a change list" does not — and this axis is _not_ step-bound: it can bind a body of flat reference too, which is how a skill with no steps still carries an exhaustiveness bar ("every rule applied"). The strongest criteria are both checkable and exhaustive.
|
||||
|
||||
_Avoid_: done condition, exit condition, stopping rule
|
||||
|
||||
### Legwork
|
||||
|
||||
The work an agent does behind the scenes within a single step — reading files, exploring the codebase, making changes, digging up what it needs rather than offloading to the user. It lives below the step structure: never written as its own step, latent in the wording, controlled by the agent rather than the skill. The within-step counterpart to **post-completion steps**' across-step pull. Raised by a **leading word** (_comprehensive_, _thorough_) or a **completion criterion** that demands the work be exhaustive — including the demand axis applied to flat reference, which is what drives a skill of flat reference to cover all its rungs. Goes thin either when that demand is missing or when **premature completion** cuts the step short.
|
||||
|
||||
_Avoid_: scope, effort, diligence, coverage
|
||||
|
||||
### Post-Completion Steps
|
||||
|
||||
The **steps** that follow the current step. Visible, they pull the agent forward into **premature completion** — the more it sees, the stronger the tug; the defence is to hide them by splitting the sequence of steps into two.
|
||||
|
||||
_Avoid_: horizon, fog of war, lookahead
|
||||
|
||||
### Premature Completion
|
||||
|
||||
_Failure mode._ Ending the current step before it is genuinely done, because the agent's attention slips to being done rather than to the work. A between-steps failure: it needs **steps** to occur — a skill with no steps that quits early isn't premature completion but thin **legwork** under an unmet demand. A tug-of-war between two forces: visible **post-completion steps** (the pull forward) and the **completion criterion**'s clarity (the resistance — a sharp, checkable bar holds; a vague one gives way). Fuzziness is the necessary condition: a sharp bound resists the pull no matter how many later steps are visible, so a step that never rushes needs no defending. Two levers hold a step that does, but reach for them in order: **sharpen the bound first** — it is local and cheap. Only when the criterion is irreducibly fuzzy _and_ you actually observe the rush do you **hide the later steps** — and hiding only works across a real context boundary (a user-invoked hand-off or a subagent dispatch; an inline model-invoked call leaves the later steps in context and clears nothing). One cause of thin legwork, but distinct from it: legwork can be thin even when a step runs to full completion.
|
||||
|
||||
_Avoid_: premature closure, the rush, rushing, shortcutting
|
||||
|
||||
### Negation
|
||||
|
||||
_Failure mode._ Steering by prohibition — telling the agent what _not_ to do — which drags the forbidden behaviour into context and makes it _more_ available, not less. _Don't think of an elephant_, and the elephant is all there is; _never write verbose comments_, and verbosity is the pattern the agent has just read. The negation is a weak modifier the strongly-activated concept overruns, so the ban half-reads as an instruction to do the thing. Its **leading word** is the _elephant_: whatever a prohibition names into the frame. Cure: prompt the **positive** — describe the target behaviour ("write one-line comments") so the banned one is never spoken. A prohibition earns its place only as a hard guardrail on a behaviour you cannot phrase positively; even then, pair it with the positive target so attention lands on what to do.
|
||||
|
||||
_Avoid_: ironic rebound, don't-prompting, the pink elephant
|
||||
|
||||
## Pruning
|
||||
|
||||
Keeping a skill lean — each remedy paired with the failure it cures.
|
||||
|
||||
### Single Source of Truth
|
||||
|
||||
The desired state where each meaning lives in exactly one authoritative place, so a change to the skill's behaviour is a change in one place. **Duplication** is its violation.
|
||||
|
||||
_Avoid_: home, canonical location
|
||||
|
||||
### Duplication
|
||||
|
||||
_Failure mode._ The same meaning given more than one **single source of truth**. It costs maintenance (change one place, you must change the others), costs tokens, and inflates prominence — repeating a meaning weights it on the ladder past its real rank. The accidental inverse of a **leading word**, which raises attention on purpose by repeating a token, never the meaning.
|
||||
|
||||
_Avoid_: repetition, redundancy
|
||||
|
||||
### Relevance
|
||||
|
||||
Whether a line still bears on what the skill does — the lens for what to keep. A line loses relevance either by never bearing on the task (mere exposition, or a **branch** that should be disclosed) or by going stale: drifting out of date as the behaviour or world it describes changes. Shorter skills are easier to keep relevant, because each line is cheaper to check. Distinct from **no-op**: relevance asks whether a line bears on the task, not whether it changes behaviour.
|
||||
|
||||
_Avoid_: load-bearing, staleness, freshness
|
||||
|
||||
### Sediment
|
||||
|
||||
_Failure mode._ Layers of old content that settle in a skill and are never cleared, because adding feels safe and removing feels risky — so stale and irrelevant lines accumulate and you must core down through them to find what is still live. The default fate of any skill without a pruning discipline; the slow erosion of **relevance**, as opposed to **duplication**'s repeated meaning.
|
||||
|
||||
_Avoid_: accretion, bloat, cruft, rot
|
||||
|
||||
### No-Op
|
||||
|
||||
_Failure mode._ An instruction that changes nothing because the model already does it by default — you pay load to tell the agent what it would do anyway. The test: does a line change behaviour versus the default? A line can be perfectly **relevant** and still be a no-op. The same priors that make a **leading word** free make a no-op worthless.
|
||||
|
||||
A leading word is a _technique_; No-Op is a _verdict_ on a line — and they cross. A leading word too weak to beat the default is a no-op (_be thorough_ when the agent is already thorough-ish), and the fix is a stronger word that passes the verdict (_relentless_), not a different technique. So the No-Op test — does it change behaviour versus the default? — is also how you grade whether a leading word is earning its repetitions. This is model-relative, not reader-relative: two people disagreeing over whether a line is a no-op disagree about the default, and settle it by running the skill, not by debate.
|
||||
|
||||
_Avoid_: redundant instruction, restating the obvious, belaboring
|
||||
@@ -1,83 +0,0 @@
|
||||
---
|
||||
name: writing-great-skills
|
||||
description: Reference for writing and editing skills well — the vocabulary and principles that make a skill predictable.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
A skill exists to wrangle determinism out of a stochastic system. **Predictability** — the agent taking the same _process_ every run, not producing the same output — is the root virtue; every lever below serves it.
|
||||
|
||||
**Bold terms** are defined in [`GLOSSARY.md`](GLOSSARY.md); look them up there for the full meaning.
|
||||
|
||||
## Invocation
|
||||
|
||||
Two choices, trading different costs:
|
||||
|
||||
- A **model-invoked** skill keeps a **description**, so the agent can fire it autonomously _and_ other skills can reach it (you can still type its name too). It contributes to **context load** — the description sits in the window every turn. Mechanics: omit `disable-model-invocation`, and write a model-facing description with rich trigger phrasing ("Use when the user wants…, mentions…").
|
||||
- A **user-invoked** skill strips the description from the agent's reach: only you, typing its name, can invoke it — and no other skill can. Zero context load, but it spends **cognitive load**: _you_ are the index that must remember it exists. Mechanics: set `disable-model-invocation: true`; the `description` becomes human-facing — a one-line summary, trigger lists stripped.
|
||||
|
||||
Pick model-invocation only when the agent must reach the skill on its own, or another skill must. If it only ever fires by hand, make it user-invoked and pay no context load.
|
||||
|
||||
When user-invoked skills multiply past what you can remember, that piled-up cognitive load is cured by a **router skill**: one user-invoked skill that names the others and when to reach for each.
|
||||
|
||||
## Writing the description
|
||||
|
||||
A model-invoked **description** does two jobs — state what the skill is, and list the **branches** that should trigger it. Every word increases **context load**, so a description earns even harder pruning than the body:
|
||||
|
||||
- **Front-load the skill's leading word** — the description is where it does its invocation work.
|
||||
- **One trigger per branch.** Synonyms that rename a single branch are **duplication** — "build features using TDD … asks for test-first development" is one branch written twice. Collapse them; keep only genuinely distinct branches.
|
||||
- **Cut identity that's already in the body.** Keep the description to triggers, plus any "when another skill needs…" reach clause.
|
||||
|
||||
## Information hierarchy
|
||||
|
||||
A skill is built from two content types — **steps** and **reference** — that mix freely: a skill can be all steps, all reference, or both. The core decision is which to use and where each sits on the **information hierarchy**, a ladder ranked by how immediately the agent needs the material:
|
||||
|
||||
1. **In-skill step** — an ordered action in `SKILL.md`, the primary tier: what the agent does, in order. Each step ends on a **completion criterion**, the condition that tells the agent the work is done. Make it _checkable_ (can the agent tell done from not-done?) and, where it matters, _exhaustive_ ("every modified model accounted for", not "produce a change list") — a vague criterion invites **premature completion**.
|
||||
2. **In-skill reference** — a definition, rule, or fact in `SKILL.md`, consulted on demand. Often a legitimately flat peer-set (every rule of a review on one rung) — a fine arrangement, not a smell. _This skill is all reference._
|
||||
3. **External reference** — reference pushed out of `SKILL.md` into a separate file, reached by a **context pointer**, loaded only when the pointer fires. (Spans _disclosed_ reference — a sibling file like `GLOSSARY.md`, still part of the skill — through fully **external reference** that lives outside the skill system and any skill can point at.)
|
||||
|
||||
A demanding completion criterion drives thorough **legwork** — the digging the agent does within the work — whether the skill has steps or not, since "every rule applied" binds flat reference just as "every step done" binds a sequence.
|
||||
|
||||
Push too little down and the top bloats; push too much and you hide material the agent actually needs. That tension is the whole decision.
|
||||
|
||||
**Progressive disclosure** is the move down the ladder — out of `SKILL.md` into a linked file — so the top stays legible. Mechanics: a linked `.md` file in the skill folder, named for what it holds (this skill discloses its full definitions to `GLOSSARY.md`). Some skills are used in more than one way, and each distinct way is a **branch** — different runs taking different paths through the skill. Branching is the cleanest disclosure test: inline what every branch needs, and push behind a pointer what only some branches reach. A **context pointer**'s _wording_, not its target, decides when and how reliably the agent reaches the material.
|
||||
|
||||
Where the ladder decides _how far down_ a piece sits, **co-location** decides _what sits beside it_ once there: keep a concept's definition, rules, and caveats under one heading rather than scattered, so reading one part brings its neighbours with it.
|
||||
|
||||
## When to split
|
||||
|
||||
**Granularity** is how finely you divide skills, and each cut spends one of the two loads, so split only when the cut earns it. Two cuts:
|
||||
|
||||
- **By invocation** — split off a **model-invoked** skill when you have a distinct **leading word** that should trigger it on its own, or another skill must reach it. You pay **context load** for the new always-loaded **description**, so that independent reach has to be worth it.
|
||||
- **By sequence** — split a run of **steps** when the steps still ahead (a step's **post-completion steps**) tempt the agent to rush the one in front of it (**premature completion**). Keeping them out of view encourages the agent to do more **legwork** on the current task.
|
||||
|
||||
## Pruning
|
||||
|
||||
Keep each meaning in a **single source of truth**: one authoritative place, so changing the behaviour is a one-place edit.
|
||||
|
||||
Check every line for **relevance**: does it still bear on what the skill does?
|
||||
|
||||
Then hunt **no-ops** sentence by sentence, not just line by line: run the no-op test on each sentence in isolation, and when one fails, delete the whole sentence rather than trim words from it. Be aggressive — most prose that fails should go, not be rewritten.
|
||||
|
||||
## Leading words
|
||||
|
||||
A **leading word** is a compact concept already living in the model's pretraining that the agent thinks with while running the skill (e.g. _lesson_, _fog of war_, _tracer bullets_). Repeated throughout the text (though not necessarily - a strong leading word might only be needed once), it accumulates a distributed definition and anchors a whole region of behaviour in the fewest tokens, by recruiting priors the model already holds.
|
||||
|
||||
It serves predictability twice. In the body it anchors _execution_: the agent reaches for the same behaviour every time the word appears. In the description it anchors _invocation_: when the same word lives in your prompts, docs, and code, the agent links that shared language to the skill and fires it more reliably.
|
||||
|
||||
Hunt for opportunities to refactor skills to use leading words. A triad spelled out at three sites (**duplication**), a description spending a sentence to gesture at one idea — each is a passage begging to **collapse** into a single token. Examples include:
|
||||
|
||||
- "fast, deterministic, low-overhead" -> _tight_ — one quality restated across a phase — into a single pretrained word (a _tight_ loop).
|
||||
- "a loop you believe in" -> _red_ — converts a fuzzy gate into a binary observable state (the loop goes _red_ on the bug, or it doesn't).
|
||||
|
||||
You win twice over: fewer tokens, _and_ a sharper hook for the agent to hang its thinking on. Assume every skill is carrying restatements that leading words retire — go find them.
|
||||
|
||||
## Failure modes
|
||||
|
||||
Use these to diagnose issues the user may be having with the skill.
|
||||
|
||||
- **Premature completion** — ending a step before it's genuinely done, attention slipping to _being done_. Defence, in order: sharpen the completion criterion first (cheap, local); only if it is irreducibly fuzzy _and_ you observe the rush, hide the post-completion steps by splitting (the sequence cut).
|
||||
- **Duplication** — the same meaning in more than one place. Costs maintenance and tokens, and inflates a meaning's prominence on the ladder past its real rank.
|
||||
- **Sediment** — stale layers that settle because adding feels safe and removing feels risky. The default fate of any skill without a pruning discipline.
|
||||
- **Sprawl** — a skill simply too long, even when every line is live and unique. Hurts readability and maintainability and wastes tokens. The cure is the ladder: disclose **reference** behind pointers, and split by **branch** or sequence so each path carries only what it needs.
|
||||
- **No-op** — a line the model already obeys by default, so you pay load to say nothing. The test: does it change behaviour versus the default? A weak leading word (_be thorough_ when the agent is already thorough-ish) is a no-op; the fix is a stronger word (_relentless_), not a different technique.
|
||||
- **Negation** — steering by prohibition backfires: _don't think of an elephant_ names the elephant and makes it more available, not less. Prompt the **positive** — state the target behaviour so the banned one is never spoken; keep a prohibition only as a hard guardrail you can't phrase positively, and even then pair it with what to do instead.
|
||||
Reference in New Issue
Block a user