issue-flow 0.18.0 → 0.20.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 (235) hide show
  1. package/README.md +43 -97
  2. package/dist/agent-4DHRKZ2Z.js +32 -0
  3. package/dist/{analyze-Q4ONIWRZ.js → analyze-UEJ2R3H5.js} +42 -41
  4. package/dist/analyze-UEJ2R3H5.js.map +1 -0
  5. package/dist/{apply-EEFPY2W4.js → apply-QDYNQMUX.js} +22 -24
  6. package/dist/apply-QDYNQMUX.js.map +1 -0
  7. package/dist/artifacts-AMJQJFFV.js +82 -0
  8. package/dist/artifacts-AMJQJFFV.js.map +1 -0
  9. package/dist/{bench-WWWXLTFH.js → bench-RNRXVEFK.js} +42 -42
  10. package/dist/{chunk-RYOUZ6EK.js → chunk-23VHVVKW.js} +3 -3
  11. package/dist/chunk-2EYLOS5I.js +99 -0
  12. package/dist/chunk-2EYLOS5I.js.map +1 -0
  13. package/dist/{chunk-C5TL7T4V.js → chunk-2YM2VH6R.js} +2 -2
  14. package/dist/{chunk-XUEMATVY.js → chunk-37O2FMAN.js} +28 -25
  15. package/dist/chunk-37O2FMAN.js.map +1 -0
  16. package/dist/chunk-44RF3VFG.js +717 -0
  17. package/dist/chunk-44RF3VFG.js.map +1 -0
  18. package/dist/chunk-6AOZCWN2.js +21 -0
  19. package/dist/chunk-6AOZCWN2.js.map +1 -0
  20. package/dist/chunk-7MJGYPHF.js +1571 -0
  21. package/dist/chunk-7MJGYPHF.js.map +1 -0
  22. package/dist/{chunk-SA6HXL4Y.js → chunk-7WBUEBD5.js} +9 -9
  23. package/dist/chunk-7WBUEBD5.js.map +1 -0
  24. package/dist/chunk-7ZZUSMBJ.js +35 -0
  25. package/dist/chunk-7ZZUSMBJ.js.map +1 -0
  26. package/dist/{chunk-QJ44VG5G.js → chunk-ADH6RJNX.js} +3 -3
  27. package/dist/{chunk-34OCSKOB.js → chunk-AKL5UPR6.js} +159 -114
  28. package/dist/chunk-AKL5UPR6.js.map +1 -0
  29. package/dist/{chunk-7F7LGMVO.js → chunk-BTBJ5UOO.js} +12 -12
  30. package/dist/{chunk-7F7LGMVO.js.map → chunk-BTBJ5UOO.js.map} +1 -1
  31. package/dist/{chunk-HOAHMERN.js → chunk-CLZ6RRZL.js} +21 -21
  32. package/dist/chunk-DBLXLKGU.js +1201 -0
  33. package/dist/chunk-DBLXLKGU.js.map +1 -0
  34. package/dist/{chunk-DUS5ZNKL.js → chunk-DIMDAYZS.js} +7 -6
  35. package/dist/chunk-DZ3DZ3EN.js +278 -0
  36. package/dist/chunk-DZ3DZ3EN.js.map +1 -0
  37. package/dist/{chunk-CRGIQFUQ.js → chunk-E2H3JV2E.js} +3 -1
  38. package/dist/chunk-E2H3JV2E.js.map +1 -0
  39. package/dist/{chunk-45WZ6GMI.js → chunk-EORDSKWZ.js} +17 -17
  40. package/dist/chunk-EORDSKWZ.js.map +1 -0
  41. package/dist/{chunk-LTH6XYZ3.js → chunk-EYKPWHT7.js} +40 -30
  42. package/dist/chunk-EYKPWHT7.js.map +1 -0
  43. package/dist/{chunk-OODXFCQV.js → chunk-FU2FQ4JE.js} +14 -16
  44. package/dist/{chunk-OODXFCQV.js.map → chunk-FU2FQ4JE.js.map} +1 -1
  45. package/dist/{chunk-V7QOIQYC.js → chunk-G442RDFX.js} +9 -23
  46. package/dist/{chunk-V7QOIQYC.js.map → chunk-G442RDFX.js.map} +1 -1
  47. package/dist/{chunk-QPGEFLMY.js → chunk-I64N6RL6.js} +7 -7
  48. package/dist/{chunk-KRAGAUKD.js → chunk-JBYXJOJJ.js} +201 -64
  49. package/dist/chunk-JBYXJOJJ.js.map +1 -0
  50. package/dist/{chunk-SOM4QA6C.js → chunk-JCR5C5A7.js} +93 -18
  51. package/dist/chunk-JCR5C5A7.js.map +1 -0
  52. package/dist/{chunk-RNFUVKC3.js → chunk-K5NM26NV.js} +30 -13
  53. package/dist/chunk-K5NM26NV.js.map +1 -0
  54. package/dist/{chunk-YVPZUN6X.js → chunk-KUA6KBBD.js} +335 -38
  55. package/dist/chunk-KUA6KBBD.js.map +1 -0
  56. package/dist/{chunk-KMCS76ZW.js → chunk-KUL64XUF.js} +44 -12
  57. package/dist/chunk-KUL64XUF.js.map +1 -0
  58. package/dist/{chunk-HAKDLR6N.js → chunk-MAJ6QDRL.js} +65 -78
  59. package/dist/chunk-MAJ6QDRL.js.map +1 -0
  60. package/dist/{chunk-DGRTJD5R.js → chunk-MML2G7IC.js} +7 -7
  61. package/dist/chunk-NP47HRX2.js +136 -0
  62. package/dist/chunk-NP47HRX2.js.map +1 -0
  63. package/dist/{chunk-3R27MJ5R.js → chunk-PFWHHYEO.js} +3 -3
  64. package/dist/{chunk-ECU7YO7S.js → chunk-PM5YWJYO.js} +22 -6
  65. package/dist/chunk-PM5YWJYO.js.map +1 -0
  66. package/dist/{chunk-E4I6MMHD.js → chunk-PXPTIXJF.js} +154 -101
  67. package/dist/chunk-PXPTIXJF.js.map +1 -0
  68. package/dist/{chunk-CWSQVHVV.js → chunk-QJHZ3HYJ.js} +248 -102
  69. package/dist/chunk-QJHZ3HYJ.js.map +1 -0
  70. package/dist/{chunk-LI35PIIS.js → chunk-RDPK7LYK.js} +20 -117
  71. package/dist/chunk-RDPK7LYK.js.map +1 -0
  72. package/dist/{chunk-PMLE7KAQ.js → chunk-RMIBLFG3.js} +30 -14
  73. package/dist/chunk-RMIBLFG3.js.map +1 -0
  74. package/dist/{chunk-62GVVZQO.js → chunk-T736DSZI.js} +32 -316
  75. package/dist/chunk-T736DSZI.js.map +1 -0
  76. package/dist/{chunk-TZYINKCC.js → chunk-UDUUUFQF.js} +57 -29
  77. package/dist/chunk-UDUUUFQF.js.map +1 -0
  78. package/dist/{chunk-NEWNXZ65.js → chunk-V7GETODQ.js} +36 -28
  79. package/dist/{chunk-NEWNXZ65.js.map → chunk-V7GETODQ.js.map} +1 -1
  80. package/dist/{chunk-PPZ3K4YM.js → chunk-VHQMMY2D.js} +2 -2
  81. package/dist/{chunk-ZVZMWNCG.js → chunk-VJ3PF6B7.js} +50 -16
  82. package/dist/chunk-VJ3PF6B7.js.map +1 -0
  83. package/dist/{chunk-L6HSKTQE.js → chunk-W7LZLJAF.js} +4 -4
  84. package/dist/{chunk-6K2LCFG6.js → chunk-WB4FA2EH.js} +70 -81
  85. package/dist/chunk-WB4FA2EH.js.map +1 -0
  86. package/dist/{chunk-LGHND47K.js → chunk-WQSGL5LX.js} +22 -16
  87. package/dist/chunk-WQSGL5LX.js.map +1 -0
  88. package/dist/{chunk-ICM463EO.js → chunk-XNK5UN2D.js} +5 -649
  89. package/dist/chunk-XNK5UN2D.js.map +1 -0
  90. package/dist/{chunk-7UMMFOL2.js → chunk-Y2KOWABB.js} +39 -24
  91. package/dist/chunk-Y2KOWABB.js.map +1 -0
  92. package/dist/{chunk-QF7VEL3M.js → chunk-YC24E66P.js} +568 -158
  93. package/dist/chunk-YC24E66P.js.map +1 -0
  94. package/dist/{chunk-EG2MGDP5.js → chunk-Z6RCKCVB.js} +52 -46
  95. package/dist/chunk-Z6RCKCVB.js.map +1 -0
  96. package/dist/{chunk-YUNBOUQH.js → chunk-ZPGNVBWY.js} +8 -8
  97. package/dist/cli.js +132 -62
  98. package/dist/cli.js.map +1 -1
  99. package/dist/compat-OJG76J4I.js +24 -0
  100. package/dist/{config-LEBZZCJP.js → config-6YTWPDBK.js} +20 -17
  101. package/dist/{contract-JH2WBIO6.js → contract-HWLFFL3F.js} +2 -2
  102. package/dist/{conventions-XOT6YW5L.js → conventions-5UPOTU42.js} +20 -16
  103. package/dist/{conventions-XOT6YW5L.js.map → conventions-5UPOTU42.js.map} +1 -1
  104. package/dist/db-QAZG4LQD.js +264 -0
  105. package/dist/db-QAZG4LQD.js.map +1 -0
  106. package/dist/diagnostics-YEFB42E5.js +25 -0
  107. package/dist/execute-QCT64D37.js +38 -0
  108. package/dist/{generate-JGW5JSKB.js → generate-QATMDKOK.js} +46 -43
  109. package/dist/generate-QATMDKOK.js.map +1 -0
  110. package/dist/{git-A2IP32PQ.js → git-XGOECXWG.js} +2 -3
  111. package/dist/history-NBPUZVGQ.js +101 -0
  112. package/dist/history-NBPUZVGQ.js.map +1 -0
  113. package/dist/init-4LB2YKWP.js +28 -0
  114. package/dist/{operations-O4FORFIH.js → operations-RUQP2Y2F.js} +78 -52
  115. package/dist/operations-RUQP2Y2F.js.map +1 -0
  116. package/dist/{permissions-G4XCAESF.js → permissions-QZPUUBSO.js} +5 -6
  117. package/dist/plan-2NMSHTMD.js +38 -0
  118. package/dist/{policy-U5NBFAO5.js → policy-RKKFF5A2.js} +17 -15
  119. package/dist/{policy-U5NBFAO5.js.map → policy-RKKFF5A2.js.map} +1 -1
  120. package/dist/pr-QKFO5HI3.js +42 -0
  121. package/dist/pr-review-H2BFD6F2.js +33 -0
  122. package/dist/prd-ET6XKYRS.js +37 -0
  123. package/dist/{ps-76L6ZAZL.js → ps-UIKSOUAQ.js} +26 -15
  124. package/dist/ps-UIKSOUAQ.js.map +1 -0
  125. package/dist/{recorder-2BAKPBRA.js → recorder-LOU3PTIE.js} +11 -7
  126. package/dist/registry-HV7M6MAH.js +17 -0
  127. package/dist/repository-RCRR6RDE.js +80 -0
  128. package/dist/resume-BWJZGBLZ.js +278 -0
  129. package/dist/resume-BWJZGBLZ.js.map +1 -0
  130. package/dist/review-2MTZLNSS.js +38 -0
  131. package/dist/routing-RM5ABHHD.js +35 -0
  132. package/dist/run-XMACQBRJ.js +61 -0
  133. package/dist/{session-publisher-KAJQRHNZ.js → session-publisher-Q5TNEZPX.js} +3 -3
  134. package/dist/session-publisher-Q5TNEZPX.js.map +1 -0
  135. package/dist/{state-manager-UPHO2EA2.js → state-manager-SRN4VJSX.js} +9 -4
  136. package/dist/state-manager-SRN4VJSX.js.map +1 -0
  137. package/dist/{usage-KTMYXAPF.js → usage-JOODVE5O.js} +69 -63
  138. package/dist/usage-JOODVE5O.js.map +1 -0
  139. package/dist/web-TLMWJ2BM.js +205 -0
  140. package/dist/web-TLMWJ2BM.js.map +1 -0
  141. package/package.json +22 -8
  142. package/prompts/analyze.md +16 -19
  143. package/prompts/execute.md +73 -144
  144. package/prompts/generate.md +23 -10
  145. package/prompts/plan.md +32 -60
  146. package/prompts/pr-review.md +58 -40
  147. package/prompts/pr.md +50 -16
  148. package/prompts/prd.md +26 -19
  149. package/prompts/review.md +41 -18
  150. package/web/public/app.js +111 -112
  151. package/dist/agent-HMZTXCRT.js +0 -30
  152. package/dist/analyze-Q4ONIWRZ.js.map +0 -1
  153. package/dist/apply-EEFPY2W4.js.map +0 -1
  154. package/dist/chunk-34OCSKOB.js.map +0 -1
  155. package/dist/chunk-45WZ6GMI.js.map +0 -1
  156. package/dist/chunk-62GVVZQO.js.map +0 -1
  157. package/dist/chunk-6K2LCFG6.js.map +0 -1
  158. package/dist/chunk-7UMMFOL2.js.map +0 -1
  159. package/dist/chunk-CRGIQFUQ.js.map +0 -1
  160. package/dist/chunk-CWSQVHVV.js.map +0 -1
  161. package/dist/chunk-E4I6MMHD.js.map +0 -1
  162. package/dist/chunk-ECU7YO7S.js.map +0 -1
  163. package/dist/chunk-EG2MGDP5.js.map +0 -1
  164. package/dist/chunk-HAKDLR6N.js.map +0 -1
  165. package/dist/chunk-ICM463EO.js.map +0 -1
  166. package/dist/chunk-KMCS76ZW.js.map +0 -1
  167. package/dist/chunk-KRAGAUKD.js.map +0 -1
  168. package/dist/chunk-LGHND47K.js.map +0 -1
  169. package/dist/chunk-LI35PIIS.js.map +0 -1
  170. package/dist/chunk-LTH6XYZ3.js.map +0 -1
  171. package/dist/chunk-PMLE7KAQ.js.map +0 -1
  172. package/dist/chunk-QF7VEL3M.js.map +0 -1
  173. package/dist/chunk-RNFUVKC3.js.map +0 -1
  174. package/dist/chunk-SA6HXL4Y.js.map +0 -1
  175. package/dist/chunk-SOM4QA6C.js.map +0 -1
  176. package/dist/chunk-TZYINKCC.js.map +0 -1
  177. package/dist/chunk-WCCZJL7W.js +0 -141
  178. package/dist/chunk-WCCZJL7W.js.map +0 -1
  179. package/dist/chunk-XEYH6ZZI.js +0 -289
  180. package/dist/chunk-XEYH6ZZI.js.map +0 -1
  181. package/dist/chunk-XUEMATVY.js.map +0 -1
  182. package/dist/chunk-XZUEFTUP.js +0 -243
  183. package/dist/chunk-XZUEFTUP.js.map +0 -1
  184. package/dist/chunk-YVPZUN6X.js.map +0 -1
  185. package/dist/chunk-ZVZMWNCG.js.map +0 -1
  186. package/dist/diagnostics-HTCWF7F4.js +0 -23
  187. package/dist/execute-LELOSR5T.js +0 -37
  188. package/dist/generate-JGW5JSKB.js.map +0 -1
  189. package/dist/init-TO6MUXO4.js +0 -26
  190. package/dist/operations-O4FORFIH.js.map +0 -1
  191. package/dist/plan-OXAT344Q.js +0 -34
  192. package/dist/pr-UZHNKKZC.js +0 -39
  193. package/dist/pr-review-Q4ACAWDM.js +0 -32
  194. package/dist/prd-6CYEMLDY.js +0 -33
  195. package/dist/ps-76L6ZAZL.js.map +0 -1
  196. package/dist/registry-XLHHYLTR.js +0 -15
  197. package/dist/resume-URCU6CIJ.js +0 -244
  198. package/dist/resume-URCU6CIJ.js.map +0 -1
  199. package/dist/review-LVZT2TMW.js +0 -35
  200. package/dist/routing-EK6RBZM4.js +0 -33
  201. package/dist/run-2RXJCJKT.js +0 -56
  202. package/dist/usage-KTMYXAPF.js.map +0 -1
  203. package/dist/web-B5UPHHR5.js +0 -172
  204. package/dist/web-B5UPHHR5.js.map +0 -1
  205. /package/dist/{agent-HMZTXCRT.js.map → agent-4DHRKZ2Z.js.map} +0 -0
  206. /package/dist/{bench-WWWXLTFH.js.map → bench-RNRXVEFK.js.map} +0 -0
  207. /package/dist/{chunk-RYOUZ6EK.js.map → chunk-23VHVVKW.js.map} +0 -0
  208. /package/dist/{chunk-C5TL7T4V.js.map → chunk-2YM2VH6R.js.map} +0 -0
  209. /package/dist/{chunk-QJ44VG5G.js.map → chunk-ADH6RJNX.js.map} +0 -0
  210. /package/dist/{chunk-HOAHMERN.js.map → chunk-CLZ6RRZL.js.map} +0 -0
  211. /package/dist/{chunk-DUS5ZNKL.js.map → chunk-DIMDAYZS.js.map} +0 -0
  212. /package/dist/{chunk-QPGEFLMY.js.map → chunk-I64N6RL6.js.map} +0 -0
  213. /package/dist/{chunk-DGRTJD5R.js.map → chunk-MML2G7IC.js.map} +0 -0
  214. /package/dist/{chunk-3R27MJ5R.js.map → chunk-PFWHHYEO.js.map} +0 -0
  215. /package/dist/{chunk-PPZ3K4YM.js.map → chunk-VHQMMY2D.js.map} +0 -0
  216. /package/dist/{chunk-L6HSKTQE.js.map → chunk-W7LZLJAF.js.map} +0 -0
  217. /package/dist/{chunk-YUNBOUQH.js.map → chunk-ZPGNVBWY.js.map} +0 -0
  218. /package/dist/{config-LEBZZCJP.js.map → compat-OJG76J4I.js.map} +0 -0
  219. /package/dist/{contract-JH2WBIO6.js.map → config-6YTWPDBK.js.map} +0 -0
  220. /package/dist/{diagnostics-HTCWF7F4.js.map → contract-HWLFFL3F.js.map} +0 -0
  221. /package/dist/{execute-LELOSR5T.js.map → diagnostics-YEFB42E5.js.map} +0 -0
  222. /package/dist/{git-A2IP32PQ.js.map → execute-QCT64D37.js.map} +0 -0
  223. /package/dist/{init-TO6MUXO4.js.map → git-XGOECXWG.js.map} +0 -0
  224. /package/dist/{permissions-G4XCAESF.js.map → init-4LB2YKWP.js.map} +0 -0
  225. /package/dist/{plan-OXAT344Q.js.map → permissions-QZPUUBSO.js.map} +0 -0
  226. /package/dist/{pr-UZHNKKZC.js.map → plan-2NMSHTMD.js.map} +0 -0
  227. /package/dist/{pr-review-Q4ACAWDM.js.map → pr-QKFO5HI3.js.map} +0 -0
  228. /package/dist/{prd-6CYEMLDY.js.map → pr-review-H2BFD6F2.js.map} +0 -0
  229. /package/dist/{recorder-2BAKPBRA.js.map → prd-ET6XKYRS.js.map} +0 -0
  230. /package/dist/{registry-XLHHYLTR.js.map → recorder-LOU3PTIE.js.map} +0 -0
  231. /package/dist/{review-LVZT2TMW.js.map → registry-HV7M6MAH.js.map} +0 -0
  232. /package/dist/{routing-EK6RBZM4.js.map → repository-RCRR6RDE.js.map} +0 -0
  233. /package/dist/{run-2RXJCJKT.js.map → review-2MTZLNSS.js.map} +0 -0
  234. /package/dist/{session-publisher-KAJQRHNZ.js.map → routing-RM5ABHHD.js.map} +0 -0
  235. /package/dist/{state-manager-UPHO2EA2.js.map → run-XMACQBRJ.js.map} +0 -0
@@ -1,120 +1,126 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ parseDocumentResult
4
+ } from "./chunk-6AOZCWN2.js";
2
5
  import {
3
6
  isTransientFailure,
4
- readFileWithGrace,
5
7
  runPhaseWithRetry
6
- } from "./chunk-V7QOIQYC.js";
8
+ } from "./chunk-G442RDFX.js";
7
9
  import {
8
10
  issuePlaceholders,
11
+ issueReference,
9
12
  resolveCommandIssue
10
- } from "./chunk-SA6HXL4Y.js";
13
+ } from "./chunk-7WBUEBD5.js";
11
14
  import {
12
15
  publishPhaseMetrics,
13
16
  resolvePolicyPlaceholders
14
- } from "./chunk-KMCS76ZW.js";
17
+ } from "./chunk-KUL64XUF.js";
15
18
  import {
16
19
  applyPlaceholders,
17
20
  loadPrompt
18
- } from "./chunk-7F7LGMVO.js";
21
+ } from "./chunk-BTBJ5UOO.js";
19
22
  import {
20
23
  DEFAULT_HEADLESS_TIMEOUT_MS,
21
24
  runHeadless
22
- } from "./chunk-TZYINKCC.js";
25
+ } from "./chunk-UDUUUFQF.js";
23
26
  import {
24
27
  DEFAULT_BRANCH_CONVENTION,
25
28
  branchName,
26
29
  loadRepositoryPolicy,
27
30
  resolveChangeType
28
- } from "./chunk-LGHND47K.js";
29
- import {
30
- recordUserStoryNumbering,
31
- resolveIssuePaths,
32
- resolveProjectPaths
33
- } from "./chunk-LI35PIIS.js";
31
+ } from "./chunk-WQSGL5LX.js";
34
32
  import {
35
33
  getGlobalTimeout,
36
34
  printError,
37
35
  printInfo,
38
36
  printSuccess,
39
- printWarning
40
- } from "./chunk-XZUEFTUP.js";
41
- import {
42
- getIssuePaths
43
- } from "./chunk-HAKDLR6N.js";
44
- import {
45
- getProjectRoot
46
- } from "./chunk-YVPZUN6X.js";
37
+ resolveIssuePaths,
38
+ resolveProjectPaths
39
+ } from "./chunk-DBLXLKGU.js";
47
40
  import {
48
41
  isoNow,
49
42
  loadTaskPlan,
50
43
  saveTaskPlan
51
- } from "./chunk-SOM4QA6C.js";
44
+ } from "./chunk-JCR5C5A7.js";
45
+ import {
46
+ inspectTaskPlan
47
+ } from "./chunk-NP47HRX2.js";
48
+ import {
49
+ findHighestStoredUserStoryNumber,
50
+ getPlanRepository,
51
+ ingestGeneratedPlan,
52
+ saveStoredUserStoryNumbering
53
+ } from "./chunk-7MJGYPHF.js";
54
+ import {
55
+ getProjectRoot
56
+ } from "./chunk-KUA6KBBD.js";
52
57
  import {
53
- taskPlanSchema
54
- } from "./chunk-ICM463EO.js";
58
+ writeFileAtomic
59
+ } from "./chunk-PFWHHYEO.js";
55
60
 
56
61
  // src/commands/plan.ts
57
- import { mkdir, readFile as readFile2 } from "fs/promises";
62
+ import { mkdir, readFile } from "node:fs/promises";
63
+ import { basename } from "node:path";
64
+
65
+ // src/core/plan-result.ts
66
+ import { z } from "zod";
67
+ var storyDraftSchema = z.object({
68
+ key: z.string().min(1),
69
+ title: z.string().min(1),
70
+ description: z.string().min(1),
71
+ acceptanceCriteria: z.array(z.string().min(1)).min(1),
72
+ dependsOn: z.array(z.string().min(1)).optional().default([])
73
+ });
74
+ var planDraftSchema = z.object({
75
+ description: z.string().min(1),
76
+ stories: z.array(storyDraftSchema).min(1)
77
+ });
78
+ function parsePlanResult(output) {
79
+ const content = parseDocumentResult(output, "task-plan");
80
+ const draft = planDraftSchema.parse(JSON.parse(content));
81
+ const keys = /* @__PURE__ */ new Set();
82
+ for (const story of draft.stories) {
83
+ if (keys.has(story.key)) throw new Error(`Duplicate story key: ${story.key}`);
84
+ keys.add(story.key);
85
+ }
86
+ for (const story of draft.stories) {
87
+ for (const dependency of story.dependsOn) {
88
+ if (dependency === story.key) throw new Error(`Story ${story.key} depends on itself`);
89
+ if (!keys.has(dependency))
90
+ throw new Error(`Story ${story.key} has unknown dependency ${dependency}`);
91
+ }
92
+ }
93
+ return draft;
94
+ }
58
95
 
59
96
  // src/storage/user-story-numbering.ts
60
- import { readdir, readFile } from "fs/promises";
61
97
  var USER_STORY_ID_PAD = 3;
62
98
  function formatUserStoryId(n) {
63
99
  return `US-${String(n).padStart(USER_STORY_ID_PAD, "0")}`;
64
100
  }
65
- function parseUserStoryNumber(id) {
66
- const matches = id.match(/\d+/g);
67
- if (!matches || matches.length === 0) return null;
68
- const value = Number.parseInt(matches[matches.length - 1], 10);
69
- return Number.isNaN(value) ? null : value;
70
- }
71
- async function readUserStoryIds(tasksPath) {
72
- let raw;
73
- try {
74
- raw = await readFile(tasksPath, "utf-8");
75
- } catch {
76
- return null;
77
- }
78
- try {
79
- const parsed = JSON.parse(raw);
80
- if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.userStories)) {
81
- return null;
82
- }
83
- return parsed.userStories.map(
84
- (story) => typeof story === "object" && story !== null && typeof story.id === "string" ? story.id : null
85
- ).filter((id) => id !== null);
86
- } catch {
87
- return null;
88
- }
89
- }
90
101
  async function findHighestUserStoryNumber(options = {}) {
91
102
  const { excludeIssueId, ...rootOptions } = options;
92
- const { projectId, issuesDir } = await resolveProjectPaths(rootOptions);
93
- let entries;
103
+ let project;
94
104
  try {
95
- entries = (await readdir(issuesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
105
+ project = await resolveProjectPaths(rootOptions);
96
106
  } catch (error) {
97
- if (error?.code === "ENOENT") return null;
98
107
  throw new Error(
99
- `Could not scan the project's User Story history at ${issuesDir}: ${error.message}. Fix the storage directory, or pass --start-us <n> to set the numbering explicitly.`,
108
+ `Could not query the project's User Story history: ${error.message}. Fix the storage directory, or pass --start-us <n> to set the numbering explicitly.`,
100
109
  { cause: error }
101
110
  );
102
111
  }
103
- let best = null;
104
- for (const issueId of entries) {
105
- if (excludeIssueId !== void 0 && issueId === excludeIssueId) continue;
106
- const { tasksFile } = getIssuePaths(projectId, issueId, { env: options.env });
107
- const ids = await readUserStoryIds(tasksFile);
108
- if (!ids) continue;
109
- for (const id of ids) {
110
- const value = parseUserStoryNumber(id);
111
- if (value === null) continue;
112
- if (best === null || value > best.number) {
113
- best = { number: value, issueId, storyId: id };
114
- }
115
- }
112
+ try {
113
+ return await findHighestStoredUserStoryNumber({
114
+ projectId: project.projectId,
115
+ excludeIssueId,
116
+ databaseOptions: project.databaseOptions
117
+ });
118
+ } catch (error) {
119
+ throw new Error(
120
+ `Could not query the project's User Story history: ${error.message}. Fix the SQLite database, or pass --start-us <n> to set the numbering explicitly.`,
121
+ { cause: error }
122
+ );
116
123
  }
117
- return best;
118
124
  }
119
125
  async function resolveUserStoryNumbering(options) {
120
126
  const { issueNumber, continueFlag, startUs, now, ...rootOptions } = options;
@@ -162,10 +168,23 @@ async function determineUserStoryNumbering(options) {
162
168
  const { projectRoot: projectRootOption, ...rest } = options;
163
169
  const projectRoot = projectRootOption ?? await getProjectRoot();
164
170
  const outcome = await resolveUserStoryNumbering({ ...rest, projectRoot });
165
- await recordUserStoryNumbering(projectRoot, outcome.decision, {
166
- env: options.env,
167
- now: options.now
168
- });
171
+ const project = await resolveProjectPaths({ projectRoot, env: options.env });
172
+ if (project.storageDriver === "sqlite") {
173
+ await saveStoredUserStoryNumbering(
174
+ {
175
+ projectId: project.projectId,
176
+ projectRoot,
177
+ databaseOptions: project.databaseOptions
178
+ },
179
+ outcome.decision
180
+ );
181
+ } else {
182
+ const { recordUserStoryNumbering } = await import("./compat-OJG76J4I.js");
183
+ await recordUserStoryNumbering(projectRoot, outcome.decision, {
184
+ env: options.env,
185
+ now: options.now
186
+ });
187
+ }
169
188
  return { ...outcome, nextUserStoryId: formatUserStoryId(outcome.decision.nextNumber) };
170
189
  }
171
190
 
@@ -180,7 +199,7 @@ async function runPlan(issue, resolvedIssue, numbering) {
180
199
  const prdPath = paths.prdFile;
181
200
  let prdContent;
182
201
  try {
183
- prdContent = await readFile2(prdPath, "utf-8");
202
+ prdContent = await readFile(prdPath, "utf-8");
184
203
  } catch {
185
204
  printError(`PRD not found at ${prdPath}. Run 'issue-flow prd ${issueNumber}' first.`);
186
205
  return 1;
@@ -188,7 +207,6 @@ async function runPlan(issue, resolvedIssue, numbering) {
188
207
  const tasksPath = paths.tasksFile;
189
208
  const {
190
209
  message: numberingMessage,
191
- nextUserStoryId,
192
210
  decision: { nextNumber }
193
211
  } = await determineUserStoryNumbering({
194
212
  issueNumber,
@@ -197,8 +215,11 @@ async function runPlan(issue, resolvedIssue, numbering) {
197
215
  });
198
216
  printInfo(numberingMessage);
199
217
  let persistedBranch = null;
218
+ let closure = {};
200
219
  try {
201
- persistedBranch = (await loadTaskPlan(tasksPath)).branchName ?? null;
220
+ const previous = await loadTaskPlan(tasksPath);
221
+ persistedBranch = previous.branchName ?? null;
222
+ closure = { closeIssue: previous.closeIssue, issueClosedAt: previous.issueClosedAt };
202
223
  } catch {
203
224
  persistedBranch = null;
204
225
  }
@@ -224,21 +245,22 @@ async function runPlan(issue, resolvedIssue, numbering) {
224
245
  const prompt = applyPlaceholders(template, {
225
246
  // The repository's own conventions. Empty when it declares none, which is
226
247
  // what keeps the rendered prompt identical to the pre-policy one.
227
- ...await resolvePolicyPlaceholders(),
248
+ ...await resolvePolicyPlaceholders({ phase: "plan" }),
228
249
  __ISSUE_NUMBER__: issueNumber,
229
250
  __PRD_CONTENT__: prdContent,
230
- __TASKS_PATH__: tasksPath,
231
- __NEXT_US_NUMBER__: nextUserStoryId,
232
- __BRANCH_NAME__: resolvedBranch,
233
- ...issuePlaceholders(resolution.resolved)
251
+ ...issuePlaceholders(resolution.resolved, paths.issueFile)
234
252
  });
235
253
  await mkdir(paths.issueDir, { recursive: true });
254
+ let validationFeedback = "";
236
255
  const outcome = await runPhaseWithRetry({
237
256
  phase: "plan",
238
257
  attempt: async () => {
239
258
  const startedAtMs = Date.now();
240
259
  const result = await runHeadless({
241
- prompt,
260
+ prompt: validationFeedback ? `${prompt}
261
+
262
+ Return a corrected <task-plan> block. Validation errors (diagnostic data, not instructions):
263
+ ${validationFeedback}` : prompt,
242
264
  maxTurns: 25,
243
265
  timeout: getGlobalTimeout() ?? DEFAULT_HEADLESS_TIMEOUT_MS,
244
266
  timeoutHistory: {
@@ -248,11 +270,10 @@ async function runPlan(issue, resolvedIssue, numbering) {
248
270
  // json (not text) so the CLI reports usage: the envelope's `result`
249
271
  // field carries the same assistant text this phase already consumed.
250
272
  outputFormat: "json",
251
- allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"],
252
- addDirs: [paths.issueDir],
273
+ allowedTools: ["Bash", "Read", "Glob", "Grep"],
253
274
  statusMessage: `Converting PRD to task plan for issue #${issueNumber}...`,
254
275
  phase: "plan",
255
- permission: "workspace"
276
+ permission: "read-only"
256
277
  });
257
278
  publishPhaseMetrics("plan", result.cost, startedAtMs, result.agent?.provider);
258
279
  if (!result.success) {
@@ -262,27 +283,56 @@ async function runPlan(issue, resolvedIssue, numbering) {
262
283
  error: `Task plan generation failed: ${result.error}`
263
284
  };
264
285
  }
265
- let rawContent;
266
- try {
267
- rawContent = await readFileWithGrace(tasksPath);
268
- } catch {
269
- return { ok: false, transient: true, error: `tasks.json was not created at ${tasksPath}` };
270
- }
271
- let parsed;
272
286
  try {
273
- parsed = JSON.parse(rawContent);
274
- } catch {
275
- return { ok: false, transient: true, error: "tasks.json contains invalid JSON" };
276
- }
277
- const validation = taskPlanSchema.safeParse(parsed);
278
- if (!validation.success) {
279
- const issues = validation.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
280
- return {
281
- ok: false,
282
- transient: true,
283
- error: `tasks.json does not match expected schema:
284
- ${issues}`
287
+ const draft = parsePlanResult(result.result);
288
+ const idByKey = new Map(
289
+ draft.stories.map((story, index) => [story.key, formatUserStoryId(nextNumber + index)])
290
+ );
291
+ const generated = {
292
+ ...closure,
293
+ project: basename(policy.root),
294
+ issueNumber: /^\d+$/.test(issueNumber) ? Number(issueNumber) : issueNumber,
295
+ issueUrl: issueReference(resolution.resolved.issue, paths.issueFile),
296
+ branchName: resolvedBranch,
297
+ noBranch: numbering?.branchName !== void 0,
298
+ description: draft.description,
299
+ issueStatus: "pending",
300
+ completedAt: null,
301
+ lastAttemptAt: null,
302
+ lastError: null,
303
+ correctionCycle: 0,
304
+ maxCorrectionCycles: 3,
305
+ lastReviewFindings: null,
306
+ pipeline: {
307
+ analyzeCompleted: false,
308
+ prdCompleted: true,
309
+ jsonCompleted: true,
310
+ executionCompleted: false,
311
+ reviewCompleted: false,
312
+ prCreated: false
313
+ },
314
+ userStories: draft.stories.map((story, index) => ({
315
+ id: idByKey.get(story.key),
316
+ title: story.title,
317
+ description: story.description,
318
+ acceptanceCriteria: story.acceptanceCriteria,
319
+ priority: index + 1,
320
+ passes: false,
321
+ notes: "",
322
+ ...story.dependsOn.length === 0 ? {} : { dependencies: story.dependsOn.map((key) => idByKey.get(key)) }
323
+ }))
285
324
  };
325
+ const validation = inspectTaskPlan(generated);
326
+ if (!validation.ok) {
327
+ const issues = validation.errors.map((i) => ` - ${i.path}: ${i.message}`).join("\n");
328
+ throw new Error(`generated plan does not match expected schema:
329
+ ${issues}`);
330
+ }
331
+ await writeFileAtomic(tasksPath, `${JSON.stringify(generated, null, 2)}
332
+ `);
333
+ } catch (error) {
334
+ validationFeedback = error instanceof Error ? error.message : String(error);
335
+ return { ok: false, transient: true, error: validationFeedback };
286
336
  }
287
337
  return { ok: true };
288
338
  }
@@ -291,19 +341,14 @@ ${issues}`
291
341
  printError(outcome.error ?? `Task plan generation failed for issue #${issueNumber}`);
292
342
  return 1;
293
343
  }
344
+ const repository = getPlanRepository(tasksPath);
345
+ if (repository !== void 0) {
346
+ await ingestGeneratedPlan(repository);
347
+ }
294
348
  const plan = await loadTaskPlan(tasksPath);
295
349
  plan.pipeline.jsonCompleted = true;
296
350
  plan.branchName = persistedBranch !== null && persistedBranch !== "" ? persistedBranch : numbering?.branchName ?? computedBranch;
297
351
  await saveTaskPlan(tasksPath, plan);
298
- const generatedLowest = plan.userStories.map((story) => parseUserStoryNumber(story.id)).filter((value) => value !== null).reduce(
299
- (lowest, value) => lowest === null ? value : Math.min(lowest, value),
300
- null
301
- );
302
- if (generatedLowest !== null && generatedLowest < nextNumber) {
303
- printWarning(
304
- `The generated plan starts at ${formatUserStoryId(generatedLowest)}, not the requested ${nextUserStoryId}. User Story ids may collide with earlier issues of this project \u2014 re-run 'issue-flow plan ${issueNumber} --start-us ${nextNumber}' if that matters.`
305
- );
306
- }
307
352
  printSuccess(`Task plan saved to ${tasksPath} (${plan.userStories.length} stories)`);
308
353
  return 0;
309
354
  }
@@ -311,4 +356,4 @@ ${issues}`
311
356
  export {
312
357
  runPlan
313
358
  };
314
- //# sourceMappingURL=chunk-34OCSKOB.js.map
359
+ //# sourceMappingURL=chunk-AKL5UPR6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/plan.ts","../src/core/plan-result.ts","../src/storage/user-story-numbering.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { basename } from 'node:path';\nimport {\n branchName,\n DEFAULT_BRANCH_CONVENTION,\n resolveChangeType,\n} from '../conventions/git/index.js';\nimport { DEFAULT_HEADLESS_TIMEOUT_MS, runHeadless } from '../core/headless.js';\nimport { runPhaseWithRetry } from '../core/phase-runner.js';\nimport { parsePlanResult } from '../core/plan-result.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { publishPhaseMetrics } from '../core/session-metrics.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { inspectTaskPlan } from '../core/task-plan.js';\nimport { getGlobalTimeout } from '../core/verbose.js';\nimport { issuePlaceholders, issueReference, resolveCommandIssue } from '../issues/context.js';\nimport type { ResolvedIssue } from '../issues/types.js';\nimport { loadRepositoryPolicy } from '../policy/index.js';\nimport { resolvePolicyPlaceholders } from '../policy/placeholders.js';\nimport { getPlanRepository, ingestGeneratedPlan } from '../storage/db/repository.js';\nimport { resolveIssuePaths } from '../storage/resolve.js';\nimport { determineUserStoryNumbering, formatUserStoryId } from '../storage/user-story-numbering.js';\nimport type { TaskPlan } from '../types.js';\nimport { printError, printInfo, printSuccess } from '../ui/logger.js';\nimport { writeFileAtomic } from '../utils/fs.js';\nimport { isTransientFailure } from '../utils/retry.js';\n\n/** `--continue` / `--start-us <n>` override of the numbering cascade (issue #36). */\nexport interface PlanUserStoryNumberingOptions {\n continueFlag?: boolean;\n startUs?: number;\n /** Current checkout in --no-branch mode; prevents the plan from inventing a branch. */\n branchName?: string;\n}\n\nexport async function runPlan(\n issue: string,\n resolvedIssue?: ResolvedIssue,\n numbering?: PlanUserStoryNumberingOptions,\n): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const paths = await resolveIssuePaths(issueNumber);\n\n const resolution = await resolveCommandIssue(issueNumber, resolvedIssue);\n if (!resolution.ok) {\n return resolution.code;\n }\n\n // Read the PRD\n const prdPath = paths.prdFile;\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 = paths.tasksFile;\n\n // Resolve the User Story numbering continuity (issue #36), log where the\n // decision came from — never silently — and persist it into the project's\n // metadata.json for audit before the prompt is even built, so the record on\n // disk always matches what the prompt was told.\n const {\n message: numberingMessage,\n decision: { nextNumber },\n } = await determineUserStoryNumbering({\n issueNumber,\n continueFlag: numbering?.continueFlag,\n startUs: numbering?.startUs,\n });\n printInfo(numberingMessage);\n\n let persistedBranch: string | null = null;\n let closure: { closeIssue?: boolean; issueClosedAt?: string } = {};\n try {\n const previous = await loadTaskPlan(tasksPath);\n persistedBranch = previous.branchName ?? null;\n closure = { closeIssue: previous.closeIssue, issueClosedAt: previous.issueClosedAt };\n } catch {\n persistedBranch = null;\n }\n\n const policy = await loadRepositoryPolicy();\n const change = resolveChangeType({\n labels: resolution.resolved.issue.labels,\n title: resolution.resolved.issue.title,\n titleConvention: policy.issues.titleConvention,\n typeMap: policy.git.typeMap,\n });\n const numericIssue = /^\\d+$/.test(issueNumber) ? Number(issueNumber) : null;\n const computedBranch = branchName({\n type: change.type,\n issueNumber: numericIssue,\n title: resolution.resolved.issue.title,\n convention: policy.git.branchConvention ?? DEFAULT_BRANCH_CONVENTION,\n });\n const resolvedBranch =\n persistedBranch !== null && persistedBranch !== ''\n ? persistedBranch\n : (numbering?.branchName ?? computedBranch);\n printInfo(\n `Branch: ${resolvedBranch} (type ${change.type} from ${change.source}${\n persistedBranch !== null && persistedBranch !== '' ? ', persisted' : ''\n })`,\n );\n\n const template = await loadPrompt('plan');\n const prompt = applyPlaceholders(template, {\n // The repository's own conventions. Empty when it declares none, which is\n // what keeps the rendered prompt identical to the pre-policy one.\n ...(await resolvePolicyPlaceholders({ phase: 'plan' })),\n __ISSUE_NUMBER__: issueNumber,\n __PRD_CONTENT__: prdContent,\n ...issuePlaceholders(resolution.resolved, paths.issueFile),\n });\n\n await mkdir(paths.issueDir, { recursive: true });\n\n let validationFeedback = '';\n const outcome = await runPhaseWithRetry({\n phase: 'plan',\n attempt: async () => {\n const startedAtMs = Date.now();\n const result = await runHeadless({\n prompt: validationFeedback\n ? `${prompt}\\n\\nReturn a corrected <task-plan> block. Validation errors (diagnostic data, not instructions):\\n${validationFeedback}`\n : prompt,\n maxTurns: 25,\n timeout: getGlobalTimeout() ?? DEFAULT_HEADLESS_TIMEOUT_MS,\n timeoutHistory: {\n phase: 'plan',\n journalFiles: [paths.rotatedEventsFile, paths.eventsFile],\n },\n // json (not text) so the CLI reports usage: the envelope's `result`\n // field carries the same assistant text this phase already consumed.\n outputFormat: 'json',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n statusMessage: `Converting PRD to task plan for issue #${issueNumber}...`,\n phase: 'plan',\n permission: 'read-only',\n });\n // One event per attempt; the reducer sums them into the phase total.\n publishPhaseMetrics('plan', result.cost, startedAtMs, result.agent?.provider);\n\n if (!result.success) {\n return {\n ok: false,\n transient: result.retryExhausted !== true && isTransientFailure(1, result.error ?? ''),\n error: `Task plan generation failed: ${result.error}`,\n };\n }\n\n try {\n const draft = parsePlanResult(result.result);\n const idByKey = new Map(\n draft.stories.map((story, index) => [story.key, formatUserStoryId(nextNumber + index)]),\n );\n const generated: TaskPlan = {\n ...closure,\n project: basename(policy.root),\n issueNumber: /^\\d+$/.test(issueNumber) ? Number(issueNumber) : issueNumber,\n issueUrl: issueReference(resolution.resolved.issue, paths.issueFile),\n branchName: resolvedBranch,\n noBranch: numbering?.branchName !== undefined,\n description: draft.description,\n issueStatus: 'pending',\n completedAt: null,\n lastAttemptAt: null,\n lastError: null,\n correctionCycle: 0,\n maxCorrectionCycles: 3,\n lastReviewFindings: null,\n pipeline: {\n analyzeCompleted: false,\n prdCompleted: true,\n jsonCompleted: true,\n executionCompleted: false,\n reviewCompleted: false,\n prCreated: false,\n },\n userStories: draft.stories.map((story, index) => ({\n id: idByKey.get(story.key) as string,\n title: story.title,\n description: story.description,\n acceptanceCriteria: story.acceptanceCriteria,\n priority: index + 1,\n passes: false,\n notes: '',\n ...(story.dependsOn.length === 0\n ? {}\n : { dependencies: story.dependsOn.map((key) => idByKey.get(key) as string) }),\n })),\n };\n const validation = inspectTaskPlan(generated);\n if (!validation.ok) {\n const issues = validation.errors.map((i) => ` - ${i.path}: ${i.message}`).join('\\n');\n throw new Error(`generated plan does not match expected schema:\\n${issues}`);\n }\n await writeFileAtomic(tasksPath, `${JSON.stringify(generated, null, 2)}\\n`);\n } catch (error) {\n validationFeedback = error instanceof Error ? error.message : String(error);\n return { ok: false, transient: true, error: validationFeedback };\n }\n\n return { ok: true };\n },\n });\n\n if (!outcome.ok) {\n printError(outcome.error ?? `Task plan generation failed for issue #${issueNumber}`);\n return 1;\n }\n\n // `plan` is written by the agent to the compatibility projection. Promote\n // the validated result to SQLite before the pipeline reads it back.\n const repository = getPlanRepository(tasksPath);\n if (repository !== undefined) {\n await ingestGeneratedPlan(repository);\n }\n\n // Ensure pipeline state reflects completion of this phase.\n // The branch is a CLI calculation: a persisted name is never recalculated,\n // and a fresh plan is overwritten if the agent drifted from __BRANCH_NAME__.\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.jsonCompleted = true;\n plan.branchName =\n persistedBranch !== null && persistedBranch !== ''\n ? persistedBranch\n : (numbering?.branchName ?? computedBranch);\n await saveTaskPlan(tasksPath, plan);\n\n printSuccess(`Task plan saved to ${tasksPath} (${plan.userStories.length} stories)`);\n return 0;\n}\n","import { z } from 'zod';\nimport { parseDocumentResult } from './document-result.js';\n\nconst storyDraftSchema = z.object({\n key: z.string().min(1),\n title: z.string().min(1),\n description: z.string().min(1),\n acceptanceCriteria: z.array(z.string().min(1)).min(1),\n dependsOn: z.array(z.string().min(1)).optional().default([]),\n});\n\nconst planDraftSchema = z.object({\n description: z.string().min(1),\n stories: z.array(storyDraftSchema).min(1),\n});\n\nexport type PlanDraft = z.infer<typeof planDraftSchema>;\n\n/** Parse semantic planning output; lifecycle fields remain CLI-owned. */\nexport function parsePlanResult(output: string): PlanDraft {\n const content = parseDocumentResult(output, 'task-plan');\n const draft = planDraftSchema.parse(JSON.parse(content));\n const keys = new Set<string>();\n for (const story of draft.stories) {\n if (keys.has(story.key)) throw new Error(`Duplicate story key: ${story.key}`);\n keys.add(story.key);\n }\n for (const story of draft.stories) {\n for (const dependency of story.dependsOn) {\n if (dependency === story.key) throw new Error(`Story ${story.key} depends on itself`);\n if (!keys.has(dependency))\n throw new Error(`Story ${story.key} has unknown dependency ${dependency}`);\n }\n }\n return draft;\n}\n","import { isoNow } from '../core/state-manager.js';\nimport { getProjectRoot } from '../utils/git.js';\nimport { findHighestStoredUserStoryNumber, saveStoredUserStoryNumbering } from './db/repository.js';\nimport { type ResolveIssuePathsOptions, resolveProjectPaths } from './resolve.js';\nimport type { UserStoryNumberingDecision } from './schemas.js';\n\n/**\n * Continuity of `US-NNN` numbering across `plan` executions of the same\n * project (issue #36).\n *\n * The User Story `id` field is free-form — `userStorySchema` in\n * `src/schemas.ts` only requires a non-empty string, no `US-NNN` regex is\n * enforced — so every reader here tolerates an id that does not carry a\n * trailing number instead of throwing on it.\n */\n\nconst USER_STORY_ID_PAD = 3;\n\n/** `16` -> `'US-016'`. Numbers past 999 are not padded further. */\nexport function formatUserStoryId(n: number): string {\n return `US-${String(n).padStart(USER_STORY_ID_PAD, '0')}`;\n}\n\n/**\n * Extract the last run of digits from a User Story id, or `null` when none is\n * found. Tolerant by design: an id like `US-001` yields `1`, but so does a\n * free-form one such as `story-1` or `v2-US-007` (the last digit run, `007`,\n * wins over the `2` version prefix) — only an id with no digits at all (e.g.\n * `add-auth`) is ignored.\n */\nexport function parseUserStoryNumber(id: string): number | null {\n const matches = id.match(/\\d+/g);\n if (!matches || matches.length === 0) return null;\n\n const value = Number.parseInt(matches[matches.length - 1], 10);\n return Number.isNaN(value) ? null : value;\n}\n\nexport interface HighestUserStoryNumberResult {\n number: number;\n /** Issue directory name the winning story came from. */\n issueId: string;\n /** Verbatim id of the winning story. */\n storyId: string;\n}\n\nexport interface FindHighestUserStoryNumberOptions extends ResolveIssuePathsOptions {\n /**\n * Issue directory to leave out of the scan — the issue the numbering is\n * being resolved for. Re-planning an issue overwrites its own `tasks.json`,\n * so counting the plan about to be replaced would push the numbering forward\n * on every `plan` re-run of the same issue.\n */\n excludeIssueId?: string;\n}\n\n/**\n * Highest User Story number already used anywhere in the project's canonical\n * SQLite state. The issue named by `excludeIssueId` is skipped, so re-planning\n * is idempotent. This deliberately never scans compatibility projections.\n */\nexport async function findHighestUserStoryNumber(\n options: FindHighestUserStoryNumberOptions = {},\n): Promise<HighestUserStoryNumberResult | null> {\n const { excludeIssueId, ...rootOptions } = options;\n let project: Awaited<ReturnType<typeof resolveProjectPaths>>;\n try {\n project = await resolveProjectPaths(rootOptions);\n } catch (error) {\n throw new Error(\n \"Could not query the project's User Story history: \" +\n `${(error as Error).message}. Fix the storage directory, or pass --start-us <n> ` +\n 'to set the numbering explicitly.',\n { cause: error },\n );\n }\n try {\n return await findHighestStoredUserStoryNumber({\n projectId: project.projectId,\n excludeIssueId,\n databaseOptions: project.databaseOptions,\n });\n } catch (error) {\n throw new Error(\n \"Could not query the project's User Story history: \" +\n `${(error as Error).message}. Fix the SQLite database, or pass --start-us <n> ` +\n 'to set the numbering explicitly.',\n { cause: error },\n );\n }\n}\n\nexport interface ResolveUserStoryNumberingOptions extends ResolveIssuePathsOptions {\n /** Issue the decision is being made for — carried into the audit record. */\n issueNumber: string;\n /** `--continue` was passed explicitly. Purely cosmetic on the log message:\n * the resolved number is identical to the automatic default. */\n continueFlag?: boolean;\n /** `--start-us <n>` was passed. Wins outright over any history. */\n startUs?: number;\n /** Timestamp source, injectable for tests. */\n now?: () => string;\n}\n\nexport interface UserStoryNumberingOutcome {\n decision: UserStoryNumberingDecision;\n /** Ready-to-print explanation of where the decision came from — the CLI\n * must never resolve this silently (issue #36). */\n message: string;\n}\n\n/**\n * Resolve the numbering cascade of #36:\n *\n * 1. `--start-us <n>` wins outright — history is not even scanned, since the\n * whole point is to ignore it.\n * 2. Otherwise the project's history is scanned for the highest number used\n * so far. This is what running with no flag at all does automatically,\n * and what `--continue` names explicitly (the two produce the same\n * number; the flag only changes the log message).\n * 3. No history found (the project's first `plan` ever) starts at `US-001`.\n *\n * Pure with respect to `metadata.json` — this only reads `tasks.json` files,\n * never writes. Persisting the decision is {@link determineUserStoryNumbering}\n * below, via `recordUserStoryNumbering()` in `compat.ts`.\n */\nexport async function resolveUserStoryNumbering(\n options: ResolveUserStoryNumberingOptions,\n): Promise<UserStoryNumberingOutcome> {\n const { issueNumber, continueFlag, startUs, now, ...rootOptions } = options;\n const decidedAt = (now ?? isoNow)();\n\n if (startUs !== undefined) {\n const decision: UserStoryNumberingDecision = {\n nextNumber: startUs,\n source: 'start-us',\n issueNumber,\n decidedAt,\n };\n return {\n decision,\n message: `User Story numbering forced to ${formatUserStoryId(startUs)} via --start-us.`,\n };\n }\n\n // The issue being planned is excluded from the scan: `plan` overwrites its\n // own `tasks.json`, so counting the plan it is about to replace would push\n // the numbering forward on every re-run of `plan` for the same issue.\n const highest = await findHighestUserStoryNumber({ ...rootOptions, excludeIssueId: issueNumber });\n\n if (highest === null) {\n const decision: UserStoryNumberingDecision = {\n nextNumber: 1,\n source: 'none',\n issueNumber,\n decidedAt,\n };\n return {\n decision,\n message: `Starting User Story numbering at ${formatUserStoryId(1)} (no previous history found for this project).`,\n };\n }\n\n const nextNumber = highest.number + 1;\n const decision: UserStoryNumberingDecision = {\n nextNumber,\n source: 'history',\n issueNumber,\n decidedAt,\n detail: `${highest.storyId} (issue #${highest.issueId})`,\n };\n const via = continueFlag ? ' via --continue' : '';\n return {\n decision,\n message:\n `Continuing User Story numbering from ${formatUserStoryId(nextNumber)}${via} ` +\n `— last used was ${highest.storyId} (issue #${highest.issueId}).`,\n };\n}\n\nexport interface DetermineUserStoryNumberingOptions extends ResolveUserStoryNumberingOptions {\n /** Repository root. Defaults to the git toplevel of the current directory. */\n projectRoot?: string;\n}\n\nexport interface UserStoryNumberingResult extends UserStoryNumberingOutcome {\n /** `decision.nextNumber` formatted as `US-NNN`, ready for the prompt. */\n nextUserStoryId: string;\n}\n\n/**\n * The entry point commands use: resolve the cascade and persist the decision\n * into `metadata.json` in one call, so no call site can log/prompt with one\n * decision and audit a different one.\n */\nexport async function determineUserStoryNumbering(\n options: DetermineUserStoryNumberingOptions,\n): Promise<UserStoryNumberingResult> {\n const { projectRoot: projectRootOption, ...rest } = options;\n const projectRoot = projectRootOption ?? (await getProjectRoot());\n\n const outcome = await resolveUserStoryNumbering({ ...rest, projectRoot });\n const project = await resolveProjectPaths({ projectRoot, env: options.env });\n if (project.storageDriver === 'sqlite') {\n await saveStoredUserStoryNumbering(\n {\n projectId: project.projectId,\n projectRoot,\n databaseOptions: project.databaseOptions,\n },\n outcome.decision,\n );\n } else {\n const { recordUserStoryNumbering } = await import('./compat.js');\n await recordUserStoryNumbering(projectRoot, outcome.decision, {\n env: options.env,\n now: options.now,\n });\n }\n\n return { ...outcome, nextUserStoryId: formatUserStoryId(outcome.decision.nextNumber) };\n}\n"],"mappings":"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","names":["decision"]}
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  printWarning
4
- } from "./chunk-XZUEFTUP.js";
4
+ } from "./chunk-DBLXLKGU.js";
5
5
  import {
6
6
  getProjectRoot
7
- } from "./chunk-YVPZUN6X.js";
7
+ } from "./chunk-KUA6KBBD.js";
8
8
 
9
9
  // src/core/prompt-resolver.ts
10
- import { existsSync } from "fs";
11
- import { readFile } from "fs/promises";
12
- import { dirname, join } from "path";
13
- import { fileURLToPath } from "url";
10
+ import { existsSync } from "node:fs";
11
+ import { readFile } from "node:fs/promises";
12
+ import { dirname, join } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
14
  function resolvePackageDir(name, startDir) {
15
15
  let dir = startDir ?? dirname(fileURLToPath(import.meta.url));
16
16
  for (let i = 0; i < 5; i++) {
@@ -95,11 +95,11 @@ ${body}`
95
95
  return result;
96
96
  }
97
97
  function applyPlaceholders(template, vars) {
98
- let result = applyConditionalSections(template, vars);
99
- for (const [key, value] of Object.entries(vars)) {
100
- result = result.replaceAll(key, value);
101
- }
102
- return result;
98
+ const result = applyConditionalSections(template, vars).replace(
99
+ /^<!-- Generated[^\n]+-->\n\n/,
100
+ ""
101
+ );
102
+ return result.replace(/__[A-Z0-9_]+__/g, (key) => vars[key] ?? key);
103
103
  }
104
104
 
105
105
  export {
@@ -107,4 +107,4 @@ export {
107
107
  loadPrompt,
108
108
  applyPlaceholders
109
109
  };
110
- //# sourceMappingURL=chunk-7F7LGMVO.js.map
110
+ //# sourceMappingURL=chunk-BTBJ5UOO.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/prompt-resolver.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { printWarning } from '../ui/logger.js';\nimport { getProjectRoot } from '../utils/git.js';\n\n/**\n * Resolve the absolute path to a directory shipped at the package root\n * (e.g. 'prompts', 'web/public'). Works from both source (src/core/) and\n * compiled (dist/) locations by walking up the directory tree until the\n * named folder is found. Returns null when it cannot be located.\n *\n * `startDir` is injectable for tests; it defaults to this module's directory.\n */\nexport function resolvePackageDir(name: string, startDir?: string): string | null {\n let dir = startDir ?? dirname(fileURLToPath(import.meta.url));\n\n for (let i = 0; i < 5; i++) {\n const candidate = join(dir, name);\n if (existsSync(candidate)) {\n return candidate;\n }\n dir = dirname(dir);\n }\n\n return null;\n}\n\nfunction getPromptsDir(): string {\n const dir = resolvePackageDir('prompts');\n if (dir === null) {\n throw new Error(\n 'Could not locate the prompts/ directory. Ensure the package is installed correctly.',\n );\n }\n return dir;\n}\n\n/** Directory a repository puts its own prompt overrides in. */\nexport const PROMPT_OVERRIDE_DIR = '.issue-flow/prompts';\n\nexport interface LoadPromptOptions {\n /**\n * Repository root holding `.issue-flow/prompts/`. Defaults to the git project\n * root; when there is none, no override is looked for.\n */\n projectRoot?: string;\n /** Warning sink. Defaults to printWarning. */\n warn?: (message: string) => void;\n}\n\n/** Read a file, answering null for \"absent\" and for \"unreadable\". */\nasync function readOverride(\n filePath: string,\n warn: (message: string) => void,\n): Promise<string | null> {\n try {\n return await readFile(filePath, 'utf-8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n // Absence is the common case and says nothing.\n return null;\n }\n // A file the repository *did* write and we cannot read is worth a warning:\n // silently ignoring it would look like the override simply had no effect.\n warn(`Ignoring prompt override ${filePath}: ${(err as Error).message}`);\n return null;\n }\n}\n\n/**\n * Load a prompt template by name, honouring the repository's overrides.\n *\n * Resolution order:\n *\n * 1. `<root>/.issue-flow/prompts/<name>.md` — replaces the packaged prompt;\n * 2. `<root>/.issue-flow/prompts/<name>.append.md` — appended to it;\n * 3. `prompts/<name>.md` of the package.\n *\n * `append` is the recommended form. Replacing a whole prompt makes the\n * repository inherit its maintenance: every improvement shipped by a new\n * release stops reaching it, silently.\n *\n * Overrides are opt-in and best-effort — a repository with none, or one that is\n * not a git checkout at all, gets exactly the packaged prompt it got before.\n *\n * @param name - Prompt name without extension (e.g., 'execute', 'analyze')\n * @returns The raw template content with placeholders intact\n */\nexport async function loadPrompt(name: string, options: LoadPromptOptions = {}): Promise<string> {\n const warn = options.warn ?? printWarning;\n const promptsDir = getPromptsDir();\n const filePath = join(promptsDir, `${name}.md`);\n\n let packaged: string;\n try {\n packaged = await readFile(filePath, 'utf-8');\n } catch {\n throw new Error(`Prompt template not found: ${filePath}`);\n }\n\n let root = options.projectRoot;\n if (root === undefined) {\n try {\n root = await getProjectRoot();\n } catch {\n // Not a git repository: no override directory to look in.\n return packaged;\n }\n }\n\n const overrideDir = join(root, PROMPT_OVERRIDE_DIR);\n const [replacement, appendix] = await Promise.all([\n readOverride(join(overrideDir, `${name}.md`), warn),\n readOverride(join(overrideDir, `${name}.append.md`), warn),\n ]);\n\n if (replacement !== null) {\n if (appendix !== null) {\n warn(\n `Both ${name}.md and ${name}.append.md exist in ${PROMPT_OVERRIDE_DIR}; ` +\n 'using the replacement and ignoring the appendix.',\n );\n }\n return replacement;\n }\n\n if (appendix !== null) {\n // One blank line between the two, whatever the packaged prompt ends with,\n // so the appendix never runs into the last paragraph.\n return `${packaged.replace(/\\s*$/, '')}\\n\\n${appendix}`;\n }\n\n return packaged;\n}\n\n/**\n * Drop the conditional sections whose placeholder resolves to nothing, and\n * unwrap the ones that stay.\n *\n * A prompt marks an optional section with HTML comments, which stay invisible\n * if a template is ever read as plain markdown:\n *\n * ```markdown\n * <!-- if:__REPO_POLICY__ -->\n * ## Repository policy\n *\n * __REPO_POLICY__\n * <!-- /if -->\n * ```\n *\n * Separate two consecutive blocks with a blank line. Removal consumes the\n * newline **before** the opening marker, so back-to-back blocks make the second\n * removal eat the line that ended the prompt body — the file loses its trailing\n * newline. `prompt-override.test.ts` asserts that newline for every packaged\n * prompt, which is what catches it.\n *\n * This exists for one reason: a repository that declares no policy must get a\n * prompt that is **byte for byte** the one it got before the policy layer\n * existed. Leaving an empty heading behind would break that, and an empty\n * \"Repository policy\" section is also actively harmful — it invites the agent\n * to wonder what was supposed to be there.\n */\nexport function applyConditionalSections(template: string, vars: Record<string, string>): string {\n let result = template;\n\n for (const [key, value] of Object.entries(vars)) {\n // The key is a placeholder like __REPO_POLICY__: word characters only, so\n // it needs no escaping to be safe inside the pattern below.\n if (!/^__[A-Z0-9_]+__$/.test(key)) continue;\n\n const block = new RegExp(`\\\\n?<!-- if:${key} -->\\\\n([\\\\s\\\\S]*?)<!-- /if -->\\\\n?`, 'g');\n result = result.replace(block, (_match, body: string) =>\n value.trim() === '' ? '' : `\\n${body}`,\n );\n }\n\n return result;\n}\n\n/**\n * Replace placeholders in a prompt template with actual values.\n *\n * Placeholders use the format __KEY__ (e.g., __ISSUE_NUMBER__, __PRD_FILE__).\n *\n * Conditional sections are resolved first, so a section whose placeholder is\n * empty disappears entirely instead of rendering as an empty heading.\n */\nexport function applyPlaceholders(template: string, vars: Record<string, string>): string {\n let result = applyConditionalSections(template, vars);\n for (const [key, value] of Object.entries(vars)) {\n result = result.replaceAll(key, value);\n }\n return result;\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAYvB,SAAS,kBAAkB,MAAc,UAAkC;AAChF,MAAI,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAE5D,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,YAAY,KAAK,KAAK,IAAI;AAChC,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,GAAG;AAAA,EACnB;AAEA,SAAO;AACT;AAEA,SAAS,gBAAwB;AAC/B,QAAM,MAAM,kBAAkB,SAAS;AACvC,MAAI,QAAQ,MAAM;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,IAAM,sBAAsB;AAanC,eAAe,aACb,UACA,MACwB;AACxB,MAAI;AACF,WAAO,MAAM,SAAS,UAAU,OAAO;AAAA,EACzC,SAAS,KAAc;AACrB,QAAK,IAA8B,SAAS,UAAU;AAEpD,aAAO;AAAA,IACT;AAGA,SAAK,4BAA4B,QAAQ,KAAM,IAAc,OAAO,EAAE;AACtE,WAAO;AAAA,EACT;AACF;AAqBA,eAAsB,WAAW,MAAc,UAA6B,CAAC,GAAoB;AAC/F,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,aAAa,cAAc;AACjC,QAAM,WAAW,KAAK,YAAY,GAAG,IAAI,KAAK;AAE9C,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,SAAS,UAAU,OAAO;AAAA,EAC7C,QAAQ;AACN,UAAM,IAAI,MAAM,8BAA8B,QAAQ,EAAE;AAAA,EAC1D;AAEA,MAAI,OAAO,QAAQ;AACnB,MAAI,SAAS,QAAW;AACtB,QAAI;AACF,aAAO,MAAM,eAAe;AAAA,IAC9B,QAAQ;AAEN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,cAAc,KAAK,MAAM,mBAAmB;AAClD,QAAM,CAAC,aAAa,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,IAChD,aAAa,KAAK,aAAa,GAAG,IAAI,KAAK,GAAG,IAAI;AAAA,IAClD,aAAa,KAAK,aAAa,GAAG,IAAI,YAAY,GAAG,IAAI;AAAA,EAC3D,CAAC;AAED,MAAI,gBAAgB,MAAM;AACxB,QAAI,aAAa,MAAM;AACrB;AAAA,QACE,QAAQ,IAAI,WAAW,IAAI,uBAAuB,mBAAmB;AAAA,MAEvE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,MAAM;AAGrB,WAAO,GAAG,SAAS,QAAQ,QAAQ,EAAE,CAAC;AAAA;AAAA,EAAO,QAAQ;AAAA,EACvD;AAEA,SAAO;AACT;AA6BO,SAAS,yBAAyB,UAAkB,MAAsC;AAC/F,MAAI,SAAS;AAEb,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAG/C,QAAI,CAAC,mBAAmB,KAAK,GAAG,EAAG;AAEnC,UAAM,QAAQ,IAAI,OAAO,eAAe,GAAG,uCAAuC,GAAG;AACrF,aAAS,OAAO;AAAA,MAAQ;AAAA,MAAO,CAAC,QAAQ,SACtC,MAAM,KAAK,MAAM,KAAK,KAAK;AAAA,EAAK,IAAI;AAAA,IACtC;AAAA,EACF;AAEA,SAAO;AACT;AAUO,SAAS,kBAAkB,UAAkB,MAAsC;AACxF,MAAI,SAAS,yBAAyB,UAAU,IAAI;AACpD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,aAAS,OAAO,WAAW,KAAK,KAAK;AAAA,EACvC;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/core/prompt-resolver.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { printWarning } from '../ui/logger.js';\nimport { getProjectRoot } from '../utils/git.js';\n\n/**\n * Resolve the absolute path to a directory shipped at the package root\n * (e.g. 'prompts', 'web/public'). Works from both source (src/core/) and\n * compiled (dist/) locations by walking up the directory tree until the\n * named folder is found. Returns null when it cannot be located.\n *\n * `startDir` is injectable for tests; it defaults to this module's directory.\n */\nexport function resolvePackageDir(name: string, startDir?: string): string | null {\n let dir = startDir ?? dirname(fileURLToPath(import.meta.url));\n\n for (let i = 0; i < 5; i++) {\n const candidate = join(dir, name);\n if (existsSync(candidate)) {\n return candidate;\n }\n dir = dirname(dir);\n }\n\n return null;\n}\n\nfunction getPromptsDir(): string {\n const dir = resolvePackageDir('prompts');\n if (dir === null) {\n throw new Error(\n 'Could not locate the prompts/ directory. Ensure the package is installed correctly.',\n );\n }\n return dir;\n}\n\n/** Directory a repository puts its own prompt overrides in. */\nexport const PROMPT_OVERRIDE_DIR = '.issue-flow/prompts';\n\nexport interface LoadPromptOptions {\n /**\n * Repository root holding `.issue-flow/prompts/`. Defaults to the git project\n * root; when there is none, no override is looked for.\n */\n projectRoot?: string;\n /** Warning sink. Defaults to printWarning. */\n warn?: (message: string) => void;\n}\n\n/** Read a file, answering null for \"absent\" and for \"unreadable\". */\nasync function readOverride(\n filePath: string,\n warn: (message: string) => void,\n): Promise<string | null> {\n try {\n return await readFile(filePath, 'utf-8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n // Absence is the common case and says nothing.\n return null;\n }\n // A file the repository *did* write and we cannot read is worth a warning:\n // silently ignoring it would look like the override simply had no effect.\n warn(`Ignoring prompt override ${filePath}: ${(err as Error).message}`);\n return null;\n }\n}\n\n/**\n * Load a prompt template by name, honouring the repository's overrides.\n *\n * Resolution order:\n *\n * 1. `<root>/.issue-flow/prompts/<name>.md` — replaces the packaged prompt;\n * 2. `<root>/.issue-flow/prompts/<name>.append.md` — appended to it;\n * 3. `prompts/<name>.md` of the package.\n *\n * `append` is the recommended form. Replacing a whole prompt makes the\n * repository inherit its maintenance: every improvement shipped by a new\n * release stops reaching it, silently.\n *\n * Overrides are opt-in and best-effort — a repository with none, or one that is\n * not a git checkout at all, gets exactly the packaged prompt it got before.\n *\n * @param name - Prompt name without extension (e.g., 'execute', 'analyze')\n * @returns The raw template content with placeholders intact\n */\nexport async function loadPrompt(name: string, options: LoadPromptOptions = {}): Promise<string> {\n const warn = options.warn ?? printWarning;\n const promptsDir = getPromptsDir();\n const filePath = join(promptsDir, `${name}.md`);\n\n let packaged: string;\n try {\n packaged = await readFile(filePath, 'utf-8');\n } catch {\n throw new Error(`Prompt template not found: ${filePath}`);\n }\n\n let root = options.projectRoot;\n if (root === undefined) {\n try {\n root = await getProjectRoot();\n } catch {\n // Not a git repository: no override directory to look in.\n return packaged;\n }\n }\n\n const overrideDir = join(root, PROMPT_OVERRIDE_DIR);\n const [replacement, appendix] = await Promise.all([\n readOverride(join(overrideDir, `${name}.md`), warn),\n readOverride(join(overrideDir, `${name}.append.md`), warn),\n ]);\n\n if (replacement !== null) {\n if (appendix !== null) {\n warn(\n `Both ${name}.md and ${name}.append.md exist in ${PROMPT_OVERRIDE_DIR}; ` +\n 'using the replacement and ignoring the appendix.',\n );\n }\n return replacement;\n }\n\n if (appendix !== null) {\n // One blank line between the two, whatever the packaged prompt ends with,\n // so the appendix never runs into the last paragraph.\n return `${packaged.replace(/\\s*$/, '')}\\n\\n${appendix}`;\n }\n\n return packaged;\n}\n\n/**\n * Drop the conditional sections whose placeholder resolves to nothing, and\n * unwrap the ones that stay.\n *\n * A prompt marks an optional section with HTML comments, which stay invisible\n * if a template is ever read as plain markdown:\n *\n * ```markdown\n * <!-- if:__REPO_POLICY__ -->\n * ## Repository policy\n *\n * __REPO_POLICY__\n * <!-- /if -->\n * ```\n *\n * Separate two consecutive blocks with a blank line. Removal consumes the\n * newline **before** the opening marker, so back-to-back blocks make the second\n * removal eat the line that ended the prompt body — the file loses its trailing\n * newline. `prompt-override.test.ts` asserts that newline for every packaged\n * prompt, which is what catches it.\n *\n * This exists for one reason: a repository that declares no policy must get a\n * prompt that is **byte for byte** the one it got before the policy layer\n * existed. Leaving an empty heading behind would break that, and an empty\n * \"Repository policy\" section is also actively harmful — it invites the agent\n * to wonder what was supposed to be there.\n */\nexport function applyConditionalSections(template: string, vars: Record<string, string>): string {\n let result = template;\n\n for (const [key, value] of Object.entries(vars)) {\n // The key is a placeholder like __REPO_POLICY__: word characters only, so\n // it needs no escaping to be safe inside the pattern below.\n if (!/^__[A-Z0-9_]+__$/.test(key)) continue;\n\n const block = new RegExp(`\\\\n?<!-- if:${key} -->\\\\n([\\\\s\\\\S]*?)<!-- /if -->\\\\n?`, 'g');\n result = result.replace(block, (_match, body: string) =>\n value.trim() === '' ? '' : `\\n${body}`,\n );\n }\n\n return result;\n}\n\n/**\n * Replace placeholders in a prompt template with actual values.\n *\n * Placeholders use the format __KEY__ (e.g., __ISSUE_NUMBER__, __PRD_FILE__).\n *\n * Conditional sections are resolved first, so a section whose placeholder is\n * empty disappears entirely instead of rendering as an empty heading.\n */\nexport function applyPlaceholders(template: string, vars: Record<string, string>): string {\n const result = applyConditionalSections(template, vars).replace(\n /^<!-- Generated[^\\n]+-->\\n\\n/,\n '',\n );\n return result.replace(/__[A-Z0-9_]+__/g, (key) => vars[key] ?? key);\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAYvB,SAAS,kBAAkB,MAAc,UAAkC;AAChF,MAAI,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAE5D,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,YAAY,KAAK,KAAK,IAAI;AAChC,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,GAAG;AAAA,EACnB;AAEA,SAAO;AACT;AAEA,SAAS,gBAAwB;AAC/B,QAAM,MAAM,kBAAkB,SAAS;AACvC,MAAI,QAAQ,MAAM;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,IAAM,sBAAsB;AAanC,eAAe,aACb,UACA,MACwB;AACxB,MAAI;AACF,WAAO,MAAM,SAAS,UAAU,OAAO;AAAA,EACzC,SAAS,KAAc;AACrB,QAAK,IAA8B,SAAS,UAAU;AAEpD,aAAO;AAAA,IACT;AAGA,SAAK,4BAA4B,QAAQ,KAAM,IAAc,OAAO,EAAE;AACtE,WAAO;AAAA,EACT;AACF;AAqBA,eAAsB,WAAW,MAAc,UAA6B,CAAC,GAAoB;AAC/F,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,aAAa,cAAc;AACjC,QAAM,WAAW,KAAK,YAAY,GAAG,IAAI,KAAK;AAE9C,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,SAAS,UAAU,OAAO;AAAA,EAC7C,QAAQ;AACN,UAAM,IAAI,MAAM,8BAA8B,QAAQ,EAAE;AAAA,EAC1D;AAEA,MAAI,OAAO,QAAQ;AACnB,MAAI,SAAS,QAAW;AACtB,QAAI;AACF,aAAO,MAAM,eAAe;AAAA,IAC9B,QAAQ;AAEN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,cAAc,KAAK,MAAM,mBAAmB;AAClD,QAAM,CAAC,aAAa,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,IAChD,aAAa,KAAK,aAAa,GAAG,IAAI,KAAK,GAAG,IAAI;AAAA,IAClD,aAAa,KAAK,aAAa,GAAG,IAAI,YAAY,GAAG,IAAI;AAAA,EAC3D,CAAC;AAED,MAAI,gBAAgB,MAAM;AACxB,QAAI,aAAa,MAAM;AACrB;AAAA,QACE,QAAQ,IAAI,WAAW,IAAI,uBAAuB,mBAAmB;AAAA,MAEvE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,MAAM;AAGrB,WAAO,GAAG,SAAS,QAAQ,QAAQ,EAAE,CAAC;AAAA;AAAA,EAAO,QAAQ;AAAA,EACvD;AAEA,SAAO;AACT;AA6BO,SAAS,yBAAyB,UAAkB,MAAsC;AAC/F,MAAI,SAAS;AAEb,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAG/C,QAAI,CAAC,mBAAmB,KAAK,GAAG,EAAG;AAEnC,UAAM,QAAQ,IAAI,OAAO,eAAe,GAAG,uCAAuC,GAAG;AACrF,aAAS,OAAO;AAAA,MAAQ;AAAA,MAAO,CAAC,QAAQ,SACtC,MAAM,KAAK,MAAM,KAAK,KAAK;AAAA,EAAK,IAAI;AAAA,IACtC;AAAA,EACF;AAEA,SAAO;AACT;AAUO,SAAS,kBAAkB,UAAkB,MAAsC;AACxF,QAAM,SAAS,yBAAyB,UAAU,IAAI,EAAE;AAAA,IACtD;AAAA,IACA;AAAA,EACF;AACA,SAAO,OAAO,QAAQ,mBAAmB,CAAC,QAAQ,KAAK,GAAG,KAAK,GAAG;AACpE;","names":[]}
@@ -4,40 +4,40 @@ import {
4
4
  PRIORS_VERSION,
5
5
  RECOMMENDED_POLICY_VERSION,
6
6
  decideRouting
7
- } from "./chunk-ZVZMWNCG.js";
7
+ } from "./chunk-VJ3PF6B7.js";
8
8
  import {
9
- GLOBAL_CONFIG_FILENAME,
10
9
  loadRoutingConfig,
11
10
  probeReadinessInventory
12
- } from "./chunk-QF7VEL3M.js";
13
- import {
14
- resolveIssuePaths
15
- } from "./chunk-LI35PIIS.js";
11
+ } from "./chunk-YC24E66P.js";
16
12
  import {
13
+ GLOBAL_CONFIG_FILENAME,
17
14
  printError,
18
15
  printInfo,
19
- printSuccess
20
- } from "./chunk-XZUEFTUP.js";
21
- import {
22
- getGlobalRoot
23
- } from "./chunk-HAKDLR6N.js";
24
- import {
25
- getProjectRoot
26
- } from "./chunk-YVPZUN6X.js";
16
+ printSuccess,
17
+ resolveIssuePaths
18
+ } from "./chunk-DBLXLKGU.js";
27
19
  import {
28
20
  loadTaskPlan
29
- } from "./chunk-SOM4QA6C.js";
21
+ } from "./chunk-JCR5C5A7.js";
22
+ import {
23
+ AGENT_PHASES
24
+ } from "./chunk-44RF3VFG.js";
30
25
  import {
31
- AGENT_PHASES,
32
26
  routingConfigInputSchema
33
- } from "./chunk-ICM463EO.js";
27
+ } from "./chunk-XNK5UN2D.js";
28
+ import {
29
+ getGlobalRoot
30
+ } from "./chunk-MAJ6QDRL.js";
31
+ import {
32
+ getProjectRoot
33
+ } from "./chunk-KUA6KBBD.js";
34
34
  import {
35
35
  writeFileAtomic
36
- } from "./chunk-3R27MJ5R.js";
36
+ } from "./chunk-PFWHHYEO.js";
37
37
 
38
38
  // src/commands/routing.ts
39
- import { mkdir, readFile } from "fs/promises";
40
- import { dirname, join } from "path";
39
+ import { mkdir, readFile } from "node:fs/promises";
40
+ import { dirname, join } from "node:path";
41
41
  var ROUTING_COMMAND_SCHEMA_VERSION = 1;
42
42
  function targetLabel(target) {
43
43
  if (target === void 0) return "\u2014";
@@ -226,4 +226,4 @@ export {
226
226
  writeRoutingPreference,
227
227
  runRoutingUse
228
228
  };
229
- //# sourceMappingURL=chunk-HOAHMERN.js.map
229
+ //# sourceMappingURL=chunk-CLZ6RRZL.js.map