issue-flow 0.3.0 → 0.4.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/README.md +25 -0
- package/dist/analyze-3SN7ZAZU.js +12 -0
- package/dist/{chunk-G36DIQ4J.js → chunk-2JZCWJEQ.js} +8 -8
- package/dist/chunk-2JZCWJEQ.js.map +1 -0
- package/dist/chunk-4OQ2ARLP.js +275 -0
- package/dist/chunk-4OQ2ARLP.js.map +1 -0
- package/dist/{chunk-D4WBCVB4.js → chunk-4PZXIOXD.js} +8 -8
- package/dist/chunk-4PZXIOXD.js.map +1 -0
- package/dist/{chunk-JEUHCE3V.js → chunk-CSSXTPWE.js} +2 -2
- package/dist/{chunk-7FRXZ2XA.js → chunk-CYWHYPTU.js} +7 -7
- package/dist/chunk-CYWHYPTU.js.map +1 -0
- package/dist/{chunk-7APADNBV.js → chunk-MEB3B2FI.js} +8 -7
- package/dist/chunk-MEB3B2FI.js.map +1 -0
- package/dist/{chunk-ZOX7M2C2.js → chunk-MWUIIUAS.js} +8 -8
- package/dist/chunk-MWUIIUAS.js.map +1 -0
- package/dist/{chunk-V356G3JS.js → chunk-WM6AYRHQ.js} +44 -45
- package/dist/chunk-WM6AYRHQ.js.map +1 -0
- package/dist/chunk-YAFHVFV5.js +199 -0
- package/dist/chunk-YAFHVFV5.js.map +1 -0
- package/dist/cli.js +60 -24
- package/dist/cli.js.map +1 -1
- package/dist/{execute-UF3IY2MG.js → execute-6AAKNSLM.js} +3 -3
- package/dist/{generate-WMYJEPI4.js → generate-HP4CPMJG.js} +7 -7
- package/dist/generate-HP4CPMJG.js.map +1 -0
- package/dist/init-SXFGOCML.js +9 -0
- package/dist/plan-HWZRG5GP.js +12 -0
- package/dist/pr-TTOFYUCV.js +12 -0
- package/dist/prd-ET2QN7GB.js +12 -0
- package/dist/review-3QDZCOMT.js +12 -0
- package/dist/run-6CR5FDYH.js +451 -0
- package/dist/run-6CR5FDYH.js.map +1 -0
- package/package.json +3 -2
- package/dist/analyze-YYQQP64A.js +0 -12
- package/dist/chunk-7APADNBV.js.map +0 -1
- package/dist/chunk-7FRXZ2XA.js.map +0 -1
- package/dist/chunk-D4WBCVB4.js.map +0 -1
- package/dist/chunk-G36DIQ4J.js.map +0 -1
- package/dist/chunk-L7RIGFP6.js +0 -92
- package/dist/chunk-L7RIGFP6.js.map +0 -1
- package/dist/chunk-PINC2LST.js +0 -82
- package/dist/chunk-PINC2LST.js.map +0 -1
- package/dist/chunk-V356G3JS.js.map +0 -1
- package/dist/chunk-ZOX7M2C2.js.map +0 -1
- package/dist/generate-WMYJEPI4.js.map +0 -1
- package/dist/init-QES2HLP5.js +0 -9
- package/dist/plan-BKSUDN34.js +0 -12
- package/dist/pr-E2ASXBWE.js +0 -12
- package/dist/prd-WYW35SGL.js +0 -12
- package/dist/review-YI2WPNC4.js +0 -12
- package/dist/run-J62FIB5O.js +0 -288
- package/dist/run-J62FIB5O.js.map +0 -1
- /package/dist/{analyze-YYQQP64A.js.map → analyze-3SN7ZAZU.js.map} +0 -0
- /package/dist/{chunk-JEUHCE3V.js.map → chunk-CSSXTPWE.js.map} +0 -0
- /package/dist/{execute-UF3IY2MG.js.map → execute-6AAKNSLM.js.map} +0 -0
- /package/dist/{init-QES2HLP5.js.map → init-SXFGOCML.js.map} +0 -0
- /package/dist/{plan-BKSUDN34.js.map → plan-HWZRG5GP.js.map} +0 -0
- /package/dist/{pr-E2ASXBWE.js.map → pr-TTOFYUCV.js.map} +0 -0
- /package/dist/{prd-WYW35SGL.js.map → prd-ET2QN7GB.js.map} +0 -0
- /package/dist/{review-YI2WPNC4.js.map → review-3QDZCOMT.js.map} +0 -0
package/README.md
CHANGED
|
@@ -44,6 +44,31 @@ npm install -g issue-flow
|
|
|
44
44
|
issue-flow run 42
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
## Global Options
|
|
48
|
+
|
|
49
|
+
All commands support the following options:
|
|
50
|
+
|
|
51
|
+
| Flag | Description |
|
|
52
|
+
|------|-------------|
|
|
53
|
+
| `-v, --verbose` | Show Claude progress output in real time |
|
|
54
|
+
| `-t, --timeout <seconds>` | Override headless timeout in seconds (0 = no limit) |
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Run with verbose output
|
|
58
|
+
npx issue-flow analyze 42 --verbose
|
|
59
|
+
|
|
60
|
+
# Override timeout to 10 minutes
|
|
61
|
+
npx issue-flow analyze 42 --timeout 600
|
|
62
|
+
|
|
63
|
+
# Disable timeout entirely
|
|
64
|
+
npx issue-flow run 42 --timeout 0
|
|
65
|
+
|
|
66
|
+
# Combine flags
|
|
67
|
+
npx issue-flow analyze 42 -v -t 600
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Default timeouts vary per command (5 min for most, 3 min for `generate` and `pr`). Use `--timeout` to override when working with large or complex issues.
|
|
71
|
+
|
|
47
72
|
## Commands
|
|
48
73
|
|
|
49
74
|
### `run` -- Full pipeline (end-to-end)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runAnalyze
|
|
4
|
+
} from "./chunk-MEB3B2FI.js";
|
|
5
|
+
import "./chunk-4OQ2ARLP.js";
|
|
6
|
+
import "./chunk-64FU2L2T.js";
|
|
7
|
+
import "./chunk-OZVHOVDT.js";
|
|
8
|
+
import "./chunk-YAFHVFV5.js";
|
|
9
|
+
export {
|
|
10
|
+
runAnalyze
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=analyze-3SN7ZAZU.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runHeadless
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4OQ2ARLP.js";
|
|
5
5
|
import {
|
|
6
6
|
loadTaskPlan,
|
|
7
7
|
saveTaskPlan
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
loadPrompt
|
|
12
12
|
} from "./chunk-OZVHOVDT.js";
|
|
13
13
|
import {
|
|
14
|
+
getGlobalTimeout,
|
|
14
15
|
printError,
|
|
15
|
-
printInfo,
|
|
16
16
|
printSuccess
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-YAFHVFV5.js";
|
|
18
18
|
|
|
19
19
|
// src/commands/prd.ts
|
|
20
20
|
import { mkdir, readFile } from "fs/promises";
|
|
@@ -23,7 +23,6 @@ async function runPrd(issue) {
|
|
|
23
23
|
const issueNumber = issue.replace(/^#/, "");
|
|
24
24
|
const issueDir = join("issues", issueNumber);
|
|
25
25
|
const prdPath = join(issueDir, "prd.md");
|
|
26
|
-
printInfo(`Generating PRD for issue #${issueNumber}...`);
|
|
27
26
|
await mkdir(issueDir, { recursive: true });
|
|
28
27
|
let analysisContext = "";
|
|
29
28
|
const analysisPath = join(issueDir, "analysis.md");
|
|
@@ -44,10 +43,11 @@ ${content}`;
|
|
|
44
43
|
});
|
|
45
44
|
const result = await runHeadless({
|
|
46
45
|
prompt,
|
|
47
|
-
maxTurns:
|
|
48
|
-
timeout:
|
|
46
|
+
maxTurns: 25,
|
|
47
|
+
timeout: getGlobalTimeout() ?? 3e5,
|
|
49
48
|
outputFormat: "text",
|
|
50
|
-
allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"]
|
|
49
|
+
allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"],
|
|
50
|
+
statusMessage: `Generating PRD for issue #${issueNumber}...`
|
|
51
51
|
});
|
|
52
52
|
if (!result.success) {
|
|
53
53
|
printError(`PRD generation failed: ${result.error}`);
|
|
@@ -77,4 +77,4 @@ ${content}`;
|
|
|
77
77
|
export {
|
|
78
78
|
runPrd
|
|
79
79
|
};
|
|
80
|
-
//# sourceMappingURL=chunk-
|
|
80
|
+
//# sourceMappingURL=chunk-2JZCWJEQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/prd.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\nexport async function runPrd(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const prdPath = join(issueDir, 'prd.md');\n\n await mkdir(issueDir, { recursive: true });\n\n // Read analysis if it exists\n let analysisContext = '';\n const analysisPath = join(issueDir, 'analysis.md');\n try {\n const content = await readFile(analysisPath, 'utf-8');\n analysisContext = `\\n\\nHere is the existing analysis for this issue:\\n\\n${content}`;\n } catch {\n // No analysis available — that's OK\n }\n\n const template = await loadPrompt('prd');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __ANALYSIS_CONTEXT__: analysisContext,\n __PRD_PATH__: prdPath,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep', 'Write'],\n statusMessage: `Generating PRD for issue #${issueNumber}...`,\n });\n\n if (!result.success) {\n printError(`PRD generation failed: ${result.error}`);\n return 1;\n }\n\n // Verify the file was created\n try {\n const content = await readFile(prdPath, 'utf-8');\n if (content.length < 10) {\n printError('PRD file was created but appears empty');\n return 1;\n }\n } catch {\n printError(`PRD file was not created at ${prdPath}`);\n return 1;\n }\n\n // Update pipeline state\n const tasksPath = join(issueDir, 'tasks.json');\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prdCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist yet\n }\n\n printSuccess(`PRD saved to ${prdPath}`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,OAAO,gBAAgB;AAChC,SAAS,YAAY;AAOrB,eAAsB,OAAO,OAAgC;AAC3D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAC3C,QAAM,UAAU,KAAK,UAAU,QAAQ;AAEvC,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAGzC,MAAI,kBAAkB;AACtB,QAAM,eAAe,KAAK,UAAU,aAAa;AACjD,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,cAAc,OAAO;AACpD,sBAAkB;AAAA;AAAA;AAAA;AAAA,EAAwD,OAAO;AAAA,EACnF,QAAQ;AAAA,EAER;AAEA,QAAM,WAAW,MAAM,WAAW,KAAK;AACvC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,sBAAsB;AAAA,IACtB,cAAc;AAAA,EAChB,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS,iBAAiB,KAAK;AAAA,IAC/B,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,OAAO;AAAA,IACtD,eAAe,6BAA6B,WAAW;AAAA,EACzD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,0BAA0B,OAAO,KAAK,EAAE;AACnD,WAAO;AAAA,EACT;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,SAAS,OAAO;AAC/C,QAAI,QAAQ,SAAS,IAAI;AACvB,iBAAW,wCAAwC;AACnD,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AACN,eAAW,+BAA+B,OAAO,EAAE;AACnD,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,KAAK,UAAU,YAAY;AAC7C,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,SAAS;AACzC,SAAK,SAAS,eAAe;AAC7B,UAAM,aAAa,WAAW,IAAI;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,eAAa,gBAAgB,OAAO,EAAE;AACtC,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ElapsedTimer,
|
|
4
|
+
createSpinner,
|
|
5
|
+
formatDuration,
|
|
6
|
+
getIcons,
|
|
7
|
+
getOutputCallback,
|
|
8
|
+
isVerbose,
|
|
9
|
+
useColor
|
|
10
|
+
} from "./chunk-YAFHVFV5.js";
|
|
11
|
+
|
|
12
|
+
// src/core/headless.ts
|
|
13
|
+
import { createInterface } from "readline";
|
|
14
|
+
import chalk from "chalk";
|
|
15
|
+
import { execa } from "execa";
|
|
16
|
+
function getToolContext(name, input) {
|
|
17
|
+
switch (name) {
|
|
18
|
+
case "Read":
|
|
19
|
+
return shortPath(input.file_path);
|
|
20
|
+
case "Write":
|
|
21
|
+
case "Edit":
|
|
22
|
+
return shortPath(input.file_path);
|
|
23
|
+
case "Glob":
|
|
24
|
+
return input.pattern ?? "";
|
|
25
|
+
case "Grep":
|
|
26
|
+
return input.pattern ?? "";
|
|
27
|
+
case "Bash": {
|
|
28
|
+
const cmd = input.command ?? "";
|
|
29
|
+
return cmd.length > 60 ? `${cmd.substring(0, 57)}...` : cmd;
|
|
30
|
+
}
|
|
31
|
+
default:
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function shortPath(filePath) {
|
|
36
|
+
if (!filePath) return "";
|
|
37
|
+
const cwd = process.cwd();
|
|
38
|
+
if (filePath.startsWith(cwd)) {
|
|
39
|
+
return filePath.substring(cwd.length + 1);
|
|
40
|
+
}
|
|
41
|
+
const parts = filePath.split("/");
|
|
42
|
+
if (parts.length > 2) {
|
|
43
|
+
return `.../${parts.slice(-2).join("/")}`;
|
|
44
|
+
}
|
|
45
|
+
return filePath;
|
|
46
|
+
}
|
|
47
|
+
function printStreamEvent(line, state, onOutput) {
|
|
48
|
+
let event;
|
|
49
|
+
try {
|
|
50
|
+
event = JSON.parse(line);
|
|
51
|
+
} catch {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const icons = getIcons();
|
|
55
|
+
const colored = useColor();
|
|
56
|
+
const emit = onOutput ?? ((msg) => process.stderr.write(`${msg}
|
|
57
|
+
`));
|
|
58
|
+
if (event.type === "assistant" && event.message?.content) {
|
|
59
|
+
state.turnCount++;
|
|
60
|
+
for (const block of event.message.content) {
|
|
61
|
+
if (block.type === "text" && block.text) {
|
|
62
|
+
const lines = block.text.split("\n");
|
|
63
|
+
for (const textLine of lines) {
|
|
64
|
+
if (!textLine.trim()) continue;
|
|
65
|
+
const prefix = colored ? chalk.dim(` ${icons.connector} `) : ` ${icons.connector} `;
|
|
66
|
+
const text = colored ? chalk.dim(textLine) : textLine;
|
|
67
|
+
emit(`${prefix}${text}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (block.type === "tool_use" && block.name) {
|
|
71
|
+
const context = block.input ? getToolContext(block.name, block.input) : "";
|
|
72
|
+
const toolName = block.name.padEnd(12);
|
|
73
|
+
const prefix = colored ? chalk.dim(` ${icons.connector} `) : ` ${icons.connector} `;
|
|
74
|
+
const toolIcon = colored ? chalk.cyan(icons.tool) : icons.tool;
|
|
75
|
+
const toolLabel = colored ? chalk.cyan(toolName) : toolName;
|
|
76
|
+
const contextText = context ? colored ? chalk.dim(context) : context : "";
|
|
77
|
+
emit(`${prefix}${toolIcon} ${toolLabel} ${contextText}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function runHeadlessVerbose(prompt, maxTurns, timeout, allowedTools, statusMessage, onOutput) {
|
|
83
|
+
const icons = getIcons();
|
|
84
|
+
const colored = useColor();
|
|
85
|
+
const emit = onOutput ?? ((msg) => process.stderr.write(`${msg}
|
|
86
|
+
`));
|
|
87
|
+
if (statusMessage) {
|
|
88
|
+
const msg = colored ? chalk.blue(`${icons.start} ${statusMessage}`) : `${icons.start} ${statusMessage}`;
|
|
89
|
+
emit(msg);
|
|
90
|
+
}
|
|
91
|
+
const connectorLine = colored ? chalk.dim(` ${icons.connector}`) : ` ${icons.connector}`;
|
|
92
|
+
emit(connectorLine);
|
|
93
|
+
const startTime = Date.now();
|
|
94
|
+
const args = [
|
|
95
|
+
"-p",
|
|
96
|
+
prompt,
|
|
97
|
+
"--output-format",
|
|
98
|
+
"stream-json",
|
|
99
|
+
"--verbose",
|
|
100
|
+
"--max-turns",
|
|
101
|
+
String(maxTurns)
|
|
102
|
+
];
|
|
103
|
+
if (allowedTools && allowedTools.length > 0) {
|
|
104
|
+
for (const tool of allowedTools) {
|
|
105
|
+
args.push("--allowedTools", tool);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const subprocess = execa("claude", args, {
|
|
109
|
+
stdin: "ignore",
|
|
110
|
+
reject: false,
|
|
111
|
+
timeout,
|
|
112
|
+
stripFinalNewline: false
|
|
113
|
+
});
|
|
114
|
+
let resultText = "";
|
|
115
|
+
let isError = false;
|
|
116
|
+
let costData = null;
|
|
117
|
+
const state = { turnCount: 0 };
|
|
118
|
+
if (subprocess.stdout) {
|
|
119
|
+
const rl = createInterface({ input: subprocess.stdout });
|
|
120
|
+
for await (const line of rl) {
|
|
121
|
+
printStreamEvent(line, state, onOutput);
|
|
122
|
+
try {
|
|
123
|
+
const event = JSON.parse(line);
|
|
124
|
+
if (event.type === "result") {
|
|
125
|
+
resultText = event.result ?? "";
|
|
126
|
+
isError = event.is_error === true;
|
|
127
|
+
if (event.total_cost_usd != null && event.usage) {
|
|
128
|
+
costData = {
|
|
129
|
+
inputTokens: event.usage.input_tokens,
|
|
130
|
+
outputTokens: event.usage.output_tokens
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const proc = await subprocess;
|
|
139
|
+
const elapsedSec = Math.floor((Date.now() - startTime) / 1e3);
|
|
140
|
+
const durationStr = formatDuration(elapsedSec);
|
|
141
|
+
const doneLine = colored ? chalk.dim(` ${icons.connector} ${chalk.italic(`Done in ${durationStr}`)}`) : ` ${icons.connector} Done in ${durationStr}`;
|
|
142
|
+
emit(doneLine);
|
|
143
|
+
emit(connectorLine);
|
|
144
|
+
if (proc.exitCode !== 0 && !resultText) {
|
|
145
|
+
const stderr = proc.stderr?.toString() ?? "";
|
|
146
|
+
const isTimeout = proc.exitCode === 143 || "signalName" in proc && proc.signalName === "SIGTERM";
|
|
147
|
+
const errorMsg = isTimeout ? `Headless invocation timed out after ${timeout}ms` : stderr || `claude exited with code ${proc.exitCode}`;
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
result: "",
|
|
151
|
+
cost: null,
|
|
152
|
+
error: errorMsg
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
success: !isError,
|
|
157
|
+
result: resultText,
|
|
158
|
+
cost: costData,
|
|
159
|
+
error: isError ? resultText : null
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async function runHeadless(options) {
|
|
163
|
+
const {
|
|
164
|
+
prompt,
|
|
165
|
+
maxTurns = 10,
|
|
166
|
+
timeout = 3e5,
|
|
167
|
+
outputFormat = "json",
|
|
168
|
+
allowedTools,
|
|
169
|
+
statusMessage,
|
|
170
|
+
onOutput
|
|
171
|
+
} = options;
|
|
172
|
+
if (isVerbose()) {
|
|
173
|
+
const effectiveOnOutput = onOutput ?? getOutputCallback();
|
|
174
|
+
return runHeadlessVerbose(
|
|
175
|
+
prompt,
|
|
176
|
+
maxTurns,
|
|
177
|
+
timeout,
|
|
178
|
+
allowedTools,
|
|
179
|
+
statusMessage,
|
|
180
|
+
effectiveOnOutput
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const spinner = statusMessage ? createSpinner(statusMessage).start() : null;
|
|
184
|
+
let timer = null;
|
|
185
|
+
if (spinner) {
|
|
186
|
+
timer = new ElapsedTimer((elapsed) => {
|
|
187
|
+
spinner.suffixText = useColor() ? chalk.dim(`(${elapsed})`) : `(${elapsed})`;
|
|
188
|
+
}).start();
|
|
189
|
+
}
|
|
190
|
+
const args = [
|
|
191
|
+
"-p",
|
|
192
|
+
prompt,
|
|
193
|
+
"--output-format",
|
|
194
|
+
outputFormat,
|
|
195
|
+
"--max-turns",
|
|
196
|
+
String(maxTurns)
|
|
197
|
+
];
|
|
198
|
+
if (allowedTools && allowedTools.length > 0) {
|
|
199
|
+
for (const tool of allowedTools) {
|
|
200
|
+
args.push("--allowedTools", tool);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const proc = await execa("claude", args, {
|
|
205
|
+
stdin: "ignore",
|
|
206
|
+
reject: false,
|
|
207
|
+
timeout,
|
|
208
|
+
stripFinalNewline: false
|
|
209
|
+
});
|
|
210
|
+
const stdout = proc.stdout?.toString() ?? "";
|
|
211
|
+
const stderr = proc.stderr?.toString() ?? "";
|
|
212
|
+
if (proc.exitCode !== 0) {
|
|
213
|
+
const elapsed2 = timer?.stop() ?? 0;
|
|
214
|
+
const dur2 = useColor() ? chalk.dim(` (${formatDuration(elapsed2)})`) : ` (${formatDuration(elapsed2)})`;
|
|
215
|
+
spinner?.fail(`${statusMessage}${dur2}`);
|
|
216
|
+
return {
|
|
217
|
+
success: false,
|
|
218
|
+
result: "",
|
|
219
|
+
cost: null,
|
|
220
|
+
error: stderr || stdout || `claude exited with code ${proc.exitCode}`
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const elapsed = timer?.stop() ?? 0;
|
|
224
|
+
const dur = useColor() ? chalk.dim(` (${formatDuration(elapsed)})`) : ` (${formatDuration(elapsed)})`;
|
|
225
|
+
spinner?.succeed(`${statusMessage}${dur}`);
|
|
226
|
+
if (outputFormat === "json") {
|
|
227
|
+
try {
|
|
228
|
+
const parsed = JSON.parse(stdout);
|
|
229
|
+
return {
|
|
230
|
+
success: true,
|
|
231
|
+
result: parsed.result ?? stdout,
|
|
232
|
+
cost: parsed.cost_usd != null ? { inputTokens: parsed.num_input_tokens, outputTokens: parsed.num_output_tokens } : null,
|
|
233
|
+
error: null
|
|
234
|
+
};
|
|
235
|
+
} catch {
|
|
236
|
+
return {
|
|
237
|
+
success: true,
|
|
238
|
+
result: stdout,
|
|
239
|
+
cost: null,
|
|
240
|
+
error: null
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
success: true,
|
|
246
|
+
result: stdout,
|
|
247
|
+
cost: null,
|
|
248
|
+
error: null
|
|
249
|
+
};
|
|
250
|
+
} catch (err) {
|
|
251
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
252
|
+
const catchElapsed = timer?.stop() ?? 0;
|
|
253
|
+
const catchDur = useColor() ? chalk.dim(` (${formatDuration(catchElapsed)})`) : ` (${formatDuration(catchElapsed)})`;
|
|
254
|
+
spinner?.fail(`${statusMessage}${catchDur}`);
|
|
255
|
+
if (message.includes("timed out") || message.includes("ETIMEDOUT")) {
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
result: "",
|
|
259
|
+
cost: null,
|
|
260
|
+
error: `Headless invocation timed out after ${timeout}ms`
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
success: false,
|
|
265
|
+
result: "",
|
|
266
|
+
cost: null,
|
|
267
|
+
error: message
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export {
|
|
273
|
+
runHeadless
|
|
274
|
+
};
|
|
275
|
+
//# sourceMappingURL=chunk-4OQ2ARLP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/headless.ts"],"sourcesContent":["import { createInterface } from 'node:readline';\nimport chalk from 'chalk';\nimport { execa } from 'execa';\nimport { createSpinner, ElapsedTimer, formatDuration, getIcons, useColor } from '../ui/logger.js';\nimport { getOutputCallback, isVerbose } from './verbose.js';\n\nexport interface HeadlessOptions {\n prompt: string;\n maxTurns?: number;\n timeout?: number;\n outputFormat?: 'json' | 'text' | 'stream-json';\n allowedTools?: string[];\n /** Status message displayed as spinner (non-verbose) or header (verbose). */\n statusMessage?: string;\n /** Optional callback for routing verbose output (e.g., through listr2 task.output). When provided, verbose stream events are sent here instead of directly to stderr. */\n onOutput?: (line: string) => void;\n}\n\nexport interface HeadlessCost {\n inputTokens?: number;\n outputTokens?: number;\n}\n\nexport interface HeadlessResult {\n success: boolean;\n result: string;\n cost: HeadlessCost | null;\n error: string | null;\n}\n\n/* ── verbose stream formatting ──────────────────────────────────────────── */\n\n/**\n * Extract a short context string from a tool_use input object.\n */\nfunction getToolContext(name: string, input: Record<string, unknown>): string {\n switch (name) {\n case 'Read':\n return shortPath(input.file_path as string);\n case 'Write':\n case 'Edit':\n return shortPath(input.file_path as string);\n case 'Glob':\n return (input.pattern as string) ?? '';\n case 'Grep':\n return (input.pattern as string) ?? '';\n case 'Bash': {\n const cmd = (input.command as string) ?? '';\n return cmd.length > 60 ? `${cmd.substring(0, 57)}...` : cmd;\n }\n default:\n return '';\n }\n}\n\n/**\n * Shorten a file path to be relative-friendly.\n */\nfunction shortPath(filePath: string | undefined): string {\n if (!filePath) return '';\n const cwd = process.cwd();\n if (filePath.startsWith(cwd)) {\n return filePath.substring(cwd.length + 1);\n }\n // Show last 2 segments for absolute paths\n const parts = filePath.split('/');\n if (parts.length > 2) {\n return `.../${parts.slice(-2).join('/')}`;\n }\n return filePath;\n}\n\n/**\n * Print a formatted stream event line to stderr.\n */\nfunction printStreamEvent(\n line: string,\n state: { turnCount: number },\n onOutput?: (line: string) => void,\n): void {\n let event: {\n type?: string;\n subtype?: string;\n message?: {\n content?: { type: string; text?: string; name?: string; input?: Record<string, unknown> }[];\n };\n };\n\n try {\n event = JSON.parse(line);\n } catch {\n return;\n }\n\n const icons = getIcons();\n const colored = useColor();\n const emit = onOutput ?? ((msg: string) => process.stderr.write(`${msg}\\n`));\n\n if (event.type === 'assistant' && event.message?.content) {\n state.turnCount++;\n\n for (const block of event.message.content) {\n if (block.type === 'text' && block.text) {\n // Wrap text with connector prefix\n const lines = block.text.split('\\n');\n for (const textLine of lines) {\n if (!textLine.trim()) continue;\n const prefix = colored ? chalk.dim(` ${icons.connector} `) : ` ${icons.connector} `;\n const text = colored ? chalk.dim(textLine) : textLine;\n emit(`${prefix}${text}`);\n }\n }\n\n if (block.type === 'tool_use' && block.name) {\n const context = block.input ? getToolContext(block.name, block.input) : '';\n const toolName = block.name.padEnd(12);\n\n const prefix = colored ? chalk.dim(` ${icons.connector} `) : ` ${icons.connector} `;\n const toolIcon = colored ? chalk.cyan(icons.tool) : icons.tool;\n const toolLabel = colored ? chalk.cyan(toolName) : toolName;\n const contextText = context ? (colored ? chalk.dim(context) : context) : '';\n\n emit(`${prefix}${toolIcon} ${toolLabel} ${contextText}`);\n }\n }\n }\n}\n\n/* ── verbose execution ──────────────────────────────────────────────────── */\n\n/**\n * Run headless in verbose mode using stream-json to display real-time progress.\n */\nasync function runHeadlessVerbose(\n prompt: string,\n maxTurns: number,\n timeout: number,\n allowedTools?: string[],\n statusMessage?: string,\n onOutput?: (line: string) => void,\n): Promise<HeadlessResult> {\n const icons = getIcons();\n const colored = useColor();\n const emit = onOutput ?? ((msg: string) => process.stderr.write(`${msg}\\n`));\n\n // Print header\n if (statusMessage) {\n const msg = colored\n ? chalk.blue(`${icons.start} ${statusMessage}`)\n : `${icons.start} ${statusMessage}`;\n emit(msg);\n }\n\n // Print opening connector\n const connectorLine = colored ? chalk.dim(` ${icons.connector}`) : ` ${icons.connector}`;\n emit(connectorLine);\n\n const startTime = Date.now();\n\n const args: string[] = [\n '-p',\n prompt,\n '--output-format',\n 'stream-json',\n '--verbose',\n '--max-turns',\n String(maxTurns),\n ];\n\n if (allowedTools && allowedTools.length > 0) {\n for (const tool of allowedTools) {\n args.push('--allowedTools', tool);\n }\n }\n\n const subprocess = execa('claude', args, {\n stdin: 'ignore',\n reject: false,\n timeout,\n stripFinalNewline: false,\n });\n\n let resultText = '';\n let isError = false;\n let costData: HeadlessCost | null = null;\n const state = { turnCount: 0 };\n\n if (subprocess.stdout) {\n const rl = createInterface({ input: subprocess.stdout });\n for await (const line of rl) {\n printStreamEvent(line, state, onOutput);\n\n try {\n const event = JSON.parse(line);\n if (event.type === 'result') {\n resultText = event.result ?? '';\n isError = event.is_error === true;\n if (event.total_cost_usd != null && event.usage) {\n costData = {\n inputTokens: event.usage.input_tokens,\n outputTokens: event.usage.output_tokens,\n };\n }\n }\n } catch {\n // ignore malformed lines\n }\n }\n }\n\n // Wait for the process to finish\n const proc = await subprocess;\n\n // Close connector with elapsed time\n const elapsedSec = Math.floor((Date.now() - startTime) / 1000);\n const durationStr = formatDuration(elapsedSec);\n const doneLine = colored\n ? chalk.dim(` ${icons.connector} ${chalk.italic(`Done in ${durationStr}`)}`)\n : ` ${icons.connector} Done in ${durationStr}`;\n emit(doneLine);\n emit(connectorLine);\n\n if (proc.exitCode !== 0 && !resultText) {\n const stderr = proc.stderr?.toString() ?? '';\n const isTimeout =\n proc.exitCode === 143 || ('signalName' in proc && proc.signalName === 'SIGTERM');\n const errorMsg = isTimeout\n ? `Headless invocation timed out after ${timeout}ms`\n : stderr || `claude exited with code ${proc.exitCode}`;\n return {\n success: false,\n result: '',\n cost: null,\n error: errorMsg,\n };\n }\n\n return {\n success: !isError,\n result: resultText,\n cost: costData,\n error: isError ? resultText : null,\n };\n}\n\n/* ── standard execution ─────────────────────────────────────────────────── */\n\n/**\n * Invoke Claude Code in headless mode via `claude -p`.\n *\n * Each invocation is an isolated session — no context is shared between calls.\n * Output is parsed as JSON when outputFormat is 'json' (default).\n *\n * When verbose mode is active, uses stream-json to display real-time progress.\n * Otherwise, shows a spinner while waiting.\n */\nexport async function runHeadless(options: HeadlessOptions): Promise<HeadlessResult> {\n const {\n prompt,\n maxTurns = 10,\n timeout = 300_000,\n outputFormat = 'json',\n allowedTools,\n statusMessage,\n onOutput,\n } = options;\n\n if (isVerbose()) {\n // Use explicit onOutput, fall back to global output callback, or default to stderr\n const effectiveOnOutput = onOutput ?? getOutputCallback();\n return runHeadlessVerbose(\n prompt,\n maxTurns,\n timeout,\n allowedTools,\n statusMessage,\n effectiveOnOutput,\n );\n }\n\n // Non-verbose: use spinner with elapsed timer\n const spinner = statusMessage ? createSpinner(statusMessage).start() : null;\n\n let timer: ElapsedTimer | null = null;\n if (spinner) {\n timer = new ElapsedTimer((elapsed) => {\n spinner.suffixText = useColor() ? chalk.dim(`(${elapsed})`) : `(${elapsed})`;\n }).start();\n }\n\n const args: string[] = [\n '-p',\n prompt,\n '--output-format',\n outputFormat,\n '--max-turns',\n String(maxTurns),\n ];\n\n if (allowedTools && allowedTools.length > 0) {\n for (const tool of allowedTools) {\n args.push('--allowedTools', tool);\n }\n }\n\n try {\n const proc = await execa('claude', args, {\n stdin: 'ignore',\n reject: false,\n timeout,\n stripFinalNewline: false,\n });\n\n const stdout = proc.stdout?.toString() ?? '';\n const stderr = proc.stderr?.toString() ?? '';\n\n if (proc.exitCode !== 0) {\n const elapsed = timer?.stop() ?? 0;\n const dur = useColor()\n ? chalk.dim(` (${formatDuration(elapsed)})`)\n : ` (${formatDuration(elapsed)})`;\n spinner?.fail(`${statusMessage}${dur}`);\n return {\n success: false,\n result: '',\n cost: null,\n error: stderr || stdout || `claude exited with code ${proc.exitCode}`,\n };\n }\n\n const elapsed = timer?.stop() ?? 0;\n const dur = useColor()\n ? chalk.dim(` (${formatDuration(elapsed)})`)\n : ` (${formatDuration(elapsed)})`;\n spinner?.succeed(`${statusMessage}${dur}`);\n\n if (outputFormat === 'json') {\n try {\n const parsed = JSON.parse(stdout);\n return {\n success: true,\n result: parsed.result ?? stdout,\n cost:\n parsed.cost_usd != null\n ? { inputTokens: parsed.num_input_tokens, outputTokens: parsed.num_output_tokens }\n : null,\n error: null,\n };\n } catch {\n return {\n success: true,\n result: stdout,\n cost: null,\n error: null,\n };\n }\n }\n\n return {\n success: true,\n result: stdout,\n cost: null,\n error: null,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const catchElapsed = timer?.stop() ?? 0;\n const catchDur = useColor()\n ? chalk.dim(` (${formatDuration(catchElapsed)})`)\n : ` (${formatDuration(catchElapsed)})`;\n spinner?.fail(`${statusMessage}${catchDur}`);\n\n if (message.includes('timed out') || message.includes('ETIMEDOUT')) {\n return {\n success: false,\n result: '',\n cost: null,\n error: `Headless invocation timed out after ${timeout}ms`,\n };\n }\n\n return {\n success: false,\n result: '',\n cost: null,\n error: message,\n };\n }\n}\n"],"mappings":"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","names":["elapsed","dur"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runHeadless
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4OQ2ARLP.js";
|
|
5
5
|
import {
|
|
6
6
|
loadTaskPlan,
|
|
7
7
|
saveTaskPlan
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
loadPrompt
|
|
12
12
|
} from "./chunk-OZVHOVDT.js";
|
|
13
13
|
import {
|
|
14
|
+
getGlobalTimeout,
|
|
14
15
|
printError,
|
|
15
|
-
printInfo,
|
|
16
16
|
printSuccess
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-YAFHVFV5.js";
|
|
18
18
|
|
|
19
19
|
// src/commands/review.ts
|
|
20
20
|
import { join } from "path";
|
|
@@ -44,7 +44,6 @@ async function runReview(issue) {
|
|
|
44
44
|
const issueNumber = issue.replace(/^#/, "");
|
|
45
45
|
const issueDir = join("issues", issueNumber);
|
|
46
46
|
const tasksPath = join(issueDir, "tasks.json");
|
|
47
|
-
printInfo(`Reviewing issue #${issueNumber} resolution...`);
|
|
48
47
|
const template = await loadPrompt("review");
|
|
49
48
|
const prompt = applyPlaceholders(template, {
|
|
50
49
|
__ISSUE_NUMBER__: issueNumber,
|
|
@@ -52,10 +51,11 @@ async function runReview(issue) {
|
|
|
52
51
|
});
|
|
53
52
|
const result = await runHeadless({
|
|
54
53
|
prompt,
|
|
55
|
-
maxTurns:
|
|
56
|
-
timeout:
|
|
54
|
+
maxTurns: 25,
|
|
55
|
+
timeout: getGlobalTimeout() ?? 3e5,
|
|
57
56
|
outputFormat: "text",
|
|
58
|
-
allowedTools: ["Bash", "Read", "Glob", "Grep"]
|
|
57
|
+
allowedTools: ["Bash", "Read", "Glob", "Grep"],
|
|
58
|
+
statusMessage: `Reviewing issue #${issueNumber} resolution...`
|
|
59
59
|
});
|
|
60
60
|
if (!result.success) {
|
|
61
61
|
printError(`Review failed: ${result.error}`);
|
|
@@ -82,4 +82,4 @@ async function runReview(issue) {
|
|
|
82
82
|
export {
|
|
83
83
|
runReview
|
|
84
84
|
};
|
|
85
|
-
//# sourceMappingURL=chunk-
|
|
85
|
+
//# sourceMappingURL=chunk-4PZXIOXD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/review.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\nexport interface ReviewResult {\n status: 'PASS' | 'FAIL';\n findings?: string;\n}\n\n/**\n * Parse the <review-result> block from headless output.\n */\nfunction parseReviewResult(output: string): ReviewResult {\n const match = output.match(/<review-result>([\\s\\S]*?)<\\/review-result>/);\n if (!match) {\n // Try to detect PASS/FAIL from the raw output\n if (/STATUS:\\s*PASS/i.test(output)) {\n return { status: 'PASS' };\n }\n if (/STATUS:\\s*FAIL/i.test(output)) {\n const findingsMatch = output.match(/FINDINGS:([\\s\\S]*?)(?:$|<\\/)/);\n return { status: 'FAIL', findings: findingsMatch?.[1]?.trim() ?? 'Unknown findings' };\n }\n // Default to PASS if no explicit status found and no errors\n return { status: 'PASS' };\n }\n\n const block = match[1];\n if (/STATUS:\\s*PASS/i.test(block)) {\n return { status: 'PASS' };\n }\n\n const findingsMatch = block.match(/FINDINGS:([\\s\\S]*)/);\n return {\n status: 'FAIL',\n findings: findingsMatch?.[1]?.trim() ?? 'Unknown findings',\n };\n}\n\nexport async function runReview(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const tasksPath = join(issueDir, 'tasks.json');\n\n const template = await loadPrompt('review');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __TASKS_PATH__: tasksPath,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n statusMessage: `Reviewing issue #${issueNumber} resolution...`,\n });\n\n if (!result.success) {\n printError(`Review failed: ${result.error}`);\n return 1;\n }\n\n const review = parseReviewResult(result.result);\n\n if (review.status === 'PASS') {\n // Update pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.reviewCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n printSuccess('Review PASSED — implementation meets acceptance criteria');\n return 0;\n }\n\n printError('Review FAILED — issues found:');\n if (review.findings) {\n console.log(review.findings);\n }\n return 1;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY;AAerB,SAAS,kBAAkB,QAA8B;AACvD,QAAM,QAAQ,OAAO,MAAM,4CAA4C;AACvE,MAAI,CAAC,OAAO;AAEV,QAAI,kBAAkB,KAAK,MAAM,GAAG;AAClC,aAAO,EAAE,QAAQ,OAAO;AAAA,IAC1B;AACA,QAAI,kBAAkB,KAAK,MAAM,GAAG;AAClC,YAAMA,iBAAgB,OAAO,MAAM,8BAA8B;AACjE,aAAO,EAAE,QAAQ,QAAQ,UAAUA,iBAAgB,CAAC,GAAG,KAAK,KAAK,mBAAmB;AAAA,IACtF;AAEA,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAEA,QAAM,QAAQ,MAAM,CAAC;AACrB,MAAI,kBAAkB,KAAK,KAAK,GAAG;AACjC,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAEA,QAAM,gBAAgB,MAAM,MAAM,oBAAoB;AACtD,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,UAAU,gBAAgB,CAAC,GAAG,KAAK,KAAK;AAAA,EAC1C;AACF;AAEA,eAAsB,UAAU,OAAgC;AAC9D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAC3C,QAAM,YAAY,KAAK,UAAU,YAAY;AAE7C,QAAM,WAAW,MAAM,WAAW,QAAQ;AAC1C,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,EAClB,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS,iBAAiB,KAAK;AAAA,IAC/B,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC7C,eAAe,oBAAoB,WAAW;AAAA,EAChD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,kBAAkB,OAAO,KAAK,EAAE;AAC3C,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,kBAAkB,OAAO,MAAM;AAE9C,MAAI,OAAO,WAAW,QAAQ;AAE5B,QAAI;AACF,YAAM,OAAO,MAAM,aAAa,SAAS;AACzC,WAAK,SAAS,kBAAkB;AAChC,YAAM,aAAa,WAAW,IAAI;AAAA,IACpC,QAAQ;AAAA,IAER;AAEA,iBAAa,+DAA0D;AACvE,WAAO;AAAA,EACT;AAEA,aAAW,oCAA+B;AAC1C,MAAI,OAAO,UAAU;AACnB,YAAQ,IAAI,OAAO,QAAQ;AAAA,EAC7B;AACA,SAAO;AACT;","names":["findingsMatch"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
printError,
|
|
4
4
|
printInfo,
|
|
5
5
|
printSuccess
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YAFHVFV5.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/init.ts
|
|
9
9
|
import { execa } from "execa";
|
|
@@ -120,4 +120,4 @@ async function runInit() {
|
|
|
120
120
|
export {
|
|
121
121
|
runInit
|
|
122
122
|
};
|
|
123
|
-
//# sourceMappingURL=chunk-
|
|
123
|
+
//# sourceMappingURL=chunk-CSSXTPWE.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runHeadless
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4OQ2ARLP.js";
|
|
5
5
|
import {
|
|
6
6
|
loadTaskPlan,
|
|
7
7
|
saveTaskPlan
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
loadPrompt
|
|
12
12
|
} from "./chunk-OZVHOVDT.js";
|
|
13
13
|
import {
|
|
14
|
+
getGlobalTimeout,
|
|
14
15
|
printError,
|
|
15
|
-
printInfo,
|
|
16
16
|
printSuccess
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-YAFHVFV5.js";
|
|
18
18
|
|
|
19
19
|
// src/commands/pr.ts
|
|
20
20
|
import { join } from "path";
|
|
@@ -27,7 +27,6 @@ async function runPr(issue) {
|
|
|
27
27
|
const issueNumber = issue.replace(/^#/, "");
|
|
28
28
|
const issueDir = join("issues", issueNumber);
|
|
29
29
|
const tasksPath = join(issueDir, "tasks.json");
|
|
30
|
-
printInfo(`Creating PR for issue #${issueNumber}...`);
|
|
31
30
|
let branchName;
|
|
32
31
|
try {
|
|
33
32
|
const proc = await execa("git", ["branch", "--show-current"], { reject: false });
|
|
@@ -49,9 +48,10 @@ async function runPr(issue) {
|
|
|
49
48
|
const result = await runHeadless({
|
|
50
49
|
prompt,
|
|
51
50
|
maxTurns: 15,
|
|
52
|
-
timeout:
|
|
51
|
+
timeout: getGlobalTimeout() ?? 3e5,
|
|
53
52
|
outputFormat: "text",
|
|
54
|
-
allowedTools: ["Bash", "Read", "Glob", "Grep"]
|
|
53
|
+
allowedTools: ["Bash", "Read", "Glob", "Grep"],
|
|
54
|
+
statusMessage: `Creating PR for issue #${issueNumber}...`
|
|
55
55
|
});
|
|
56
56
|
if (!result.success) {
|
|
57
57
|
printError(`PR creation failed: ${result.error}`);
|
|
@@ -75,4 +75,4 @@ async function runPr(issue) {
|
|
|
75
75
|
export {
|
|
76
76
|
runPr
|
|
77
77
|
};
|
|
78
|
-
//# sourceMappingURL=chunk-
|
|
78
|
+
//# sourceMappingURL=chunk-CYWHYPTU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/pr.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { execa } from 'execa';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\n/**\n * Extract a PR URL from headless output.\n */\nfunction parsePrUrl(output: string): string | null {\n const match = output.match(/(https:\\/\\/github\\.com\\/[^\\s]+\\/pull\\/\\d+)/);\n return match?.[1] ?? null;\n}\n\nexport async function runPr(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const tasksPath = join(issueDir, 'tasks.json');\n\n // Get current branch\n let branchName: string;\n try {\n const proc = await execa('git', ['branch', '--show-current'], { reject: false });\n branchName = proc.stdout?.toString().trim() ?? '';\n if (!branchName) {\n printError('Could not determine current branch');\n return 1;\n }\n } catch {\n printError('Failed to get current branch');\n return 1;\n }\n\n const template = await loadPrompt('pr');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __BRANCH_NAME__: branchName,\n __TASKS_PATH__: tasksPath,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n statusMessage: `Creating PR for issue #${issueNumber}...`,\n });\n\n if (!result.success) {\n printError(`PR creation failed: ${result.error}`);\n return 1;\n }\n\n const prUrl = parsePrUrl(result.result);\n\n // Update pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prCreated = true;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n if (prUrl) {\n printSuccess(`PR created: ${prUrl}`);\n } else {\n printSuccess('PR creation completed');\n }\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY;AACrB,SAAS,aAAa;AAUtB,SAAS,WAAW,QAA+B;AACjD,QAAM,QAAQ,OAAO,MAAM,4CAA4C;AACvE,SAAO,QAAQ,CAAC,KAAK;AACvB;AAEA,eAAsB,MAAM,OAAgC;AAC1D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAC3C,QAAM,YAAY,KAAK,UAAU,YAAY;AAG7C,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,OAAO,CAAC,UAAU,gBAAgB,GAAG,EAAE,QAAQ,MAAM,CAAC;AAC/E,iBAAa,KAAK,QAAQ,SAAS,EAAE,KAAK,KAAK;AAC/C,QAAI,CAAC,YAAY;AACf,iBAAW,oCAAoC;AAC/C,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AACN,eAAW,8BAA8B;AACzC,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,MAAM,WAAW,IAAI;AACtC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,EAClB,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS,iBAAiB,KAAK;AAAA,IAC/B,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC7C,eAAe,0BAA0B,WAAW;AAAA,EACtD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,uBAAuB,OAAO,KAAK,EAAE;AAChD,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,WAAW,OAAO,MAAM;AAGtC,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,SAAS;AACzC,SAAK,SAAS,YAAY;AAC1B,UAAM,aAAa,WAAW,IAAI;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,MAAI,OAAO;AACT,iBAAa,eAAe,KAAK,EAAE;AAAA,EACrC,OAAO;AACL,iBAAa,uBAAuB;AAAA,EACtC;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runHeadless
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4OQ2ARLP.js";
|
|
5
5
|
import {
|
|
6
6
|
loadTaskPlan,
|
|
7
7
|
saveTaskPlan
|
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
loadPrompt
|
|
12
12
|
} from "./chunk-OZVHOVDT.js";
|
|
13
13
|
import {
|
|
14
|
+
getGlobalTimeout,
|
|
14
15
|
printError,
|
|
15
16
|
printInfo,
|
|
16
17
|
printSuccess
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YAFHVFV5.js";
|
|
18
19
|
|
|
19
20
|
// src/commands/analyze.ts
|
|
20
21
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -23,7 +24,6 @@ async function runAnalyze(issue) {
|
|
|
23
24
|
const issueNumber = issue.replace(/^#/, "");
|
|
24
25
|
const issueDir = join("issues", issueNumber);
|
|
25
26
|
const analysisPath = join(issueDir, "analysis.md");
|
|
26
|
-
printInfo(`Analyzing issue #${issueNumber}...`);
|
|
27
27
|
await mkdir(issueDir, { recursive: true });
|
|
28
28
|
const template = await loadPrompt("analyze");
|
|
29
29
|
const prompt = applyPlaceholders(template, {
|
|
@@ -32,10 +32,11 @@ async function runAnalyze(issue) {
|
|
|
32
32
|
});
|
|
33
33
|
const result = await runHeadless({
|
|
34
34
|
prompt,
|
|
35
|
-
maxTurns:
|
|
36
|
-
timeout:
|
|
35
|
+
maxTurns: 30,
|
|
36
|
+
timeout: getGlobalTimeout() ?? 3e5,
|
|
37
37
|
outputFormat: "text",
|
|
38
|
-
allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"]
|
|
38
|
+
allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"],
|
|
39
|
+
statusMessage: `Analyzing issue #${issueNumber}...`
|
|
39
40
|
});
|
|
40
41
|
if (!result.success) {
|
|
41
42
|
printError(`Analysis failed: ${result.error}`);
|
|
@@ -65,4 +66,4 @@ async function runAnalyze(issue) {
|
|
|
65
66
|
export {
|
|
66
67
|
runAnalyze
|
|
67
68
|
};
|
|
68
|
-
//# sourceMappingURL=chunk-
|
|
69
|
+
//# sourceMappingURL=chunk-MEB3B2FI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/analyze.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { printError, printInfo, printSuccess } from '../ui/logger.js';\n\nexport async function runAnalyze(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const analysisPath = join(issueDir, 'analysis.md');\n\n await mkdir(issueDir, { recursive: true });\n\n const template = await loadPrompt('analyze');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __ANALYSIS_PATH__: analysisPath,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 30,\n timeout: getGlobalTimeout() ?? 300_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep', 'Write'],\n statusMessage: `Analyzing issue #${issueNumber}...`,\n });\n\n if (!result.success) {\n printError(`Analysis failed: ${result.error}`);\n return 1;\n }\n\n // Verify the file was created\n try {\n const content = await readFile(analysisPath, 'utf-8');\n if (content.length < 10) {\n printError('Analysis file was created but appears empty');\n return 1;\n }\n } catch {\n // File wasn't created by headless — save the result as analysis\n printInfo('Headless did not create analysis file; saving output directly');\n await writeFile(analysisPath, result.result, 'utf-8');\n }\n\n // Update pipeline state\n const tasksPath = join(issueDir, 'tasks.json');\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.analyzeCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist yet — that's OK for standalone analyze\n }\n\n printSuccess(`Analysis saved to ${analysisPath}`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,SAAS,OAAO,UAAU,iBAAiB;AAC3C,SAAS,YAAY;AAOrB,eAAsB,WAAW,OAAgC;AAC/D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAC3C,QAAM,eAAe,KAAK,UAAU,aAAa;AAEjD,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAEzC,QAAM,WAAW,MAAM,WAAW,SAAS;AAC3C,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,EACrB,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS,iBAAiB,KAAK;AAAA,IAC/B,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,OAAO;AAAA,IACtD,eAAe,oBAAoB,WAAW;AAAA,EAChD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,oBAAoB,OAAO,KAAK,EAAE;AAC7C,WAAO;AAAA,EACT;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,cAAc,OAAO;AACpD,QAAI,QAAQ,SAAS,IAAI;AACvB,iBAAW,6CAA6C;AACxD,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAEN,cAAU,+DAA+D;AACzE,UAAM,UAAU,cAAc,OAAO,QAAQ,OAAO;AAAA,EACtD;AAGA,QAAM,YAAY,KAAK,UAAU,YAAY;AAC7C,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,SAAS;AACzC,SAAK,SAAS,mBAAmB;AACjC,UAAM,aAAa,WAAW,IAAI;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,eAAa,qBAAqB,YAAY,EAAE;AAChD,SAAO;AACT;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runHeadless
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4OQ2ARLP.js";
|
|
5
5
|
import {
|
|
6
6
|
loadTaskPlan,
|
|
7
7
|
saveTaskPlan,
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
loadPrompt
|
|
13
13
|
} from "./chunk-OZVHOVDT.js";
|
|
14
14
|
import {
|
|
15
|
+
getGlobalTimeout,
|
|
15
16
|
printError,
|
|
16
|
-
printInfo,
|
|
17
17
|
printSuccess
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YAFHVFV5.js";
|
|
19
19
|
|
|
20
20
|
// src/commands/plan.ts
|
|
21
21
|
import { mkdir, readFile } from "fs/promises";
|
|
@@ -23,7 +23,6 @@ import { join } from "path";
|
|
|
23
23
|
async function runPlan(issue) {
|
|
24
24
|
const issueNumber = issue.replace(/^#/, "");
|
|
25
25
|
const issueDir = join("issues", issueNumber);
|
|
26
|
-
printInfo(`Converting PRD to task plan for issue #${issueNumber}...`);
|
|
27
26
|
const prdPath = join(issueDir, "prd.md");
|
|
28
27
|
let prdContent;
|
|
29
28
|
try {
|
|
@@ -42,10 +41,11 @@ async function runPlan(issue) {
|
|
|
42
41
|
await mkdir(issueDir, { recursive: true });
|
|
43
42
|
const result = await runHeadless({
|
|
44
43
|
prompt,
|
|
45
|
-
maxTurns:
|
|
46
|
-
timeout:
|
|
44
|
+
maxTurns: 25,
|
|
45
|
+
timeout: getGlobalTimeout() ?? 3e5,
|
|
47
46
|
outputFormat: "text",
|
|
48
|
-
allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"]
|
|
47
|
+
allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"],
|
|
48
|
+
statusMessage: `Converting PRD to task plan for issue #${issueNumber}...`
|
|
49
49
|
});
|
|
50
50
|
if (!result.success) {
|
|
51
51
|
printError(`Task plan generation failed: ${result.error}`);
|
|
@@ -82,4 +82,4 @@ ${issues}`);
|
|
|
82
82
|
export {
|
|
83
83
|
runPlan
|
|
84
84
|
};
|
|
85
|
-
//# sourceMappingURL=chunk-
|
|
85
|
+
//# sourceMappingURL=chunk-MWUIIUAS.js.map
|