claude-dev-env 1.65.1 → 1.66.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/agents/plan-packet-validator.md +34 -0
- package/audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md +6 -0
- package/commands/plan.md +6 -52
- package/hooks/blocking/code_rules_enforcer.py +2 -0
- package/hooks/blocking/code_rules_test_assertions.py +123 -1
- package/hooks/blocking/open_questions_in_plans_blocker.py +8 -1
- package/hooks/blocking/test_code_rules_enforcer_split_test_assertions.py +90 -0
- package/hooks/blocking/test_open_questions_in_plans_blocker.py +43 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
- package/hooks/hooks_constants/open_questions_in_plans_blocker_constants.py +4 -0
- package/hooks/hooks_constants/test_open_questions_in_plans_blocker_constants.py +13 -1
- package/package.json +1 -1
- package/skills/anthropic-plan/SKILL.md +46 -85
- package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/__init__.py +0 -0
- package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/validate_packet_constants.py +33 -0
- package/skills/anthropic-plan/scripts/test_validate_packet.py +405 -0
- package/skills/anthropic-plan/scripts/validate_packet.py +397 -0
- package/skills/anthropic-plan/templates/README.md +20 -0
- package/skills/anthropic-plan/templates/build-prompt.md +9 -0
- package/skills/anthropic-plan/templates/source-map.md +5 -0
- package/skills/anthropic-plan/test_skill_contract.py +53 -0
- package/skills/anthropic-plan/workflow/plan-packet.contract.test.mjs +79 -0
- package/skills/anthropic-plan/workflow/plan-packet.mjs +299 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'plan-packet',
|
|
3
|
+
description: 'Create a repo-local implementation planning packet under docs/plans/<slug>, validate it deterministically, verify it with a fresh validator agent, repair findings, and stop before implementation.',
|
|
4
|
+
whenToUse: 'Launched by the anthropic-plan skill for non-trivial implementation planning, scoping, design, or plan-first requests.',
|
|
5
|
+
phases: [
|
|
6
|
+
{ title: 'Discover', detail: 'Resolve repo root, read instructions, inspect matching source files, tests, configs, docs, skills, hooks, agents, and workflows.' },
|
|
7
|
+
{ title: 'Write packet', detail: 'Create the required docs/plans/<slug>/ tree with a thin README hub and detailed second-level docs.' },
|
|
8
|
+
{ title: 'Validate', detail: 'Run scripts/validate_packet.py, spawn plan-packet-validator in fresh context, and repair findings up to the cap.' },
|
|
9
|
+
{ title: 'Approval', detail: 'Return the packet path and validation verdict, then stop before implementation work.' },
|
|
10
|
+
],
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function validationSchema() {
|
|
14
|
+
return {
|
|
15
|
+
type: 'object',
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
properties: {
|
|
18
|
+
allPassed: { type: 'boolean' },
|
|
19
|
+
findings: {
|
|
20
|
+
type: 'array',
|
|
21
|
+
items: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
additionalProperties: false,
|
|
24
|
+
properties: {
|
|
25
|
+
file: { type: 'string' },
|
|
26
|
+
check: { type: 'string' },
|
|
27
|
+
detail: { type: 'string' },
|
|
28
|
+
},
|
|
29
|
+
required: ['file', 'check', 'detail'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
summary: { type: 'string' },
|
|
33
|
+
},
|
|
34
|
+
required: ['allPassed', 'findings', 'summary'],
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function packetWriteSchema() {
|
|
39
|
+
return {
|
|
40
|
+
type: 'object',
|
|
41
|
+
additionalProperties: false,
|
|
42
|
+
properties: {
|
|
43
|
+
packetPath: { type: 'string' },
|
|
44
|
+
slug: { type: 'string' },
|
|
45
|
+
filesWritten: { type: 'array', items: { type: 'string' } },
|
|
46
|
+
summary: { type: 'string' },
|
|
47
|
+
},
|
|
48
|
+
required: ['packetPath', 'slug', 'filesWritten', 'summary'],
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function deterministicSchema() {
|
|
53
|
+
return {
|
|
54
|
+
type: 'object',
|
|
55
|
+
additionalProperties: false,
|
|
56
|
+
properties: {
|
|
57
|
+
passed: { type: 'boolean' },
|
|
58
|
+
stdout: { type: 'string' },
|
|
59
|
+
stderr: { type: 'string' },
|
|
60
|
+
findings: { type: 'array', items: { type: 'string' } },
|
|
61
|
+
},
|
|
62
|
+
required: ['passed', 'stdout', 'stderr', 'findings'],
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function repairSchema() {
|
|
67
|
+
return {
|
|
68
|
+
type: 'object',
|
|
69
|
+
additionalProperties: false,
|
|
70
|
+
properties: {
|
|
71
|
+
repaired: { type: 'boolean' },
|
|
72
|
+
summary: { type: 'string' },
|
|
73
|
+
},
|
|
74
|
+
required: ['repaired', 'summary'],
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function normalizeRunInput(rawInput) {
|
|
79
|
+
if (rawInput && typeof rawInput === 'object') return rawInput
|
|
80
|
+
if (typeof rawInput !== 'string' || rawInput.trim() === '') return {}
|
|
81
|
+
try {
|
|
82
|
+
const parsedInput = JSON.parse(rawInput)
|
|
83
|
+
return parsedInput && typeof parsedInput === 'object' ? parsedInput : {}
|
|
84
|
+
} catch {
|
|
85
|
+
return { task: rawInput }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function slugFromTask(taskText) {
|
|
90
|
+
const words = String(taskText || 'implementation-plan')
|
|
91
|
+
.toLowerCase()
|
|
92
|
+
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
93
|
+
.split(/\s+/)
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.filter((eachWord) => !['the', 'and', 'for', 'with', 'this', 'that'].includes(eachWord))
|
|
96
|
+
.slice(0, 4)
|
|
97
|
+
return words.length ? words.join('-') : 'implementation-plan'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function buildPacketPath(runInput) {
|
|
101
|
+
const cwd = runInput.cwd || runInput.repoRoot || '.'
|
|
102
|
+
const slug = runInput.slug || slugFromTask(runInput.task || runInput.prompt || runInput.arguments)
|
|
103
|
+
return `${cwd.replace(/[\\/]$/, '')}/docs/plans/${slug}`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function requiredPacketTree() {
|
|
107
|
+
return [
|
|
108
|
+
'README.md',
|
|
109
|
+
'packet.json',
|
|
110
|
+
'context/user-request.md',
|
|
111
|
+
'context/source-map.md',
|
|
112
|
+
'context/current-state.md',
|
|
113
|
+
'context/existing-patterns.md',
|
|
114
|
+
'context/constraints.md',
|
|
115
|
+
'context/glossary.md',
|
|
116
|
+
'spec/scope.md',
|
|
117
|
+
'spec/behavior.md',
|
|
118
|
+
'spec/interfaces.md',
|
|
119
|
+
'spec/data-flow.md',
|
|
120
|
+
'spec/failure-modes.md',
|
|
121
|
+
'spec/acceptance.md',
|
|
122
|
+
'implementation/strategy.md',
|
|
123
|
+
'implementation/steps.md',
|
|
124
|
+
'implementation/tdd-plan.md',
|
|
125
|
+
'implementation/file-plan.md',
|
|
126
|
+
'implementation/refactor-checkpoints.md',
|
|
127
|
+
'validation/validator-report.md',
|
|
128
|
+
'validation/deterministic-checks.md',
|
|
129
|
+
'validation/unresolved-risks.md',
|
|
130
|
+
'handoff/build-prompt.md',
|
|
131
|
+
'handoff/review-prompt.md',
|
|
132
|
+
'handoff/verification-commands.md',
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function packetContractText() {
|
|
137
|
+
return (
|
|
138
|
+
`Create this exact packet tree under docs/plans/<slug>/:\n${requiredPacketTree().map((eachPath) => `- ${eachPath}`).join('\n')}\n\n` +
|
|
139
|
+
`README.md stays a thin hub. First-level folders group purpose. Second-level files carry real detail. Do not add deeper nesting unless more than twelve source files or more than three subsystems are found; then add context/subsystems/<name>.md.\n\n` +
|
|
140
|
+
`Every material claim must be source-backed in context/source-map.md, user-confirmed in context/user-request.md, or listed as an assumption in packet.json. Do not write an Open Questions section. Resolve discoverable unknowns by reading/searching. Ask the user only for product choices that cannot be derived.\n\n` +
|
|
141
|
+
`The packet must stop before implementation. The build prompt must stand alone for a blind build agent and say to use only this packet.`
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function discoveryPrompt(runInput, packetPath) {
|
|
146
|
+
return (
|
|
147
|
+
`Plan packet discovery for: ${runInput.task || runInput.prompt || runInput.arguments || 'the current user request'}\n\n` +
|
|
148
|
+
`Target packet path: ${packetPath}\n\n` +
|
|
149
|
+
`Collect context before writing:\n` +
|
|
150
|
+
`1. Resolve the repo root and current working directory.\n` +
|
|
151
|
+
`2. Read project instructions in priority order: AGENTS.md or CLAUDE.md, nearest .claude rules, relevant skill docs, package manifests, tool manifests.\n` +
|
|
152
|
+
`3. Search for user terms and likely entrypoints: commands, hooks, agents, skills, configs, schemas, tests, docs, scripts, workflows.\n` +
|
|
153
|
+
`4. Build a source inventory with production files, tests, configs/constants, docs, and workflow scripts.\n` +
|
|
154
|
+
`5. Extract exact facts for source-map.md: path, relevant symbol or section, observed behavior, and plan implication.\n\n` +
|
|
155
|
+
`Return a concise discovery summary. Do not edit files.`
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function writePacketPrompt(runInput, packetPath, discoverySummary) {
|
|
160
|
+
return (
|
|
161
|
+
`Write the plan packet for: ${runInput.task || runInput.prompt || runInput.arguments || 'the current user request'}\n\n` +
|
|
162
|
+
`Packet path: ${packetPath}\n\n` +
|
|
163
|
+
`Discovery summary:\n${discoverySummary}\n\n` +
|
|
164
|
+
`${packetContractText()}\n\n` +
|
|
165
|
+
`Use the templates in the anthropic-plan skill if helpful. Write docs only. Do not edit source code. Do not run implementation commands. ` +
|
|
166
|
+
`After writing, ensure packet.json includes schemaVersion 1, slug, repoRoot, packetPath, sourceFiles, assumptions, and validator fields.`
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function deterministicValidationPrompt(packetPath) {
|
|
171
|
+
return (
|
|
172
|
+
`Run the deterministic packet validator exactly:\n` +
|
|
173
|
+
`python "$HOME/.claude/skills/anthropic-plan/scripts/validate_packet.py" "${packetPath}"\n\n` +
|
|
174
|
+
`Return passed=true only when the command exits 0. Put stdout, stderr, and each stderr line as findings. Do not edit files.`
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function semanticValidationPrompt(packetPath) {
|
|
179
|
+
return (
|
|
180
|
+
`Validate the plan packet at ${packetPath}. Re-read the packet and the source files it cites. ` +
|
|
181
|
+
`Every material claim must be source-backed, user-confirmed, or an explicit assumption. ` +
|
|
182
|
+
`Check that referenced paths exist or are clearly proposed as new, source facts match actual files, implementation steps are enough for a blind build agent, the TDD sequence is real, scope matches the user request, no commands/APIs/schemas/conventions are invented, and acceptance criteria prove the behavior end to end. ` +
|
|
183
|
+
`Return allPassed=true only when the packet is accurate and complete. Do not edit files.`
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function repairPrompt(packetPath, deterministicValidation, semanticValidation) {
|
|
188
|
+
return (
|
|
189
|
+
`Repair only the plan packet at ${packetPath}. Do not edit source code.\n\n` +
|
|
190
|
+
`Deterministic validation findings:\n${JSON.stringify(deterministicValidation.findings || [])}\n\n` +
|
|
191
|
+
`Semantic validation findings:\n${JSON.stringify(semanticValidation.findings || [])}\n\n` +
|
|
192
|
+
`Make the packet pass by correcting documentation, adding missing source grounding, removing placeholders, strengthening TDD steps, and updating validation/validator-report.md.`
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function discoverContext(runInput, packetPath) {
|
|
197
|
+
return agent(discoveryPrompt(runInput, packetPath), {
|
|
198
|
+
label: `${meta.name}-discover`,
|
|
199
|
+
phase: 'Discover',
|
|
200
|
+
agentType: 'general-purpose',
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function writePacket(runInput, packetPath, discoverySummary) {
|
|
205
|
+
return agent(writePacketPrompt(runInput, packetPath, discoverySummary), {
|
|
206
|
+
label: `${meta.name}-write`,
|
|
207
|
+
phase: 'Write packet',
|
|
208
|
+
schema: packetWriteSchema(),
|
|
209
|
+
agentType: 'docs-agent',
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function runDeterministicValidation(packetPath) {
|
|
214
|
+
return agent(deterministicValidationPrompt(packetPath), {
|
|
215
|
+
label: `${meta.name}-deterministic-validation`,
|
|
216
|
+
phase: 'Validate',
|
|
217
|
+
schema: deterministicSchema(),
|
|
218
|
+
agentType: 'general-purpose',
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function runSemanticValidator(packetPath) {
|
|
223
|
+
const prompt =
|
|
224
|
+
`${semanticValidationPrompt(packetPath)}\n\n` +
|
|
225
|
+
`Confirm the packet is source-backed and complete enough for a blind build agent.`
|
|
226
|
+
return agent(prompt, {
|
|
227
|
+
label: `${meta.name}-semantic-validator`,
|
|
228
|
+
phase: 'Validate',
|
|
229
|
+
schema: validationSchema(),
|
|
230
|
+
agentType: 'plan-packet-validator',
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function repairPacket(packetPath, deterministicValidation, semanticValidation) {
|
|
235
|
+
return agent(repairPrompt(packetPath, deterministicValidation, semanticValidation), {
|
|
236
|
+
label: `${meta.name}-repair`,
|
|
237
|
+
phase: 'Validate',
|
|
238
|
+
schema: repairSchema(),
|
|
239
|
+
agentType: 'docs-agent',
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function runPlanPacketWorkflow(rawInput) {
|
|
244
|
+
const runInput = normalizeRunInput(rawInput)
|
|
245
|
+
const policy = { maxRepairLoops: 3 }
|
|
246
|
+
const packetPath = buildPacketPath(runInput)
|
|
247
|
+
let repairLoops = 0
|
|
248
|
+
let packetWrite = null
|
|
249
|
+
let deterministicValidation = null
|
|
250
|
+
let semanticValidation = null
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
const discoverySummary = await discoverContext(runInput, packetPath)
|
|
254
|
+
packetWrite = await writePacket(runInput, packetPath, discoverySummary)
|
|
255
|
+
deterministicValidation = await runDeterministicValidation(packetPath)
|
|
256
|
+
semanticValidation = await runSemanticValidator(packetPath)
|
|
257
|
+
const hasCleanValidation = () =>
|
|
258
|
+
deterministicValidation?.passed === true && semanticValidation && semanticValidation.allPassed === true
|
|
259
|
+
|
|
260
|
+
while (!hasCleanValidation() && repairLoops < policy.maxRepairLoops) {
|
|
261
|
+
repairLoops += 1
|
|
262
|
+
await repairPacket(packetPath, deterministicValidation, semanticValidation)
|
|
263
|
+
deterministicValidation = await runDeterministicValidation(packetPath)
|
|
264
|
+
semanticValidation = await runSemanticValidator(packetPath)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const passed = hasCleanValidation()
|
|
268
|
+
return {
|
|
269
|
+
packetPath: packetWrite?.packetPath || packetPath,
|
|
270
|
+
slug: packetWrite?.slug || slugFromTask(runInput.task || runInput.prompt || runInput.arguments),
|
|
271
|
+
validationPassed: passed,
|
|
272
|
+
repairLoops,
|
|
273
|
+
deterministicFindings: deterministicValidation?.findings || [],
|
|
274
|
+
semanticFindings: semanticValidation?.findings || [],
|
|
275
|
+
implementationStarted: false,
|
|
276
|
+
approvalRequired: true,
|
|
277
|
+
}
|
|
278
|
+
} catch (workflowError) {
|
|
279
|
+
return {
|
|
280
|
+
packetPath,
|
|
281
|
+
slug: packetWrite?.slug || slugFromTask(runInput.task || runInput.prompt || runInput.arguments),
|
|
282
|
+
validationPassed: false,
|
|
283
|
+
repairLoops,
|
|
284
|
+
deterministicFindings: deterministicValidation?.findings || [],
|
|
285
|
+
semanticFindings: [
|
|
286
|
+
...(semanticValidation?.findings || []),
|
|
287
|
+
{
|
|
288
|
+
file: 'workflow/plan-packet.mjs',
|
|
289
|
+
check: 'workflow phase error',
|
|
290
|
+
detail: String(workflowError?.message || workflowError),
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
implementationStarted: false,
|
|
294
|
+
approvalRequired: true,
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return await runPlanPacketWorkflow(input)
|