issue-flow 0.12.0 → 0.13.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 (185) hide show
  1. package/README.md +79 -274
  2. package/dist/agent-BXRIEKVM.js +238 -0
  3. package/dist/agent-BXRIEKVM.js.map +1 -0
  4. package/dist/{analyze-OEET7KGE.js → analyze-WS47UYGN.js} +28 -16
  5. package/dist/analyze-WS47UYGN.js.map +1 -0
  6. package/dist/{apply-ZIA3C52S.js → apply-OS62UWRN.js} +15 -7
  7. package/dist/{apply-ZIA3C52S.js.map → apply-OS62UWRN.js.map} +1 -1
  8. package/dist/bench-SA3BOME5.js +906 -0
  9. package/dist/bench-SA3BOME5.js.map +1 -0
  10. package/dist/chunk-37N2RJW2.js +120 -0
  11. package/dist/chunk-37N2RJW2.js.map +1 -0
  12. package/dist/chunk-3XLCNVMD.js +180 -0
  13. package/dist/chunk-3XLCNVMD.js.map +1 -0
  14. package/dist/chunk-5TB6KLC2.js +72 -0
  15. package/dist/chunk-5TB6KLC2.js.map +1 -0
  16. package/dist/{chunk-MJULIKQU.js → chunk-664Y3NDX.js} +23 -47
  17. package/dist/chunk-664Y3NDX.js.map +1 -0
  18. package/dist/chunk-6FDFRNXZ.js +357 -0
  19. package/dist/chunk-6FDFRNXZ.js.map +1 -0
  20. package/dist/{chunk-56KS3TFQ.js → chunk-7KD66LIM.js} +115 -160
  21. package/dist/chunk-7KD66LIM.js.map +1 -0
  22. package/dist/chunk-7Z4Y3VKM.js +26 -0
  23. package/dist/chunk-7Z4Y3VKM.js.map +1 -0
  24. package/dist/chunk-ARFVHC2F.js +1111 -0
  25. package/dist/chunk-ARFVHC2F.js.map +1 -0
  26. package/dist/{chunk-NS6R37II.js → chunk-BHJCGWQX.js} +141 -12
  27. package/dist/chunk-BHJCGWQX.js.map +1 -0
  28. package/dist/chunk-BQDQTIM7.js +74 -0
  29. package/dist/chunk-BQDQTIM7.js.map +1 -0
  30. package/dist/chunk-CNBYCMSU.js +2709 -0
  31. package/dist/chunk-CNBYCMSU.js.map +1 -0
  32. package/dist/chunk-CRGIQFUQ.js +83 -0
  33. package/dist/chunk-CRGIQFUQ.js.map +1 -0
  34. package/dist/chunk-DIG5XKQQ.js +298 -0
  35. package/dist/chunk-DIG5XKQQ.js.map +1 -0
  36. package/dist/chunk-FZDR7CGH.js +71 -0
  37. package/dist/chunk-FZDR7CGH.js.map +1 -0
  38. package/dist/chunk-HEF2RMKN.js +413 -0
  39. package/dist/chunk-HEF2RMKN.js.map +1 -0
  40. package/dist/{chunk-M5UCXTIT.js → chunk-HJGGNLWI.js} +22 -18
  41. package/dist/chunk-HJGGNLWI.js.map +1 -0
  42. package/dist/chunk-HQAHNIAR.js +283 -0
  43. package/dist/chunk-HQAHNIAR.js.map +1 -0
  44. package/dist/{chunk-FJP3UQGD.js → chunk-IHIEXUTC.js} +39 -19
  45. package/dist/chunk-IHIEXUTC.js.map +1 -0
  46. package/dist/{chunk-Y5ZDJ4PJ.js → chunk-IV5SS4JN.js} +18 -10
  47. package/dist/chunk-IV5SS4JN.js.map +1 -0
  48. package/dist/{chunk-BYZDPR7N.js → chunk-IYV6KNVF.js} +146 -13
  49. package/dist/chunk-IYV6KNVF.js.map +1 -0
  50. package/dist/{chunk-TSOLWK7Y.js → chunk-J62QSVP6.js} +8 -8
  51. package/dist/chunk-KQHB7ZT3.js +559 -0
  52. package/dist/chunk-KQHB7ZT3.js.map +1 -0
  53. package/dist/chunk-L6HSKTQE.js +77 -0
  54. package/dist/chunk-L6HSKTQE.js.map +1 -0
  55. package/dist/{chunk-3QHK4YQA.js → chunk-LZ6GFQAG.js} +334 -117
  56. package/dist/chunk-LZ6GFQAG.js.map +1 -0
  57. package/dist/{chunk-LAFNLLQP.js → chunk-MIVHK5YI.js} +5 -5
  58. package/dist/chunk-NEZZ6KDR.js +1217 -0
  59. package/dist/chunk-NEZZ6KDR.js.map +1 -0
  60. package/dist/{chunk-NTMMF5UD.js → chunk-OPNGPUKJ.js} +436 -254
  61. package/dist/chunk-OPNGPUKJ.js.map +1 -0
  62. package/dist/chunk-P3LRUNC6.js +16 -0
  63. package/dist/chunk-P3LRUNC6.js.map +1 -0
  64. package/dist/{chunk-FADTCQGM.js → chunk-PIVGJM6K.js} +94 -32
  65. package/dist/chunk-PIVGJM6K.js.map +1 -0
  66. package/dist/{chunk-73ATPIYX.js → chunk-PV4OX5TC.js} +36 -71
  67. package/dist/chunk-PV4OX5TC.js.map +1 -0
  68. package/dist/chunk-QCXPNSEL.js +141 -0
  69. package/dist/chunk-QCXPNSEL.js.map +1 -0
  70. package/dist/{chunk-XQUSIEKA.js → chunk-QXDNBYQF.js} +52 -14
  71. package/dist/chunk-QXDNBYQF.js.map +1 -0
  72. package/dist/chunk-SNTTOQAD.js +30 -0
  73. package/dist/chunk-SNTTOQAD.js.map +1 -0
  74. package/dist/{chunk-QCPGHBSZ.js → chunk-TM67EHSV.js} +63 -21
  75. package/dist/chunk-TM67EHSV.js.map +1 -0
  76. package/dist/chunk-TVN2JVOF.js +77 -0
  77. package/dist/chunk-TVN2JVOF.js.map +1 -0
  78. package/dist/chunk-TX5WIVYP.js +129 -0
  79. package/dist/chunk-TX5WIVYP.js.map +1 -0
  80. package/dist/{chunk-A4SLHOW7.js → chunk-UJ7XWK7J.js} +24 -18
  81. package/dist/chunk-UJ7XWK7J.js.map +1 -0
  82. package/dist/{chunk-EZATO3DW.js → chunk-UOCPC2Y2.js} +594 -17
  83. package/dist/chunk-UOCPC2Y2.js.map +1 -0
  84. package/dist/chunk-XEYH6ZZI.js +289 -0
  85. package/dist/chunk-XEYH6ZZI.js.map +1 -0
  86. package/dist/cli.js +264 -44
  87. package/dist/cli.js.map +1 -1
  88. package/dist/{config-PRJGWW7J.js → config-MEM2IWWN.js} +28 -5
  89. package/dist/contract-JH2WBIO6.js +8 -0
  90. package/dist/conventions-WV4NDXGH.js +143 -0
  91. package/dist/conventions-WV4NDXGH.js.map +1 -0
  92. package/dist/execute-NITIHHCK.js +31 -0
  93. package/dist/{generate-EGB4KV2B.js → generate-MVQI7A45.js} +34 -20
  94. package/dist/{generate-EGB4KV2B.js.map → generate-MVQI7A45.js.map} +1 -1
  95. package/dist/git-YRVRUN3C.js +31 -0
  96. package/dist/init-OKWEC3EV.js +21 -0
  97. package/dist/{operations-BYT626KQ.js → operations-LU6JA3BX.js} +24 -15
  98. package/dist/{operations-BYT626KQ.js.map → operations-LU6JA3BX.js.map} +1 -1
  99. package/dist/permissions-7NMDFU27.js +18 -0
  100. package/dist/plan-LEJJF3DE.js +31 -0
  101. package/dist/{policy-6S4GFC76.js → policy-JSYEK5B2.js} +17 -6
  102. package/dist/policy-JSYEK5B2.js.map +1 -0
  103. package/dist/pr-HPVUZ7S3.js +36 -0
  104. package/dist/pr-review-R2QZBT3F.js +29 -0
  105. package/dist/prd-5O6GYCC6.js +30 -0
  106. package/dist/ps-TNE44R5V.js +90 -0
  107. package/dist/ps-TNE44R5V.js.map +1 -0
  108. package/dist/recorder-XGF55WXI.js +31 -0
  109. package/dist/recorder-XGF55WXI.js.map +1 -0
  110. package/dist/registry-LGPE53QD.js +15 -0
  111. package/dist/registry-LGPE53QD.js.map +1 -0
  112. package/dist/{resume-PPCNQVVV.js → resume-QMEPMSZW.js} +51 -34
  113. package/dist/{resume-PPCNQVVV.js.map → resume-QMEPMSZW.js.map} +1 -1
  114. package/dist/review-YKWD6SNN.js +32 -0
  115. package/dist/review-YKWD6SNN.js.map +1 -0
  116. package/dist/routing-2ZUMZPJB.js +91 -0
  117. package/dist/routing-2ZUMZPJB.js.map +1 -0
  118. package/dist/run-YU4TR62R.js +50 -0
  119. package/dist/run-YU4TR62R.js.map +1 -0
  120. package/dist/{state-manager-2EDVDBAR.js → state-manager-X3PT3FGT.js} +4 -2
  121. package/dist/state-manager-X3PT3FGT.js.map +1 -0
  122. package/dist/usage-VCFLDA5Y.js +174 -0
  123. package/dist/usage-VCFLDA5Y.js.map +1 -0
  124. package/dist/{web-EF5WIIGG.js → web-XVHLX62B.js} +41 -30
  125. package/dist/web-XVHLX62B.js.map +1 -0
  126. package/package.json +6 -2
  127. package/prompts/execute.md +7 -8
  128. package/prompts/plan.md +4 -5
  129. package/prompts/pr.md +2 -2
  130. package/web/AGENTS.md +3 -3
  131. package/web/public/app.css +38 -0
  132. package/web/public/app.js +121 -11
  133. package/web/public/index.html +24 -0
  134. package/dist/analyze-OEET7KGE.js.map +0 -1
  135. package/dist/chunk-3QHK4YQA.js.map +0 -1
  136. package/dist/chunk-42PJFMAR.js +0 -605
  137. package/dist/chunk-42PJFMAR.js.map +0 -1
  138. package/dist/chunk-4JSOYE4G.js +0 -368
  139. package/dist/chunk-4JSOYE4G.js.map +0 -1
  140. package/dist/chunk-56KS3TFQ.js.map +0 -1
  141. package/dist/chunk-73ATPIYX.js.map +0 -1
  142. package/dist/chunk-A4SLHOW7.js.map +0 -1
  143. package/dist/chunk-BAUPQXPE.js +0 -173
  144. package/dist/chunk-BAUPQXPE.js.map +0 -1
  145. package/dist/chunk-BYZDPR7N.js.map +0 -1
  146. package/dist/chunk-CD5ULRVC.js +0 -615
  147. package/dist/chunk-CD5ULRVC.js.map +0 -1
  148. package/dist/chunk-DIDUVL6O.js +0 -1365
  149. package/dist/chunk-DIDUVL6O.js.map +0 -1
  150. package/dist/chunk-EZATO3DW.js.map +0 -1
  151. package/dist/chunk-FADTCQGM.js.map +0 -1
  152. package/dist/chunk-FJP3UQGD.js.map +0 -1
  153. package/dist/chunk-LWRSMTTZ.js +0 -18
  154. package/dist/chunk-LWRSMTTZ.js.map +0 -1
  155. package/dist/chunk-M5UCXTIT.js.map +0 -1
  156. package/dist/chunk-M66SJHQQ.js +0 -206
  157. package/dist/chunk-M66SJHQQ.js.map +0 -1
  158. package/dist/chunk-MJULIKQU.js.map +0 -1
  159. package/dist/chunk-NS6R37II.js.map +0 -1
  160. package/dist/chunk-NTMMF5UD.js.map +0 -1
  161. package/dist/chunk-QCPGHBSZ.js.map +0 -1
  162. package/dist/chunk-XQUSIEKA.js.map +0 -1
  163. package/dist/chunk-Y5ZDJ4PJ.js.map +0 -1
  164. package/dist/execute-XBNVL753.js +0 -18
  165. package/dist/init-AJHCXJBP.js +0 -10
  166. package/dist/plan-QOZ3RIO6.js +0 -21
  167. package/dist/policy-6S4GFC76.js.map +0 -1
  168. package/dist/pr-EBBHD2ZX.js +0 -26
  169. package/dist/pr-review-VVMW5PO4.js +0 -20
  170. package/dist/prd-JQSZF7PR.js +0 -21
  171. package/dist/review-3F6MARWH.js +0 -19
  172. package/dist/run-RXE5GDQO.js +0 -36
  173. package/dist/web-EF5WIIGG.js.map +0 -1
  174. /package/dist/{chunk-TSOLWK7Y.js.map → chunk-J62QSVP6.js.map} +0 -0
  175. /package/dist/{chunk-LAFNLLQP.js.map → chunk-MIVHK5YI.js.map} +0 -0
  176. /package/dist/{config-PRJGWW7J.js.map → config-MEM2IWWN.js.map} +0 -0
  177. /package/dist/{execute-XBNVL753.js.map → contract-JH2WBIO6.js.map} +0 -0
  178. /package/dist/{init-AJHCXJBP.js.map → execute-NITIHHCK.js.map} +0 -0
  179. /package/dist/{plan-QOZ3RIO6.js.map → git-YRVRUN3C.js.map} +0 -0
  180. /package/dist/{pr-EBBHD2ZX.js.map → init-OKWEC3EV.js.map} +0 -0
  181. /package/dist/{pr-review-VVMW5PO4.js.map → permissions-7NMDFU27.js.map} +0 -0
  182. /package/dist/{prd-JQSZF7PR.js.map → plan-LEJJF3DE.js.map} +0 -0
  183. /package/dist/{review-3F6MARWH.js.map → pr-HPVUZ7S3.js.map} +0 -0
  184. /package/dist/{run-RXE5GDQO.js.map → pr-review-R2QZBT3F.js.map} +0 -0
  185. /package/dist/{state-manager-2EDVDBAR.js.map → prd-5O6GYCC6.js.map} +0 -0
package/README.md CHANGED
@@ -1,310 +1,115 @@
1
1
  # issue-flow
2
2
 
3
- > This is the npm package README. For full project documentation, see the [root README](../../README.md).
3
+ **Turn an issue into a reviewed Pull Request, without sitting in front of it.**
4
4
 
5
- Unified CLI that orchestrates the full issue-to-PR pipeline via [Claude Code](https://docs.anthropic.com/en/docs/claude-code) Headless mode. Analyzes issues, generates PRDs, creates task plans, implements code iteratively, reviews results, and opens pull requests -- all programmatically, without interactive sessions.
6
-
7
- Built on the [Ralph pattern](https://ghuntley.com/ralph/) for autonomous AI agent loops.
8
-
9
- ## Pipeline Flow
10
-
11
- ```mermaid
12
- flowchart LR
13
- subgraph "issue-flow run 42"
14
- A["init"] --> B["analyze"]
15
- B --> C["prd"]
16
- C --> D["plan"]
17
- D --> E["execute"]
18
- E --> F["review"]
19
- F --> G{PASS?}
20
- G -- Yes --> H["pr"]
21
- G -- No --> I{"Retries\n< max?"}
22
- I -- Yes --> E
23
- I -- No --> J["Stop"]
24
- end
25
- ```
26
-
27
- ## Requirements
28
-
29
- - **Node.js** >= 18.0.0
30
- - **git** installed and available in PATH
31
- - **Claude Code** (`npm install -g @anthropic-ai/claude-code`)
32
- - **GitHub CLI** (`gh`) authenticated (`gh auth login`)
33
-
34
- Run `npx issue-flow init` to verify all prerequisites.
35
-
36
- ## Installation
5
+ A CLI that orchestrates the whole path analyse, plan, implement, verify,
6
+ review, deliver — by driving a coding agent in headless mode:
7
+ [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (the default),
8
+ [Codex CLI](https://developers.openai.com/codex/noninteractive), Cursor CLI or
9
+ [Antigravity CLI](https://antigravity.google/docs/cli/getting-started/), one
10
+ agent per phase if you want.
37
11
 
38
12
  ```bash
39
- # Run directly via npx (no install needed)
40
- npx issue-flow run 42
41
-
42
- # Or install globally
43
- npm install -g issue-flow
44
- issue-flow run 42
13
+ npx issue-flow init # check prerequisites and repository conventions
14
+ npx issue-flow run 42 # prd → plan → execute → review → pr
15
+ npx issue-flow run 42 --web # …and watch it live at http://localhost:3737
45
16
  ```
46
17
 
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) |
18
+ > **Full documentation lives in the repository:**
19
+ > [README](https://github.com/fabioassuncao/issue-flow#readme) ·
20
+ > [Commands](https://github.com/fabioassuncao/issue-flow/blob/main/docs/commands.md) ·
21
+ > [Configuration](https://github.com/fabioassuncao/issue-flow/blob/main/docs/configuration.md) ·
22
+ > [Agents](https://github.com/fabioassuncao/issue-flow/blob/main/docs/agents.md) ·
23
+ > [Storage](https://github.com/fabioassuncao/issue-flow/blob/main/docs/storage.md) ·
24
+ > [Web monitoring](https://github.com/fabioassuncao/issue-flow/blob/main/docs/web-monitor.md) ·
25
+ > [Resilience](https://github.com/fabioassuncao/issue-flow/blob/main/docs/resilience.md)
55
26
 
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
- ```
27
+ ## Requirements
69
28
 
70
- Every phase that invokes Claude once (`analyze`, `prd`, `plan`, `review`, `pr`, `pr-review`, `generate`) shares a default limit of **15 minutes** per invocation; `execute` has no limit, since its iteration budget is what bounds it. Use `--timeout` to raise it on a large issue, or `--timeout 0` to remove it. When an invocation is cut short the phase reports it as a timeout and retries with backoff, so the limit is the first thing to raise if a phase keeps dying at the same elapsed time.
29
+ - **Node.js** 22
30
+ - **Git**, available in `PATH`, inside a repository
31
+ - **A coding agent** — `npm install -g @anthropic-ai/claude-code` for the default
32
+ - **GitHub CLI** (`gh`), authenticated — only for GitHub issues; a run on local
33
+ issues does not need it
34
+
35
+ ## What it does
36
+
37
+ - The full pipeline `prd` → `plan` → `execute` → `review` → `pr`, plus an
38
+ optional whole-PR review. Every phase is also a standalone command.
39
+ - An iterative execute loop: each iteration is a fresh agent instance that picks
40
+ the highest-priority pending user story, implements it, runs quality checks and
41
+ commits.
42
+ - An objective acceptance contract (typecheck, lint, tests) before the LLM
43
+ judges. An empty contract finishes `unverified`, never green.
44
+ - Per-phase agent selection, resolved explicitly and never inferred from which
45
+ binary happens to be installed.
46
+ - Resilience for long unattended runs: a failure taxonomy, per-kind retry
47
+ budgets, provider failover with circuit breakers, an inactivity watchdog and an
48
+ append-only event journal.
49
+ - Multi-issue queues discovered from sub-issues and dependencies: one branch, one
50
+ Pull Request.
51
+ - A read-only live web monitor, one card per active run across every project.
52
+ - Issues from GitHub or from plain files — the local provider needs nothing
53
+ beyond git.
54
+ - Repository conventions (templates, labels, base branch, commit and branch
55
+ format) discovered rather than imposed.
71
56
 
72
57
  ## Commands
73
58
 
74
- ### `run` -- Full pipeline (end-to-end)
75
-
76
- ```bash
77
- # Run the complete pipeline for an issue
78
- npx issue-flow run 42
79
-
80
- # Resume from a specific phase
81
- npx issue-flow run 42 --from execute
82
-
83
- # Manual mode (artifacts only, no execution)
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
59
+ | Command | What it does |
60
+ |---------|--------------|
61
+ | `run <issues...>` | The full pipeline, for one issue or a queue |
62
+ | `resume [issue]` | Continue an interrupted pipeline, explicitly |
63
+ | `generate` | Draft and create an issue on GitHub, locally, or both |
64
+ | `init` | Check prerequisites and report (or create) missing conventions |
65
+ | `analyze`, `prd`, `plan`, `execute`, `review`, `pr`, `pr-review` | The phases, standalone |
66
+ | `status`, `ps`, `runs`, `logs`, `usage`, `pause`, `cancel` | Operate a running pipeline |
67
+ | `agent`, `policy`, `conventions`, `routing` | Inspect what was resolved, and why |
68
+ | `web serve`, `web stop` | The monitoring server |
88
69
 
89
- # Continue User Story numbering from the last used in this project
90
- npx issue-flow run 42 --continue
70
+ Run `issue-flow <command> --help` for the flags, or read the
71
+ [command reference](https://github.com/fabioassuncao/issue-flow/blob/main/docs/commands.md).
91
72
 
92
- # Force User Story numbering to start at a specific number
93
- npx issue-flow run 42 --start-us 27
94
- ```
95
-
96
- 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, its findings are saved verbatim to `lastReviewFindings` in `tasks.json`, and a correction cycle (re-execute + re-review) runs up to `maxCorrectionCycles`. The re-execute step reads `lastReviewFindings` and treats the issue as unresolved even if every user story already has `passes: true`, until the findings are addressed and the field is cleared back to `null`.
97
-
98
- `--continue` and `--start-us <n>` control User Story (`US-NNN`) numbering continuity across `plan` runs of the same project — see [`plan`](#plan----convert-prd-to-task-plan) below.
99
-
100
- ### `init` -- Check prerequisites
101
-
102
- ```bash
103
- npx issue-flow init
104
- ```
105
-
106
- Verifies that `claude`, `gh` (authenticated), and `git` (inside a repo) are available. Reports pass/fail for each with install hints.
73
+ ## Where things are written
107
74
 
108
- ### `analyze` -- Analyze an issue
109
-
110
- ```bash
111
- npx issue-flow analyze 42
112
- ```
113
-
114
- Invokes Claude headlessly to fetch issue data, analyze the codebase, and produce a structured analysis saved to `~/.issue-flow/…/issues/42/analysis.md` (see [Pipeline State](#pipeline-state)).
115
-
116
- ### `prd` -- Generate a PRD
117
-
118
- ```bash
119
- npx issue-flow prd 42
120
- ```
121
-
122
- Generates a Product Requirements Document from the issue analysis. Reads `analysis.md` from the same directory as context if available. Saves to `~/.issue-flow/…/issues/42/prd.md`.
123
-
124
- ### `plan` -- Convert PRD to task plan
125
-
126
- ```bash
127
- npx issue-flow plan 42
128
-
129
- # Continue User Story numbering from the last used in this project
130
- npx issue-flow plan 42 --continue
131
-
132
- # Force numbering to start at a specific number, ignoring history
133
- npx issue-flow plan 42 --start-us 27
134
- ```
135
-
136
- Converts the PRD into a structured `~/.issue-flow/…/issues/42/tasks.json` with ordered user stories, acceptance criteria, and pipeline state. Validates the output with zod schemas.
137
-
138
- `US-NNN` numbering continues automatically across `plan` runs of the same project: the highest number already used in any of the project's `tasks.json` files is recovered and the next story continues from there, falling back to `US-001` when the project has no history yet. `--continue` names that automatic recovery explicitly; `--start-us <n>` forces a specific starting number instead, ignoring history — the two flags are mutually exclusive. The decision is always printed to the terminal and recorded in the project's `metadata.json` (`userStoryNumbering`) for audit. See the root [README](https://github.com/fabioassuncao/issue-flow#user-story-numbering-continuity) for the full cascade.
139
-
140
- ### `execute` -- Run the story execution loop
141
-
142
- ```bash
143
- npx issue-flow execute --issue 42
144
- npx issue-flow execute --issue 42 --max-iterations 15
145
- npx issue-flow execute --issue 42 --retry-forever
146
- ```
147
-
148
- Runs the iterative agent loop. Each iteration is a fresh Claude instance that picks the next pending story, implements it, runs quality checks, and commits.
149
-
150
- | Flag | Description |
151
- |------|-------------|
152
- | `--issue N` | Issue number -- reads artifacts from `~/.issue-flow/…/issues/N/` |
153
- | `--max-iterations N` | Stop after N iterations (default: unlimited) |
154
- | `--retry-limit N` | Retry transient Claude failures up to N consecutive times (default: 10) |
155
- | `--retry-forever` | Retry transient Claude failures indefinitely |
156
-
157
- ### `review` -- Validate the implementation
158
-
159
- ```bash
160
- npx issue-flow review 42
161
- ```
162
-
163
- Invokes Claude headlessly to verify acceptance criteria, run tests, and check for regressions. Outputs `PASS` or `FAIL` with findings.
164
-
165
- ### `pr` -- Create a pull request
166
-
167
- ```bash
168
- npx issue-flow pr 42
169
- ```
170
-
171
- Creates a well-structured PR referencing the issue, with summary and test plan.
172
-
173
- ### `generate` -- Create a new issue
174
-
175
- ```bash
176
- npx issue-flow generate --prompt "Add dark mode support to the settings page"
177
- ```
178
-
179
- Analyzes the project and creates a detailed GitHub issue via Claude headless.
180
-
181
- ## Web Monitoring
182
-
183
- `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.
184
-
185
- ```bash
186
- # Enable with defaults -- binds to 0.0.0.0:3737, reachable from your LAN/VPN
187
- npx issue-flow run 42 --web
188
-
189
- # Restrict to this machine only
190
- npx issue-flow run 42 --web --host 127.0.0.1
191
- ```
192
-
193
- Each setting resolves with the precedence **CLI flag > environment variable > `.issue-flow.json` > default**:
194
-
195
- | CLI flag | Environment variable | `.issue-flow.json` key | Default |
196
- |----------|----------------------|------------------------|---------|
197
- | `--web` / `--serve` | `ISSUE_FLOW_WEB` | `web.enabled` | `false` |
198
- | `--port <n>` | `ISSUE_FLOW_WEB_PORT` | `web.port` | `3737` |
199
- | `--host <h>` | `ISSUE_FLOW_WEB_HOST` | `web.host` | `0.0.0.0` |
200
- | `--refresh <s>` | `ISSUE_FLOW_WEB_REFRESH` | `web.refreshSeconds` | `5` |
201
- | `--web-log-limit <n>` | `ISSUE_FLOW_WEB_LOG_LIMIT` | `web.logLimit` | `200` |
202
- | `--web-no-logs` | -- | `web.includeLogs` | logs included |
203
-
204
- 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 `~/.issue-flow/…/issues/N/session.json` -- outside your working tree, so there is nothing to add to `.gitignore`.
205
-
206
- For the full documentation (endpoints, `session.json` format, Tailscale setup), see the [root README](../../README.md#web-monitoring).
207
-
208
- ## Pipeline State
209
-
210
- Each issue's state is tracked in a directory of its own under `~/.issue-flow`, keyed by a deterministic project id and the issue identifier:
75
+ Nothing is written inside your repository. Artifacts live in a machine-wide tree
76
+ keyed by a deterministic project id:
211
77
 
212
78
  ```
213
79
  ~/.issue-flow/projects/<project-id>/issues/42/
214
- analysis.md # Issue analysis
215
- prd.md # Product requirements
216
- tasks.json # Task plan with pipeline state and user stories
217
- progress.txt # Execution log
218
- session.json # Live session snapshot (web monitoring only)
219
- pr-review/ # PR review reports and index
80
+ prd.md tasks.json progress.txt session.json events.jsonl pr-review/
220
81
  ```
221
82
 
222
- Nothing is written to your repository. A `<projectRoot>/issues/` tree left by an earlier release is copied here automatically on first use and then treated as read-only -- see the [root README](../../README.md#global-storage) for the full layout, the project id derivation and the migration.
223
-
224
- The `pipeline` field tracks which phases have completed, enabling resume from any point:
225
-
226
- ```json
227
- {
228
- "pipeline": {
229
- "analyzeCompleted": true,
230
- "prdCompleted": true,
231
- "jsonCompleted": true,
232
- "executionCompleted": false,
233
- "reviewCompleted": false,
234
- "prCreated": false
235
- }
236
- }
237
- ```
83
+ `ISSUE_FLOW_HOME` relocates the whole tree. A legacy `<projectRoot>/issues/`
84
+ directory from an earlier release is copied in automatically on first use and
85
+ then left read-only.
238
86
 
239
- The top-level `lastReviewFindings` field (`string | null`) holds the verbatim findings of the most recent failed `review` phase. Non-null overrides the "issue already complete" check even when every user story has `passes: true`, so a correction cycle's re-execute step is guaranteed to run instead of exiting immediately — see `core/engine.ts`.
87
+ ## Configuration
240
88
 
241
- ## Architecture
242
-
243
- ```
244
- src/
245
- cli.ts # Entry point, subcommand registration (commander)
246
- config.ts # Configuration resolution and defaults
247
- types.ts # Shared TypeScript interfaces
248
- schemas.ts # Zod validation schemas
249
- commands/
250
- init.ts # Prerequisite verification
251
- generate.ts # Headless issue creation
252
- run.ts # Full pipeline orchestrator
253
- analyze.ts # Headless issue analysis
254
- prd.ts # Headless PRD generation
255
- plan.ts # Headless PRD-to-JSON conversion
256
- execute.ts # Iterative story execution (engine wrapper)
257
- review.ts # Headless implementation review
258
- pr.ts # Headless PR creation
259
- core/
260
- engine.ts # Main agent loop
261
- executor.ts # Claude CLI invocation via execa
262
- headless.ts # Typed wrapper for claude -p invocations
263
- pipeline.ts # Pipeline state machine
264
- state-manager.ts # Typed CRUD for tasks.json
265
- prompt-resolver.ts # Prompt resolution and templating
266
- session-state.ts # Session snapshot reducer and publishers
267
- session-publisher.ts # Global session publisher slot
268
- session-git.ts # Commit/PR enrichment for the snapshot
269
- web/
270
- server.ts # Web monitoring HTTP server (assets in web/public/)
271
- ui/
272
- logger.ts # Colored logging utilities
273
- progress.ts # Progress bar and iteration headers
274
- summary.ts # Box drawing and summary display
275
- utils/
276
- shell.ts # Shell command execution
277
- git.ts # Git operations
278
- retry.ts # Transient failure detection and backoff
279
- ```
89
+ Everything resolves through **CLI flag > environment variable >
90
+ `.issue-flow.json` > `~/.issue-flow/config.json` > default**. Nothing is
91
+ mandatory. See the
92
+ [configuration reference](https://github.com/fabioassuncao/issue-flow/blob/main/docs/configuration.md).
280
93
 
281
94
  ## Development
282
95
 
283
96
  ```bash
284
- # Install dependencies
285
97
  npm install
286
-
287
- # Build
288
- npm run build
289
-
290
- # Type check
98
+ npm run build # tsup → dist/
291
99
  npm run typecheck
292
-
293
- # Run tests
294
100
  npm test
295
-
296
- # Watch mode
297
- npm run dev
101
+ npm run smoke # end-to-end, against deterministic stand-ins for claude and gh
102
+ npm run check # biome + typecheck
298
103
  ```
299
104
 
300
- For the full development setup and local testing guide, see [CONTRIBUTING.md](CONTRIBUTING.md).
301
- The official release procedure (changelog, version bump, tag, `npm publish`, GitHub
302
- Release) is documented in [CONTRIBUTING.md → Release process](CONTRIBUTING.md#release-process).
105
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full setup and the release
106
+ process.
303
107
 
304
108
  ## Credits
305
109
 
306
- Based on [Geoffrey Huntley's Ralph pattern](https://ghuntley.com/ralph/) and the [snarktank/ralph](https://github.com/snarktank/ralph) repository.
110
+ Based on [Geoffrey Huntley's Ralph pattern](https://ghuntley.com/ralph/) and the
111
+ [snarktank/ralph](https://github.com/snarktank/ralph) repository.
307
112
 
308
- ## See Also
113
+ ## License
309
114
 
310
- - [Skills & Sub-Agent Architecture](../../docs/skills-and-agents.md) -- Using Issue Flow interactively via Claude Code skills and the `resolve-issue` sub-agent.
115
+ [MIT](LICENSE)
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ GLOBAL_CONFIG_FILENAME,
4
+ PROJECT_CONFIG_FILENAME,
5
+ describeRunAgents,
6
+ loadAgentConfig,
7
+ probeAgent
8
+ } from "./chunk-CNBYCMSU.js";
9
+ import "./chunk-HQAHNIAR.js";
10
+ import {
11
+ printError,
12
+ printSuccess
13
+ } from "./chunk-IYV6KNVF.js";
14
+ import "./chunk-6FDFRNXZ.js";
15
+ import "./chunk-P3LRUNC6.js";
16
+ import "./chunk-DIG5XKQQ.js";
17
+ import {
18
+ AGENT_PHASES,
19
+ AGENT_SCHEMA_VERSION,
20
+ isAgentPhase,
21
+ isAgentProviderId
22
+ } from "./chunk-NEZZ6KDR.js";
23
+ import {
24
+ getGlobalRoot
25
+ } from "./chunk-TX5WIVYP.js";
26
+ import {
27
+ getProjectRoot
28
+ } from "./chunk-KQHB7ZT3.js";
29
+ import {
30
+ writeFileAtomic
31
+ } from "./chunk-7Z4Y3VKM.js";
32
+ import "./chunk-XEYH6ZZI.js";
33
+
34
+ // src/commands/agent.ts
35
+ import { mkdir, readFile } from "fs/promises";
36
+ import { dirname, join } from "path";
37
+ var AGENT_COMMAND_SCHEMA_VERSION = AGENT_SCHEMA_VERSION;
38
+ function originLabel(origin) {
39
+ switch (origin) {
40
+ case "default":
41
+ return "default";
42
+ case "global":
43
+ return "~/.issue-flow/config.json";
44
+ case "project":
45
+ return ".issue-flow.json";
46
+ case "env":
47
+ return "ISSUE_FLOW_*";
48
+ case "cli":
49
+ return "CLI";
50
+ default:
51
+ return origin;
52
+ }
53
+ }
54
+ function formatPhaseLine(phase, provider, model, origin, inherited) {
55
+ const modelBit = model ? ` \xB7 ${model}` : "";
56
+ const source = inherited ? "herdado do padr\xE3o" : originLabel(origin);
57
+ return `${` ${phase.padEnd(12)} ${provider}${modelBit}`.padEnd(40)}(${source})`;
58
+ }
59
+ async function runAgent(options = {}) {
60
+ const config = await loadAgentConfig();
61
+ const summary = await describeRunAgents();
62
+ const [claude, codex, cursor, antigravity] = await Promise.all([
63
+ probeAgent("claude"),
64
+ probeAgent("codex"),
65
+ probeAgent("cursor"),
66
+ probeAgent("antigravity")
67
+ ]);
68
+ if (options.json === true) {
69
+ const phases = {};
70
+ for (const phase of AGENT_PHASES) {
71
+ const resolved = summary.byPhase[phase];
72
+ const inherited = config.phases[phase] === void 0;
73
+ phases[phase] = {
74
+ provider: resolved.provider,
75
+ model: resolved.model,
76
+ inherited
77
+ };
78
+ }
79
+ console.log(
80
+ JSON.stringify(
81
+ {
82
+ schemaVersion: AGENT_COMMAND_SCHEMA_VERSION,
83
+ default: {
84
+ provider: summary.defaultProvider,
85
+ model: summary.defaultModel
86
+ },
87
+ phases,
88
+ availability: [
89
+ {
90
+ id: "claude",
91
+ installed: claude.installed,
92
+ version: claude.version,
93
+ authenticated: claude.authenticated
94
+ },
95
+ {
96
+ id: "codex",
97
+ installed: codex.installed,
98
+ version: codex.version,
99
+ authenticated: codex.authenticated
100
+ },
101
+ {
102
+ id: "cursor",
103
+ installed: cursor.installed,
104
+ version: cursor.version,
105
+ authenticated: cursor.authenticated
106
+ },
107
+ {
108
+ id: "antigravity",
109
+ installed: antigravity.installed,
110
+ version: antigravity.version,
111
+ authenticated: antigravity.authenticated,
112
+ authProbe: "none"
113
+ }
114
+ ]
115
+ },
116
+ null,
117
+ 2
118
+ )
119
+ );
120
+ return 0;
121
+ }
122
+ const modelLabel = summary.defaultModel ?? "(default do provider)";
123
+ console.log(`Agente padr\xE3o ${summary.defaultProvider.padEnd(22)} (${originLabel("default")})`);
124
+ console.log(`Modelo ${modelLabel}`);
125
+ console.log("");
126
+ console.log("Por fase");
127
+ for (const phase of AGENT_PHASES) {
128
+ const resolved = summary.byPhase[phase];
129
+ const inherited = config.phases[phase] === void 0;
130
+ console.log(
131
+ formatPhaseLine(
132
+ phase,
133
+ resolved.provider,
134
+ resolved.model,
135
+ inherited ? "default" : "project",
136
+ inherited
137
+ )
138
+ );
139
+ }
140
+ console.log("");
141
+ console.log("Disponibilidade");
142
+ console.log(` claude ${(claude.version ?? "\u2014").padEnd(16)} ${claude.detail}`);
143
+ console.log(` codex ${(codex.version ?? "\u2014").padEnd(16)} ${codex.detail}`);
144
+ console.log(` cursor ${(cursor.version ?? "\u2014").padEnd(16)} ${cursor.detail}`);
145
+ console.log(
146
+ ` antigravity ${(antigravity.version ?? "\u2014").padEnd(16)} ${antigravity.detail} (no auth probe)`
147
+ );
148
+ if (antigravity.installed) {
149
+ console.log(
150
+ " Warning: every Antigravity invocation passes --dangerously-skip-permissions. --mode plan is the write containment."
151
+ );
152
+ }
153
+ return 0;
154
+ }
155
+ async function runAgentUse(providerRaw, options = {}) {
156
+ if (!isAgentProviderId(providerRaw)) {
157
+ printError(
158
+ `Unknown agent provider '${providerRaw}'. Valid providers: claude, codex, cursor, antigravity.`
159
+ );
160
+ return 1;
161
+ }
162
+ const provider = providerRaw;
163
+ let phase;
164
+ if (options.phase !== void 0) {
165
+ if (!isAgentPhase(options.phase)) {
166
+ printError(
167
+ `Unknown agent phase '${options.phase}'. Valid phases: ${AGENT_PHASES.join(", ")}.`
168
+ );
169
+ return 1;
170
+ }
171
+ phase = options.phase;
172
+ }
173
+ const target = options.project === true ? "project" : "global";
174
+ try {
175
+ const path = await writeAgentPreference({
176
+ target,
177
+ provider,
178
+ model: options.model,
179
+ phase
180
+ });
181
+ printSuccess(`Wrote agent preference to ${path}`);
182
+ return 0;
183
+ } catch (err) {
184
+ printError(err instanceof Error ? err.message : String(err));
185
+ return 1;
186
+ }
187
+ }
188
+ async function writeAgentPreference(input) {
189
+ const path = input.target === "global" ? join(getGlobalRoot(), GLOBAL_CONFIG_FILENAME) : join(input.projectRoot ?? await getProjectRoot(), PROJECT_CONFIG_FILENAME);
190
+ let existing = {};
191
+ try {
192
+ const raw = await readFile(path, "utf-8");
193
+ const parsed = JSON.parse(raw);
194
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
195
+ existing = parsed;
196
+ }
197
+ } catch (err) {
198
+ if (err.code !== "ENOENT") {
199
+ throw new Error(`Could not read ${path}: ${err.message}`);
200
+ }
201
+ }
202
+ const currentAgent = existing.agent !== null && typeof existing.agent === "object" && !Array.isArray(existing.agent) ? { ...existing.agent } : {};
203
+ if (input.phase) {
204
+ const phases = currentAgent.phases !== null && typeof currentAgent.phases === "object" && !Array.isArray(currentAgent.phases) ? { ...currentAgent.phases } : {};
205
+ phases[input.phase] = {
206
+ provider: input.provider,
207
+ ...input.model ? { model: input.model } : {}
208
+ };
209
+ currentAgent.phases = phases;
210
+ } else {
211
+ currentAgent.provider = input.provider;
212
+ if (input.model) currentAgent.model = input.model;
213
+ else delete currentAgent.model;
214
+ }
215
+ existing.agent = currentAgent;
216
+ await mkdir(dirname(path), { recursive: true });
217
+ await writeFileAtomic(path, `${JSON.stringify(existing, null, 2)}
218
+ `);
219
+ if (input.provider === "cursor") {
220
+ const { ensureCursorStorageGrant } = await import("./permissions-7NMDFU27.js");
221
+ await ensureCursorStorageGrant({
222
+ mode: input.target === "project" ? "project" : "global",
223
+ ...input.projectRoot === void 0 ? {} : { projectRoot: input.projectRoot }
224
+ });
225
+ }
226
+ return path;
227
+ }
228
+ async function persistFirstAgentChoice(provider) {
229
+ return writeAgentPreference({ target: "global", provider });
230
+ }
231
+ export {
232
+ AGENT_COMMAND_SCHEMA_VERSION,
233
+ persistFirstAgentChoice,
234
+ runAgent,
235
+ runAgentUse,
236
+ writeAgentPreference
237
+ };
238
+ //# sourceMappingURL=agent-BXRIEKVM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/agent.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { probeAgent } from '../agents/availability.js';\nimport { describeRunAgents } from '../agents/resolve.js';\nimport {\n AGENT_PHASES,\n AGENT_SCHEMA_VERSION,\n type AgentPhase,\n type AgentProviderId,\n isAgentPhase,\n isAgentProviderId,\n} from '../agents/types.js';\nimport { GLOBAL_CONFIG_FILENAME, loadAgentConfig, PROJECT_CONFIG_FILENAME } from '../config.js';\nimport { getGlobalRoot } from '../storage/paths.js';\nimport { printError, printSuccess } from '../ui/logger.js';\nimport { writeFileAtomic } from '../utils/fs.js';\nimport { getProjectRoot } from '../utils/git.js';\n\nexport const AGENT_COMMAND_SCHEMA_VERSION = AGENT_SCHEMA_VERSION;\n\nexport interface AgentCommandOptions {\n json?: boolean;\n}\n\nexport interface AgentUseOptions {\n model?: string;\n global?: boolean;\n project?: boolean;\n phase?: string;\n}\n\nfunction originLabel(origin: string): string {\n switch (origin) {\n case 'default':\n return 'default';\n case 'global':\n return '~/.issue-flow/config.json';\n case 'project':\n return '.issue-flow.json';\n case 'env':\n return 'ISSUE_FLOW_*';\n case 'cli':\n return 'CLI';\n default:\n return origin;\n }\n}\n\nfunction formatPhaseLine(\n phase: AgentPhase,\n provider: string,\n model: string | null,\n origin: string,\n inherited: boolean,\n): string {\n const modelBit = model ? ` · ${model}` : '';\n const source = inherited ? 'herdado do padrão' : originLabel(origin);\n return `${` ${phase.padEnd(12)} ${provider}${modelBit}`.padEnd(40)}(${source})`;\n}\n\nexport async function runAgent(options: AgentCommandOptions = {}): Promise<number> {\n const config = await loadAgentConfig();\n const summary = await describeRunAgents();\n const [claude, codex, cursor, antigravity] = await Promise.all([\n probeAgent('claude'),\n probeAgent('codex'),\n probeAgent('cursor'),\n probeAgent('antigravity'),\n ]);\n\n if (options.json === true) {\n const phases: Record<string, unknown> = {};\n for (const phase of AGENT_PHASES) {\n const resolved = summary.byPhase[phase];\n const inherited = config.phases[phase] === undefined;\n phases[phase] = {\n provider: resolved.provider,\n model: resolved.model,\n inherited,\n };\n }\n console.log(\n JSON.stringify(\n {\n schemaVersion: AGENT_COMMAND_SCHEMA_VERSION,\n default: {\n provider: summary.defaultProvider,\n model: summary.defaultModel,\n },\n phases,\n availability: [\n {\n id: 'claude',\n installed: claude.installed,\n version: claude.version,\n authenticated: claude.authenticated,\n },\n {\n id: 'codex',\n installed: codex.installed,\n version: codex.version,\n authenticated: codex.authenticated,\n },\n {\n id: 'cursor',\n installed: cursor.installed,\n version: cursor.version,\n authenticated: cursor.authenticated,\n },\n {\n id: 'antigravity',\n installed: antigravity.installed,\n version: antigravity.version,\n authenticated: antigravity.authenticated,\n authProbe: 'none',\n },\n ],\n },\n null,\n 2,\n ),\n );\n return 0;\n }\n\n const modelLabel = summary.defaultModel ?? '(default do provider)';\n console.log(`Agente padrão ${summary.defaultProvider.padEnd(22)} (${originLabel('default')})`);\n console.log(`Modelo ${modelLabel}`);\n console.log('');\n console.log('Por fase');\n for (const phase of AGENT_PHASES) {\n const resolved = summary.byPhase[phase];\n const inherited = config.phases[phase] === undefined;\n console.log(\n formatPhaseLine(\n phase,\n resolved.provider,\n resolved.model,\n inherited ? 'default' : 'project',\n inherited,\n ),\n );\n }\n console.log('');\n console.log('Disponibilidade');\n console.log(` claude ${(claude.version ?? '—').padEnd(16)} ${claude.detail}`);\n console.log(` codex ${(codex.version ?? '—').padEnd(16)} ${codex.detail}`);\n console.log(` cursor ${(cursor.version ?? '—').padEnd(16)} ${cursor.detail}`);\n console.log(\n ` antigravity ${(antigravity.version ?? '—').padEnd(16)} ${antigravity.detail} (no auth probe)`,\n );\n if (antigravity.installed) {\n console.log(\n ' Warning: every Antigravity invocation passes --dangerously-skip-permissions. --mode plan is the write containment.',\n );\n }\n return 0;\n}\n\nexport async function runAgentUse(\n providerRaw: string,\n options: AgentUseOptions = {},\n): Promise<number> {\n if (!isAgentProviderId(providerRaw)) {\n printError(\n `Unknown agent provider '${providerRaw}'. Valid providers: claude, codex, cursor, antigravity.`,\n );\n return 1;\n }\n const provider = providerRaw as AgentProviderId;\n\n let phase: AgentPhase | undefined;\n if (options.phase !== undefined) {\n if (!isAgentPhase(options.phase)) {\n printError(\n `Unknown agent phase '${options.phase}'. Valid phases: ${AGENT_PHASES.join(', ')}.`,\n );\n return 1;\n }\n phase = options.phase;\n }\n\n const target: 'global' | 'project' = options.project === true ? 'project' : 'global';\n try {\n const path = await writeAgentPreference({\n target,\n provider,\n model: options.model,\n phase,\n });\n printSuccess(`Wrote agent preference to ${path}`);\n return 0;\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n}\n\nexport async function writeAgentPreference(input: {\n target: 'global' | 'project';\n provider: AgentProviderId;\n model?: string;\n phase?: AgentPhase;\n projectRoot?: string;\n}): Promise<string> {\n const path =\n input.target === 'global'\n ? join(getGlobalRoot(), GLOBAL_CONFIG_FILENAME)\n : join(input.projectRoot ?? (await getProjectRoot()), PROJECT_CONFIG_FILENAME);\n\n let existing: Record<string, unknown> = {};\n try {\n const raw = await readFile(path, 'utf-8');\n const parsed: unknown = JSON.parse(raw);\n if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {\n existing = parsed as Record<string, unknown>;\n }\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new Error(`Could not read ${path}: ${(err as Error).message}`);\n }\n }\n\n const currentAgent =\n existing.agent !== null && typeof existing.agent === 'object' && !Array.isArray(existing.agent)\n ? { ...(existing.agent as Record<string, unknown>) }\n : {};\n\n if (input.phase) {\n const phases =\n currentAgent.phases !== null &&\n typeof currentAgent.phases === 'object' &&\n !Array.isArray(currentAgent.phases)\n ? { ...(currentAgent.phases as Record<string, unknown>) }\n : {};\n phases[input.phase] = {\n provider: input.provider,\n ...(input.model ? { model: input.model } : {}),\n };\n currentAgent.phases = phases;\n } else {\n currentAgent.provider = input.provider;\n if (input.model) currentAgent.model = input.model;\n else delete currentAgent.model;\n }\n\n existing.agent = currentAgent;\n await mkdir(dirname(path), { recursive: true });\n await writeFileAtomic(path, `${JSON.stringify(existing, null, 2)}\\n`);\n if (input.provider === 'cursor') {\n const { ensureCursorStorageGrant } = await import('../agents/permissions.js');\n await ensureCursorStorageGrant({\n mode: input.target === 'project' ? 'project' : 'global',\n ...(input.projectRoot === undefined ? {} : { projectRoot: input.projectRoot }),\n });\n }\n return path;\n}\n\n/** Used by init to persist a first-run choice without touching other keys. */\nexport async function persistFirstAgentChoice(provider: AgentProviderId): Promise<string> {\n return writeAgentPreference({ target: 'global', provider });\n}\n"],"mappings":"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","names":[]}