issue-flow 0.4.4 → 0.5.1

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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -1
  3. package/dist/{analyze-PDDIU7HD.js → analyze-IJ2SYXVM.js} +7 -9
  4. package/dist/{analyze-PDDIU7HD.js.map → analyze-IJ2SYXVM.js.map} +1 -1
  5. package/dist/{chunk-4OQ2ARLP.js → chunk-2XWXVWVD.js} +10 -2
  6. package/dist/chunk-2XWXVWVD.js.map +1 -0
  7. package/dist/{chunk-ZJB2IMPU.js → chunk-AY7UZAGX.js} +8 -10
  8. package/dist/{chunk-ZJB2IMPU.js.map → chunk-AY7UZAGX.js.map} +1 -1
  9. package/dist/chunk-FNLXZXWP.js +846 -0
  10. package/dist/chunk-FNLXZXWP.js.map +1 -0
  11. package/dist/{chunk-LTPXKMPE.js → chunk-GYR4YHEB.js} +100 -113
  12. package/dist/chunk-GYR4YHEB.js.map +1 -0
  13. package/dist/{chunk-65KDGHLM.js → chunk-MKQIP7YW.js} +7 -9
  14. package/dist/{chunk-65KDGHLM.js.map → chunk-MKQIP7YW.js.map} +1 -1
  15. package/dist/{chunk-CSSXTPWE.js → chunk-Q2TQCJDN.js} +2 -2
  16. package/dist/{chunk-SKTTLX7R.js → chunk-SI4ALORB.js} +7 -9
  17. package/dist/{chunk-SKTTLX7R.js.map → chunk-SI4ALORB.js.map} +1 -1
  18. package/dist/{chunk-OZVHOVDT.js → chunk-SLHZPB3C.js} +15 -8
  19. package/dist/chunk-SLHZPB3C.js.map +1 -0
  20. package/dist/chunk-TYOFZ6DD.js +240 -0
  21. package/dist/chunk-TYOFZ6DD.js.map +1 -0
  22. package/dist/{chunk-HPSU5GGE.js → chunk-XJJP4TSK.js} +7 -9
  23. package/dist/{chunk-HPSU5GGE.js.map → chunk-XJJP4TSK.js.map} +1 -1
  24. package/dist/cli.js +55 -18
  25. package/dist/cli.js.map +1 -1
  26. package/dist/execute-DALVYZIH.js +11 -0
  27. package/dist/{generate-HP4CPMJG.js → generate-5OWWNRIB.js} +4 -4
  28. package/dist/init-6AWQI3IE.js +9 -0
  29. package/dist/plan-RQXK2OCP.js +11 -0
  30. package/dist/pr-UU4H7ZZR.js +11 -0
  31. package/dist/prd-WB3QH6QF.js +11 -0
  32. package/dist/review-2WHYJG7M.js +11 -0
  33. package/dist/{run-A44XQ77Z.js → run-UCSNYSQO.js} +290 -26
  34. package/dist/run-UCSNYSQO.js.map +1 -0
  35. package/package.json +13 -3
  36. package/web/public/app.css +481 -0
  37. package/web/public/app.js +522 -0
  38. package/web/public/index.html +113 -0
  39. package/dist/chunk-4OQ2ARLP.js.map +0 -1
  40. package/dist/chunk-LTPXKMPE.js.map +0 -1
  41. package/dist/chunk-OZVHOVDT.js.map +0 -1
  42. package/dist/chunk-X5QCISNM.js +0 -180
  43. package/dist/chunk-X5QCISNM.js.map +0 -1
  44. package/dist/chunk-YAFHVFV5.js +0 -199
  45. package/dist/chunk-YAFHVFV5.js.map +0 -1
  46. package/dist/execute-WKYSGIGH.js +0 -11
  47. package/dist/init-SXFGOCML.js +0 -9
  48. package/dist/plan-JUOBYTCD.js +0 -12
  49. package/dist/pr-CYZYJZZV.js +0 -12
  50. package/dist/prd-PZOY3FCC.js +0 -12
  51. package/dist/review-MK5FTVUE.js +0 -12
  52. package/dist/run-A44XQ77Z.js.map +0 -1
  53. /package/dist/{chunk-CSSXTPWE.js.map → chunk-Q2TQCJDN.js.map} +0 -0
  54. /package/dist/{execute-WKYSGIGH.js.map → execute-DALVYZIH.js.map} +0 -0
  55. /package/dist/{generate-HP4CPMJG.js.map → generate-5OWWNRIB.js.map} +0 -0
  56. /package/dist/{init-SXFGOCML.js.map → init-6AWQI3IE.js.map} +0 -0
  57. /package/dist/{plan-JUOBYTCD.js.map → plan-RQXK2OCP.js.map} +0 -0
  58. /package/dist/{pr-CYZYJZZV.js.map → pr-UU4H7ZZR.js.map} +0 -0
  59. /package/dist/{prd-PZOY3FCC.js.map → prd-WB3QH6QF.js.map} +0 -0
  60. /package/dist/{review-MK5FTVUE.js.map → review-2WHYJG7M.js.map} +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fábio Assunção
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -82,6 +82,9 @@ npx issue-flow run 42 --from execute
82
82
 
83
83
  # Manual mode (artifacts only, no execution)
84
84
  npx issue-flow run 42 --mode manual
85
+
86
+ # Watch the run live in the browser (see "Web Monitoring" below)
87
+ npx issue-flow run 42 --web
85
88
  ```
86
89
 
87
90
  Executes all phases in order: **init** → **analyze** → **prd** → **plan** → **execute** → **review** → **pr**. Automatically resumes from the last incomplete phase if pipeline state exists. On review failure, runs correction cycles (re-execute + re-review) up to `maxCorrectionCycles`.
@@ -159,6 +162,33 @@ npx issue-flow generate --prompt "Add dark mode support to the settings page"
159
162
 
160
163
  Analyzes the project and creates a detailed GitHub issue via Claude headless.
161
164
 
165
+ ## Web Monitoring
166
+
167
+ `run` and `execute` support an optional (off by default) real-time monitoring mode: a local HTTP server (plain `node:http`, zero new dependencies) serves a self-contained, read-only web UI with live progress -- current phase and activity, user stories, commits, pull requests, logs, and time estimates.
168
+
169
+ ```bash
170
+ # Enable with defaults (http://127.0.0.1:3737)
171
+ npx issue-flow run 42 --web
172
+
173
+ # Remote access over Tailscale: bind to your machine's Tailscale IP
174
+ npx issue-flow run 42 --web --host 100.101.102.103
175
+ ```
176
+
177
+ Each setting resolves with the precedence **CLI flag > environment variable > `.issue-flow.json` > default**:
178
+
179
+ | CLI flag | Environment variable | `.issue-flow.json` key | Default |
180
+ |----------|----------------------|------------------------|---------|
181
+ | `--web` / `--serve` | `ISSUE_FLOW_WEB` | `web.enabled` | `false` |
182
+ | `--port <n>` | `ISSUE_FLOW_WEB_PORT` | `web.port` | `3737` |
183
+ | `--host <h>` | `ISSUE_FLOW_WEB_HOST` | `web.host` | `127.0.0.1` |
184
+ | `--refresh <s>` | `ISSUE_FLOW_WEB_REFRESH` | `web.refreshSeconds` | `5` |
185
+ | `--web-log-limit <n>` | `ISSUE_FLOW_WEB_LOG_LIMIT` | `web.logLimit` | `200` |
186
+ | `--web-no-logs` | -- | `web.includeLogs` | logs included |
187
+
188
+ Monitoring never affects the pipeline: with `--web` off the behavior is byte-for-byte identical, a busy port just skips the server with a warning, and killing the server mid-run has no effect on the execution. While enabled, the snapshot served at `/api/status` is also persisted to `issues/N/session.json` -- if your project commits the `issues/` directory, add `issues/*/session.json` to your `.gitignore`.
189
+
190
+ For the full documentation (endpoints, `session.json` format, Tailscale setup), see the [root README](../../README.md#web-monitoring).
191
+
162
192
  ## Pipeline State
163
193
 
164
194
  Each issue's state is tracked in `issues/N/tasks.json`:
@@ -211,6 +241,11 @@ src/
211
241
  pipeline.ts # Pipeline state machine
212
242
  state-manager.ts # Typed CRUD for tasks.json
213
243
  prompt-resolver.ts # Prompt resolution and templating
244
+ session-state.ts # Session snapshot reducer and publishers
245
+ session-publisher.ts # Global session publisher slot
246
+ session-git.ts # Commit/PR enrichment for the snapshot
247
+ web/
248
+ server.ts # Web monitoring HTTP server (assets in web/public/)
214
249
  ui/
215
250
  logger.ts # Colored logging utilities
216
251
  progress.ts # Progress bar and iteration headers
@@ -240,7 +275,9 @@ npm test
240
275
  npm run dev
241
276
  ```
242
277
 
243
- For the full development setup, local testing, and NPM publishing guide, see [CONTRIBUTING.md](CONTRIBUTING.md).
278
+ For the full development setup and local testing guide, see [CONTRIBUTING.md](CONTRIBUTING.md).
279
+ The official release procedure (changelog, version bump, tag, `npm publish`, GitHub
280
+ Release) is documented in [CONTRIBUTING.md → Release process](CONTRIBUTING.md#release-process).
244
281
 
245
282
  ## Credits
246
283
 
@@ -1,21 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runHeadless
4
- } from "./chunk-4OQ2ARLP.js";
5
- import {
6
- loadTaskPlan,
7
- saveTaskPlan
8
- } from "./chunk-X5QCISNM.js";
4
+ } from "./chunk-2XWXVWVD.js";
9
5
  import {
10
6
  applyPlaceholders,
11
7
  loadPrompt
12
- } from "./chunk-OZVHOVDT.js";
8
+ } from "./chunk-SLHZPB3C.js";
13
9
  import {
14
10
  getGlobalTimeout,
11
+ loadTaskPlan,
15
12
  printError,
16
13
  printInfo,
17
- printSuccess
18
- } from "./chunk-YAFHVFV5.js";
14
+ printSuccess,
15
+ saveTaskPlan
16
+ } from "./chunk-FNLXZXWP.js";
19
17
 
20
18
  // src/commands/analyze.ts
21
19
  import { mkdir, readFile, writeFile } from "fs/promises";
@@ -65,4 +63,4 @@ async function runAnalyze(issue) {
65
63
  export {
66
64
  runAnalyze
67
65
  };
68
- //# sourceMappingURL=analyze-PDDIU7HD.js.map
66
+ //# sourceMappingURL=analyze-IJ2SYXVM.js.map
@@ -1 +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
+ {"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":[]}
@@ -5,9 +5,11 @@ import {
5
5
  formatDuration,
6
6
  getIcons,
7
7
  getOutputCallback,
8
+ getSessionPublisher,
8
9
  isVerbose,
10
+ isoNow,
9
11
  useColor
10
- } from "./chunk-YAFHVFV5.js";
12
+ } from "./chunk-FNLXZXWP.js";
11
13
 
12
14
  // src/core/headless.ts
13
15
  import { createInterface } from "readline";
@@ -69,6 +71,12 @@ function printStreamEvent(line, state, onOutput) {
69
71
  }
70
72
  if (block.type === "tool_use" && block.name) {
71
73
  const context = block.input ? getToolContext(block.name, block.input) : "";
74
+ getSessionPublisher().publish({
75
+ type: "activity",
76
+ at: isoNow(),
77
+ tool: block.name,
78
+ detail: context || void 0
79
+ });
72
80
  const toolName = block.name.padEnd(12);
73
81
  const prefix = colored ? chalk.dim(` ${icons.connector} `) : ` ${icons.connector} `;
74
82
  const toolIcon = colored ? chalk.cyan(icons.tool) : icons.tool;
@@ -272,4 +280,4 @@ async function runHeadless(options) {
272
280
  export {
273
281
  runHeadless
274
282
  };
275
- //# sourceMappingURL=chunk-4OQ2ARLP.js.map
283
+ //# sourceMappingURL=chunk-2XWXVWVD.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 { getSessionPublisher } from './session-publisher.js';\nimport { isoNow } from './state-manager.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 getSessionPublisher().publish({\n type: 'activity',\n at: isoNow(),\n tool: block.name,\n detail: context || undefined,\n });\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,21 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runHeadless
4
- } from "./chunk-4OQ2ARLP.js";
5
- import {
6
- loadTaskPlan,
7
- saveTaskPlan,
8
- taskPlanSchema
9
- } from "./chunk-X5QCISNM.js";
4
+ } from "./chunk-2XWXVWVD.js";
10
5
  import {
11
6
  applyPlaceholders,
12
7
  loadPrompt
13
- } from "./chunk-OZVHOVDT.js";
8
+ } from "./chunk-SLHZPB3C.js";
14
9
  import {
15
10
  getGlobalTimeout,
11
+ loadTaskPlan,
16
12
  printError,
17
- printSuccess
18
- } from "./chunk-YAFHVFV5.js";
13
+ printSuccess,
14
+ saveTaskPlan,
15
+ taskPlanSchema
16
+ } from "./chunk-FNLXZXWP.js";
19
17
 
20
18
  // src/commands/plan.ts
21
19
  import { mkdir, readFile } from "fs/promises";
@@ -82,4 +80,4 @@ ${issues}`);
82
80
  export {
83
81
  runPlan
84
82
  };
85
- //# sourceMappingURL=chunk-ZJB2IMPU.js.map
83
+ //# sourceMappingURL=chunk-AY7UZAGX.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/plan.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 { taskPlanSchema } from '../schemas.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\nexport async function runPlan(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n\n // Read the PRD\n const prdPath = join(issueDir, 'prd.md');\n let prdContent: string;\n try {\n prdContent = await readFile(prdPath, 'utf-8');\n } catch {\n printError(`PRD not found at ${prdPath}. Run 'issue-flow prd ${issueNumber}' first.`);\n return 1;\n }\n\n const tasksPath = join(issueDir, 'tasks.json');\n\n const template = await loadPrompt('plan');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __PRD_CONTENT__: prdContent,\n __TASKS_PATH__: tasksPath,\n });\n\n await mkdir(issueDir, { recursive: true });\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: `Converting PRD to task plan for issue #${issueNumber}...`,\n });\n\n if (!result.success) {\n printError(`Task plan generation failed: ${result.error}`);\n return 1;\n }\n\n // Validate the created file\n let rawContent: string;\n try {\n rawContent = await readFile(tasksPath, 'utf-8');\n } catch {\n printError(`tasks.json was not created at ${tasksPath}`);\n return 1;\n }\n\n // Validate JSON structure\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawContent);\n } catch {\n printError('tasks.json contains invalid JSON');\n return 1;\n }\n\n // Validate with zod schema\n const validation = taskPlanSchema.safeParse(parsed);\n if (!validation.success) {\n const issues = validation.error.issues\n .map((i) => ` - ${i.path.join('.')}: ${i.message}`)\n .join('\\n');\n printError(`tasks.json does not match expected schema:\\n${issues}`);\n return 1;\n }\n\n // Ensure pipeline state reflects completion of this phase\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.jsonCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n\n printSuccess(`Task plan saved to ${tasksPath} (${plan.userStories.length} stories)`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,SAAS,OAAO,gBAAgB;AAChC,SAAS,YAAY;AAQrB,eAAsB,QAAQ,OAAgC;AAC5D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAG3C,QAAM,UAAU,KAAK,UAAU,QAAQ;AACvC,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,SAAS,SAAS,OAAO;AAAA,EAC9C,QAAQ;AACN,eAAW,oBAAoB,OAAO,yBAAyB,WAAW,UAAU;AACpF,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,KAAK,UAAU,YAAY;AAE7C,QAAM,WAAW,MAAM,WAAW,MAAM;AACxC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,EAClB,CAAC;AAED,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAEzC,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,0CAA0C,WAAW;AAAA,EACtE,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,gCAAgC,OAAO,KAAK,EAAE;AACzD,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,SAAS,WAAW,OAAO;AAAA,EAChD,QAAQ;AACN,eAAW,iCAAiC,SAAS,EAAE;AACvD,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,UAAU;AAAA,EAChC,QAAQ;AACN,eAAW,kCAAkC;AAC7C,WAAO;AAAA,EACT;AAGA,QAAM,aAAa,eAAe,UAAU,MAAM;AAClD,MAAI,CAAC,WAAW,SAAS;AACvB,UAAM,SAAS,WAAW,MAAM,OAC7B,IAAI,CAAC,MAAM,OAAO,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAClD,KAAK,IAAI;AACZ,eAAW;AAAA,EAA+C,MAAM,EAAE;AAClE,WAAO;AAAA,EACT;AAGA,QAAM,OAAO,MAAM,aAAa,SAAS;AACzC,OAAK,SAAS,gBAAgB;AAC9B,QAAM,aAAa,WAAW,IAAI;AAElC,eAAa,sBAAsB,SAAS,KAAK,KAAK,YAAY,MAAM,WAAW;AACnF,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/commands/plan.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 { taskPlanSchema } from '../schemas.js';\nimport { printError, printSuccess } from '../ui/logger.js';\n\nexport async function runPlan(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n\n // Read the PRD\n const prdPath = join(issueDir, 'prd.md');\n let prdContent: string;\n try {\n prdContent = await readFile(prdPath, 'utf-8');\n } catch {\n printError(`PRD not found at ${prdPath}. Run 'issue-flow prd ${issueNumber}' first.`);\n return 1;\n }\n\n const tasksPath = join(issueDir, 'tasks.json');\n\n const template = await loadPrompt('plan');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __PRD_CONTENT__: prdContent,\n __TASKS_PATH__: tasksPath,\n });\n\n await mkdir(issueDir, { recursive: true });\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: `Converting PRD to task plan for issue #${issueNumber}...`,\n });\n\n if (!result.success) {\n printError(`Task plan generation failed: ${result.error}`);\n return 1;\n }\n\n // Validate the created file\n let rawContent: string;\n try {\n rawContent = await readFile(tasksPath, 'utf-8');\n } catch {\n printError(`tasks.json was not created at ${tasksPath}`);\n return 1;\n }\n\n // Validate JSON structure\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawContent);\n } catch {\n printError('tasks.json contains invalid JSON');\n return 1;\n }\n\n // Validate with zod schema\n const validation = taskPlanSchema.safeParse(parsed);\n if (!validation.success) {\n const issues = validation.error.issues\n .map((i) => ` - ${i.path.join('.')}: ${i.message}`)\n .join('\\n');\n printError(`tasks.json does not match expected schema:\\n${issues}`);\n return 1;\n }\n\n // Ensure pipeline state reflects completion of this phase\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.jsonCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n\n printSuccess(`Task plan saved to ${tasksPath} (${plan.userStories.length} stories)`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA,SAAS,OAAO,gBAAgB;AAChC,SAAS,YAAY;AAQrB,eAAsB,QAAQ,OAAgC;AAC5D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAG3C,QAAM,UAAU,KAAK,UAAU,QAAQ;AACvC,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,SAAS,SAAS,OAAO;AAAA,EAC9C,QAAQ;AACN,eAAW,oBAAoB,OAAO,yBAAyB,WAAW,UAAU;AACpF,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,KAAK,UAAU,YAAY;AAE7C,QAAM,WAAW,MAAM,WAAW,MAAM;AACxC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,EAClB,CAAC;AAED,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAEzC,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,0CAA0C,WAAW;AAAA,EACtE,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,gCAAgC,OAAO,KAAK,EAAE;AACzD,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,SAAS,WAAW,OAAO;AAAA,EAChD,QAAQ;AACN,eAAW,iCAAiC,SAAS,EAAE;AACvD,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,UAAU;AAAA,EAChC,QAAQ;AACN,eAAW,kCAAkC;AAC7C,WAAO;AAAA,EACT;AAGA,QAAM,aAAa,eAAe,UAAU,MAAM;AAClD,MAAI,CAAC,WAAW,SAAS;AACvB,UAAM,SAAS,WAAW,MAAM,OAC7B,IAAI,CAAC,MAAM,OAAO,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAClD,KAAK,IAAI;AACZ,eAAW;AAAA,EAA+C,MAAM,EAAE;AAClE,WAAO;AAAA,EACT;AAGA,QAAM,OAAO,MAAM,aAAa,SAAS;AACzC,OAAK,SAAS,gBAAgB;AAC9B,QAAM,aAAa,WAAW,IAAI;AAElC,eAAa,sBAAsB,SAAS,KAAK,KAAK,YAAY,MAAM,WAAW;AACnF,SAAO;AACT;","names":[]}