@devshop/crew 0.11.1 → 0.12.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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/skills/prep/SKILL.md +281 -74
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [0.12.0](https://github.com/devshop-software/crew/compare/v0.11.2...v0.12.0) (2026-05-12)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **prep:** folder-per-brief + interactive HTML companion ([c3b7a38](https://github.com/devshop-software/crew/commit/c3b7a383ba7e622c6bdcda9bab0085f21d84c9fb))
|
|
7
|
+
|
|
8
|
+
## [0.11.2](https://github.com/devshop-software/crew/compare/v0.11.1...v0.11.2) (2026-05-12)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **prep:** reshape boundary question as positive in-scope enumeration ([1c3db27](https://github.com/devshop-software/crew/commit/1c3db271a61d8a13cd14f6c24e68201ca980ca7c)), closes [#18](https://github.com/devshop-software/crew/issues/18)
|
|
14
|
+
|
|
1
15
|
## [0.11.1](https://github.com/devshop-software/crew/compare/v0.11.0...v0.11.1) (2026-05-09)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
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
|
|
|
@@ -60,7 +65,7 @@ Ask targeted questions in **one batch** (not drip-fed). Choose 3–6 from:
|
|
|
60
65
|
1. **What's broken / needed** — one sentence in the user's own words, if the rough description was vague.
|
|
61
66
|
2. **Concrete motivating source** — a PR, bug report, dated incident, workflow folder, ticket. "Why now?" This often becomes the brief's strongest paragraph.
|
|
62
67
|
3. **Decisions already made** — what has the user already ruled in or out? These are the non-obvious constraints no code-reading will reveal (e.g. *"we're nuking both DBs before this lands"*).
|
|
63
|
-
4. **Boundary** —
|
|
68
|
+
4. **Boundary** — what's in scope at the edges? Name 2–5 adjacent things (files, capabilities, models, flows) and for each, mark whether this feature touches it or not. Ground every candidate in something concrete you saw in Step 2 — a file path, a table, a flow — not abstract categories. Frame the question to the user as a positive enumeration (*"which of these are in scope?"*), never as negation (*"what's excluded?"*). The Out-of-scope section is derived at draft time from the candidates the user did not mark as in-scope; do not ask the user to enumerate exclusions directly.
|
|
64
69
|
5. **Acceptance shape** — what must be observably true when this is done? 1–3 items, not exhaustive. You'll flesh them out when drafting.
|
|
65
70
|
6. **Post-merge manual steps** — anything a human has to do after the PR merges (DB operations, flag flips, smoke checks)?
|
|
66
71
|
|
|
@@ -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,9 @@ 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.
|
|
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.
|
|
235
442
|
- *"The user stated an outcome and I'm writing a mechanism"* — STOP. If the user said "swap X for Y when Z," that's what the brief says. `useSidebar()`, CSS strategies, component extraction, which file to modify — those are `/spec`'s calls, made after codebase exploration. Pre-deciding them here looks helpful but strips spec-writer's ability to weigh alternatives.
|