issue-flow 0.3.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.
Files changed (51) hide show
  1. package/README.md +226 -0
  2. package/dist/analyze-YYQQP64A.js +12 -0
  3. package/dist/analyze-YYQQP64A.js.map +1 -0
  4. package/dist/chunk-64FU2L2T.js +180 -0
  5. package/dist/chunk-64FU2L2T.js.map +1 -0
  6. package/dist/chunk-7APADNBV.js +68 -0
  7. package/dist/chunk-7APADNBV.js.map +1 -0
  8. package/dist/chunk-7FRXZ2XA.js +78 -0
  9. package/dist/chunk-7FRXZ2XA.js.map +1 -0
  10. package/dist/chunk-D4WBCVB4.js +85 -0
  11. package/dist/chunk-D4WBCVB4.js.map +1 -0
  12. package/dist/chunk-G36DIQ4J.js +80 -0
  13. package/dist/chunk-G36DIQ4J.js.map +1 -0
  14. package/dist/chunk-JEUHCE3V.js +123 -0
  15. package/dist/chunk-JEUHCE3V.js.map +1 -0
  16. package/dist/chunk-L7RIGFP6.js +92 -0
  17. package/dist/chunk-L7RIGFP6.js.map +1 -0
  18. package/dist/chunk-OZVHOVDT.js +43 -0
  19. package/dist/chunk-OZVHOVDT.js.map +1 -0
  20. package/dist/chunk-PINC2LST.js +82 -0
  21. package/dist/chunk-PINC2LST.js.map +1 -0
  22. package/dist/chunk-V356G3JS.js +611 -0
  23. package/dist/chunk-V356G3JS.js.map +1 -0
  24. package/dist/chunk-ZOX7M2C2.js +85 -0
  25. package/dist/chunk-ZOX7M2C2.js.map +1 -0
  26. package/dist/cli.js +76 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/execute-UF3IY2MG.js +11 -0
  29. package/dist/execute-UF3IY2MG.js.map +1 -0
  30. package/dist/generate-WMYJEPI4.js +48 -0
  31. package/dist/generate-WMYJEPI4.js.map +1 -0
  32. package/dist/init-QES2HLP5.js +9 -0
  33. package/dist/init-QES2HLP5.js.map +1 -0
  34. package/dist/plan-BKSUDN34.js +12 -0
  35. package/dist/plan-BKSUDN34.js.map +1 -0
  36. package/dist/pr-E2ASXBWE.js +12 -0
  37. package/dist/pr-E2ASXBWE.js.map +1 -0
  38. package/dist/prd-WYW35SGL.js +12 -0
  39. package/dist/prd-WYW35SGL.js.map +1 -0
  40. package/dist/review-YI2WPNC4.js +12 -0
  41. package/dist/review-YI2WPNC4.js.map +1 -0
  42. package/dist/run-J62FIB5O.js +288 -0
  43. package/dist/run-J62FIB5O.js.map +1 -0
  44. package/package.json +58 -0
  45. package/prompts/analyze.md +28 -0
  46. package/prompts/execute.md +124 -0
  47. package/prompts/generate.md +19 -0
  48. package/prompts/plan.md +49 -0
  49. package/prompts/pr.md +19 -0
  50. package/prompts/prd.md +30 -0
  51. package/prompts/review.md +27 -0
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runReview
4
+ } from "./chunk-D4WBCVB4.js";
5
+ import {
6
+ runInit
7
+ } from "./chunk-JEUHCE3V.js";
8
+ import {
9
+ runAnalyze
10
+ } from "./chunk-7APADNBV.js";
11
+ import {
12
+ runExecute
13
+ } from "./chunk-V356G3JS.js";
14
+ import {
15
+ runPlan
16
+ } from "./chunk-ZOX7M2C2.js";
17
+ import {
18
+ runPr
19
+ } from "./chunk-7FRXZ2XA.js";
20
+ import {
21
+ runPrd
22
+ } from "./chunk-G36DIQ4J.js";
23
+ import "./chunk-PINC2LST.js";
24
+ import {
25
+ isoNow,
26
+ loadTaskPlan,
27
+ saveTaskPlan
28
+ } from "./chunk-64FU2L2T.js";
29
+ import "./chunk-OZVHOVDT.js";
30
+ import {
31
+ printError,
32
+ printInfo,
33
+ printSuccess,
34
+ printWarning
35
+ } from "./chunk-L7RIGFP6.js";
36
+
37
+ // src/commands/run.ts
38
+ import { join } from "path";
39
+ import { execa } from "execa";
40
+
41
+ // src/core/pipeline.ts
42
+ var PIPELINE_PHASES = [
43
+ "init",
44
+ "analyze",
45
+ "prd",
46
+ "plan",
47
+ "execute",
48
+ "review",
49
+ "pr"
50
+ ];
51
+ var PHASE_TO_FIELD = {
52
+ init: null,
53
+ analyze: "analyzeCompleted",
54
+ prd: "prdCompleted",
55
+ plan: "jsonCompleted",
56
+ execute: "executionCompleted",
57
+ review: "reviewCompleted",
58
+ pr: "prCreated"
59
+ };
60
+ var PipelineManager = class {
61
+ tasksJsonPath;
62
+ plan;
63
+ constructor(plan, tasksJsonPath) {
64
+ this.plan = plan;
65
+ this.tasksJsonPath = tasksJsonPath;
66
+ }
67
+ /**
68
+ * Reload state from disk.
69
+ */
70
+ async reload() {
71
+ this.plan = await loadTaskPlan(this.tasksJsonPath);
72
+ }
73
+ /**
74
+ * Check if a phase is complete.
75
+ */
76
+ isPhaseComplete(phase) {
77
+ const field = PHASE_TO_FIELD[phase];
78
+ if (field === null) return true;
79
+ return this.plan.pipeline?.[field] ?? false;
80
+ }
81
+ /**
82
+ * Get the first incomplete phase in the pipeline.
83
+ * Returns null if all phases are complete.
84
+ */
85
+ getNextPhase() {
86
+ for (const phase of PIPELINE_PHASES) {
87
+ if (!this.isPhaseComplete(phase)) {
88
+ return phase;
89
+ }
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Get the current phase (alias for getNextPhase).
95
+ */
96
+ getCurrentPhase() {
97
+ return this.getNextPhase();
98
+ }
99
+ /**
100
+ * Check whether we can resume from a specific phase.
101
+ * All prerequisite phases must be complete.
102
+ */
103
+ canResume(fromPhase) {
104
+ const idx = PIPELINE_PHASES.indexOf(fromPhase);
105
+ if (idx < 0) return false;
106
+ for (let i = 0; i < idx; i++) {
107
+ if (!this.isPhaseComplete(PIPELINE_PHASES[i])) {
108
+ return false;
109
+ }
110
+ }
111
+ return true;
112
+ }
113
+ /**
114
+ * Mark a phase as complete and persist to disk.
115
+ */
116
+ async markPhaseComplete(phase) {
117
+ const field = PHASE_TO_FIELD[phase];
118
+ if (field === null) return;
119
+ this.plan = {
120
+ ...this.plan,
121
+ pipeline: {
122
+ ...this.plan.pipeline ?? {
123
+ analyzeCompleted: false,
124
+ prdCompleted: false,
125
+ jsonCompleted: false,
126
+ executionCompleted: false,
127
+ reviewCompleted: false,
128
+ prCreated: false
129
+ },
130
+ [field]: true
131
+ }
132
+ };
133
+ await saveTaskPlan(this.tasksJsonPath, this.plan);
134
+ }
135
+ };
136
+
137
+ // src/commands/run.ts
138
+ async function runPipeline(issue, mode, from) {
139
+ const issueNumber = issue.replace(/^#/, "");
140
+ const issueDir = join("issues", issueNumber);
141
+ const tasksPath = join(issueDir, "tasks.json");
142
+ printInfo(`Starting pipeline for issue #${issueNumber} (mode: ${mode})`);
143
+ printInfo("Running prerequisite checks...");
144
+ const initCode = await runInit();
145
+ if (initCode !== 0) {
146
+ printError("Prerequisites not met. Fix the issues above and try again.");
147
+ return 1;
148
+ }
149
+ let startPhase = "analyze";
150
+ if (from) {
151
+ if (!PIPELINE_PHASES.includes(from)) {
152
+ printError(`Invalid phase: ${from}. Valid phases: ${PIPELINE_PHASES.join(", ")}`);
153
+ return 1;
154
+ }
155
+ startPhase = from;
156
+ } else {
157
+ try {
158
+ const plan = await loadTaskPlan(tasksPath);
159
+ const mgr = new PipelineManager(plan, tasksPath);
160
+ const nextPhase = mgr.getNextPhase();
161
+ if (nextPhase && nextPhase !== "init") {
162
+ startPhase = nextPhase;
163
+ printInfo(`Resuming from phase: ${startPhase}`);
164
+ }
165
+ } catch {
166
+ }
167
+ }
168
+ if (from) {
169
+ try {
170
+ const plan = await loadTaskPlan(tasksPath);
171
+ const mgr = new PipelineManager(plan, tasksPath);
172
+ if (!mgr.canResume(startPhase)) {
173
+ printError(`Cannot resume from ${startPhase}: prerequisite phases not complete`);
174
+ return 1;
175
+ }
176
+ } catch {
177
+ if (startPhase !== "analyze") {
178
+ printError(`Cannot resume from ${startPhase}: no pipeline state found`);
179
+ return 1;
180
+ }
181
+ }
182
+ }
183
+ const phaseOrder = ["analyze", "prd", "plan", "execute", "review", "pr"];
184
+ const startIdx = phaseOrder.indexOf(startPhase);
185
+ for (let i = startIdx; i < phaseOrder.length; i++) {
186
+ const phase = phaseOrder[i];
187
+ printInfo(`
188
+ --- Phase: ${phase} ---`);
189
+ let code;
190
+ switch (phase) {
191
+ case "analyze":
192
+ code = await runAnalyze(issueNumber);
193
+ break;
194
+ case "prd":
195
+ code = await runPrd(issueNumber);
196
+ break;
197
+ case "plan":
198
+ code = await runPlan(issueNumber);
199
+ break;
200
+ case "execute":
201
+ code = await runExecute(void 0, { issue: issueNumber });
202
+ break;
203
+ case "review": {
204
+ let maxCycles = 3;
205
+ try {
206
+ const plan = await loadTaskPlan(tasksPath);
207
+ maxCycles = plan.maxCorrectionCycles;
208
+ } catch {
209
+ }
210
+ code = await runReview(issueNumber);
211
+ let cycle = 0;
212
+ while (code !== 0 && cycle < maxCycles) {
213
+ cycle++;
214
+ printWarning(`Review failed. Starting correction cycle ${cycle}/${maxCycles}...`);
215
+ try {
216
+ const plan = await loadTaskPlan(tasksPath);
217
+ plan.correctionCycle = cycle;
218
+ await saveTaskPlan(tasksPath, plan);
219
+ } catch {
220
+ }
221
+ const execCode = await runExecute(void 0, { issue: issueNumber });
222
+ if (execCode !== 0) {
223
+ printError("Correction execution failed");
224
+ return 1;
225
+ }
226
+ code = await runReview(issueNumber);
227
+ }
228
+ if (code !== 0) {
229
+ printError(`Review failed after ${maxCycles} correction cycles`);
230
+ return 1;
231
+ }
232
+ break;
233
+ }
234
+ case "pr":
235
+ code = await runPr(issueNumber);
236
+ break;
237
+ default:
238
+ code = 1;
239
+ }
240
+ if (code !== 0) {
241
+ printError(`Phase ${phase} failed with exit code ${code}`);
242
+ return 1;
243
+ }
244
+ }
245
+ printInfo("Closing issue...");
246
+ try {
247
+ await execa("gh", ["issue", "close", issueNumber], { reject: false });
248
+ } catch {
249
+ printWarning("Failed to close issue automatically");
250
+ }
251
+ let prUrl = "unknown";
252
+ try {
253
+ const proc = await execa("gh", ["pr", "list", "--head", "", "--json", "url", "--limit", "1"], {
254
+ reject: false
255
+ });
256
+ const parsed = JSON.parse(proc.stdout?.toString() ?? "[]");
257
+ if (parsed[0]?.url) {
258
+ prUrl = parsed[0].url;
259
+ }
260
+ } catch {
261
+ }
262
+ let branchName = "unknown";
263
+ try {
264
+ const proc = await execa("git", ["branch", "--show-current"], { reject: false });
265
+ branchName = proc.stdout?.toString().trim() ?? "unknown";
266
+ } catch {
267
+ }
268
+ let storyCount = 0;
269
+ try {
270
+ const plan = await loadTaskPlan(tasksPath);
271
+ storyCount = plan.userStories.length;
272
+ plan.issueStatus = "completed";
273
+ plan.completedAt = isoNow();
274
+ plan.lastAttemptAt = isoNow();
275
+ await saveTaskPlan(tasksPath, plan);
276
+ } catch {
277
+ }
278
+ console.log("");
279
+ printSuccess(`Pipeline complete for issue #${issueNumber}!`);
280
+ console.log(` Branch: ${branchName}`);
281
+ console.log(` Stories: ${storyCount}`);
282
+ console.log(` PR: ${prUrl}`);
283
+ return 0;
284
+ }
285
+ export {
286
+ runPipeline
287
+ };
288
+ //# sourceMappingURL=run-J62FIB5O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/run.ts","../src/core/pipeline.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { execa } from 'execa';\nimport { PIPELINE_PHASES, PipelineManager, type PipelinePhase } from '../core/pipeline.js';\nimport { isoNow, loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { printError, printInfo, printSuccess, printWarning } from '../ui/logger.js';\nimport { runAnalyze } from './analyze.js';\nimport { runExecute } from './execute.js';\nimport { runInit } from './init.js';\nimport { runPlan } from './plan.js';\nimport { runPr } from './pr.js';\nimport { runPrd } from './prd.js';\nimport { runReview } from './review.js';\n\nexport async function runPipeline(issue: string, mode: string, from?: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const tasksPath = join(issueDir, 'tasks.json');\n\n printInfo(`Starting pipeline for issue #${issueNumber} (mode: ${mode})`);\n\n // Phase 1: Init check\n printInfo('Running prerequisite checks...');\n const initCode = await runInit();\n if (initCode !== 0) {\n printError('Prerequisites not met. Fix the issues above and try again.');\n return 1;\n }\n\n // Determine starting phase\n let startPhase: PipelinePhase = 'analyze';\n if (from) {\n if (!PIPELINE_PHASES.includes(from as PipelinePhase)) {\n printError(`Invalid phase: ${from}. Valid phases: ${PIPELINE_PHASES.join(', ')}`);\n return 1;\n }\n startPhase = from as PipelinePhase;\n } else {\n // Try to auto-resume from pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n const mgr = new PipelineManager(plan, tasksPath);\n const nextPhase = mgr.getNextPhase();\n if (nextPhase && nextPhase !== 'init') {\n startPhase = nextPhase;\n printInfo(`Resuming from phase: ${startPhase}`);\n }\n } catch {\n // No tasks.json yet — start from beginning\n }\n }\n\n // Validate resume prerequisites if starting from a later phase\n if (from) {\n try {\n const plan = await loadTaskPlan(tasksPath);\n const mgr = new PipelineManager(plan, tasksPath);\n if (!mgr.canResume(startPhase)) {\n printError(`Cannot resume from ${startPhase}: prerequisite phases not complete`);\n return 1;\n }\n } catch {\n if (startPhase !== 'analyze') {\n printError(`Cannot resume from ${startPhase}: no pipeline state found`);\n return 1;\n }\n }\n }\n\n const phaseOrder: PipelinePhase[] = ['analyze', 'prd', 'plan', 'execute', 'review', 'pr'];\n const startIdx = phaseOrder.indexOf(startPhase);\n\n for (let i = startIdx; i < phaseOrder.length; i++) {\n const phase = phaseOrder[i];\n printInfo(`\\n--- Phase: ${phase} ---`);\n\n let code: number;\n switch (phase) {\n case 'analyze':\n code = await runAnalyze(issueNumber);\n break;\n case 'prd':\n code = await runPrd(issueNumber);\n break;\n case 'plan':\n code = await runPlan(issueNumber);\n break;\n case 'execute':\n code = await runExecute(undefined, { issue: issueNumber });\n break;\n case 'review': {\n // Read maxCorrectionCycles\n let maxCycles = 3;\n try {\n const plan = await loadTaskPlan(tasksPath);\n maxCycles = plan.maxCorrectionCycles;\n } catch {\n /* use default */\n }\n\n code = await runReview(issueNumber);\n\n // Auto-correction loop on failure\n let cycle = 0;\n while (code !== 0 && cycle < maxCycles) {\n cycle++;\n printWarning(`Review failed. Starting correction cycle ${cycle}/${maxCycles}...`);\n\n // Update correction cycle in tasks.json\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.correctionCycle = cycle;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n /* non-critical */\n }\n\n // Re-execute\n const execCode = await runExecute(undefined, { issue: issueNumber });\n if (execCode !== 0) {\n printError('Correction execution failed');\n return 1;\n }\n\n // Re-review\n code = await runReview(issueNumber);\n }\n\n if (code !== 0) {\n printError(`Review failed after ${maxCycles} correction cycles`);\n return 1;\n }\n break;\n }\n case 'pr':\n code = await runPr(issueNumber);\n break;\n default:\n code = 1;\n }\n\n if (code !== 0) {\n printError(`Phase ${phase} failed with exit code ${code}`);\n return 1;\n }\n }\n\n // Close the issue\n printInfo('Closing issue...');\n try {\n await execa('gh', ['issue', 'close', issueNumber], { reject: false });\n } catch {\n printWarning('Failed to close issue automatically');\n }\n\n // Get PR URL for summary\n let prUrl = 'unknown';\n try {\n const proc = await execa('gh', ['pr', 'list', '--head', '', '--json', 'url', '--limit', '1'], {\n reject: false,\n });\n const parsed = JSON.parse(proc.stdout?.toString() ?? '[]');\n if (parsed[0]?.url) {\n prUrl = parsed[0].url;\n }\n } catch {\n /* non-critical */\n }\n\n // Get branch and story count\n let branchName = 'unknown';\n try {\n const proc = await execa('git', ['branch', '--show-current'], { reject: false });\n branchName = proc.stdout?.toString().trim() ?? 'unknown';\n } catch {\n /* non-critical */\n }\n\n let storyCount = 0;\n try {\n const plan = await loadTaskPlan(tasksPath);\n storyCount = plan.userStories.length;\n\n // Mark as completed\n plan.issueStatus = 'completed';\n plan.completedAt = isoNow();\n plan.lastAttemptAt = isoNow();\n await saveTaskPlan(tasksPath, plan);\n } catch {\n /* non-critical */\n }\n\n console.log('');\n printSuccess(`Pipeline complete for issue #${issueNumber}!`);\n console.log(` Branch: ${branchName}`);\n console.log(` Stories: ${storyCount}`);\n console.log(` PR: ${prUrl}`);\n\n return 0;\n}\n","import type { PipelineState, TaskPlan } from '../types.js';\nimport { loadTaskPlan, saveTaskPlan } from './state-manager.js';\n\n/**\n * Ordered pipeline phases. Each phase must complete before the next can start.\n */\nexport const PIPELINE_PHASES = [\n 'init',\n 'analyze',\n 'prd',\n 'plan',\n 'execute',\n 'review',\n 'pr',\n] as const;\n\nexport type PipelinePhase = (typeof PIPELINE_PHASES)[number];\n\n/**\n * Map pipeline phases to their corresponding PipelineState field.\n * 'init' has no persisted state — it's a runtime-only check.\n */\nconst PHASE_TO_FIELD: Record<PipelinePhase, keyof PipelineState | null> = {\n init: null,\n analyze: 'analyzeCompleted',\n prd: 'prdCompleted',\n plan: 'jsonCompleted',\n execute: 'executionCompleted',\n review: 'reviewCompleted',\n pr: 'prCreated',\n};\n\nexport class PipelineManager {\n private tasksJsonPath: string;\n private plan: TaskPlan;\n\n constructor(plan: TaskPlan, tasksJsonPath: string) {\n this.plan = plan;\n this.tasksJsonPath = tasksJsonPath;\n }\n\n /**\n * Reload state from disk.\n */\n async reload(): Promise<void> {\n this.plan = await loadTaskPlan(this.tasksJsonPath);\n }\n\n /**\n * Check if a phase is complete.\n */\n isPhaseComplete(phase: PipelinePhase): boolean {\n const field = PHASE_TO_FIELD[phase];\n if (field === null) return true; // init is always \"complete\" after running\n return this.plan.pipeline?.[field] ?? false;\n }\n\n /**\n * Get the first incomplete phase in the pipeline.\n * Returns null if all phases are complete.\n */\n getNextPhase(): PipelinePhase | null {\n for (const phase of PIPELINE_PHASES) {\n if (!this.isPhaseComplete(phase)) {\n return phase;\n }\n }\n return null;\n }\n\n /**\n * Get the current phase (alias for getNextPhase).\n */\n getCurrentPhase(): PipelinePhase | null {\n return this.getNextPhase();\n }\n\n /**\n * Check whether we can resume from a specific phase.\n * All prerequisite phases must be complete.\n */\n canResume(fromPhase: PipelinePhase): boolean {\n const idx = PIPELINE_PHASES.indexOf(fromPhase);\n if (idx < 0) return false;\n\n // All phases before fromPhase must be complete\n for (let i = 0; i < idx; i++) {\n if (!this.isPhaseComplete(PIPELINE_PHASES[i])) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Mark a phase as complete and persist to disk.\n */\n async markPhaseComplete(phase: PipelinePhase): Promise<void> {\n const field = PHASE_TO_FIELD[phase];\n if (field === null) return; // init has no persisted state\n\n this.plan = {\n ...this.plan,\n pipeline: {\n ...(this.plan.pipeline ?? {\n analyzeCompleted: false,\n prdCompleted: false,\n jsonCompleted: false,\n executionCompleted: false,\n reviewCompleted: false,\n prCreated: false,\n }),\n [field]: true,\n },\n };\n\n await saveTaskPlan(this.tasksJsonPath, this.plan);\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "issue-flow",
3
+ "version": "0.3.0",
4
+ "description": "Unified CLI for orchestrating the full issue-flow pipeline via Claude Code Headless",
5
+ "type": "module",
6
+ "bin": {
7
+ "issue-flow": "./dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsup",
11
+ "dev": "tsup --watch",
12
+ "typecheck": "tsc --noEmit",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "lint": "biome check src/",
16
+ "lint:fix": "biome check --write src/",
17
+ "format": "biome format --write src/",
18
+ "check": "biome check --write src/ && tsc --noEmit"
19
+ },
20
+ "engines": {
21
+ "node": ">=18.0.0"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "prompts"
26
+ ],
27
+ "keywords": [
28
+ "ai",
29
+ "agent",
30
+ "claude",
31
+ "automation",
32
+ "cli",
33
+ "pipeline",
34
+ "headless",
35
+ "issue-flow",
36
+ "github"
37
+ ],
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/fabioassuncao/issue-flow.git",
42
+ "directory": "packages/issue-flow"
43
+ },
44
+ "dependencies": {
45
+ "chalk": "^5.4.1",
46
+ "commander": "^13.1.0",
47
+ "execa": "^9.5.2",
48
+ "ora": "^8.2.0",
49
+ "zod": "^4.3.6"
50
+ },
51
+ "devDependencies": {
52
+ "@biomejs/biome": "^2.4.10",
53
+ "@types/node": "^22.14.0",
54
+ "tsup": "^8.4.0",
55
+ "typescript": "^5.8.3",
56
+ "vitest": "^3.1.1"
57
+ }
58
+ }
@@ -0,0 +1,28 @@
1
+ You are analyzing GitHub issue #__ISSUE_NUMBER__ for this repository.
2
+
3
+ Steps:
4
+ 1. Fetch the issue data using: gh issue view __ISSUE_NUMBER__ --json title,body,labels,comments
5
+ 2. Analyze the codebase to understand the affected areas, tech stack, and architecture
6
+ 3. Identify the scope, complexity, and key files/modules involved
7
+ 4. Produce a structured analysis
8
+
9
+ Save your analysis to __ANALYSIS_PATH__ with this structure:
10
+
11
+ # Issue Analysis: #__ISSUE_NUMBER__
12
+
13
+ ## Summary
14
+ [Brief description of the issue]
15
+
16
+ ## Affected Areas
17
+ [List files, modules, or systems affected]
18
+
19
+ ## Technical Context
20
+ [Relevant architecture, patterns, dependencies]
21
+
22
+ ## Complexity Assessment
23
+ [Low/Medium/High with justification]
24
+
25
+ ## Implementation Notes
26
+ [Key considerations, risks, dependencies]
27
+
28
+ IMPORTANT: You MUST write the analysis to the file path above. Do not just output it.
@@ -0,0 +1,124 @@
1
+ # Issue Flow Agent Instructions
2
+
3
+ You are an autonomous coding agent working on a software project.
4
+
5
+ ## Your Task
6
+
7
+ 1. Read the PRD at `__PRD_FILE__`
8
+ 2. Read the progress log at `__PROGRESS_FILE__` (check Codebase Patterns section first)
9
+ 3. Check you're on the correct branch from PRD `branchName`. If not, check it out or create from main.
10
+ 4. Treat the issue as unresolved unless **every** `userStories[].passes` value is `true`
11
+ 5. Pick the **highest priority** user story where `passes: false`
12
+ 6. Implement that single user story
13
+ 7. Run quality checks (e.g., typecheck, lint, test - use whatever your project requires)
14
+ 8. Update CLAUDE.md files if you discover reusable patterns (see below)
15
+ 9. If checks pass, commit ALL changes with message: `feat: [Story ID] - [Story Title]`
16
+ 10. Update the PRD to set `passes: true` for the completed story
17
+ 11. Append your progress to `__PROGRESS_FILE__`
18
+
19
+ ## Progress Report Format
20
+
21
+ APPEND to `__PROGRESS_FILE__` (never replace, always append):
22
+ ```
23
+ ## [Date/Time] - [Story ID]
24
+ - What was implemented
25
+ - Files changed
26
+ - **Learnings for future iterations:**
27
+ - Patterns discovered (e.g., "this codebase uses X for Y")
28
+ - Gotchas encountered (e.g., "don't forget to update Z when changing W")
29
+ - Useful context (e.g., "the evaluation panel is in component X")
30
+ ---
31
+ ```
32
+
33
+ The learnings section is critical - it helps future iterations avoid repeating mistakes and understand the codebase better.
34
+
35
+ ## Consolidate Patterns
36
+
37
+ If you discover a **reusable pattern** that future iterations should know, add it to the `## Codebase Patterns` section at the TOP of `__PROGRESS_FILE__` (create it if it doesn't exist). This section should consolidate the most important learnings:
38
+
39
+ ```
40
+ ## Codebase Patterns
41
+ - Example: Use `sql<number>` template for aggregations
42
+ - Example: Always use `IF NOT EXISTS` for migrations
43
+ - Example: Export types from actions.ts for UI components
44
+ ```
45
+
46
+ Only add patterns that are **general and reusable**, not story-specific details.
47
+
48
+ ## Update CLAUDE.md Files
49
+
50
+ Before committing, check if any edited files have learnings worth preserving in nearby CLAUDE.md files:
51
+
52
+ 1. **Identify directories with edited files** - Look at which directories you modified
53
+ 2. **Check for existing CLAUDE.md** - Look for CLAUDE.md in those directories or parent directories
54
+ 3. **Add valuable learnings** - If you discovered something future developers/agents should know:
55
+ - API patterns or conventions specific to that module
56
+ - Gotchas or non-obvious requirements
57
+ - Dependencies between files
58
+ - Testing approaches for that area
59
+ - Configuration or environment requirements
60
+
61
+ **Examples of good CLAUDE.md additions:**
62
+ - "When modifying X, also update Y to keep them in sync"
63
+ - "This module uses pattern Z for all API calls"
64
+ - "Tests require the dev server running on PORT 3000"
65
+ - "Field names must match the template exactly"
66
+
67
+ **Do NOT add:**
68
+ - Story-specific implementation details
69
+ - Temporary debugging notes
70
+ - Information already in progress.txt
71
+
72
+ Only update CLAUDE.md if you have **genuinely reusable knowledge** that would help future work in that directory.
73
+
74
+ ## Quality Requirements
75
+
76
+ - ALL commits must pass your project's quality checks (typecheck, lint, test)
77
+ - Do NOT commit broken code
78
+ - Keep changes focused and minimal
79
+ - Follow existing code patterns
80
+
81
+ ## Browser Testing (If Available)
82
+
83
+ For any story that changes UI, verify it works in the browser if you have browser testing tools configured (e.g., via MCP):
84
+
85
+ 1. Navigate to the relevant page
86
+ 2. Verify the UI changes work as expected
87
+ 3. Take a screenshot if helpful for the progress log
88
+
89
+ If no browser tools are available, note in your progress report that manual browser verification is needed.
90
+
91
+ ## Pipeline State Tracking
92
+
93
+ The task plan may contain a `pipeline` object that tracks orchestrator phase completion. Update these fields as appropriate:
94
+
95
+ - After completing a story: set `pipeline.executionCompleted` to `false` (still in progress)
96
+ - After ALL stories complete: set `pipeline.executionCompleted` to `true`
97
+
98
+ The task plan may also contain `correctionCycle` and `maxCorrectionCycles` fields. These track how many review-fix cycles have occurred. The execution loop does not manage the correction loop — that is handled by the orchestrator or manually.
99
+
100
+ If these fields don't exist in the task plan (older format), ignore them — they are optional.
101
+
102
+ ## Stop Condition
103
+
104
+ After completing a user story, check if ALL stories have `passes: true`.
105
+
106
+ If ALL stories are complete and passing, first update the task plan metadata:
107
+ - Set `issueStatus` to `completed`
108
+ - Set `completedAt` to the current ISO timestamp
109
+ - Set `lastAttemptAt` to the current ISO timestamp
110
+ - Clear `lastError`
111
+ - If `pipeline` object exists, set `pipeline.executionCompleted` to `true`
112
+
113
+ Then reply with:
114
+ <promise>COMPLETE</promise>
115
+
116
+ If there are still stories with `passes: false`, end your response normally (another iteration will pick up the next story).
117
+ If you need to stop for user guidance or another non-transient blocker, record it in top-level `lastError` and do not clear it.
118
+
119
+ ## Important
120
+
121
+ - Work on ONE story per iteration
122
+ - Commit frequently
123
+ - Keep CI green
124
+ - Read the Codebase Patterns section in `__PROGRESS_FILE__` before starting
@@ -0,0 +1,19 @@
1
+ You are creating a GitHub issue for this repository.
2
+
3
+ The user provided this description:
4
+ __USER_PROMPT__
5
+
6
+ Steps:
7
+ 1. Analyze the project's tech stack, architecture, and codebase
8
+ 2. Check for duplicate issues: gh issue list --state open --search "<keywords>"
9
+ 3. Create a well-structured GitHub issue using gh issue create
10
+
11
+ The issue should:
12
+ - Have a clear, descriptive title
13
+ - Include context about why the change is needed
14
+ - Include acceptance criteria
15
+ - Add appropriate labels (create them if they don't exist)
16
+
17
+ Use: gh issue create --title "..." --body "..."
18
+
19
+ IMPORTANT: Output the issue URL after creation so it can be parsed.
@@ -0,0 +1,49 @@
1
+ You are converting a PRD into a structured JSON task plan for issue #__ISSUE_NUMBER__.
2
+
3
+ Here is the PRD:
4
+
5
+ __PRD_CONTENT__
6
+
7
+ Create a tasks.json file at __TASKS_PATH__ with this exact structure:
8
+
9
+ {
10
+ "project": "<repo-name>",
11
+ "issueNumber": __ISSUE_NUMBER__,
12
+ "issueUrl": "<github-issue-url>",
13
+ "branchName": "issue/__ISSUE_NUMBER__-<slug>",
14
+ "description": "<brief description>",
15
+ "issueStatus": "pending",
16
+ "completedAt": null,
17
+ "lastAttemptAt": null,
18
+ "lastError": null,
19
+ "correctionCycle": 0,
20
+ "maxCorrectionCycles": 3,
21
+ "pipeline": {
22
+ "analyzeCompleted": true,
23
+ "prdCompleted": true,
24
+ "jsonCompleted": true,
25
+ "executionCompleted": false,
26
+ "reviewCompleted": false,
27
+ "prCreated": false
28
+ },
29
+ "userStories": [
30
+ {
31
+ "id": "US-001",
32
+ "title": "...",
33
+ "description": "As a ..., I want ... so that ...",
34
+ "acceptanceCriteria": ["..."],
35
+ "priority": 1,
36
+ "passes": false,
37
+ "notes": ""
38
+ }
39
+ ]
40
+ }
41
+
42
+ Rules:
43
+ - Each user story from the PRD becomes one entry in userStories
44
+ - Priority should order stories by dependency (build foundations first)
45
+ - acceptanceCriteria must include "Typecheck passes" for code changes
46
+ - Get the repo name and issue URL from: gh issue view __ISSUE_NUMBER__ --json url
47
+ - The branchName should use a short kebab-case slug derived from the issue title
48
+
49
+ IMPORTANT: You MUST write the tasks.json to the file path above. Do not just output it.
package/prompts/pr.md ADDED
@@ -0,0 +1,19 @@
1
+ You are creating a pull request for issue #__ISSUE_NUMBER__ on branch __BRANCH_NAME__.
2
+
3
+ Steps:
4
+ 1. Fetch the issue data: gh issue view __ISSUE_NUMBER__ --json title,body
5
+ 2. Read the task plan from __TASKS_PATH__ if it exists
6
+ 3. Review the git log for this branch: git log main..HEAD --oneline
7
+ 4. Review the diff: git diff main...HEAD --stat
8
+ 5. Create a well-structured PR using gh pr create
9
+
10
+ The PR should:
11
+ - Have a clear, concise title (under 70 characters)
12
+ - Reference the issue: "Closes #__ISSUE_NUMBER__"
13
+ - Include a summary of changes
14
+ - Include a test plan
15
+
16
+ Use this command format:
17
+ gh pr create --title "..." --body "..." --base main
18
+
19
+ IMPORTANT: Output the PR URL after creation so it can be parsed.
package/prompts/prd.md ADDED
@@ -0,0 +1,30 @@
1
+ You are generating a Product Requirements Document (PRD) for GitHub issue #__ISSUE_NUMBER__ in this repository.__ANALYSIS_CONTEXT__
2
+
3
+ Steps:
4
+ 1. If no analysis was provided above, fetch the issue data using: gh issue view __ISSUE_NUMBER__ --json title,body,labels,comments
5
+ 2. Analyze the codebase to understand the context
6
+ 3. Generate a structured PRD
7
+
8
+ Save the PRD to __PRD_PATH__ with this structure:
9
+
10
+ # PRD: [Issue Title]
11
+
12
+ ## Context
13
+ [Why this change is needed]
14
+
15
+ ## Goals
16
+ [What success looks like]
17
+
18
+ ## User Stories
19
+ [US-001, US-002, etc. with acceptance criteria]
20
+
21
+ ## Technical Approach
22
+ [High-level implementation strategy]
23
+
24
+ ## Out of Scope
25
+ [What is explicitly NOT included]
26
+
27
+ ## Dependencies
28
+ [External dependencies or prerequisites]
29
+
30
+ IMPORTANT: You MUST write the PRD to the file path above. Do not just output it.
@@ -0,0 +1,27 @@
1
+ You are reviewing whether GitHub issue #__ISSUE_NUMBER__ has been fully resolved.
2
+
3
+ IMPORTANT: You are running in --orchestrator mode. Do NOT close the issue directly. Only report results.
4
+
5
+ Steps:
6
+ 1. Fetch the issue data using: gh issue view __ISSUE_NUMBER__ --json title,body,labels
7
+ 2. Read the task plan from __TASKS_PATH__ to understand what was supposed to be implemented
8
+ 3. Analyze the codebase to verify all acceptance criteria are met
9
+ 4. Run the project's test suite and typecheck
10
+ 5. Check for regressions
11
+
12
+ At the end, output your result in this exact format:
13
+
14
+ <review-result>
15
+ STATUS: PASS
16
+ </review-result>
17
+
18
+ Or if there are issues:
19
+
20
+ <review-result>
21
+ STATUS: FAIL
22
+ FINDINGS:
23
+ - Finding 1
24
+ - Finding 2
25
+ </review-result>
26
+
27
+ IMPORTANT: You MUST include the <review-result> block in your output.