@devshop/crew 0.11.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +14 -0
- package/CHANGELOG.md +14 -0
- package/README.md +12 -0
- package/package.json +2 -1
- package/skills/plan/.claude-plugin/plugin.json +5 -0
- package/skills/plan/skills/ticket/SKILL.md +152 -0
- package/skills/prep/SKILL.md +279 -73
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crew",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "devshop-software"
|
|
5
|
+
},
|
|
6
|
+
"description": "Project-agnostic Claude Code skills for spec → implement → qa → review → ship.",
|
|
7
|
+
"plugins": [
|
|
8
|
+
{
|
|
9
|
+
"name": "plan",
|
|
10
|
+
"source": "./skills/plan",
|
|
11
|
+
"description": "Turn rough intent into agent-ready, testable GitHub Issues (plan:ticket)."
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [0.13.0](https://github.com/devshop-software/crew/compare/v0.12.0...v0.13.0) (2026-06-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **plan:** namespace ticket as plan:ticket via a plugin marketplace ([95affac](https://github.com/devshop-software/crew/commit/95affac6d7f19d050fe1e6e3cb9e19850039015c))
|
|
7
|
+
|
|
8
|
+
# [0.12.0](https://github.com/devshop-software/crew/compare/v0.11.2...v0.12.0) (2026-05-12)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **prep:** folder-per-brief + interactive HTML companion ([c3b7a38](https://github.com/devshop-software/crew/commit/c3b7a383ba7e622c6bdcda9bab0085f21d84c9fb))
|
|
14
|
+
|
|
1
15
|
## [0.11.2](https://github.com/devshop-software/crew/compare/v0.11.1...v0.11.2) (2026-05-12)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -30,6 +30,18 @@ To pull newer skill content later, run `pnpm exec crew update`. The flow:
|
|
|
30
30
|
|
|
31
31
|
This copies the skills into `./.claude/skills/`, writes a manifest, and appends a `## Workflow Config` block to `CLAUDE.md` (creating it if absent).
|
|
32
32
|
|
|
33
|
+
### Namespaced skills (plugins)
|
|
34
|
+
|
|
35
|
+
Some skills ship as Claude Code **plugins** so they're invoked under a namespace — e.g. the planning skill is **`/plan:ticket`**. No extra step is needed: `crew init` copies the plugin to `.claude/skills/plan/` (it carries a `.claude-plugin/plugin.json`), and Claude Code auto-loads any such folder as `plan@skills-dir`, exposing `/plan:ticket` on the next session.
|
|
36
|
+
|
|
37
|
+
Prefer Claude Code's own plugin system over the CLI? Install straight from the marketplace instead:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
# run inside Claude Code
|
|
41
|
+
/plugin marketplace add devshop-software/crew
|
|
42
|
+
/plugin install plan@crew
|
|
43
|
+
```
|
|
44
|
+
|
|
33
45
|
## Commands
|
|
34
46
|
|
|
35
47
|
```
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devshop/crew",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Project-agnostic Claude Code skills for spec → implement → qa → review → ship",
|
|
5
5
|
"bin": {
|
|
6
6
|
"crew": "scripts/cli.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
|
+
".claude-plugin/",
|
|
9
10
|
"skills/",
|
|
10
11
|
"templates/",
|
|
11
12
|
"scripts/",
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ticket
|
|
3
|
+
description: "Interactive ticket-writer. Interviews the user about a feature, then opens a well-formed GitHub Issue (mechanical and testable: Context / Out of scope / Acceptance criteria) that reads clearly for humans and implementation agents alike, then labels it for the implementation loop to pick up. Project conventions are read from CLAUDE.md at runtime — the skill contains no project-specific knowledge. Use when the user invokes /plan:ticket."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ticket
|
|
7
|
+
|
|
8
|
+
## Role
|
|
9
|
+
|
|
10
|
+
You produce **well-written GitHub Issues** — the unit of work that gets picked up and shipped, read by humans and implementation agents alike. A ticket captures the _outcome contract_ (what must be true when done), the _boundary_ (what's excluded and why), and how the work is _verified_ — and nothing more.
|
|
11
|
+
|
|
12
|
+
You are an interviewer first, a writer second. Your job is to pull out of the user's head the decisions and constraints that only the user knows and that no amount of code-reading will reveal, then compress them into one mechanical issue body.
|
|
13
|
+
|
|
14
|
+
**You capture the outcome and the boundary; the mechanism is chosen later, at implementation time, after the code has been read.** This division is load-bearing: if the ticket prescribes hooks, CSS strategies, or file-level edits, it pre-decides work that should be reconsidered after exploring the codebase, and creates double-specification that silently drifts.
|
|
15
|
+
|
|
16
|
+
The output is a GitHub Issue — it lands in a reviewable queue that humans triage and agents implement, so it must stand on its own without you there to explain it.
|
|
17
|
+
|
|
18
|
+
## When to Apply
|
|
19
|
+
|
|
20
|
+
Activate when called from the `/plan:ticket` command. Otherwise ignore.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Step 0 — Preflight
|
|
25
|
+
|
|
26
|
+
Confirm the issue can actually be filed before interviewing:
|
|
27
|
+
|
|
28
|
+
1. `gh auth status` — must be logged in. If not, stop and tell the user to run `gh auth login`.
|
|
29
|
+
2. `gh repo view --json nameWithOwner -q .nameWithOwner` — confirms a default GitHub remote and prints the target repo. If it fails (no remote, or multiple remotes with no default), tell the user and ask which repo to target (`gh repo set-default`).
|
|
30
|
+
|
|
31
|
+
If `gh` is unavailable, fall back to **draft mode**: run the full interview and draft, then print the issue body for the user to paste manually instead of creating it. Say so up front.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Step 1 — Read project conventions
|
|
36
|
+
|
|
37
|
+
Read `CLAUDE.md` from the CWD (walking upward until found). Extract:
|
|
38
|
+
|
|
39
|
+
- Tech stack signals (package manager, test framework, lint/build commands, CI config locations).
|
|
40
|
+
- The `## Workflow Config` table if present — note the **test / lint / build commands**. The lean ticket has no dedicated verify section, so verification folds into the **acceptance criteria** as testable outcomes; these commands inform how those criteria are phrased.
|
|
41
|
+
- Any "do not do X" constraints the ticket should echo as guardrails.
|
|
42
|
+
|
|
43
|
+
Never hardcode tool names, package managers, or framework names. Pull them from `CLAUDE.md` fresh each run. If `CLAUDE.md` is absent, warn the user — a ticket without project conventions (especially verify commands) will drift.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Step 2 — Ground in the codebase (light)
|
|
48
|
+
|
|
49
|
+
Before asking questions, spend a few minutes verifying the feature maps to real files:
|
|
50
|
+
|
|
51
|
+
- Grep/Glob for the symbols, files, or commands the user mentioned.
|
|
52
|
+
- Identify the 2–5 files most likely to be affected so the Context and acceptance criteria are concrete.
|
|
53
|
+
|
|
54
|
+
**Do not** explore to implementation depth. The goal is to ground the ticket in real paths, not to plan the implementation.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Step 3 — Interview
|
|
59
|
+
|
|
60
|
+
Ask targeted questions in **one batch** (not drip-fed). Choose 3–6 from:
|
|
61
|
+
|
|
62
|
+
1. **What's needed** — one sentence in the user's own words, if the rough description was vague.
|
|
63
|
+
2. **Why now** — a concrete motivating source (a PR, bug, incident, prior ticket). Often opens the issue's Context.
|
|
64
|
+
3. **Decisions already made** — what has the user already ruled in or out? Non-obvious constraints no code-reading reveals.
|
|
65
|
+
4. **Boundary** — name 2–5 adjacent things (files, capabilities, flows) you saw in Step 2 and ask **which are in scope** (positive enumeration, never "what's excluded?"). The Out-of-scope list is derived from the candidates the user did _not_ mark in-scope.
|
|
66
|
+
5. **Acceptance shape** — what must be observably true when done? 1–3 items; you'll flesh them out at draft time.
|
|
67
|
+
6. **Verification** — how should "done" be checked? The answer becomes a testable acceptance criterion (pull exact test/lint/build commands from `CLAUDE.md` if it has them).
|
|
68
|
+
|
|
69
|
+
If an answer is vague, follow up once. Two rounds max — don't interrogate.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Step 4 — Draft the issue body
|
|
74
|
+
|
|
75
|
+
Write the body to a temp file (`mktemp`) so `gh` reads it cleanly. Use this structure exactly:
|
|
76
|
+
|
|
77
|
+
```markdown
|
|
78
|
+
## Context
|
|
79
|
+
|
|
80
|
+
<2–4 sentences for human triage: what's needed and why. State the outcome, not the mechanism. If the work has a special path — e.g. only an admin can do it — say so here (e.g. "if an admin must do this, leave a comment on the ticket with instructions").>
|
|
81
|
+
|
|
82
|
+
## Out of scope
|
|
83
|
+
|
|
84
|
+
Phrased as _"do not add X"_, _"do not touch Y"_ — guardrails the agent must obey. Derived from the boundary candidates the user did _not_ mark in scope.
|
|
85
|
+
|
|
86
|
+
## Acceptance criteria
|
|
87
|
+
|
|
88
|
+
- [ ] Specific, testable item — observably true when done, verifiable by a reviewer and/or an e2e test. Verification lives here: bake the check into the criterion itself (e.g. _"when creating an MR the branch is accessible via Vercel and testable"_).
|
|
89
|
+
- [ ] Specific, testable item.
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Anti-spec rule
|
|
93
|
+
|
|
94
|
+
The ticket restates intent as context, testable outcomes, and constraints. **It does not outline implementation steps.** If an item reads like a to-do for a coder — "modify X to call Y", "add a hook", "extract a component" — rephrase it as an outcome and leave the mechanism to implementation.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Step 5 — Create the issue
|
|
99
|
+
|
|
100
|
+
1. Ensure the label exists (idempotent):
|
|
101
|
+
`gh label create agent-ready --color 0E8A16 --description "Ready for the implementation loop" 2>/dev/null || true`
|
|
102
|
+
2. Create the issue:
|
|
103
|
+
`gh issue create --title "<feature title>" --body-file <tmpfile> --label agent-ready`
|
|
104
|
+
3. Capture the URL `gh` prints.
|
|
105
|
+
|
|
106
|
+
The `agent-ready` label is the queue _and_ the kill switch: the implementation loop only picks up issues carrying it. The label name is a convention — if the user's loop uses a different label, ask and substitute.
|
|
107
|
+
|
|
108
|
+
In **draft mode** (no `gh`), skip this step and print the body in a fenced block for manual paste.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Step 6 — Present
|
|
113
|
+
|
|
114
|
+
Report in three lines:
|
|
115
|
+
|
|
116
|
+
1. **Issue** — the URL (or "draft — paste below" in draft mode).
|
|
117
|
+
2. **Label** — `agent-ready` (so the loop will pick it up).
|
|
118
|
+
3. **Next** — how the loop consumes it (e.g. assign to the agent, or it fires on the label).
|
|
119
|
+
|
|
120
|
+
Then ask: _"Want to tweak anything before the loop picks this up?"_ If the user requests changes, edit the issue in place with `gh issue edit <number> --body-file <tmpfile>` (and `--title` if the title changed) — don't open a second issue.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Constraints
|
|
125
|
+
|
|
126
|
+
**DO:**
|
|
127
|
+
|
|
128
|
+
- Read `CLAUDE.md` at runtime for conventions and verify commands — never hardcode them.
|
|
129
|
+
- Verify every concrete file reference by actually looking at it before writing it into the ticket.
|
|
130
|
+
- Keep the body mechanical — three sections only: Context / Out of scope / Acceptance criteria. A few sentences of human context at most.
|
|
131
|
+
- Fold verification into the acceptance criteria as testable outcomes — there is no separate How-to-verify section.
|
|
132
|
+
|
|
133
|
+
**DON'T:**
|
|
134
|
+
|
|
135
|
+
- Embed project-specific tool, framework, or package-manager names into this skill file. It must work in any repo that has a `CLAUDE.md`.
|
|
136
|
+
- Prescribe mechanisms (hooks, CSS utilities, component layout, which file to edit) unless the user explicitly committed to one in the interview. The mechanism is explored and decided at implementation time; pre-deciding here strips that option and drifts.
|
|
137
|
+
- Skip the interview. The point of `/plan:ticket` is to extract what only the user knows.
|
|
138
|
+
- Explore the codebase to implementation depth. Grounding the ticket in real paths is enough — planning the build is a later step.
|
|
139
|
+
- Open a second issue when refining — edit the existing one.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Red flags
|
|
144
|
+
|
|
145
|
+
If you catch yourself thinking any of these, stop:
|
|
146
|
+
|
|
147
|
+
- _"The user said 'make it good', I'll just draft something"_ — STOP. Ask concrete questions.
|
|
148
|
+
- _"The acceptance criteria are general on purpose, to leave flexibility"_ — STOP. Vague criteria are the #1 reason unattended runs drift. Be specific and testable.
|
|
149
|
+
- _"The acceptance criteria don't say how to check it"_ — STOP. Each criterion must be observably testable; bake the check into the criterion (pull commands from `CLAUDE.md` if relevant). A criterion an agent can't verify is a top cause of drift.
|
|
150
|
+
- _"I didn't ask about out-of-scope because the user didn't mention it"_ — STOP. Ask. Out-of-scope is where tickets silently fail.
|
|
151
|
+
- _"I'll ask the user to list what's NOT in scope"_ — STOP. The boundary question is positive enumeration (_"which of these are in scope?"_); derive Out-of-scope from what they didn't mark.
|
|
152
|
+
- _"The user stated an outcome and I'm writing a mechanism"_ — STOP. `useSidebar()`, CSS strategy, which file to modify — those are implementation-time calls after exploration, not the ticket's.
|
package/skills/prep/SKILL.md
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prep
|
|
3
|
-
description: Interactive brief-writer. Produces a
|
|
3
|
+
description: Interactive brief-writer. Produces a timestamped folder under `<project-root>/_brief/` containing `BRIEF.md` (agent brief for `/indie-agent`) and `brief.html` (interactive single-file offline view for humans, with persistent acceptance-criteria checkboxes). Project root is auto-detected: nearest ancestor whose `CLAUDE.md` contains `## Workflow Config` (works for both single-repo and multi-repo workspaces), falling back to bare-clone via `.bare/` or git toplevel if no workflow config is set yet. Reads project conventions from `CLAUDE.md` at runtime — contains no project-specific knowledge. Use when the user invokes /prep.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Prep
|
|
7
7
|
|
|
8
8
|
## Role
|
|
9
9
|
|
|
10
|
-
You produce **feature briefs** — handoff documents the user feeds to `/indie-agent` to start a full autonomous implementation. A brief captures the *why*, *what's already decided*, and *what's explicitly not included
|
|
10
|
+
You produce **feature briefs** — handoff documents the user feeds to `/indie-agent` to start a full autonomous implementation. A brief captures the *why*, *what's already decided*, and *what's explicitly not included*.
|
|
11
11
|
|
|
12
12
|
**Prep captures the outcome contract (what must be true when done) and the boundary (what's excluded and why). `/spec` picks the mechanism after reading the code.** This division is load-bearing: if the brief prescribes hooks, CSS strategies, or component layouts, it pre-decides work that spec-writer should reconsider after codebase exploration — and creates double-specification that silently drifts.
|
|
13
13
|
|
|
14
|
-
You are an interviewer first, a writer second. Your job is to pull context out of the user's head — specifically the decisions and constraints that only the user knows and that no amount of code-reading will reveal. Then you compress that context into
|
|
14
|
+
You are an interviewer first, a writer second. Your job is to pull context out of the user's head — specifically the decisions and constraints that only the user knows and that no amount of code-reading will reveal. Then you compress that context into two artifacts that live together in a per-brief folder:
|
|
15
|
+
|
|
16
|
+
- **`BRIEF.md`** — the agent brief, fed to `/indie-agent`. Mechanical, testable, no narrative. Contains In scope outcomes, Out-of-scope guardrails, Acceptance criteria checklist, and References.
|
|
17
|
+
- **`brief.html`** — an interactive single-file view for human readers. Carries all the human-readable context (TL;DR, Why this exists, Decisions, the user-facing Out-of-scope discussion, References, Post-merge manual steps) plus persistent acceptance-criteria checkboxes the human can tick post-merge to verify the work matches the brief. Strict offline — inline CSS + vanilla JS, no CDN, no web fonts.
|
|
18
|
+
|
|
19
|
+
The two artifacts are different surfaces for different audiences. Do not duplicate human-readable narrative into `BRIEF.md`; do not put agent-brief mechanics into `brief.html` (other than the embedded JSON data block that powers the page).
|
|
15
20
|
|
|
16
21
|
## When to Apply
|
|
17
22
|
|
|
@@ -25,7 +30,7 @@ Activate when called from the `/prep` command. Otherwise ignore.
|
|
|
25
30
|
|
|
26
31
|
- **Empty** — ask: *"What's the feature? A one-sentence description works."*
|
|
27
32
|
- **Free text** — a rough description. Treat it as the interview's starting point, not the final feature statement.
|
|
28
|
-
- **Path to an existing
|
|
33
|
+
- **Path to an existing brief folder OR a `BRIEF.md` inside one** — read both `BRIEF.md` and `brief.html` (extract the embedded JSON block from the HTML), identify which sections are empty or thin across either file, run the interview only for those gaps. Normalize a `.md` path to its parent folder; both forms resolve to the same brief.
|
|
29
34
|
|
|
30
35
|
---
|
|
31
36
|
|
|
@@ -68,11 +73,11 @@ If an answer is vague, follow up once. Two rounds max — don't interrogate.
|
|
|
68
73
|
|
|
69
74
|
---
|
|
70
75
|
|
|
71
|
-
## Step 4 — Draft
|
|
76
|
+
## Step 4 — Draft `BRIEF.md`
|
|
72
77
|
|
|
73
78
|
### Resolve the output location
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
Each brief is a folder at `<project-root>/_brief/<YYYYMMDD-HHMMSS>-<SLUG>/` containing `BRIEF.md` and `brief.html`. Resolve the project root generically, in this order:
|
|
76
81
|
|
|
77
82
|
1. **Workflow Config anchor (preferred)** — walk up from CWD. The first ancestor whose `CLAUDE.md` contains a `## Workflow Config` heading is the project root. This works for both shapes:
|
|
78
83
|
- **Single-repo project** — the project-root `CLAUDE.md` has `## Workflow Config` (written by `/adjust`). Found at the project root.
|
|
@@ -81,104 +86,300 @@ Briefs live in `<project-root>/_brief/<SLUG>-BRIEF.md`. Resolve the project root
|
|
|
81
86
|
3. **Regular git repo (fallback)** — otherwise, run `git rev-parse --show-toplevel`. The result is the project root.
|
|
82
87
|
4. **Final fallback** — if none of the above applies, use the CWD and warn the user that no project root was detected.
|
|
83
88
|
|
|
84
|
-
In workspace mode, the resolved project root is the **workspace root** — the brief lives there, not inside any sub-repo. This is intentional: a brief for a cross-stack feature is workspace-scoped, not stack-scoped.
|
|
85
|
-
|
|
86
|
-
Create `<project-root>/_brief/` if it does not exist. Write the file there.
|
|
87
|
-
|
|
88
|
-
### Lifecycle — the brief is ephemeral
|
|
89
|
-
|
|
90
|
-
The brief lives at the **top layer** of the project — the bare-clone root in single-repo projects, or the workspace root in multi-repo workspaces — outside any tracked working copy. It is not committed and will be deleted once consumed. History of a feature lives in `<workflow-dir>/<folder>/`, which itself lives at the same top layer (workspace root in workspace mode, bare-clone root in single mode) — that is where spec, implementation, QA, and review artifacts persist.
|
|
91
|
-
|
|
92
|
-
Consequence for downstream skills: **ingest the brief's content, do not cite its path**. A `_workflow/.../01-spec.md` that references `../_brief/FOO-BRIEF.md` will break the first time someone cleans up `_brief/`. Spec-writer (and anything else that needs the information) should copy the relevant facts into the persisted artifact rather than linking to the brief file.
|
|
93
|
-
|
|
94
|
-
### Filename
|
|
95
|
-
|
|
96
|
-
`<SLUG>-BRIEF.md` — uppercase kebab-case slug derived from the feature title (e.g. `MIGRATION-CONSOLIDATION-BRIEF.md`, `DARK-MODE-BRIEF.md`). The `-BRIEF.md` suffix is intentional even though the folder already signals the type: it makes the file recognizable when grepped, referenced, or opened in isolation.
|
|
97
|
-
|
|
98
|
-
### Structure
|
|
99
|
-
|
|
100
|
-
The brief has two clearly-delimited sections. The human section comes first so a reader can stop there.
|
|
101
|
-
|
|
102
|
-
```markdown
|
|
103
|
-
# <Feature title>
|
|
89
|
+
In workspace mode, the resolved project root is the **workspace root** — the brief folder lives there, not inside any sub-repo. This is intentional: a brief for a cross-stack feature is workspace-scoped, not stack-scoped.
|
|
104
90
|
|
|
105
|
-
|
|
106
|
-
HUMAN SECTION — readable in ≤60 seconds.
|
|
107
|
-
Prose, not checklists. A teammate should finish this section
|
|
108
|
-
and understand the "why" well enough to stop reading here.
|
|
109
|
-
============================================================ -->
|
|
91
|
+
Create `<project-root>/_brief/<folder-name>/` if it does not exist. Write `BRIEF.md` and `brief.html` inside.
|
|
110
92
|
|
|
111
|
-
|
|
93
|
+
### Folder + file naming
|
|
112
94
|
|
|
113
|
-
|
|
95
|
+
| Element | Format | Example |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| Folder | `YYYYMMDD-HHMMSS-<SLUG>` | `20260512-211530-PRODUCT-VARIANTS-SCHEMA` |
|
|
98
|
+
| Slug | UPPERCASE-KEBAB-CASE from feature title | `PRODUCT-VARIANTS-SCHEMA` |
|
|
99
|
+
| Agent brief | `BRIEF.md` (no slug prefix — folder carries it) | `BRIEF.md` |
|
|
100
|
+
| Human view | `brief.html` (lowercase per HTML convention) | `brief.html` |
|
|
114
101
|
|
|
115
|
-
|
|
102
|
+
Timestamp uses **second precision** (matches the `_workflow/` folder convention). Second precision supports parallel `/prep` invocations without folder collisions.
|
|
116
103
|
|
|
117
|
-
|
|
104
|
+
### Lifecycle
|
|
118
105
|
|
|
119
|
-
|
|
106
|
+
The brief folder lives at the **top layer** of the project — the bare-clone root in single-repo projects, or the workspace root in multi-repo workspaces — outside any tracked working copy. It is gitignored (Step 5).
|
|
120
107
|
|
|
121
|
-
-
|
|
122
|
-
- Decision — *why*.
|
|
108
|
+
Unlike `<workflow-dir>/<folder>/` (the permanent paper trail of a feature), the brief folder is the human's working artifact: `BRIEF.md` is throwaway once `/indie-agent` consumes it, but `brief.html` is the human's verification reference — keep it as long as it's useful, prune when stale. **The skill does not auto-delete; the user owns cleanup.**
|
|
123
109
|
|
|
124
|
-
|
|
110
|
+
Consequence for downstream skills: **ingest the brief's content, do not cite its path**. A `_workflow/.../01-spec.md` that references the brief by path will break the first time someone prunes `_brief/`. Spec-writer (and anything else that needs the information) should copy the relevant facts into the persisted artifact rather than linking to the brief file.
|
|
125
111
|
|
|
126
|
-
|
|
112
|
+
### `BRIEF.md` content — agent brief only
|
|
127
113
|
|
|
128
|
-
|
|
129
|
-
- Thing not included — *why not*.
|
|
114
|
+
`BRIEF.md` carries **only** the agent brief. No TL;DR, no Why, no Decisions narrative — that lives in `brief.html`. The agent reads this; the human reads the HTML.
|
|
130
115
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
1. Numbered action for the human to take after the PR merges.
|
|
134
|
-
2. ...
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
<!-- ============================================================
|
|
139
|
-
AGENT BRIEF — feed this (or the whole file) to /indie-agent.
|
|
140
|
-
Mechanical. Testable. Exact paths and checklists.
|
|
141
|
-
No narrative. Every item should be diff-able or verifiable.
|
|
142
|
-
============================================================ -->
|
|
143
|
-
|
|
144
|
-
## Feed to `/indie-agent`
|
|
116
|
+
```markdown
|
|
117
|
+
# <Feature title>
|
|
145
118
|
|
|
146
119
|
> Base: <base-branch from CLAUDE.md workflow config>
|
|
147
120
|
|
|
148
|
-
|
|
121
|
+
## In scope
|
|
149
122
|
|
|
150
123
|
Outcomes the feature must produce, framed as user-visible behavior or structural boundaries — **not** implementation steps. Paths, function names, and line numbers belong in References, not here. Spec-writer will choose the mechanism after exploring the code; pre-deciding it here removes that option.
|
|
151
124
|
|
|
152
125
|
- **Good:** *"Sidebar header swaps between wordmark and diamond when toggling between expanded and icon-collapsed states."*
|
|
153
126
|
- **Bad:** *"In `app-sidebar.tsx`, read `state` from `useSidebar()` and conditionally render `<Image>`."* (That's a spec step — picks the hook, the file, and the render strategy before anyone has read the code.)
|
|
154
127
|
|
|
155
|
-
|
|
128
|
+
## Out of scope (as constraints)
|
|
156
129
|
|
|
157
130
|
Phrased as *"do not add X"*, *"do not touch Y"*. These become guardrails the agent is expected to obey.
|
|
158
131
|
|
|
159
|
-
|
|
132
|
+
## Acceptance criteria
|
|
160
133
|
|
|
161
134
|
- [ ] Specific, testable item (verifiable by a reviewer and/or an e2e test).
|
|
162
135
|
- [ ] Specific, testable item.
|
|
163
136
|
|
|
164
|
-
|
|
137
|
+
## References — where to look
|
|
165
138
|
|
|
166
139
|
- `path/to/file.ext:LN` — one-line note on what lives there.
|
|
167
140
|
- `<workflow-dir>/<folder>/01-spec.md` — prior related work, if any.
|
|
168
141
|
- PR #N, issue #M, incident date — whatever grounds the brief.
|
|
169
142
|
```
|
|
170
143
|
|
|
144
|
+
The `## Out of scope (as constraints)` heading here is intentionally distinct from `brief.html`'s user-facing "Out of scope" section. In `BRIEF.md` it is a narrow list of explicit "do not touch X" guardrails for the agent; in `brief.html` it is the broader human-facing context derived from the boundary interview. Different audiences, different specificity.
|
|
145
|
+
|
|
171
146
|
### Anti-spec rule
|
|
172
147
|
|
|
173
|
-
The
|
|
148
|
+
The agent brief restates the user's intent as testable outcomes, constraints, and pointers. **It does not outline implementation steps.** If an item reads like a to-do for a coder — "modify X to call Y", "add a hook that does Z", "extract a component" — it's in the wrong layer. Either rephrase it as an outcome (what must be observably true) or move the file reference down to References and let `/spec` decide the mechanism.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Step 4b — Render `brief.html`
|
|
153
|
+
|
|
154
|
+
Write `brief.html` in the same folder as `BRIEF.md`. Use the template below **verbatim**, replacing only two placeholders:
|
|
155
|
+
|
|
156
|
+
- `__TITLE__` — the feature title, HTML-escaped, inserted into the `<title>` tag.
|
|
157
|
+
- `__JSON_DATA__` — the JSON object describing this brief, inserted inside the `<script type="application/json" id="prep-data">` block. The JSON schema:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"title": "<Feature title>",
|
|
162
|
+
"slug": "<SLUG>",
|
|
163
|
+
"created": "<ISO 8601 timestamp, e.g. 2026-05-12T21:15:30Z>",
|
|
164
|
+
"tldr": "<one sentence: what's happening and why>",
|
|
165
|
+
"why": "<2-5 sentence prose paragraph (or paragraphs separated by blank lines); the motivating incident, PR, constraint, or deadline with concrete references>",
|
|
166
|
+
"decisions": [
|
|
167
|
+
{"point": "<decision>", "why": "<half-a-line on why>"}
|
|
168
|
+
],
|
|
169
|
+
"outOfScope": [
|
|
170
|
+
{"thing": "<item not included>", "why": "<why not — user-facing context, broader than BRIEF.md's agent constraints>"}
|
|
171
|
+
],
|
|
172
|
+
"acceptance": [
|
|
173
|
+
"<specific, testable item — same wording as BRIEF.md's Acceptance criteria>"
|
|
174
|
+
],
|
|
175
|
+
"references": [
|
|
176
|
+
"<path/to/file.ext:LN — note>",
|
|
177
|
+
"<PR #N, issue #M, incident date>"
|
|
178
|
+
],
|
|
179
|
+
"postMerge": [
|
|
180
|
+
"<numbered action for the human after PR merges; omit the array if none>"
|
|
181
|
+
],
|
|
182
|
+
"agentBrief": "<full BRIEF.md content as a single string, with real \\n newlines>"
|
|
183
|
+
}
|
|
184
|
+
```
|
|
174
185
|
|
|
175
|
-
|
|
186
|
+
Notes on filling the JSON:
|
|
187
|
+
|
|
188
|
+
- `acceptance` should mirror `BRIEF.md`'s checklist items verbatim — the HTML's persistent checkboxes track exactly those items. If you edit one, edit both.
|
|
189
|
+
- `agentBrief` is the full `BRIEF.md` text. The HTML's "Copy agent brief" button puts this on the clipboard so the human can paste it elsewhere.
|
|
190
|
+
- `outOfScope` in the HTML is **user-facing context** — the broader "we considered this, ruled it out, here's why" content. Distinct from `BRIEF.md`'s narrower `## Out of scope (as constraints)` guardrails. They will often overlap but should be written for their respective audiences.
|
|
191
|
+
- `postMerge` may be omitted (or set to `[]`) when there are no manual steps. The HTML hides the section when empty.
|
|
192
|
+
|
|
193
|
+
### HTML template
|
|
194
|
+
|
|
195
|
+
Save this verbatim as `brief.html` in the brief folder, with the two placeholders filled:
|
|
196
|
+
|
|
197
|
+
````html
|
|
198
|
+
<!DOCTYPE html>
|
|
199
|
+
<html lang="en">
|
|
200
|
+
<head>
|
|
201
|
+
<meta charset="UTF-8">
|
|
202
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
203
|
+
<title>__TITLE__</title>
|
|
204
|
+
<style>
|
|
205
|
+
:root { --bg:#fff; --fg:#1a1a1a; --muted:#666; --accent:#2563eb; --border:#e5e5e5; --card:#fafafa; --done:#16a34a; }
|
|
206
|
+
@media (prefers-color-scheme: dark) {
|
|
207
|
+
:root { --bg:#0a0a0a; --fg:#e5e5e5; --muted:#9aa0a6; --accent:#60a5fa; --border:#2a2a2a; --card:#141414; --done:#22c55e; }
|
|
208
|
+
}
|
|
209
|
+
* { box-sizing: border-box; }
|
|
210
|
+
html, body { background: var(--bg); color: var(--fg); }
|
|
211
|
+
body { font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; max-width: 760px; margin: 2.5rem auto; padding: 0 1.25rem; line-height: 1.6; }
|
|
212
|
+
h1 { font-size: 1.875rem; margin: 0 0 0.5rem; letter-spacing: -0.01em; }
|
|
213
|
+
h2 { font-size: 0.8125rem; margin: 0 0 0.5rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
|
|
214
|
+
p { margin: 0.75rem 0; }
|
|
215
|
+
.tldr { font-size: 1.125rem; margin: 1.25rem 0 2rem; padding: 1rem 1.25rem; background: var(--card); border-left: 3px solid var(--accent); border-radius: 6px; }
|
|
216
|
+
details { margin: 0.5rem 0; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); }
|
|
217
|
+
details > summary { cursor: pointer; padding: 0.75rem 1rem; font-weight: 500; user-select: none; list-style: none; display: flex; align-items: center; gap: 0.5rem; }
|
|
218
|
+
details > summary::before { content: "▶"; font-size: 0.7rem; color: var(--muted); transition: transform 0.15s; display: inline-block; }
|
|
219
|
+
details[open] > summary::before { transform: rotate(90deg); }
|
|
220
|
+
details > summary::-webkit-details-marker { display: none; }
|
|
221
|
+
details > .body { padding: 0.25rem 1.25rem 1rem 1.75rem; }
|
|
222
|
+
ul, ol { padding-left: 1.25rem; margin: 0.5rem 0; }
|
|
223
|
+
li { margin: 0.35rem 0; }
|
|
224
|
+
li em { color: var(--muted); font-style: normal; }
|
|
225
|
+
.ac-section { margin: 2rem 0; padding: 1.25rem 1.5rem; background: var(--card); border-radius: 8px; border: 1px solid var(--border); }
|
|
226
|
+
.ac-list { margin: 0.75rem 0 0; }
|
|
227
|
+
.ac-item { display: flex; gap: 0.75rem; align-items: flex-start; padding: 0.5rem 0; border-top: 1px solid var(--border); }
|
|
228
|
+
.ac-item:first-child { border-top: none; }
|
|
229
|
+
.ac-item input { margin-top: 0.35rem; flex-shrink: 0; cursor: pointer; width: 1rem; height: 1rem; accent-color: var(--done); }
|
|
230
|
+
.ac-item label { flex: 1; cursor: pointer; }
|
|
231
|
+
.ac-item input:checked + label { color: var(--muted); text-decoration: line-through; }
|
|
232
|
+
.progress { font-size: 0.8125rem; color: var(--muted); margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border); }
|
|
233
|
+
.copy-btn { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.625rem 1rem; background: var(--accent); color: white; border: none; border-radius: 6px; font-size: 0.875rem; cursor: pointer; font-family: inherit; font-weight: 500; }
|
|
234
|
+
.copy-btn:hover { filter: brightness(1.1); }
|
|
235
|
+
.copy-btn.copied { background: var(--done); }
|
|
236
|
+
footer { margin: 3rem 0 1rem; padding-top: 1.5rem; border-top: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem; }
|
|
237
|
+
.meta { font-size: 0.8125rem; color: var(--muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
238
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--card); padding: 0.125rem 0.375rem; border-radius: 3px; font-size: 0.875em; }
|
|
239
|
+
a { color: var(--accent); }
|
|
240
|
+
</style>
|
|
241
|
+
</head>
|
|
242
|
+
<body>
|
|
243
|
+
<h1 id="title"></h1>
|
|
244
|
+
<div class="tldr" id="tldr"></div>
|
|
245
|
+
|
|
246
|
+
<details open>
|
|
247
|
+
<summary>Why this exists</summary>
|
|
248
|
+
<div class="body" id="why"></div>
|
|
249
|
+
</details>
|
|
250
|
+
|
|
251
|
+
<details>
|
|
252
|
+
<summary>Decisions already made</summary>
|
|
253
|
+
<div class="body" id="decisions"></div>
|
|
254
|
+
</details>
|
|
255
|
+
|
|
256
|
+
<details>
|
|
257
|
+
<summary>Out of scope</summary>
|
|
258
|
+
<div class="body" id="out-of-scope"></div>
|
|
259
|
+
</details>
|
|
260
|
+
|
|
261
|
+
<div class="ac-section">
|
|
262
|
+
<h2>Acceptance criteria</h2>
|
|
263
|
+
<div class="ac-list" id="acceptance"></div>
|
|
264
|
+
<div class="progress" id="progress"></div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<details>
|
|
268
|
+
<summary>References</summary>
|
|
269
|
+
<div class="body" id="references"></div>
|
|
270
|
+
</details>
|
|
271
|
+
|
|
272
|
+
<details id="post-merge-wrapper" hidden>
|
|
273
|
+
<summary>Post-merge manual steps</summary>
|
|
274
|
+
<div class="body" id="post-merge"></div>
|
|
275
|
+
</details>
|
|
276
|
+
|
|
277
|
+
<footer>
|
|
278
|
+
<span class="meta" id="meta"></span>
|
|
279
|
+
<button class="copy-btn" id="copy-btn" type="button">📋 Copy agent brief</button>
|
|
280
|
+
</footer>
|
|
281
|
+
|
|
282
|
+
<script type="application/json" id="prep-data">
|
|
283
|
+
__JSON_DATA__
|
|
284
|
+
</script>
|
|
285
|
+
|
|
286
|
+
<script>
|
|
287
|
+
(function () {
|
|
288
|
+
const data = JSON.parse(document.getElementById('prep-data').textContent);
|
|
289
|
+
const slug = data.slug;
|
|
290
|
+
const acKey = (i) => 'prep:' + slug + ':ac:' + i;
|
|
291
|
+
|
|
292
|
+
document.title = data.title;
|
|
293
|
+
document.getElementById('title').textContent = data.title;
|
|
294
|
+
document.getElementById('tldr').textContent = data.tldr || '';
|
|
295
|
+
|
|
296
|
+
document.getElementById('why').innerHTML = formatProse(data.why);
|
|
297
|
+
|
|
298
|
+
const decEl = document.getElementById('decisions');
|
|
299
|
+
decEl.innerHTML = (data.decisions || []).length
|
|
300
|
+
? '<ul>' + data.decisions.map(d => '<li><strong>' + esc(d.point) + '</strong> — <em>' + esc(d.why) + '</em></li>').join('') + '</ul>'
|
|
301
|
+
: '<p><em>None recorded.</em></p>';
|
|
302
|
+
|
|
303
|
+
const outEl = document.getElementById('out-of-scope');
|
|
304
|
+
outEl.innerHTML = (data.outOfScope || []).length
|
|
305
|
+
? '<ul>' + data.outOfScope.map(o => '<li><strong>' + esc(o.thing) + '</strong> — <em>' + esc(o.why) + '</em></li>').join('') + '</ul>'
|
|
306
|
+
: '<p><em>None recorded.</em></p>';
|
|
307
|
+
|
|
308
|
+
const acEl = document.getElementById('acceptance');
|
|
309
|
+
(data.acceptance || []).forEach((ac, i) => {
|
|
310
|
+
const wrap = document.createElement('div');
|
|
311
|
+
wrap.className = 'ac-item';
|
|
312
|
+
const cb = document.createElement('input');
|
|
313
|
+
cb.type = 'checkbox';
|
|
314
|
+
cb.id = 'ac-' + i;
|
|
315
|
+
cb.checked = localStorage.getItem(acKey(i)) === '1';
|
|
316
|
+
cb.addEventListener('change', () => {
|
|
317
|
+
localStorage.setItem(acKey(i), cb.checked ? '1' : '0');
|
|
318
|
+
updateProgress();
|
|
319
|
+
});
|
|
320
|
+
const lbl = document.createElement('label');
|
|
321
|
+
lbl.htmlFor = 'ac-' + i;
|
|
322
|
+
lbl.textContent = ac;
|
|
323
|
+
wrap.appendChild(cb);
|
|
324
|
+
wrap.appendChild(lbl);
|
|
325
|
+
acEl.appendChild(wrap);
|
|
326
|
+
});
|
|
327
|
+
function updateProgress() {
|
|
328
|
+
const total = (data.acceptance || []).length;
|
|
329
|
+
const done = (data.acceptance || []).filter((_, i) => localStorage.getItem(acKey(i)) === '1').length;
|
|
330
|
+
document.getElementById('progress').textContent = total
|
|
331
|
+
? 'Progress: ' + done + '/' + total + ' verified (saved in this browser)'
|
|
332
|
+
: '';
|
|
333
|
+
}
|
|
334
|
+
updateProgress();
|
|
335
|
+
|
|
336
|
+
const refEl = document.getElementById('references');
|
|
337
|
+
refEl.innerHTML = (data.references || []).length
|
|
338
|
+
? '<ul>' + data.references.map(r => '<li><code>' + esc(r) + '</code></li>').join('') + '</ul>'
|
|
339
|
+
: '<p><em>None recorded.</em></p>';
|
|
340
|
+
|
|
341
|
+
if ((data.postMerge || []).length) {
|
|
342
|
+
document.getElementById('post-merge-wrapper').hidden = false;
|
|
343
|
+
document.getElementById('post-merge').innerHTML = '<ol>' + data.postMerge.map(s => '<li>' + esc(s) + '</li>').join('') + '</ol>';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
document.getElementById('meta').textContent = 'Created ' + data.created + ' · ' + data.slug;
|
|
347
|
+
|
|
348
|
+
const btn = document.getElementById('copy-btn');
|
|
349
|
+
btn.addEventListener('click', async () => {
|
|
350
|
+
try {
|
|
351
|
+
await navigator.clipboard.writeText(data.agentBrief || '');
|
|
352
|
+
btn.classList.add('copied');
|
|
353
|
+
btn.textContent = '✓ Copied';
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
btn.classList.remove('copied');
|
|
356
|
+
btn.textContent = '📋 Copy agent brief';
|
|
357
|
+
}, 1800);
|
|
358
|
+
} catch (e) {
|
|
359
|
+
alert('Copy failed: ' + (e && e.message ? e.message : e));
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
function esc(s) {
|
|
364
|
+
return String(s == null ? '' : s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
|
365
|
+
}
|
|
366
|
+
function formatProse(text) {
|
|
367
|
+
if (!text) return '<p><em>Not recorded.</em></p>';
|
|
368
|
+
return esc(text).split(/\n\n+/).map(p => '<p>' + p.replace(/\n/g, '<br>') + '</p>').join('');
|
|
369
|
+
}
|
|
370
|
+
})();
|
|
371
|
+
</script>
|
|
372
|
+
</body>
|
|
373
|
+
</html>
|
|
374
|
+
````
|
|
375
|
+
|
|
376
|
+
The template is strict offline: inline `<style>`, inline `<script>`, no `<link>`, no `<script src=>`, no `@font-face`, no `@import`. Do not modify it to add CDN-loaded resources. If the styling needs to evolve, update this template in the SKILL itself — every brief should render identically.
|
|
176
377
|
|
|
177
378
|
---
|
|
178
379
|
|
|
179
380
|
## Step 5 — Gitignore the `_brief/` folder
|
|
180
381
|
|
|
181
|
-
Briefs are
|
|
382
|
+
Briefs are working artifacts and should not be committed. The `_brief/` pattern catches the entire folder tree (every per-brief sub-folder inside).
|
|
182
383
|
|
|
183
384
|
1. Determine whether the **project root** (from Step 4) is inside a git working copy (`git -C <project-root> rev-parse --is-inside-work-tree`).
|
|
184
385
|
2. If yes, read the project root's `.gitignore` and check whether `_brief/` (or a matching broader pattern) is already present.
|
|
@@ -193,13 +394,13 @@ Never create a `.gitignore` that didn't already exist — that's a project-struc
|
|
|
193
394
|
|
|
194
395
|
After writing, report in three lines:
|
|
195
396
|
|
|
196
|
-
1. **
|
|
197
|
-
2. **
|
|
198
|
-
3. **Next command** — `/indie-agent <
|
|
397
|
+
1. **Folder** — absolute path of the brief folder.
|
|
398
|
+
2. **Files written** — `BRIEF.md` (agent brief) and `brief.html` (interactive view). Suggest the user open the HTML to read (`open <folder>/brief.html` on macOS, `xdg-open` on Linux, double-click on Windows).
|
|
399
|
+
3. **Next command** — `/indie-agent <folder>/BRIEF.md` (or the appropriate invocation given the user's workflow).
|
|
199
400
|
|
|
200
401
|
Then ask: *"Want to tweak anything before this is fed to `/indie-agent`?"*
|
|
201
402
|
|
|
202
|
-
If the user requests changes, update in place.
|
|
403
|
+
If the user requests changes, update in place — edit `BRIEF.md` and/or the JSON block inside `brief.html`, then re-present only the changed section. Don't reprint the whole file.
|
|
203
404
|
|
|
204
405
|
---
|
|
205
406
|
|
|
@@ -208,18 +409,22 @@ If the user requests changes, update in place. Re-present only the changed secti
|
|
|
208
409
|
**DO:**
|
|
209
410
|
- Read `CLAUDE.md` at runtime to learn project conventions — do not hardcode tool names, package managers, or paths into this skill.
|
|
210
411
|
- Verify every concrete file reference by actually looking at it before writing it into the brief.
|
|
211
|
-
-
|
|
212
|
-
- Keep
|
|
213
|
-
-
|
|
412
|
+
- Put human-readable narrative (TL;DR, Why, Decisions, user-facing Out-of-scope context) in `brief.html` only. `BRIEF.md` is the agent brief and contains no narrative.
|
|
413
|
+
- Keep `BRIEF.md` mechanical — outcomes, constraints, checkboxes, references. Zero narrative.
|
|
414
|
+
- Mirror Acceptance criteria verbatim between `BRIEF.md`'s checklist and `brief.html`'s `acceptance` array — the HTML's persistent checkboxes track those items by identity.
|
|
415
|
+
- Embed all source data as a `<script type="application/json" id="prep-data">` block inside `brief.html` so re-runs (gap-fill mode) can read it back.
|
|
416
|
+
- Use the verbatim HTML template in Step 4b. Only fill `__TITLE__` and `__JSON_DATA__`; do not modify CSS, JS, or markup.
|
|
417
|
+
- Derive folder names from the feature title (UPPERCASE-KEBAB-CASE slug, `YYYYMMDD-HHMMSS-` prefix).
|
|
214
418
|
|
|
215
419
|
**DON'T:**
|
|
216
420
|
- Embed project-specific tool names, framework names, or conventions into the skill file itself. This skill must work in any codebase that has a `CLAUDE.md`.
|
|
217
|
-
-
|
|
218
|
-
-
|
|
421
|
+
- Put TL;DR, Why, or other human-readable narrative in `BRIEF.md`. That content lives in `brief.html`.
|
|
422
|
+
- Fetch external resources in `brief.html` — no CDN, no web fonts, no `<link rel="stylesheet">`, no `<script src=>`. Strict offline, single file.
|
|
219
423
|
- Skip the interview. The point of `/prep` is to extract what only the user knows.
|
|
220
424
|
- Explore the codebase to spec-writer depth. This is *pre-spec* work.
|
|
221
425
|
- Prescribe mechanisms (hooks, CSS utilities, component layout, file-level changes) unless the user explicitly committed to one during the interview. The downstream `/spec` does its own exploration; pre-deciding the mechanism removes its ability to reconsider and creates double-specification that silently drifts.
|
|
222
426
|
- Pre-stamp the spec's depth. `/spec` picks `lightweight | standard | deep` after exploring the code — the brief should not guess it.
|
|
427
|
+
- Auto-delete the brief folder. The user owns cleanup — `BRIEF.md` becomes throwaway once `/indie-agent` consumes it, but `brief.html` is the human's verification reference and stays as long as it's useful.
|
|
223
428
|
|
|
224
429
|
---
|
|
225
430
|
|
|
@@ -229,7 +434,8 @@ If you catch yourself thinking any of these, stop:
|
|
|
229
434
|
|
|
230
435
|
- *"The user said 'make it good', I'll just draft something"* — STOP. Ask concrete questions.
|
|
231
436
|
- *"I know this codebase uses X, I'll reference X in the brief"* — if X is not in `CLAUDE.md` or in a file you just read, you're hallucinating convention. Verify first.
|
|
232
|
-
- *"
|
|
437
|
+
- *"I'll add a TL;DR or Why section to BRIEF.md so the agent has context"* — STOP. Human-readable narrative belongs in `brief.html`. `BRIEF.md` is for `/indie-agent`, which reads outcomes, constraints, ACs, and references — not narrative. Narrative bloats the agent's context with content it doesn't act on.
|
|
438
|
+
- *"The HTML would look nicer with Tailwind / a Google Font / lucide-icons via CDN"* — STOP. Strict offline is a hard rule. Single file, inline CSS/JS, no external resources. The brief must render in 5 years when the CDN is dead.
|
|
233
439
|
- *"The acceptance criteria are general on purpose, to leave flexibility"* — STOP. Vague criteria are the #1 reason `/indie-agent` drifts. Be specific.
|
|
234
440
|
- *"This brief is ready — I didn't ask about out-of-scope because the user didn't mention it"* — STOP. Ask. Out-of-scope is where briefs silently fail.
|
|
235
441
|
- *"I'll ask the user to list what's NOT in scope"* or *"I'll show a multi-select of things to exclude"* — STOP. The boundary question is positive enumeration (*"which of these are in scope?"*). Negation framing, especially as multi-select, is ambiguous (✓ could mean include or exclude) and produces vague or empty answers. Derive the Out-of-scope section from the candidates the user did NOT mark in-scope.
|