cc-dev-template 0.1.97 → 0.1.99
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/bin/install.js +1 -0
- package/package.json +1 -1
- package/src/agents/question-generator.md +4 -4
- package/src/scripts/ship-policy-hook.json +14 -0
- package/src/scripts/ship-policy.js +224 -0
- package/src/scripts/statusline.js +15 -1
- package/src/skills/creating-agent-skills/references/create-step-2-design.md +40 -1
- package/src/skills/creating-agent-skills/references/create-step-4-review.md +6 -0
- package/src/skills/creating-agent-skills/scripts/validate-skill.js +8 -0
- package/src/skills/ship/SKILL.md +13 -0
- package/src/skills/ship/references/step-1-intent.md +1 -1
- package/src/skills/ship/references/step-2-questions.md +4 -4
- package/src/skills/ship/references/step-3-research.md +1 -1
- package/src/skills/ship/references/step-4-design.md +1 -1
- package/src/skills/ship/references/step-5-spec.md +2 -2
- package/src/skills/ship/references/step-6-verify.md +2 -2
- package/src/skills/ship/references/step-7-tasks.md +2 -2
- package/src/skills/ship/references/step-8-implement.md +1 -1
- package/src/skills/ship/references/step-9-reflect.md +5 -1
package/bin/install.js
CHANGED
|
@@ -254,6 +254,7 @@ if (fs.existsSync(mergeSettingsPath)) {
|
|
|
254
254
|
const configs = [
|
|
255
255
|
{ file: 'task-output-guard-hook.json', name: 'TaskOutput context guard' },
|
|
256
256
|
{ file: 'statusline-config.json', name: 'Custom status line' },
|
|
257
|
+
{ file: 'ship-policy-hook.json', name: 'Ship policy enforcement' },
|
|
257
258
|
// Spinner verbs - choose one (Star Trek or Factorio)
|
|
258
259
|
{ file: 'spinner-verbs-startrek.json', name: 'Star Trek spinner verbs' }
|
|
259
260
|
// { file: 'spinner-verbs-factorio.json', name: 'Factorio spinner verbs' }
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: question-generator
|
|
3
3
|
description: Generates research questions from a feature intent document. Cannot explore the codebase — produces questions only.
|
|
4
|
-
tools: Write
|
|
4
|
+
tools: Read, Write
|
|
5
5
|
permissionMode: bypassPermissions
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
You are a question generator. You
|
|
8
|
+
You are a question generator. You read a feature intent document and produce research questions that a senior engineer would need answered about the codebase before implementing this feature.
|
|
9
9
|
|
|
10
|
-
You generate questions only. You
|
|
10
|
+
You generate questions only. You can read the intent document and write the questions file — the ship policy hook restricts you to exactly those two paths.
|
|
11
11
|
|
|
12
12
|
## Process
|
|
13
13
|
|
|
14
|
-
1.
|
|
14
|
+
1. Read the intent document at the path provided in your prompt
|
|
15
15
|
2. Think deeply about what you'd need to know to actually build this — not just what the system looks like, but how you'd hook into it
|
|
16
16
|
3. Write organized, specific questions to the output path provided in your prompt
|
|
17
17
|
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ship Policy Hook — Phase-aware enforcement for the ship skill.
|
|
5
|
+
*
|
|
6
|
+
* PreToolUse hook that checks every tool call against a policy matrix
|
|
7
|
+
* of (phase, agent_type, tool_name, target_path) rules. No-op when
|
|
8
|
+
* ship is not active (state file missing or session mismatch).
|
|
9
|
+
*
|
|
10
|
+
* State: {cwd}/.claude/ship-hook-state.json
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { readFileSync, writeFileSync, existsSync } = require('fs');
|
|
14
|
+
const { join, resolve, relative } = require('path');
|
|
15
|
+
|
|
16
|
+
// Tools that bypass all policy checks
|
|
17
|
+
const BYPASS_TOOLS = new Set([
|
|
18
|
+
'AskUserQuestion', 'TaskCreate', 'TaskUpdate', 'TaskList', 'TaskGet',
|
|
19
|
+
'TaskOutput', 'TaskStop', 'ToolSearch', 'SendMessage', 'Agent',
|
|
20
|
+
'TeamCreate', 'TeamDelete',
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
// Per-phase orchestrator write permissions (beyond state files)
|
|
24
|
+
const ORCHESTRATOR_WRITES = {
|
|
25
|
+
intent: (d) => [`${d}/intent.md`],
|
|
26
|
+
questions: () => [],
|
|
27
|
+
research: (d) => [`${d}/research.md`],
|
|
28
|
+
design: (d) => [`${d}/design.md`],
|
|
29
|
+
spec: () => [],
|
|
30
|
+
verify: () => [],
|
|
31
|
+
tasks: () => [],
|
|
32
|
+
implement: () => [],
|
|
33
|
+
complete: () => [],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Spec artifacts that spec-implementer cannot overwrite
|
|
37
|
+
const PROTECTED_ARTIFACTS = [
|
|
38
|
+
'spec.md', 'test-plan.md', 'design.md', 'intent.md',
|
|
39
|
+
'questions.md', 'research.md',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
function block(reason) { return { blocked: true, reason }; }
|
|
43
|
+
function allow() { return { blocked: false }; }
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the target path relative to cwd.
|
|
47
|
+
* Returns: string (relative path), null (outside project), or undefined (no path in input).
|
|
48
|
+
*/
|
|
49
|
+
function relPath(tool, input, cwd) {
|
|
50
|
+
let raw;
|
|
51
|
+
if (tool === 'Read' || tool === 'Write' || tool === 'Edit') raw = input.file_path;
|
|
52
|
+
else if (tool === 'Grep' || tool === 'Glob') raw = input.path;
|
|
53
|
+
if (!raw) return undefined;
|
|
54
|
+
const abs = resolve(raw);
|
|
55
|
+
if (abs !== cwd && !abs.startsWith(cwd + '/')) return null;
|
|
56
|
+
return relative(cwd, abs);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function under(p, prefix) {
|
|
60
|
+
const dir = prefix.replace(/\/$/, '');
|
|
61
|
+
return p === dir || p.startsWith(dir + '/');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── Orchestrator Policy ──
|
|
65
|
+
|
|
66
|
+
function orchestratorPolicy(tool, input, cwd, specDir, phase) {
|
|
67
|
+
if (tool === 'Bash') return block('Orchestrator cannot run Bash — delegate to a sub-agent');
|
|
68
|
+
|
|
69
|
+
if (tool === 'Read' || tool === 'Grep' || tool === 'Glob') {
|
|
70
|
+
const p = relPath(tool, input, cwd);
|
|
71
|
+
if (p === null) return allow(); // Outside project (skill files, etc.)
|
|
72
|
+
|
|
73
|
+
// Grep/Glob with no path or project root — check pattern prefix
|
|
74
|
+
if ((tool === 'Grep' || tool === 'Glob') && (p === undefined || p === '')) {
|
|
75
|
+
const pat = input.pattern || '';
|
|
76
|
+
if (/^(docs|references|\.claude)(\/|$)/.test(pat)) return allow();
|
|
77
|
+
return block('Orchestrator cannot search the full project — use a path under docs/, references/, or .claude/');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (p === undefined) return allow();
|
|
81
|
+
if (under(p, 'docs') || under(p, 'references') || under(p, '.claude')) return allow();
|
|
82
|
+
return block(`Orchestrator cannot read source code (${p}) — delegate to a research agent`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (tool === 'Write' || tool === 'Edit') {
|
|
86
|
+
const p = relPath(tool, input, cwd);
|
|
87
|
+
if (p === null || p === undefined) return allow();
|
|
88
|
+
|
|
89
|
+
// State files always writable
|
|
90
|
+
if (p === `${specDir}/state.yaml` || p === '.claude/ship-hook-state.json') return allow();
|
|
91
|
+
|
|
92
|
+
// Per-phase permissions
|
|
93
|
+
const permFn = ORCHESTRATOR_WRITES[phase];
|
|
94
|
+
if (permFn && permFn(specDir).includes(p)) return allow();
|
|
95
|
+
|
|
96
|
+
return block(`Orchestrator cannot write ${p} in ${phase} phase — delegate to a sub-agent`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return allow();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Sub-Agent Policies ──
|
|
103
|
+
|
|
104
|
+
function questionGeneratorPolicy(tool, input, cwd, specDir) {
|
|
105
|
+
const p = relPath(tool, input, cwd);
|
|
106
|
+
|
|
107
|
+
if (tool === 'Read') {
|
|
108
|
+
if (p === null) return allow();
|
|
109
|
+
if (p === `${specDir}/intent.md`) return allow();
|
|
110
|
+
return block(`question-generator can only read ${specDir}/intent.md`);
|
|
111
|
+
}
|
|
112
|
+
if (tool === 'Write') {
|
|
113
|
+
if (p === null) return allow();
|
|
114
|
+
if (p === `${specDir}/questions.md`) return allow();
|
|
115
|
+
return block(`question-generator can only write ${specDir}/questions.md`);
|
|
116
|
+
}
|
|
117
|
+
return block(`question-generator cannot use ${tool}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function objectiveResearcherPolicy(tool, input, cwd, specDir) {
|
|
121
|
+
const p = relPath(tool, input, cwd);
|
|
122
|
+
|
|
123
|
+
if (tool === 'Read' || tool === 'Grep' || tool === 'Glob') {
|
|
124
|
+
if (p === null) return allow();
|
|
125
|
+
if (p === undefined) return allow(); // Grep/Glob with no path — project-wide search
|
|
126
|
+
if (p.startsWith(`${specDir}/research`) && p.endsWith('.md')) return allow(); // Own output
|
|
127
|
+
if (under(p, 'docs')) return block('objective-researcher cannot read docs/ — research must stay objective');
|
|
128
|
+
return allow();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (tool === 'Write') {
|
|
132
|
+
if (p === null) return allow();
|
|
133
|
+
if (p !== undefined && p.startsWith(`${specDir}/research`) && p.endsWith('.md')) return allow();
|
|
134
|
+
return block(`objective-researcher can only write to ${specDir}/research-*.md`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (tool === 'Bash') return allow();
|
|
138
|
+
return block(`objective-researcher cannot use ${tool}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function specDirWriterPolicy(tool, input, cwd, specDir) {
|
|
142
|
+
if (tool === 'Read' || tool === 'Grep' || tool === 'Glob') return allow();
|
|
143
|
+
if (tool === 'Write' || tool === 'Edit') {
|
|
144
|
+
const p = relPath(tool, input, cwd);
|
|
145
|
+
if (p === null || p === undefined) return allow();
|
|
146
|
+
if (under(p, specDir)) return allow();
|
|
147
|
+
return block(`Cannot write outside ${specDir}`);
|
|
148
|
+
}
|
|
149
|
+
if (tool === 'Bash') return block('This agent cannot run Bash commands');
|
|
150
|
+
return allow();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function specImplementerPolicy(tool, input, cwd, specDir) {
|
|
154
|
+
if (tool === 'Write' || tool === 'Edit') {
|
|
155
|
+
const p = relPath(tool, input, cwd);
|
|
156
|
+
if (p === null || p === undefined) return allow();
|
|
157
|
+
for (const artifact of PROTECTED_ARTIFACTS) {
|
|
158
|
+
if (p === `${specDir}/${artifact}`) return block(`spec-implementer cannot modify ${artifact}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return allow();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function specValidatorPolicy(tool, input, cwd, specDir) {
|
|
165
|
+
if (tool === 'Write' || tool === 'Edit') {
|
|
166
|
+
const p = relPath(tool, input, cwd);
|
|
167
|
+
if (p === null || p === undefined) return allow();
|
|
168
|
+
if (under(p, `${specDir}/tasks`)) return allow(); // Review Notes in task files
|
|
169
|
+
return block('spec-validator cannot write outside task files');
|
|
170
|
+
}
|
|
171
|
+
return allow();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Main ──
|
|
175
|
+
|
|
176
|
+
const AGENT_POLICIES = {
|
|
177
|
+
'question-generator': questionGeneratorPolicy,
|
|
178
|
+
'objective-researcher': objectiveResearcherPolicy,
|
|
179
|
+
'spec-writer': specDirWriterPolicy,
|
|
180
|
+
'test-planner': specDirWriterPolicy,
|
|
181
|
+
'task-breakdown': specDirWriterPolicy,
|
|
182
|
+
'spec-implementer': specImplementerPolicy,
|
|
183
|
+
'spec-validator': specValidatorPolicy,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
function main() {
|
|
187
|
+
const input = JSON.parse(readFileSync(0, 'utf-8'));
|
|
188
|
+
const cwd = process.cwd();
|
|
189
|
+
const stateFile = join(cwd, '.claude', 'ship-hook-state.json');
|
|
190
|
+
|
|
191
|
+
if (!existsSync(stateFile)) process.exit(0);
|
|
192
|
+
|
|
193
|
+
let state;
|
|
194
|
+
try { state = JSON.parse(readFileSync(stateFile, 'utf-8')); }
|
|
195
|
+
catch { process.exit(0); }
|
|
196
|
+
|
|
197
|
+
// First-touch: inject session_id if missing
|
|
198
|
+
if (!state.session_id) {
|
|
199
|
+
state.session_id = input.session_id;
|
|
200
|
+
try { writeFileSync(stateFile, JSON.stringify(state, null, 2)); } catch {}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (state.session_id !== input.session_id) process.exit(0);
|
|
204
|
+
if (BYPASS_TOOLS.has(input.tool_name)) process.exit(0);
|
|
205
|
+
|
|
206
|
+
const caller = input.agent_type || 'orchestrator';
|
|
207
|
+
const tool = input.tool_name;
|
|
208
|
+
const toolInput = input.tool_input || {};
|
|
209
|
+
|
|
210
|
+
let result;
|
|
211
|
+
if (caller === 'orchestrator') {
|
|
212
|
+
result = orchestratorPolicy(tool, toolInput, cwd, state.spec_dir, state.phase);
|
|
213
|
+
} else {
|
|
214
|
+
const policyFn = AGENT_POLICIES[caller];
|
|
215
|
+
if (!policyFn) process.exit(0); // Unknown agent — not a ship agent
|
|
216
|
+
result = policyFn(tool, toolInput, cwd, state.spec_dir);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (result.blocked) {
|
|
220
|
+
console.log(JSON.stringify({ decision: 'block', reason: result.reason }));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
main();
|
|
@@ -560,11 +560,25 @@ function main() {
|
|
|
560
560
|
usageLines.push(makeBoxLine(usageDisplay));
|
|
561
561
|
}
|
|
562
562
|
|
|
563
|
+
// Ship phase line (if active)
|
|
564
|
+
const shipLines = [];
|
|
565
|
+
try {
|
|
566
|
+
const shipStatePath = join(data.workspace.project_dir, '.claude', 'ship-hook-state.json');
|
|
567
|
+
const shipStat = statSync(shipStatePath);
|
|
568
|
+
if (Date.now() - shipStat.mtimeMs < 300000) { // < 5 min old
|
|
569
|
+
const shipState = JSON.parse(readFileSync(shipStatePath, 'utf-8'));
|
|
570
|
+
const phase = (shipState.phase || '').toUpperCase();
|
|
571
|
+
const feature = shipState.feature || '';
|
|
572
|
+
const subPhase = shipState.sub_phase ? ` ${shipState.sub_phase}` : '';
|
|
573
|
+
shipLines.push(makeBoxLine(`SHIP: ${phase} [${feature}]${subPhase}`));
|
|
574
|
+
}
|
|
575
|
+
} catch {}
|
|
576
|
+
|
|
563
577
|
// Bottom border (add 2 to match content line width)
|
|
564
578
|
const bottomBorder = `${DIM_GREY}╚${'═'.repeat(width + 2)}╝${RESET}`;
|
|
565
579
|
|
|
566
580
|
// Combine all lines
|
|
567
|
-
const allLines = [topBorder, line0, ...branchLines, ctxLine, ...usageLines, bottomBorder];
|
|
581
|
+
const allLines = [topBorder, line0, ...shipLines, ...branchLines, ctxLine, ...usageLines, bottomBorder];
|
|
568
582
|
console.log(allLines.join('\n'));
|
|
569
583
|
} catch (error) {
|
|
570
584
|
// Log error for debugging (goes to stderr, not visible in status line)
|
|
@@ -57,6 +57,45 @@ Configuration: No special setting needed
|
|
|
57
57
|
|
|
58
58
|
Configuration: `context: fork` (optionally add `agent: Explore` or `agent: Plan` for specialized behavior)
|
|
59
59
|
|
|
60
|
+
## Design Constraints
|
|
61
|
+
|
|
62
|
+
Only relevant for skills that orchestrate sub-agents or have clear role boundaries. Skip this section for simple informational skills or single-step workflows.
|
|
63
|
+
|
|
64
|
+
### Constraint Mechanisms
|
|
65
|
+
|
|
66
|
+
From weakest to strongest:
|
|
67
|
+
|
|
68
|
+
| Mechanism | What It Does | Use When |
|
|
69
|
+
|-----------|-------------|----------|
|
|
70
|
+
| Prompt instructions | Advisory guidance in skill/agent text | Soft guidance, not security-critical |
|
|
71
|
+
| `allowed-tools` (skill frontmatter) | Auto-approves listed tools (no user prompt). Does NOT block anything. | Reducing user friction for known-safe tools |
|
|
72
|
+
| `tools` (agent frontmatter) | Grants ONLY listed tools to sub-agent. Everything else denied. | Coarse tool-level restriction (e.g., read-only agent) |
|
|
73
|
+
| PreToolUse hook (settings.json) | Script checks every tool call against policy rules. Can inspect file paths, commands, agent identity. | Path-level enforcement, per-phase rules, cross-agent policies |
|
|
74
|
+
| Inline content passing | Pass file contents in Agent prompt instead of letting agent read files. Combined with restricted `tools`, agent physically cannot access unauthorized files. | Contamination prevention, strongest isolation |
|
|
75
|
+
|
|
76
|
+
### Constraint Design Questions
|
|
77
|
+
|
|
78
|
+
Ask the user these questions when the skill involves sub-agents:
|
|
79
|
+
|
|
80
|
+
- Should the orchestrator be restricted? (e.g., blocked from source code, forced to delegate)
|
|
81
|
+
- Should sub-agents only access specific files/directories?
|
|
82
|
+
- Are there contamination concerns? (agents that should be blind to certain information)
|
|
83
|
+
- Should certain deliverables only be writable by specific agents?
|
|
84
|
+
|
|
85
|
+
### Constraint Map
|
|
86
|
+
|
|
87
|
+
If constraints are needed, produce a constraint map before designing frontmatter: which agent can do what, and which mechanism enforces each rule. This feeds into the frontmatter design and may require a hook script.
|
|
88
|
+
|
|
89
|
+
Example format:
|
|
90
|
+
```
|
|
91
|
+
Orchestrator: no Bash, no source code reads → PreToolUse hook
|
|
92
|
+
Researcher agent: tools: Read, Grep, Glob, Write → agent frontmatter
|
|
93
|
+
Writer agent: only writes to docs/output/ → PreToolUse hook (path check)
|
|
94
|
+
Reviewer agent: receives content inline, no file reads → inline content passing + tools: Write
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Reference the creating-sub-agents skill for detailed sub-agent constraint configuration (tool sets, permission modes, hook examples).
|
|
98
|
+
|
|
60
99
|
## Design the Frontmatter
|
|
61
100
|
|
|
62
101
|
Every skill has YAML frontmatter. Required and optional fields:
|
|
@@ -75,7 +114,7 @@ Every skill has YAML frontmatter. Required and optional fields:
|
|
|
75
114
|
| `argument-hint` | Placeholder shown after slash command (e.g., `[issue-number]`, `[filename]`) |
|
|
76
115
|
| `disable-model-invocation` | Set `true` to prevent Claude from auto-activating — use for side-effect workflows like deploy or commit |
|
|
77
116
|
| `user-invocable` | Set `false` to hide from the `/` menu — use for background knowledge skills |
|
|
78
|
-
| `allowed-tools` |
|
|
117
|
+
| `allowed-tools` | Pre-approve tools for auto-acceptance (user won't be prompted for these). Does NOT restrict — unlisted tools still work, they just require user approval. |
|
|
79
118
|
| `model` | Override the model used when this skill is active |
|
|
80
119
|
| `context` | Set `fork` to run in an isolated sub-agent context |
|
|
81
120
|
| `agent` | Sub-agent type when `context: fork` is set (`Explore`, `Plan`, `general-purpose`, or custom) |
|
|
@@ -49,6 +49,12 @@ Go through each file and verify:
|
|
|
49
49
|
- For informational skills: reflection section at the end of SKILL.md?
|
|
50
50
|
- For forked-context skills: "Skill Observations" section in the output format?
|
|
51
51
|
|
|
52
|
+
### Constraints
|
|
53
|
+
- If the skill spawns sub-agents: are tool grants appropriate for each agent's role?
|
|
54
|
+
- Is `allowed-tools` used correctly? (auto-approval only, not restriction)
|
|
55
|
+
- If path-level restrictions are needed: is a PreToolUse hook designed?
|
|
56
|
+
- Are constraint decisions documented in the skill (so future editors understand why)?
|
|
57
|
+
|
|
52
58
|
## Run Validation
|
|
53
59
|
|
|
54
60
|
```bash
|
|
@@ -167,6 +167,14 @@ if (!frontmatter.valid) {
|
|
|
167
167
|
null,
|
|
168
168
|
'Remove all < and > characters from frontmatter values');
|
|
169
169
|
}
|
|
170
|
+
|
|
171
|
+
// Check: allowed-tools usage awareness
|
|
172
|
+
if (frontmatter.data['allowed-tools']) {
|
|
173
|
+
addFinding('CONSTRAINT', 'info',
|
|
174
|
+
'Skill uses allowed-tools — this pre-approves tools for auto-acceptance, it does NOT restrict tool access. If restriction is intended, use the tools field on sub-agent definitions instead.',
|
|
175
|
+
null,
|
|
176
|
+
'See creating-sub-agents skill for sub-agent tool restriction patterns');
|
|
177
|
+
}
|
|
170
178
|
}
|
|
171
179
|
|
|
172
180
|
// Check 3: Second person violations
|
package/src/skills/ship/SKILL.md
CHANGED
|
@@ -44,6 +44,8 @@ Look for `docs/specs/{feature-name}/state.yaml`.
|
|
|
44
44
|
| tasks | `references/step-7-tasks.md` |
|
|
45
45
|
| implement | `references/step-8-implement.md` |
|
|
46
46
|
|
|
47
|
+
Also write/update `.claude/ship-hook-state.json` with `phase`, `feature`, and `spec_dir` from state.yaml (set `sub_phase` to `null`).
|
|
48
|
+
|
|
47
49
|
Read the step file for the current phase and follow its instructions.
|
|
48
50
|
|
|
49
51
|
**If state.yaml does not exist**, this is a new feature. Create the spec directory and an initial state.yaml:
|
|
@@ -54,4 +56,15 @@ phase: intent
|
|
|
54
56
|
dir: docs/specs/{feature-name}
|
|
55
57
|
```
|
|
56
58
|
|
|
59
|
+
Also write `.claude/ship-hook-state.json` (enables policy enforcement for this session):
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"phase": "intent",
|
|
64
|
+
"feature": "{feature-name}",
|
|
65
|
+
"spec_dir": "docs/specs/{feature-name}",
|
|
66
|
+
"sub_phase": null
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
57
70
|
Then read `references/step-1-intent.md` to begin.
|
|
@@ -45,6 +45,6 @@ Present the intent document to the user for confirmation. Adjust if they have co
|
|
|
45
45
|
|
|
46
46
|
## Task 3: Proceed
|
|
47
47
|
|
|
48
|
-
Update `{spec_dir}/state.yaml` — set `phase: questions`.
|
|
48
|
+
Update `{spec_dir}/state.yaml` — set `phase: questions`. Update `.claude/ship-hook-state.json` — set `phase` to `"questions"`, `sub_phase` to `null`.
|
|
49
49
|
|
|
50
50
|
Use the Read tool on `references/step-2-questions.md` to generate research questions from the intent.
|
|
@@ -14,15 +14,15 @@ Create these tasks and work through them in order:
|
|
|
14
14
|
|
|
15
15
|
## Task 1: Generate Questions
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Spawn the question-generator sub-agent. Tell it the spec directory — it will read the intent document directly.
|
|
18
18
|
|
|
19
19
|
```
|
|
20
20
|
Agent tool:
|
|
21
21
|
subagent_type: "question-generator"
|
|
22
|
-
prompt: "
|
|
22
|
+
prompt: "Read the intent document at {spec_dir}/intent.md and write research questions to {spec_dir}/questions.md."
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
The question-generator has
|
|
25
|
+
The question-generator has `tools: Read, Write` with hook-enforced path restrictions — it can only read `{spec_dir}/intent.md` and write to `{spec_dir}/questions.md`.
|
|
26
26
|
|
|
27
27
|
## Task 2: Review Questions
|
|
28
28
|
|
|
@@ -36,6 +36,6 @@ Update `questions.md` based on user feedback. The user may add questions about p
|
|
|
36
36
|
|
|
37
37
|
## Task 3: Proceed
|
|
38
38
|
|
|
39
|
-
Update `{spec_dir}/state.yaml` — set `phase: research`.
|
|
39
|
+
Update `{spec_dir}/state.yaml` — set `phase: research`. Update `.claude/ship-hook-state.json` — set `phase` to `"research"`, `sub_phase` to `null`.
|
|
40
40
|
|
|
41
41
|
Use the Read tool on `references/step-3-research.md` to begin objective codebase research.
|
|
@@ -51,6 +51,6 @@ If the research is thin or missing critical areas, spawn the objective-researche
|
|
|
51
51
|
|
|
52
52
|
## Task 4: Proceed
|
|
53
53
|
|
|
54
|
-
Update `{spec_dir}/state.yaml` — set `phase: design`.
|
|
54
|
+
Update `{spec_dir}/state.yaml` — set `phase: design`. Update `.claude/ship-hook-state.json` — set `phase` to `"design"`, `sub_phase` to `null`.
|
|
55
55
|
|
|
56
56
|
Use the Read tool on `references/step-4-design.md` to begin the design discussion with the user.
|
|
@@ -63,6 +63,6 @@ Present to the user for confirmation.
|
|
|
63
63
|
|
|
64
64
|
## Task 4: Proceed
|
|
65
65
|
|
|
66
|
-
Update `{spec_dir}/state.yaml` — set `phase: spec`.
|
|
66
|
+
Update `{spec_dir}/state.yaml` — set `phase: spec`. Update `.claude/ship-hook-state.json` — set `phase` to `"spec"`, `sub_phase` to `null`.
|
|
67
67
|
|
|
68
68
|
Use the Read tool on `references/step-5-spec.md` to generate the implementation specification.
|
|
@@ -38,7 +38,7 @@ Agent tool:
|
|
|
38
38
|
|
|
39
39
|
## Task 3: Review Loop
|
|
40
40
|
|
|
41
|
-
Spawn a FRESH instance of spec-writer in review mode. At least one review is mandatory.
|
|
41
|
+
Spawn a FRESH instance of spec-writer in review mode. At least one review is mandatory. Before each review cycle, update `.claude/ship-hook-state.json` `sub_phase` to `"review-cycle-N"` (N = cycle number, starting at 1).
|
|
42
42
|
|
|
43
43
|
```
|
|
44
44
|
Agent tool:
|
|
@@ -71,6 +71,6 @@ Revise based on user feedback. If changes are substantial, re-run the review loo
|
|
|
71
71
|
|
|
72
72
|
## Task 5: Proceed
|
|
73
73
|
|
|
74
|
-
Update `{spec_dir}/state.yaml` — set `phase: verify`.
|
|
74
|
+
Update `{spec_dir}/state.yaml` — set `phase: verify`. Update `.claude/ship-hook-state.json` — set `phase` to `"verify"`, `sub_phase` to `null`.
|
|
75
75
|
|
|
76
76
|
Use the Read tool on `references/step-6-verify.md` to plan verification for the spec.
|
|
@@ -27,7 +27,7 @@ Agent tool:
|
|
|
27
27
|
|
|
28
28
|
## Task 2: Review Loop
|
|
29
29
|
|
|
30
|
-
Spawn a FRESH instance of test-planner in review mode. At least one review is mandatory.
|
|
30
|
+
Spawn a FRESH instance of test-planner in review mode. At least one review is mandatory. Before each review cycle, update `.claude/ship-hook-state.json` `sub_phase` to `"review-cycle-N"` (N = cycle number, starting at 1).
|
|
31
31
|
|
|
32
32
|
```
|
|
33
33
|
Agent tool:
|
|
@@ -59,6 +59,6 @@ Ask the user if the verification strategy is complete. Revise based on feedback.
|
|
|
59
59
|
|
|
60
60
|
## Task 4: Proceed
|
|
61
61
|
|
|
62
|
-
Update `{spec_dir}/state.yaml` — set `phase: tasks`.
|
|
62
|
+
Update `{spec_dir}/state.yaml` — set `phase: tasks`. Update `.claude/ship-hook-state.json` — set `phase` to `"tasks"`, `sub_phase` to `null`.
|
|
63
63
|
|
|
64
64
|
Use the Read tool on `references/step-7-tasks.md` to break the spec into implementation tasks.
|
|
@@ -25,7 +25,7 @@ Agent tool:
|
|
|
25
25
|
|
|
26
26
|
## Task 2: Review Loop
|
|
27
27
|
|
|
28
|
-
Spawn a FRESH instance of task-breakdown in review mode:
|
|
28
|
+
Spawn a FRESH instance of task-breakdown in review mode. Before each review cycle, update `.claude/ship-hook-state.json` `sub_phase` to `"review-cycle-N"` (N = cycle number, starting at 1):
|
|
29
29
|
|
|
30
30
|
```
|
|
31
31
|
Agent tool:
|
|
@@ -55,6 +55,6 @@ Revise based on user feedback. If changes are substantial, re-run the review loo
|
|
|
55
55
|
|
|
56
56
|
## Task 4: Proceed
|
|
57
57
|
|
|
58
|
-
Update `{spec_dir}/state.yaml` — set `phase: implement`.
|
|
58
|
+
Update `{spec_dir}/state.yaml` — set `phase: implement`. Update `.claude/ship-hook-state.json` — set `phase` to `"implement"`, `sub_phase` to `null`.
|
|
59
59
|
|
|
60
60
|
Use the Read tool on `references/step-8-implement.md` to begin implementation.
|
|
@@ -43,7 +43,7 @@ Run independent tasks (no dependency between them) in parallel when possible. Al
|
|
|
43
43
|
|
|
44
44
|
## Step 3: Finalize
|
|
45
45
|
|
|
46
|
-
Update `{spec_dir}/state.yaml` — set `phase: complete`.
|
|
46
|
+
Update `{spec_dir}/state.yaml` — set `phase: complete`. Update `.claude/ship-hook-state.json` — set `phase` to `"complete"`, `sub_phase` to `null`.
|
|
47
47
|
|
|
48
48
|
Present a summary to the user:
|
|
49
49
|
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# Reflect
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Cleanup
|
|
4
|
+
|
|
5
|
+
Delete `.claude/ship-hook-state.json` — policy enforcement is no longer needed for this feature.
|
|
4
6
|
|
|
5
7
|
## Self-Assessment
|
|
6
8
|
|
|
9
|
+
Review how this workflow performed and identify improvements.
|
|
10
|
+
|
|
7
11
|
Consider each phase:
|
|
8
12
|
|
|
9
13
|
1. **Intent capture**: Did the intent document accurately capture what the user wanted? Did the spec drift from the original intent?
|