claude-all-hands 1.0.1 → 1.0.2
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/agents/code-simplifier.md +52 -0
- package/.claude/agents/curator.md +189 -245
- package/.claude/agents/documentor.md +147 -0
- package/.claude/agents/planner.md +123 -166
- package/.claude/agents/researcher.md +58 -41
- package/.claude/agents/surveyor.md +81 -0
- package/.claude/agents/worker.md +74 -0
- package/.claude/commands/audit-docs.md +94 -0
- package/.claude/commands/continue.md +120 -0
- package/.claude/commands/create-docs.md +100 -0
- package/.claude/commands/create-skill.md +107 -0
- package/.claude/commands/create-specialist.md +111 -0
- package/.claude/commands/curator-audit.md +4 -0
- package/.claude/commands/debug.md +183 -0
- package/.claude/commands/plan.md +199 -102
- package/.claude/commands/validate.md +11 -0
- package/.claude/commands/whats-next.md +106 -134
- package/.claude/envoy/envoy +11 -14
- package/.claude/envoy/package-lock.json +1388 -0
- package/.claude/envoy/package.json +29 -0
- package/.claude/envoy/src/cli.ts +126 -0
- package/.claude/envoy/src/commands/base.ts +216 -0
- package/.claude/envoy/src/commands/gemini.ts +999 -0
- package/.claude/envoy/src/commands/git.ts +639 -0
- package/.claude/envoy/src/commands/index.ts +73 -0
- package/.claude/envoy/src/commands/knowledge.ts +187 -0
- package/.claude/envoy/src/commands/perplexity.ts +129 -0
- package/.claude/envoy/src/commands/plan/core.ts +134 -0
- package/.claude/envoy/src/commands/plan/findings.ts +446 -0
- package/.claude/envoy/src/commands/plan/gates.ts +672 -0
- package/.claude/envoy/src/commands/plan/index.ts +135 -0
- package/.claude/envoy/src/commands/plan/lifecycle.ts +648 -0
- package/.claude/envoy/src/commands/plan/plan-file.ts +138 -0
- package/.claude/envoy/src/commands/plan/prompts.ts +285 -0
- package/.claude/envoy/src/commands/plan/protocols.ts +166 -0
- package/.claude/envoy/src/commands/repomix.ts +99 -0
- package/.claude/envoy/src/commands/tavily.ts +220 -0
- package/.claude/envoy/src/commands/xai.ts +168 -0
- package/.claude/envoy/src/lib/design.ts +41 -0
- package/.claude/envoy/src/lib/feedback-schemas.ts +154 -0
- package/.claude/envoy/src/lib/findings.ts +215 -0
- package/.claude/envoy/src/lib/gates.ts +572 -0
- package/.claude/envoy/src/lib/git.ts +132 -0
- package/.claude/envoy/src/lib/index.ts +188 -0
- package/.claude/envoy/src/lib/knowledge.ts +594 -0
- package/.claude/envoy/src/lib/markdown.ts +75 -0
- package/.claude/envoy/src/lib/observability.ts +262 -0
- package/.claude/envoy/src/lib/paths.ts +130 -0
- package/.claude/envoy/src/lib/plan-io.ts +117 -0
- package/.claude/envoy/src/lib/prompts.ts +231 -0
- package/.claude/envoy/src/lib/protocols.ts +314 -0
- package/.claude/envoy/src/lib/repomix.ts +133 -0
- package/.claude/envoy/src/lib/retry.ts +138 -0
- package/.claude/envoy/src/lib/watcher.ts +167 -0
- package/.claude/envoy/tsconfig.json +21 -0
- package/.claude/hooks/scripts/scan_agents.py +62 -0
- package/.claude/hooks/scripts/scan_commands.py +50 -0
- package/.claude/hooks/scripts/scan_skills.py +46 -70
- package/.claude/hooks/scripts/validate_artifacts.py +128 -0
- package/.claude/hooks/startup.sh +26 -24
- package/.claude/protocols/bug-discovery.yaml +55 -0
- package/.claude/protocols/debugging.yaml +51 -0
- package/.claude/protocols/discovery.yaml +53 -0
- package/.claude/protocols/implementation.yaml +84 -0
- package/.claude/settings.json +37 -97
- package/.claude/skills/brainstorming/SKILL.md +54 -0
- package/.claude/skills/commands-development/SKILL.md +630 -0
- package/.claude/skills/commands-development/references/arguments.md +252 -0
- package/.claude/skills/commands-development/references/patterns.md +796 -0
- package/.claude/skills/commands-development/references/tool-restrictions.md +376 -0
- package/.claude/skills/discovery-mode/SKILL.md +108 -0
- package/.claude/skills/hooks-development/SKILL.md +332 -0
- package/.claude/skills/hooks-development/references/command-vs-prompt.md +269 -0
- package/.claude/skills/hooks-development/references/examples.md +658 -0
- package/.claude/skills/hooks-development/references/hook-types.md +463 -0
- package/.claude/skills/hooks-development/references/input-output-schemas.md +469 -0
- package/.claude/skills/hooks-development/references/matchers.md +470 -0
- package/.claude/skills/hooks-development/references/troubleshooting.md +587 -0
- package/.claude/skills/implementation-mode/SKILL.md +171 -0
- package/.claude/skills/research-tools/SKILL.md +35 -33
- package/.claude/skills/skills-development/SKILL.md +192 -0
- package/.claude/skills/skills-development/references/api-security.md +226 -0
- package/.claude/skills/skills-development/references/be-clear-and-direct.md +531 -0
- package/.claude/skills/skills-development/references/common-patterns.md +595 -0
- package/.claude/skills/skills-development/references/core-principles.md +437 -0
- package/.claude/skills/skills-development/references/executable-code.md +175 -0
- package/.claude/skills/skills-development/references/iteration-and-testing.md +474 -0
- package/.claude/skills/skills-development/references/recommended-structure.md +168 -0
- package/.claude/skills/skills-development/references/skill-structure.md +372 -0
- package/.claude/skills/skills-development/references/use-xml-tags.md +466 -0
- package/.claude/skills/skills-development/references/using-scripts.md +113 -0
- package/.claude/skills/skills-development/references/using-templates.md +112 -0
- package/.claude/skills/skills-development/references/workflows-and-validation.md +510 -0
- package/.claude/skills/skills-development/templates/router-skill.md +73 -0
- package/.claude/skills/skills-development/templates/simple-skill.md +33 -0
- package/.claude/skills/skills-development/workflows/add-reference.md +96 -0
- package/.claude/skills/skills-development/workflows/add-script.md +93 -0
- package/.claude/skills/skills-development/workflows/add-template.md +74 -0
- package/.claude/skills/skills-development/workflows/add-workflow.md +120 -0
- package/.claude/skills/skills-development/workflows/audit-skill.md +138 -0
- package/.claude/skills/skills-development/workflows/create-domain-expertise-skill.md +605 -0
- package/.claude/skills/skills-development/workflows/create-new-skill.md +191 -0
- package/.claude/skills/skills-development/workflows/get-guidance.md +121 -0
- package/.claude/skills/skills-development/workflows/upgrade-to-router.md +161 -0
- package/.claude/skills/skills-development/workflows/verify-skill.md +204 -0
- package/.claude/skills/subagents-development/SKILL.md +325 -0
- package/.claude/skills/subagents-development/references/context-management.md +567 -0
- package/.claude/skills/subagents-development/references/debugging-agents.md +714 -0
- package/.claude/skills/subagents-development/references/error-handling-and-recovery.md +502 -0
- package/.claude/skills/subagents-development/references/evaluation-and-testing.md +374 -0
- package/.claude/skills/subagents-development/references/orchestration-patterns.md +591 -0
- package/.claude/skills/subagents-development/references/subagents.md +508 -0
- package/.claude/skills/subagents-development/references/writing-subagent-prompts.md +517 -0
- package/.claude/statusline.sh +24 -0
- package/bin/cli.js +110 -72
- package/package.json +1 -1
- package/.claude/agents/explorer.md +0 -62
- package/.claude/agents/parallel-worker.md +0 -121
- package/.claude/commands/curation-fix.md +0 -92
- package/.claude/commands/new-branch.md +0 -36
- package/.claude/commands/parallel-discovery.md +0 -69
- package/.claude/commands/parallel-orchestration.md +0 -99
- package/.claude/commands/plan-checkpoint.md +0 -37
- package/.claude/envoy/commands/__init__.py +0 -1
- package/.claude/envoy/commands/base.py +0 -95
- package/.claude/envoy/commands/parallel.py +0 -439
- package/.claude/envoy/commands/perplexity.py +0 -86
- package/.claude/envoy/commands/plans.py +0 -451
- package/.claude/envoy/commands/tavily.py +0 -156
- package/.claude/envoy/commands/vertex.py +0 -358
- package/.claude/envoy/commands/xai.py +0 -124
- package/.claude/envoy/envoy.py +0 -122
- package/.claude/envoy/pyrightconfig.json +0 -4
- package/.claude/envoy/requirements.txt +0 -2
- package/.claude/hooks/capture-queries.sh +0 -3
- package/.claude/hooks/scripts/enforce_planning.py +0 -118
- package/.claude/hooks/scripts/enforce_rg.py +0 -34
- package/.claude/hooks/scripts/validate_skill.py +0 -81
- package/.claude/skills/claude-envoy-curation/SKILL.md +0 -162
- package/.claude/skills/claude-envoy-usage/SKILL.md +0 -46
- package/.claude/skills/command-development/SKILL.md +0 -206
- package/.claude/skills/command-development/examples/simple-commands.md +0 -212
- package/.claude/skills/command-development/references/frontmatter-reference.md +0 -221
- package/.claude/skills/hook-development/SKILL.md +0 -127
- package/.claude/skills/hook-development/examples/command-hooks.md +0 -301
- package/.claude/skills/hook-development/examples/prompt-hooks.md +0 -114
- package/.claude/skills/hook-development/references/event-reference.md +0 -226
- package/.claude/skills/repomix-extraction/SKILL.md +0 -91
- package/.claude/skills/skill-development/SKILL.md +0 -168
- package/.claude/skills/skill-development/examples/complete-skill-examples.md +0 -281
- package/.claude/skills/skill-development/references/progressive-disclosure.md +0 -141
- package/.claude/skills/skill-development/references/writing-style.md +0 -180
- package/.claude/skills/skill-development/scripts/validate-skill.sh +0 -144
- package/.claude/skills/specialist-builder/SKILL.md +0 -327
- package/.claude/skills/specialist-builder/docs/agent-catalog.md +0 -28
- package/.claude/skills/specialist-builder/examples/complete-agent-examples.md +0 -206
- package/.claude/skills/specialist-builder/references/system-prompt-patterns.md +0 -281
- package/.claude/skills/specialist-builder/references/triggering-examples.md +0 -162
- package/.claude/skills/specialist-builder/scripts/validate-agent.sh +0 -137
- /package/.claude/{envoy/claude-envoy.py → skills/claude-envoy-patterns/SKILL.md} +0 -0
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt lifecycle commands: next, start-prompt, record-implementation, complete-prompt, etc.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { execSync, spawnSync } from "child_process";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { GoogleGenAI } from "@google/genai";
|
|
8
|
+
import type { PromptFrontMatter } from "../../lib/index.js";
|
|
9
|
+
import {
|
|
10
|
+
getBaseBranch,
|
|
11
|
+
getBranch,
|
|
12
|
+
getDiff,
|
|
13
|
+
getPromptId,
|
|
14
|
+
planExists,
|
|
15
|
+
readAllPrompts,
|
|
16
|
+
readPlan,
|
|
17
|
+
readPrompt,
|
|
18
|
+
readUserInput,
|
|
19
|
+
updatePlanStage,
|
|
20
|
+
writePrompt,
|
|
21
|
+
writeSummary,
|
|
22
|
+
} from "../../lib/index.js";
|
|
23
|
+
import { BaseCommand, CommandResult } from "../base.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get next available prompts respecting dependencies.
|
|
27
|
+
*/
|
|
28
|
+
export class NextCommand extends BaseCommand {
|
|
29
|
+
readonly name = "next";
|
|
30
|
+
readonly description = "Get next available prompts respecting dependencies";
|
|
31
|
+
|
|
32
|
+
defineArguments(cmd: Command): void {
|
|
33
|
+
cmd.option("-n <count>", "Number of independent prompts to return");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async execute(args: Record<string, unknown>): Promise<CommandResult> {
|
|
37
|
+
const branch = getBranch();
|
|
38
|
+
if (!branch) {
|
|
39
|
+
return this.error("no_branch", "Not in a git repository or no branch checked out");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!planExists()) {
|
|
43
|
+
return this.error("no_plan", "No plan directory exists for this branch");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Get count from args, env var, or default to 1
|
|
47
|
+
const countArg = args.n as string | undefined;
|
|
48
|
+
const envCount = process.env.N_PARALLEL_WORKERS;
|
|
49
|
+
const count = countArg
|
|
50
|
+
? parseInt(countArg, 10)
|
|
51
|
+
: envCount
|
|
52
|
+
? parseInt(envCount, 10)
|
|
53
|
+
: 1;
|
|
54
|
+
|
|
55
|
+
if (isNaN(count) || count < 1) {
|
|
56
|
+
return this.error("invalid_count", "Count must be a positive integer");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const prompts = readAllPrompts();
|
|
60
|
+
|
|
61
|
+
// Build set of merged prompt numbers
|
|
62
|
+
const mergedNumbers = new Set<number>();
|
|
63
|
+
for (const p of prompts) {
|
|
64
|
+
if (p.frontMatter.status === "merged") {
|
|
65
|
+
mergedNumbers.add(p.number);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Find prompts that can be worked on (not merged, not in_progress, deps satisfied)
|
|
70
|
+
const availablePrompts: Array<{
|
|
71
|
+
number: number;
|
|
72
|
+
variant: string | null;
|
|
73
|
+
frontMatter: PromptFrontMatter;
|
|
74
|
+
content: string;
|
|
75
|
+
}> = [];
|
|
76
|
+
|
|
77
|
+
for (const p of prompts) {
|
|
78
|
+
// Skip already merged or in_progress
|
|
79
|
+
if (p.frontMatter.status === "merged" || p.frontMatter.in_progress) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check all dependencies are merged
|
|
84
|
+
const depsAreMerged = p.frontMatter.depends_on.every((dep) => mergedNumbers.has(dep));
|
|
85
|
+
if (!depsAreMerged) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
availablePrompts.push(p);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Sort: debug prompts first, then by number, then by variant
|
|
93
|
+
availablePrompts.sort((a, b) => {
|
|
94
|
+
// Debug prompts first
|
|
95
|
+
const aDebug = a.frontMatter.kind === "debug" ? 0 : 1;
|
|
96
|
+
const bDebug = b.frontMatter.kind === "debug" ? 0 : 1;
|
|
97
|
+
if (aDebug !== bDebug) return aDebug - bDebug;
|
|
98
|
+
|
|
99
|
+
// Then by number
|
|
100
|
+
if (a.number !== b.number) return a.number - b.number;
|
|
101
|
+
|
|
102
|
+
// Then by variant (null first, then alphabetically)
|
|
103
|
+
if (!a.variant && b.variant) return -1;
|
|
104
|
+
if (a.variant && !b.variant) return 1;
|
|
105
|
+
return (a.variant || "").localeCompare(b.variant || "");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Select up to count prompts, pulling all variants when one is selected
|
|
109
|
+
const selectedNumbers = new Set<number>();
|
|
110
|
+
const selected: typeof availablePrompts = [];
|
|
111
|
+
|
|
112
|
+
for (const p of availablePrompts) {
|
|
113
|
+
if (selectedNumbers.size >= count && !selectedNumbers.has(p.number)) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
selectedNumbers.add(p.number);
|
|
117
|
+
selected.push(p);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Format response
|
|
121
|
+
const result = selected.map((p) => ({
|
|
122
|
+
prompt_num: p.number,
|
|
123
|
+
variant: p.variant,
|
|
124
|
+
description: p.frontMatter.description,
|
|
125
|
+
relevant_files: p.frontMatter.relevant_files,
|
|
126
|
+
kind: p.frontMatter.kind,
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
return this.success({
|
|
130
|
+
prompts: result,
|
|
131
|
+
count: result.length,
|
|
132
|
+
requested: count,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Start working on a prompt - set in_progress and tracking info.
|
|
139
|
+
*/
|
|
140
|
+
export class StartPromptCommand extends BaseCommand {
|
|
141
|
+
readonly name = "start-prompt";
|
|
142
|
+
readonly description = "Start working on a prompt";
|
|
143
|
+
|
|
144
|
+
defineArguments(cmd: Command): void {
|
|
145
|
+
cmd.argument("<number>", "Prompt number (integer)");
|
|
146
|
+
cmd.argument("[variant]", "Optional variant letter (A, B, etc.)");
|
|
147
|
+
cmd.option("--specialist <name>", "Name of the specialist/agent working on this prompt");
|
|
148
|
+
cmd.option("--worktree <branch>", "Worktree branch name for tracking");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async execute(args: Record<string, unknown>): Promise<CommandResult> {
|
|
152
|
+
const branch = getBranch();
|
|
153
|
+
if (!branch) {
|
|
154
|
+
return this.error("no_branch", "Not in a git repository or no branch checked out");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const number = parseInt(args.number as string, 10);
|
|
158
|
+
if (isNaN(number) || number < 1) {
|
|
159
|
+
return this.error("invalid_number", "Prompt number must be a positive integer");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const variant = args.variant as string | undefined;
|
|
163
|
+
if (variant && !/^[A-Z]$/.test(variant)) {
|
|
164
|
+
return this.error("invalid_variant", "Variant must be a single uppercase letter (A-Z)");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const id = getPromptId(number, variant || null);
|
|
168
|
+
const prompt = readPrompt(number, variant || null);
|
|
169
|
+
if (!prompt) {
|
|
170
|
+
return this.error("not_found", `Prompt ${id} not found`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const specialist = args.specialist as string | undefined;
|
|
174
|
+
const worktree = args.worktree as string | undefined;
|
|
175
|
+
|
|
176
|
+
// Update prompt front matter
|
|
177
|
+
const updatedFrontMatter: PromptFrontMatter = {
|
|
178
|
+
...prompt.frontMatter,
|
|
179
|
+
in_progress: true,
|
|
180
|
+
current_iteration: 1,
|
|
181
|
+
delegated_to: (specialist as PromptFrontMatter["delegated_to"]) || null,
|
|
182
|
+
worktree_branch_name: worktree || null,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
writePrompt(number, variant || null, updatedFrontMatter, prompt.content);
|
|
186
|
+
|
|
187
|
+
return this.success({
|
|
188
|
+
id,
|
|
189
|
+
in_progress: true,
|
|
190
|
+
specialist: specialist || null,
|
|
191
|
+
worktree: worktree || null,
|
|
192
|
+
current_iteration: 1,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Record implementation walkthrough for a prompt.
|
|
199
|
+
*/
|
|
200
|
+
export class RecordImplementationCommand extends BaseCommand {
|
|
201
|
+
readonly name = "record-implementation";
|
|
202
|
+
readonly description = "Record implementation walkthrough for a prompt";
|
|
203
|
+
|
|
204
|
+
defineArguments(cmd: Command): void {
|
|
205
|
+
cmd.argument("<number>", "Prompt number (integer)");
|
|
206
|
+
cmd.argument("[variant]", "Optional variant letter (A, B, etc.)");
|
|
207
|
+
cmd.option("--walkthrough <walkthrough>", "Structured walkthrough markdown");
|
|
208
|
+
cmd.option("--iteration <n>", "Iteration number (1 for initial, 2+ for refinements)");
|
|
209
|
+
cmd.option("--refinement-reason <reason>", "Context for why this iteration was needed");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async execute(args: Record<string, unknown>): Promise<CommandResult> {
|
|
213
|
+
const branch = getBranch();
|
|
214
|
+
if (!branch) {
|
|
215
|
+
return this.error("no_branch", "Not in a git repository or no branch checked out");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const number = parseInt(args.number as string, 10);
|
|
219
|
+
if (isNaN(number) || number < 1) {
|
|
220
|
+
return this.error("invalid_number", "Prompt number must be a positive integer");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const variant = args.variant as string | undefined;
|
|
224
|
+
if (variant && !/^[A-Z]$/.test(variant)) {
|
|
225
|
+
return this.error("invalid_variant", "Variant must be a single uppercase letter (A-Z)");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const id = getPromptId(number, variant || null);
|
|
229
|
+
const prompt = readPrompt(number, variant || null);
|
|
230
|
+
if (!prompt) {
|
|
231
|
+
return this.error("not_found", `Prompt ${id} not found`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const walkthrough = args.walkthrough as string | undefined;
|
|
235
|
+
if (!walkthrough) {
|
|
236
|
+
return this.error("missing_walkthrough", "Walkthrough is required");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const iterationStr = args.iteration as string | undefined;
|
|
240
|
+
const iteration = iterationStr ? parseInt(iterationStr, 10) : 1;
|
|
241
|
+
if (isNaN(iteration) || iteration < 1) {
|
|
242
|
+
return this.error("invalid_iteration", "Iteration must be a positive integer");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const refinementReason = args.refinementReason as string | undefined;
|
|
246
|
+
if (iteration > 1 && !refinementReason) {
|
|
247
|
+
return this.error("missing_refinement_reason", "Refinement reason is required for iteration > 1");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Build walkthrough entry
|
|
251
|
+
const walkthroughEntry: PromptFrontMatter["walkthrough"][0] = {
|
|
252
|
+
iteration,
|
|
253
|
+
type: iteration === 1 ? "initial" : "review-refinement",
|
|
254
|
+
refinement_reason: refinementReason || null,
|
|
255
|
+
approach: walkthrough,
|
|
256
|
+
changes: [],
|
|
257
|
+
decisions: [],
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// Append to existing walkthrough
|
|
261
|
+
const existingWalkthrough = prompt.frontMatter.walkthrough || [];
|
|
262
|
+
const updatedWalkthrough = [...existingWalkthrough, walkthroughEntry];
|
|
263
|
+
|
|
264
|
+
const updatedFrontMatter: PromptFrontMatter = {
|
|
265
|
+
...prompt.frontMatter,
|
|
266
|
+
status: "implemented",
|
|
267
|
+
current_iteration: iteration,
|
|
268
|
+
walkthrough: updatedWalkthrough,
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
writePrompt(number, variant || null, updatedFrontMatter, prompt.content);
|
|
272
|
+
|
|
273
|
+
return this.success({
|
|
274
|
+
id,
|
|
275
|
+
status: "implemented",
|
|
276
|
+
iteration,
|
|
277
|
+
walkthrough_count: updatedWalkthrough.length,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Complete a prompt - set status to merged.
|
|
284
|
+
*/
|
|
285
|
+
export class CompletePromptCommand extends BaseCommand {
|
|
286
|
+
readonly name = "complete-prompt";
|
|
287
|
+
readonly description = "Complete a prompt (set status to merged)";
|
|
288
|
+
|
|
289
|
+
defineArguments(cmd: Command): void {
|
|
290
|
+
cmd.argument("<number>", "Prompt number (integer)");
|
|
291
|
+
cmd.argument("[variant]", "Optional variant letter (A, B, etc.)");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async execute(args: Record<string, unknown>): Promise<CommandResult> {
|
|
295
|
+
const branch = getBranch();
|
|
296
|
+
if (!branch) {
|
|
297
|
+
return this.error("no_branch", "Not in a git repository or no branch checked out");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const number = parseInt(args.number as string, 10);
|
|
301
|
+
if (isNaN(number) || number < 1) {
|
|
302
|
+
return this.error("invalid_number", "Prompt number must be a positive integer");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const variant = args.variant as string | undefined;
|
|
306
|
+
if (variant && !/^[A-Z]$/.test(variant)) {
|
|
307
|
+
return this.error("invalid_variant", "Variant must be a single uppercase letter (A-Z)");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const id = getPromptId(number, variant || null);
|
|
311
|
+
const prompt = readPrompt(number, variant || null);
|
|
312
|
+
if (!prompt) {
|
|
313
|
+
return this.error("not_found", `Prompt ${id} not found`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const updatedFrontMatter: PromptFrontMatter = {
|
|
317
|
+
...prompt.frontMatter,
|
|
318
|
+
status: "merged",
|
|
319
|
+
in_progress: false,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
writePrompt(number, variant || null, updatedFrontMatter, prompt.content);
|
|
323
|
+
|
|
324
|
+
return this.success({
|
|
325
|
+
id,
|
|
326
|
+
status: "merged",
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get prompt walkthrough for documentation extraction.
|
|
333
|
+
*/
|
|
334
|
+
export class GetPromptWalkthroughCommand extends BaseCommand {
|
|
335
|
+
readonly name = "get-prompt-walkthrough";
|
|
336
|
+
readonly description = "Get prompt walkthrough for documentation extraction";
|
|
337
|
+
|
|
338
|
+
defineArguments(cmd: Command): void {
|
|
339
|
+
cmd.argument("<number>", "Prompt number (integer)");
|
|
340
|
+
cmd.argument("[variant]", "Optional variant letter (A, B, etc.)");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async execute(args: Record<string, unknown>): Promise<CommandResult> {
|
|
344
|
+
const branch = getBranch();
|
|
345
|
+
if (!branch) {
|
|
346
|
+
return this.error("no_branch", "Not in a git repository or no branch checked out");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const number = parseInt(args.number as string, 10);
|
|
350
|
+
if (isNaN(number) || number < 1) {
|
|
351
|
+
return this.error("invalid_number", "Prompt number must be a positive integer");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const variant = args.variant as string | undefined;
|
|
355
|
+
if (variant && !/^[A-Z]$/.test(variant)) {
|
|
356
|
+
return this.error("invalid_variant", "Variant must be a single uppercase letter (A-Z)");
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const id = getPromptId(number, variant || null);
|
|
360
|
+
const prompt = readPrompt(number, variant || null);
|
|
361
|
+
if (!prompt) {
|
|
362
|
+
return this.error("not_found", `Prompt ${id} not found`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Get git diff summary for this prompt's changes
|
|
366
|
+
let gitDiffSummary = "";
|
|
367
|
+
const mergeCommit = prompt.frontMatter.merge_commit_hash;
|
|
368
|
+
const relevantFiles = prompt.frontMatter.relevant_files || [];
|
|
369
|
+
|
|
370
|
+
if (mergeCommit) {
|
|
371
|
+
try {
|
|
372
|
+
const result = spawnSync(
|
|
373
|
+
"git",
|
|
374
|
+
["diff", "--stat", `${mergeCommit}^..${mergeCommit}`],
|
|
375
|
+
{ encoding: "utf-8" }
|
|
376
|
+
);
|
|
377
|
+
gitDiffSummary = result.stdout || "(No changes in merge commit)";
|
|
378
|
+
} catch {
|
|
379
|
+
gitDiffSummary = "(Unable to get merge commit diff)";
|
|
380
|
+
}
|
|
381
|
+
} else if (relevantFiles.length > 0) {
|
|
382
|
+
try {
|
|
383
|
+
const baseBranch = getBaseBranch();
|
|
384
|
+
const result = spawnSync(
|
|
385
|
+
"git",
|
|
386
|
+
["diff", "--stat", baseBranch, "--", ...relevantFiles],
|
|
387
|
+
{ encoding: "utf-8" }
|
|
388
|
+
);
|
|
389
|
+
gitDiffSummary = result.stdout || "(No changes)";
|
|
390
|
+
} catch {
|
|
391
|
+
gitDiffSummary = "(Unable to get diff)";
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return this.success({
|
|
396
|
+
prompt_num: number,
|
|
397
|
+
variant: variant || null,
|
|
398
|
+
description: prompt.frontMatter.description,
|
|
399
|
+
success_criteria: prompt.frontMatter.success_criteria,
|
|
400
|
+
walkthrough: prompt.frontMatter.walkthrough || [],
|
|
401
|
+
git_diff_summary: gitDiffSummary,
|
|
402
|
+
merge_commit_hash: mergeCommit || null,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Mark a prompt as having its documentation extracted.
|
|
409
|
+
*/
|
|
410
|
+
export class MarkPromptExtractedCommand extends BaseCommand {
|
|
411
|
+
readonly name = "mark-prompt-extracted";
|
|
412
|
+
readonly description = "Mark prompt documentation as extracted";
|
|
413
|
+
|
|
414
|
+
defineArguments(cmd: Command): void {
|
|
415
|
+
cmd.argument("<number>", "Prompt number (integer)");
|
|
416
|
+
cmd.argument("[variant]", "Optional variant letter (A, B, etc.)");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async execute(args: Record<string, unknown>): Promise<CommandResult> {
|
|
420
|
+
const branch = getBranch();
|
|
421
|
+
if (!branch) {
|
|
422
|
+
return this.error("no_branch", "Not in a git repository or no branch checked out");
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const number = parseInt(args.number as string, 10);
|
|
426
|
+
if (isNaN(number) || number < 1) {
|
|
427
|
+
return this.error("invalid_number", "Prompt number must be a positive integer");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const variant = args.variant as string | undefined;
|
|
431
|
+
if (variant && !/^[A-Z]$/.test(variant)) {
|
|
432
|
+
return this.error("invalid_variant", "Variant must be a single uppercase letter (A-Z)");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const id = getPromptId(number, variant || null);
|
|
436
|
+
const prompt = readPrompt(number, variant || null);
|
|
437
|
+
if (!prompt) {
|
|
438
|
+
return this.error("not_found", `Prompt ${id} not found`);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const updatedFrontMatter: PromptFrontMatter = {
|
|
442
|
+
...prompt.frontMatter,
|
|
443
|
+
documentation_extracted: true,
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
writePrompt(number, variant || null, updatedFrontMatter, prompt.content);
|
|
447
|
+
|
|
448
|
+
return this.success({
|
|
449
|
+
id,
|
|
450
|
+
documentation_extracted: true,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Release all prompts from in_progress status.
|
|
457
|
+
*/
|
|
458
|
+
export class ReleaseAllPromptsCommand extends BaseCommand {
|
|
459
|
+
readonly name = "release-all-prompts";
|
|
460
|
+
readonly description = "Release all prompts from in_progress status";
|
|
461
|
+
|
|
462
|
+
defineArguments(_cmd: Command): void {
|
|
463
|
+
// No arguments
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
async execute(_args: Record<string, unknown>): Promise<CommandResult> {
|
|
467
|
+
const branch = getBranch();
|
|
468
|
+
if (!branch) {
|
|
469
|
+
return this.error("no_branch", "Not in a git repository or no branch checked out");
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (!planExists()) {
|
|
473
|
+
return this.error("no_plan", "No plan directory exists for this branch");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const prompts = readAllPrompts();
|
|
477
|
+
let releasedCount = 0;
|
|
478
|
+
|
|
479
|
+
for (const p of prompts) {
|
|
480
|
+
if (p.frontMatter.in_progress) {
|
|
481
|
+
const updatedFrontMatter: PromptFrontMatter = {
|
|
482
|
+
...p.frontMatter,
|
|
483
|
+
in_progress: false,
|
|
484
|
+
};
|
|
485
|
+
writePrompt(p.number, p.variant, updatedFrontMatter, p.content);
|
|
486
|
+
releasedCount++;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return this.success({
|
|
491
|
+
released_count: releasedCount,
|
|
492
|
+
total_prompts: prompts.length,
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Complete the plan - generate summary, create PR.
|
|
499
|
+
*/
|
|
500
|
+
export class CompleteCommand extends BaseCommand {
|
|
501
|
+
readonly name = "complete";
|
|
502
|
+
readonly description = "Complete the plan - generate summary and create PR";
|
|
503
|
+
|
|
504
|
+
private readonly SUMMARY_PROMPT = `You are a technical writer creating a pull request summary.
|
|
505
|
+
|
|
506
|
+
Given a plan, implementation walkthroughs, user input, and git diff, generate a clear PR description.
|
|
507
|
+
|
|
508
|
+
Format:
|
|
509
|
+
## Summary
|
|
510
|
+
[1-3 bullet points summarizing what was implemented]
|
|
511
|
+
|
|
512
|
+
## Changes
|
|
513
|
+
[Grouped by area/feature, list major changes]
|
|
514
|
+
|
|
515
|
+
## Testing
|
|
516
|
+
[How to test these changes]
|
|
517
|
+
|
|
518
|
+
## Notes
|
|
519
|
+
[Any additional context, breaking changes, or follow-up items]`;
|
|
520
|
+
|
|
521
|
+
defineArguments(_cmd: Command): void {
|
|
522
|
+
// No arguments
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async execute(_args: Record<string, unknown>): Promise<CommandResult> {
|
|
526
|
+
const branch = getBranch();
|
|
527
|
+
if (!branch) {
|
|
528
|
+
return this.error("no_branch", "Not in a git repository or no branch checked out");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (!planExists()) {
|
|
532
|
+
return this.error("no_plan", "No plan directory exists for this branch");
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const apiKey = process.env.VERTEX_API_KEY;
|
|
536
|
+
if (!apiKey) {
|
|
537
|
+
return this.error("auth_error", "VERTEX_API_KEY not set");
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Gather context
|
|
541
|
+
const plan = readPlan();
|
|
542
|
+
const userInput = readUserInput();
|
|
543
|
+
const prompts = readAllPrompts();
|
|
544
|
+
const baseBranch = getBaseBranch();
|
|
545
|
+
const diff = getDiff(baseBranch);
|
|
546
|
+
|
|
547
|
+
// Build context for summary generation
|
|
548
|
+
const promptSummaries = prompts.map((p) => {
|
|
549
|
+
const id = getPromptId(p.number, p.variant);
|
|
550
|
+
const walkthrough = p.frontMatter.walkthrough || [];
|
|
551
|
+
return `### Prompt ${id}: ${p.frontMatter.description}
|
|
552
|
+
Status: ${p.frontMatter.status}
|
|
553
|
+
Iterations: ${walkthrough.length}
|
|
554
|
+
${walkthrough.map((w) => `- Iteration ${w.iteration} (${w.type}): ${w.approach.substring(0, 200)}...`).join("\n")}`;
|
|
555
|
+
}).join("\n\n");
|
|
556
|
+
|
|
557
|
+
const fullPrompt = `${this.SUMMARY_PROMPT}
|
|
558
|
+
|
|
559
|
+
## Plan
|
|
560
|
+
${plan?.content || "(No plan content)"}
|
|
561
|
+
|
|
562
|
+
## User Input
|
|
563
|
+
${userInput || "(No user input)"}
|
|
564
|
+
|
|
565
|
+
## Prompts Implemented
|
|
566
|
+
${promptSummaries}
|
|
567
|
+
|
|
568
|
+
## Git Diff (against ${baseBranch})
|
|
569
|
+
\`\`\`diff
|
|
570
|
+
${diff.substring(0, 50000)}
|
|
571
|
+
\`\`\`
|
|
572
|
+
|
|
573
|
+
Generate the PR summary now.`;
|
|
574
|
+
|
|
575
|
+
try {
|
|
576
|
+
// Generate summary with Gemini
|
|
577
|
+
const [summary, durationMs] = await this.timedExecute(async () => {
|
|
578
|
+
const client = new GoogleGenAI({ vertexai: true, apiKey });
|
|
579
|
+
const result = await client.models.generateContent({
|
|
580
|
+
model: "gemini-2.0-flash",
|
|
581
|
+
contents: fullPrompt,
|
|
582
|
+
});
|
|
583
|
+
return result.text ?? "";
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
// Write summary
|
|
587
|
+
writeSummary(summary);
|
|
588
|
+
|
|
589
|
+
// Update plan stage to completed
|
|
590
|
+
updatePlanStage("completed");
|
|
591
|
+
|
|
592
|
+
// Push to remote
|
|
593
|
+
try {
|
|
594
|
+
execSync(`git push -u origin ${branch}`, { encoding: "utf-8", stdio: "pipe" });
|
|
595
|
+
} catch {
|
|
596
|
+
// Push might fail if already up to date or no remote
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Create PR using gh CLI
|
|
600
|
+
let prUrl = "";
|
|
601
|
+
try {
|
|
602
|
+
const prResult = spawnSync(
|
|
603
|
+
"gh",
|
|
604
|
+
[
|
|
605
|
+
"pr",
|
|
606
|
+
"create",
|
|
607
|
+
"--title",
|
|
608
|
+
plan?.frontMatter?.branch_name || branch,
|
|
609
|
+
"--body",
|
|
610
|
+
summary,
|
|
611
|
+
"--base",
|
|
612
|
+
baseBranch,
|
|
613
|
+
],
|
|
614
|
+
{ encoding: "utf-8" }
|
|
615
|
+
);
|
|
616
|
+
if (prResult.status === 0) {
|
|
617
|
+
prUrl = prResult.stdout.trim();
|
|
618
|
+
} else {
|
|
619
|
+
// PR might already exist, try to get URL
|
|
620
|
+
const viewResult = spawnSync("gh", ["pr", "view", "--json", "url"], {
|
|
621
|
+
encoding: "utf-8",
|
|
622
|
+
});
|
|
623
|
+
if (viewResult.status === 0) {
|
|
624
|
+
const parsed = JSON.parse(viewResult.stdout);
|
|
625
|
+
prUrl = parsed.url || "";
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
} catch {
|
|
629
|
+
// gh CLI might not be available
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
return this.success(
|
|
633
|
+
{
|
|
634
|
+
success: true,
|
|
635
|
+
pr_url: prUrl,
|
|
636
|
+
summary_written: true,
|
|
637
|
+
stage: "completed",
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
command: "plan complete",
|
|
641
|
+
duration_ms: durationMs,
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
} catch (e) {
|
|
645
|
+
return this.error("api_error", e instanceof Error ? e.message : String(e));
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|