issue-flow 0.17.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +34 -100
  2. package/dist/agent-EWKA7357.js +32 -0
  3. package/dist/{analyze-6M3KEOX7.js → analyze-Z7ODC27F.js} +42 -41
  4. package/dist/analyze-Z7ODC27F.js.map +1 -0
  5. package/dist/{apply-ADB3ZK7W.js → apply-VNSWQS6O.js} +22 -24
  6. package/dist/apply-VNSWQS6O.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-FM7I7EIV.js → bench-ZJEIACUK.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-ANI2NTR4.js → chunk-2EZW4X57.js} +28 -25
  14. package/dist/chunk-2EZW4X57.js.map +1 -0
  15. package/dist/{chunk-E6KXCV3T.js → chunk-2Q6PG3MT.js} +15 -15
  16. package/dist/chunk-2Q6PG3MT.js.map +1 -0
  17. package/dist/{chunk-C5TL7T4V.js → chunk-2YM2VH6R.js} +2 -2
  18. package/dist/{chunk-LPWPFIIB.js → chunk-2YYP7AE6.js} +14 -16
  19. package/dist/{chunk-LPWPFIIB.js.map → chunk-2YYP7AE6.js.map} +1 -1
  20. package/dist/chunk-3JD25HHJ.js +278 -0
  21. package/dist/chunk-3JD25HHJ.js.map +1 -0
  22. package/dist/{chunk-ISZ54SVE.js → chunk-46S7APLQ.js} +41 -17
  23. package/dist/chunk-46S7APLQ.js.map +1 -0
  24. package/dist/chunk-6AOZCWN2.js +21 -0
  25. package/dist/chunk-6AOZCWN2.js.map +1 -0
  26. package/dist/{chunk-QPGEFLMY.js → chunk-7CMVL4X6.js} +7 -7
  27. package/dist/chunk-7MJGYPHF.js +1571 -0
  28. package/dist/chunk-7MJGYPHF.js.map +1 -0
  29. package/dist/chunk-7ZZUSMBJ.js +35 -0
  30. package/dist/chunk-7ZZUSMBJ.js.map +1 -0
  31. package/dist/{chunk-DUS5ZNKL.js → chunk-DIMDAYZS.js} +7 -6
  32. package/dist/{chunk-DGRTJD5R.js → chunk-DXH4TQRB.js} +7 -7
  33. package/dist/{chunk-CRGIQFUQ.js → chunk-E2H3JV2E.js} +3 -1
  34. package/dist/chunk-E2H3JV2E.js.map +1 -0
  35. package/dist/{chunk-YUNBOUQH.js → chunk-ERAG5IQB.js} +8 -8
  36. package/dist/{chunk-D6ZBGJ3L.js → chunk-F2NFJ27Z.js} +130 -36
  37. package/dist/chunk-F2NFJ27Z.js.map +1 -0
  38. package/dist/{chunk-SOM4QA6C.js → chunk-GD7JZMSJ.js} +93 -18
  39. package/dist/chunk-GD7JZMSJ.js.map +1 -0
  40. package/dist/{chunk-YII4U7ZD.js → chunk-HEHHQU4G.js} +70 -81
  41. package/dist/chunk-HEHHQU4G.js.map +1 -0
  42. package/dist/{chunk-KZ5EY5XX.js → chunk-HHXWAWU6.js} +159 -114
  43. package/dist/chunk-HHXWAWU6.js.map +1 -0
  44. package/dist/{chunk-SGTTR4XW.js → chunk-HWWB22HC.js} +152 -100
  45. package/dist/chunk-HWWB22HC.js.map +1 -0
  46. package/dist/chunk-IMB6FA2F.js +673 -0
  47. package/dist/chunk-IMB6FA2F.js.map +1 -0
  48. package/dist/{chunk-6YQT3SSM.js → chunk-IQYXXFRC.js} +22 -6
  49. package/dist/chunk-IQYXXFRC.js.map +1 -0
  50. package/dist/chunk-J6VJ5M2U.js +1201 -0
  51. package/dist/chunk-J6VJ5M2U.js.map +1 -0
  52. package/dist/{chunk-B3TSS7BT.js → chunk-KDQRNVBZ.js} +3 -3
  53. package/dist/{chunk-A4H7P7T7.js → chunk-LRWIP7QS.js} +41 -26
  54. package/dist/chunk-LRWIP7QS.js.map +1 -0
  55. package/dist/{chunk-DV7HUC4Z.js → chunk-LYC57OXF.js} +20 -117
  56. package/dist/chunk-LYC57OXF.js.map +1 -0
  57. package/dist/{chunk-HAKDLR6N.js → chunk-MAJ6QDRL.js} +65 -78
  58. package/dist/chunk-MAJ6QDRL.js.map +1 -0
  59. package/dist/{chunk-HJBF4WD2.js → chunk-MHA7Q3KU.js} +288 -108
  60. package/dist/chunk-MHA7Q3KU.js.map +1 -0
  61. package/dist/{chunk-UK4E2FUA.js → chunk-MQTVMB7W.js} +103 -76
  62. package/dist/chunk-MQTVMB7W.js.map +1 -0
  63. package/dist/chunk-NP47HRX2.js +136 -0
  64. package/dist/chunk-NP47HRX2.js.map +1 -0
  65. package/dist/{chunk-MI5TREN3.js → chunk-NYDAHE3Y.js} +52 -46
  66. package/dist/chunk-NYDAHE3Y.js.map +1 -0
  67. package/dist/{chunk-UWDCIFTP.js → chunk-P7ZYOC23.js} +41 -57
  68. package/dist/chunk-P7ZYOC23.js.map +1 -0
  69. package/dist/{chunk-3R27MJ5R.js → chunk-PFWHHYEO.js} +3 -3
  70. package/dist/{chunk-2SUFAE6X.js → chunk-PHPAK2L6.js} +44 -33
  71. package/dist/{chunk-2SUFAE6X.js.map → chunk-PHPAK2L6.js.map} +1 -1
  72. package/dist/{chunk-PPZ3K4YM.js → chunk-PRGPSNGA.js} +2 -2
  73. package/dist/{chunk-32JSYXEK.js → chunk-RGFMQFX7.js} +9 -23
  74. package/dist/{chunk-32JSYXEK.js.map → chunk-RGFMQFX7.js.map} +1 -1
  75. package/dist/{chunk-YVPZUN6X.js → chunk-TJVX42KY.js} +335 -38
  76. package/dist/chunk-TJVX42KY.js.map +1 -0
  77. package/dist/{chunk-RCZJQUTC.js → chunk-UDRAD7MF.js} +60 -27
  78. package/dist/chunk-UDRAD7MF.js.map +1 -0
  79. package/dist/{chunk-HY5HLFDU.js → chunk-UHDXXLYH.js} +148 -135
  80. package/dist/chunk-UHDXXLYH.js.map +1 -0
  81. package/dist/{chunk-RNFUVKC3.js → chunk-UPAAZU4W.js} +30 -13
  82. package/dist/chunk-UPAAZU4W.js.map +1 -0
  83. package/dist/{chunk-L6HSKTQE.js → chunk-W7LZLJAF.js} +4 -4
  84. package/dist/{chunk-HLAPOUEM.js → chunk-WB2FVIUS.js} +44 -12
  85. package/dist/chunk-WB2FVIUS.js.map +1 -0
  86. package/dist/{chunk-VE3IYNDN.js → chunk-WPHC6JBR.js} +9 -9
  87. package/dist/chunk-WPHC6JBR.js.map +1 -0
  88. package/dist/{chunk-KZXQWC7U.js → chunk-XHCP6ZNO.js} +201 -64
  89. package/dist/chunk-XHCP6ZNO.js.map +1 -0
  90. package/dist/{chunk-7F7LGMVO.js → chunk-XKR3VD2Z.js} +12 -12
  91. package/dist/{chunk-7F7LGMVO.js.map → chunk-XKR3VD2Z.js.map} +1 -1
  92. package/dist/{chunk-ICM463EO.js → chunk-XNK5UN2D.js} +5 -649
  93. package/dist/chunk-XNK5UN2D.js.map +1 -0
  94. package/dist/{chunk-Z5O2XBWA.js → chunk-XQLNZW7I.js} +40 -30
  95. package/dist/chunk-XQLNZW7I.js.map +1 -0
  96. package/dist/{chunk-QMIQR7EE.js → chunk-ZAYATXJD.js} +32 -316
  97. package/dist/chunk-ZAYATXJD.js.map +1 -0
  98. package/dist/cli.js +128 -60
  99. package/dist/cli.js.map +1 -1
  100. package/dist/compat-YPDZB34U.js +24 -0
  101. package/dist/{config-KGZUPAUP.js → config-56B4OHZE.js} +20 -17
  102. package/dist/{contract-JH2WBIO6.js → contract-HWLFFL3F.js} +2 -2
  103. package/dist/{conventions-RYN7OXJN.js → conventions-DW2CM5I3.js} +20 -16
  104. package/dist/{conventions-RYN7OXJN.js.map → conventions-DW2CM5I3.js.map} +1 -1
  105. package/dist/db-5NU77PJA.js +264 -0
  106. package/dist/db-5NU77PJA.js.map +1 -0
  107. package/dist/diagnostics-UCTS2VUF.js +25 -0
  108. package/dist/execute-R7AVOLNG.js +38 -0
  109. package/dist/{generate-CX6KNHZS.js → generate-O7GUXLN4.js} +46 -43
  110. package/dist/generate-O7GUXLN4.js.map +1 -0
  111. package/dist/{git-A2IP32PQ.js → git-LCG45YXD.js} +2 -3
  112. package/dist/history-6GSURU3H.js +101 -0
  113. package/dist/history-6GSURU3H.js.map +1 -0
  114. package/dist/init-DD6DBRC5.js +28 -0
  115. package/dist/{operations-CAKIT2BO.js → operations-HU5ZFEBA.js} +78 -52
  116. package/dist/operations-HU5ZFEBA.js.map +1 -0
  117. package/dist/{permissions-G4XCAESF.js → permissions-NNNSRC4P.js} +5 -6
  118. package/dist/plan-XR6B7EM3.js +38 -0
  119. package/dist/{policy-WX6O2JID.js → policy-RFJOJQL6.js} +17 -15
  120. package/dist/{policy-WX6O2JID.js.map → policy-RFJOJQL6.js.map} +1 -1
  121. package/dist/pr-OSCNJCOO.js +42 -0
  122. package/dist/pr-review-IKIYHFBO.js +33 -0
  123. package/dist/prd-HFDZJJQR.js +37 -0
  124. package/dist/{ps-76L6ZAZL.js → ps-AKRHGPSR.js} +26 -15
  125. package/dist/ps-AKRHGPSR.js.map +1 -0
  126. package/dist/{recorder-N7U66HCO.js → recorder-T7426CKV.js} +11 -7
  127. package/dist/registry-DWRKD65A.js +17 -0
  128. package/dist/repository-TABBRW2C.js +80 -0
  129. package/dist/resume-GDXKK5XP.js +278 -0
  130. package/dist/resume-GDXKK5XP.js.map +1 -0
  131. package/dist/review-JJHMQA2O.js +38 -0
  132. package/dist/routing-FPNUVE7T.js +35 -0
  133. package/dist/run-RLCURLLL.js +61 -0
  134. package/dist/{session-publisher-KAJQRHNZ.js → session-publisher-Q5TNEZPX.js} +3 -3
  135. package/dist/session-publisher-Q5TNEZPX.js.map +1 -0
  136. package/dist/{state-manager-UPHO2EA2.js → state-manager-IGNTD4AB.js} +9 -4
  137. package/dist/state-manager-IGNTD4AB.js.map +1 -0
  138. package/dist/{usage-U7QNGCPK.js → usage-XW27AL5K.js} +69 -63
  139. package/dist/usage-XW27AL5K.js.map +1 -0
  140. package/dist/web-NCDBVZ5Y.js +205 -0
  141. package/dist/web-NCDBVZ5Y.js.map +1 -0
  142. package/package.json +20 -7
  143. package/prompts/analyze.md +16 -19
  144. package/prompts/execute.md +73 -144
  145. package/prompts/generate.md +23 -10
  146. package/prompts/plan.md +32 -60
  147. package/prompts/pr-review.md +58 -40
  148. package/prompts/pr.md +50 -16
  149. package/prompts/prd.md +26 -19
  150. package/prompts/review.md +41 -18
  151. package/web/AGENTS.md +115 -7
  152. package/web/public/app.css +393 -245
  153. package/web/public/app.js +212 -143
  154. package/web/public/index.html +106 -78
  155. package/dist/agent-TB6W7PT7.js +0 -30
  156. package/dist/analyze-6M3KEOX7.js.map +0 -1
  157. package/dist/apply-ADB3ZK7W.js.map +0 -1
  158. package/dist/chunk-6YQT3SSM.js.map +0 -1
  159. package/dist/chunk-A4H7P7T7.js.map +0 -1
  160. package/dist/chunk-ANI2NTR4.js.map +0 -1
  161. package/dist/chunk-CRGIQFUQ.js.map +0 -1
  162. package/dist/chunk-D6ZBGJ3L.js.map +0 -1
  163. package/dist/chunk-DV7HUC4Z.js.map +0 -1
  164. package/dist/chunk-E6KXCV3T.js.map +0 -1
  165. package/dist/chunk-HAKDLR6N.js.map +0 -1
  166. package/dist/chunk-HJBF4WD2.js.map +0 -1
  167. package/dist/chunk-HLAPOUEM.js.map +0 -1
  168. package/dist/chunk-HY5HLFDU.js.map +0 -1
  169. package/dist/chunk-ICM463EO.js.map +0 -1
  170. package/dist/chunk-ISZ54SVE.js.map +0 -1
  171. package/dist/chunk-KZ5EY5XX.js.map +0 -1
  172. package/dist/chunk-KZXQWC7U.js.map +0 -1
  173. package/dist/chunk-MI5TREN3.js.map +0 -1
  174. package/dist/chunk-QMIQR7EE.js.map +0 -1
  175. package/dist/chunk-RCZJQUTC.js.map +0 -1
  176. package/dist/chunk-RNFUVKC3.js.map +0 -1
  177. package/dist/chunk-SGTTR4XW.js.map +0 -1
  178. package/dist/chunk-SOM4QA6C.js.map +0 -1
  179. package/dist/chunk-UK4E2FUA.js.map +0 -1
  180. package/dist/chunk-UWDCIFTP.js.map +0 -1
  181. package/dist/chunk-VE3IYNDN.js.map +0 -1
  182. package/dist/chunk-WCCZJL7W.js +0 -141
  183. package/dist/chunk-WCCZJL7W.js.map +0 -1
  184. package/dist/chunk-XEYH6ZZI.js +0 -289
  185. package/dist/chunk-XEYH6ZZI.js.map +0 -1
  186. package/dist/chunk-XZUEFTUP.js +0 -243
  187. package/dist/chunk-XZUEFTUP.js.map +0 -1
  188. package/dist/chunk-YII4U7ZD.js.map +0 -1
  189. package/dist/chunk-YVPZUN6X.js.map +0 -1
  190. package/dist/chunk-Z5O2XBWA.js.map +0 -1
  191. package/dist/diagnostics-HTCWF7F4.js +0 -23
  192. package/dist/execute-QL3ZQIAP.js +0 -37
  193. package/dist/generate-CX6KNHZS.js.map +0 -1
  194. package/dist/init-PDVQDRS6.js +0 -24
  195. package/dist/operations-CAKIT2BO.js.map +0 -1
  196. package/dist/plan-PRORGS3L.js +0 -34
  197. package/dist/pr-27RMQI5F.js +0 -39
  198. package/dist/pr-review-VNESMAMT.js +0 -32
  199. package/dist/prd-ZNC4T5BS.js +0 -33
  200. package/dist/ps-76L6ZAZL.js.map +0 -1
  201. package/dist/registry-XLHHYLTR.js +0 -15
  202. package/dist/resume-VLWIE264.js +0 -244
  203. package/dist/resume-VLWIE264.js.map +0 -1
  204. package/dist/review-7CREJDKB.js +0 -35
  205. package/dist/routing-VCDYHSXA.js +0 -33
  206. package/dist/run-3MPORLF5.js +0 -56
  207. package/dist/usage-U7QNGCPK.js.map +0 -1
  208. package/dist/web-HUNDHCJ4.js +0 -172
  209. package/dist/web-HUNDHCJ4.js.map +0 -1
  210. /package/dist/{agent-TB6W7PT7.js.map → agent-EWKA7357.js.map} +0 -0
  211. /package/dist/{bench-FM7I7EIV.js.map → bench-ZJEIACUK.js.map} +0 -0
  212. /package/dist/{chunk-RYOUZ6EK.js.map → chunk-23VHVVKW.js.map} +0 -0
  213. /package/dist/{chunk-C5TL7T4V.js.map → chunk-2YM2VH6R.js.map} +0 -0
  214. /package/dist/{chunk-QPGEFLMY.js.map → chunk-7CMVL4X6.js.map} +0 -0
  215. /package/dist/{chunk-DUS5ZNKL.js.map → chunk-DIMDAYZS.js.map} +0 -0
  216. /package/dist/{chunk-DGRTJD5R.js.map → chunk-DXH4TQRB.js.map} +0 -0
  217. /package/dist/{chunk-YUNBOUQH.js.map → chunk-ERAG5IQB.js.map} +0 -0
  218. /package/dist/{chunk-B3TSS7BT.js.map → chunk-KDQRNVBZ.js.map} +0 -0
  219. /package/dist/{chunk-3R27MJ5R.js.map → chunk-PFWHHYEO.js.map} +0 -0
  220. /package/dist/{chunk-PPZ3K4YM.js.map → chunk-PRGPSNGA.js.map} +0 -0
  221. /package/dist/{chunk-L6HSKTQE.js.map → chunk-W7LZLJAF.js.map} +0 -0
  222. /package/dist/{config-KGZUPAUP.js.map → compat-YPDZB34U.js.map} +0 -0
  223. /package/dist/{contract-JH2WBIO6.js.map → config-56B4OHZE.js.map} +0 -0
  224. /package/dist/{diagnostics-HTCWF7F4.js.map → contract-HWLFFL3F.js.map} +0 -0
  225. /package/dist/{execute-QL3ZQIAP.js.map → diagnostics-UCTS2VUF.js.map} +0 -0
  226. /package/dist/{git-A2IP32PQ.js.map → execute-R7AVOLNG.js.map} +0 -0
  227. /package/dist/{init-PDVQDRS6.js.map → git-LCG45YXD.js.map} +0 -0
  228. /package/dist/{permissions-G4XCAESF.js.map → init-DD6DBRC5.js.map} +0 -0
  229. /package/dist/{plan-PRORGS3L.js.map → permissions-NNNSRC4P.js.map} +0 -0
  230. /package/dist/{pr-27RMQI5F.js.map → plan-XR6B7EM3.js.map} +0 -0
  231. /package/dist/{pr-review-VNESMAMT.js.map → pr-OSCNJCOO.js.map} +0 -0
  232. /package/dist/{prd-ZNC4T5BS.js.map → pr-review-IKIYHFBO.js.map} +0 -0
  233. /package/dist/{recorder-N7U66HCO.js.map → prd-HFDZJJQR.js.map} +0 -0
  234. /package/dist/{registry-XLHHYLTR.js.map → recorder-T7426CKV.js.map} +0 -0
  235. /package/dist/{review-7CREJDKB.js.map → registry-DWRKD65A.js.map} +0 -0
  236. /package/dist/{routing-VCDYHSXA.js.map → repository-TABBRW2C.js.map} +0 -0
  237. /package/dist/{run-3MPORLF5.js.map → review-JJHMQA2O.js.map} +0 -0
  238. /package/dist/{session-publisher-KAJQRHNZ.js.map → routing-FPNUVE7T.js.map} +0 -0
  239. /package/dist/{state-manager-UPHO2EA2.js.map → run-RLCURLLL.js.map} +0 -0
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ensureProvidersRegistered,
4
+ getProvider,
5
+ getRegisteredSources
6
+ } from "./chunk-ZAYATXJD.js";
7
+ import {
8
+ loadIssuesConfig
9
+ } from "./chunk-UHDXXLYH.js";
10
+ import {
11
+ printInfo,
12
+ printWarning
13
+ } from "./chunk-J6VJ5M2U.js";
14
+ import {
15
+ actionOf,
16
+ failureOf
17
+ } from "./chunk-TJVX42KY.js";
18
+
19
+ // src/issues/resolver.ts
20
+ import { createInterface } from "node:readline";
21
+ var IssueResolutionError = class extends Error {
22
+ exitCode;
23
+ /**
24
+ * The verdict of the origin that failed, when one origin failed for a reason
25
+ * worth naming. It is what tells "this Issue does not exist" apart from
26
+ * "GitHub could not be reached", which are the same message today.
27
+ */
28
+ failure;
29
+ /** What a human has to do, for a failure that waiting cannot fix. */
30
+ action;
31
+ constructor(message, exitCode = 1, details = {}) {
32
+ super(message);
33
+ this.name = "IssueResolutionError";
34
+ this.exitCode = exitCode;
35
+ this.failure = details.failure;
36
+ this.action = details.action;
37
+ }
38
+ };
39
+ var MAX_PROMPT_ATTEMPTS = 3;
40
+ var SOURCE_LABELS = {
41
+ github: "GitHub",
42
+ local: "local"
43
+ };
44
+ function sourceLabel(source) {
45
+ return SOURCE_LABELS[source] ?? source;
46
+ }
47
+ function promptLabel(source) {
48
+ const label = sourceLabel(source);
49
+ return label.charAt(0).toUpperCase() + label.slice(1);
50
+ }
51
+ function errorMessage(err) {
52
+ return err instanceof Error ? err.message : String(err);
53
+ }
54
+ async function fetchCandidate(source, id, warn) {
55
+ let provider;
56
+ try {
57
+ provider = getProvider(source);
58
+ } catch (err) {
59
+ return { source, issue: null, reason: errorMessage(err) };
60
+ }
61
+ let availability;
62
+ try {
63
+ availability = await provider.checkAvailability?.() ?? {
64
+ available: await provider.isAvailable()
65
+ };
66
+ } catch (err) {
67
+ return { source, issue: null, reason: `provider unavailable (${errorMessage(err)})` };
68
+ }
69
+ if (!availability.available) {
70
+ const reason = availability.failure?.message ?? "provider unavailable";
71
+ return {
72
+ source,
73
+ issue: null,
74
+ reason,
75
+ ...availability.failure === void 0 ? {} : { failure: availability.failure },
76
+ ...availability.action === void 0 ? {} : { action: availability.action }
77
+ };
78
+ }
79
+ try {
80
+ const issue = await provider.get(id);
81
+ return { source, issue, reason: issue === null ? "not found" : null };
82
+ } catch (err) {
83
+ const reason = errorMessage(err);
84
+ warn(`Could not read issue '${id}' from ${sourceLabel(source)}: ${reason}`);
85
+ const failure = failureOf(err);
86
+ const action = actionOf(err);
87
+ return {
88
+ source,
89
+ issue: null,
90
+ reason,
91
+ ...failure === null ? {} : { failure },
92
+ ...action === null ? {} : { action }
93
+ };
94
+ }
95
+ }
96
+ function shortHash(hash) {
97
+ const hex = hash.startsWith("sha256:") ? hash.slice("sha256:".length) : hash;
98
+ return hex.slice(0, 12);
99
+ }
100
+ function describeCandidate(source, issue) {
101
+ const lines = issue.body.length === 0 ? 0 : issue.body.split("\n").length;
102
+ return ` ${sourceLabel(source).padEnd(6)} title: "${issue.title}" | body: ${lines} line(s), ${issue.body.length} char(s) | updated: ${issue.updatedAt} | hash: ${shortHash(issue.contentHash)}`;
103
+ }
104
+ function listSources(candidates) {
105
+ const labels = candidates.map((candidate) => sourceLabel(candidate.source));
106
+ if (labels.length <= 1) {
107
+ return labels.join("");
108
+ }
109
+ return `${labels.slice(0, -1).join(", ")} and ${labels[labels.length - 1]}`;
110
+ }
111
+ function createLineReader(rl, output) {
112
+ const queued = [];
113
+ let pending = null;
114
+ let closed = false;
115
+ rl.on("line", (line) => {
116
+ if (pending !== null) {
117
+ const resolve = pending;
118
+ pending = null;
119
+ resolve(line);
120
+ return;
121
+ }
122
+ queued.push(line);
123
+ });
124
+ rl.on("close", () => {
125
+ closed = true;
126
+ if (pending !== null) {
127
+ const resolve = pending;
128
+ pending = null;
129
+ resolve(null);
130
+ }
131
+ });
132
+ return (query) => new Promise((resolve) => {
133
+ const buffered = queued.shift();
134
+ if (buffered !== void 0) {
135
+ resolve(buffered);
136
+ return;
137
+ }
138
+ if (closed) {
139
+ resolve(null);
140
+ return;
141
+ }
142
+ output.write(query);
143
+ pending = resolve;
144
+ });
145
+ }
146
+ async function promptChoice(sources, stdin, stdout, warn) {
147
+ const options = sources.map((source, index) => `[${index + 1}] ${promptLabel(source)}`);
148
+ const cancelOption = sources.length + 1;
149
+ const query = `Which version should be used? ${options.join(" ")} [${cancelOption}] Cancel: `;
150
+ const numbers = Array.from({ length: cancelOption }, (_, index) => String(index + 1));
151
+ const accepted = `${numbers.slice(0, -1).join(", ")} or ${numbers[numbers.length - 1]}`;
152
+ const rl = createInterface({ input: stdin, output: stdout });
153
+ const ask = createLineReader(rl, stdout);
154
+ try {
155
+ for (let attempt = 0; attempt < MAX_PROMPT_ATTEMPTS; attempt++) {
156
+ const answer = await ask(query);
157
+ if (answer === null) {
158
+ return "cancel";
159
+ }
160
+ const choice = answer.trim();
161
+ const picked = sources[Number(choice) - 1];
162
+ if (choice.length > 0 && picked !== void 0) {
163
+ return picked;
164
+ }
165
+ if (choice === String(cancelOption)) {
166
+ return "cancel";
167
+ }
168
+ warn(`Invalid choice: '${choice}'. Enter ${accepted}.`);
169
+ }
170
+ return "cancel";
171
+ } finally {
172
+ rl.close();
173
+ }
174
+ }
175
+ function isInteractiveByDefault() {
176
+ const ci = process.env.CI;
177
+ const inCi = ci !== void 0 && ci !== "" && ci !== "0" && ci.toLowerCase() !== "false";
178
+ return Boolean(process.stdin.isTTY) && !inCi;
179
+ }
180
+ function buildResolved(issue, source, local, github, divergent) {
181
+ return { issue, source, local, github, divergent };
182
+ }
183
+ function policyTarget(policy) {
184
+ if (policy === "prefer-local") {
185
+ return "local";
186
+ }
187
+ if (policy === "prefer-github") {
188
+ return "github";
189
+ }
190
+ return null;
191
+ }
192
+ async function resolveIssue(id, opts = {}) {
193
+ const info = opts.info ?? printInfo;
194
+ const warn = opts.warn ?? printWarning;
195
+ const config = opts.config ?? await loadIssuesConfig({ warn });
196
+ ensureProvidersRegistered();
197
+ const sources = opts.sources ?? getRegisteredSources();
198
+ const candidates = await Promise.all(
199
+ sources.map((source) => fetchCandidate(source, id, warn))
200
+ );
201
+ const issueOf = (source) => candidates.find((candidate) => candidate.source === source)?.issue ?? null;
202
+ const local = issueOf("local");
203
+ const github = issueOf("github");
204
+ const found = candidates.filter(
205
+ (candidate) => candidate.issue !== null
206
+ );
207
+ if (found.length === 0) {
208
+ const details = candidates.map((candidate) => `${sourceLabel(candidate.source)}: ${candidate.reason ?? "not found"}`).join("; ");
209
+ const where = details.length > 0 ? ` (${details})` : "";
210
+ const blocking = candidates.find((candidate) => candidate.action !== void 0);
211
+ if (blocking !== void 0) {
212
+ throw new IssueResolutionError(
213
+ `Cannot read issue '${id}' from ${sourceLabel(blocking.source)}: ${blocking.reason}`,
214
+ 1,
215
+ {
216
+ ...blocking.failure === void 0 ? {} : { failure: blocking.failure },
217
+ ...blocking.action === void 0 ? {} : { action: blocking.action }
218
+ }
219
+ );
220
+ }
221
+ throw new IssueResolutionError(`Issue '${id}' not found in any registered origin${where}`);
222
+ }
223
+ const build = (candidate, divergent) => buildResolved(candidate.issue, candidate.source, local, github, divergent);
224
+ const firstFound = found[0];
225
+ if (found.length === 1) {
226
+ return build(firstFound, false);
227
+ }
228
+ const preferred = found.find((candidate) => candidate.source === config.preferredProvider) ?? firstFound;
229
+ const distinct = new Set(found.map((candidate) => candidate.issue.contentHash));
230
+ if (distinct.size === 1) {
231
+ info(
232
+ `Issue '${id}' has identical content in ${listSources(found)}; using ${sourceLabel(preferred.source)}.`
233
+ );
234
+ return build(preferred, false);
235
+ }
236
+ info(`Issue '${id}' differs between origins:`);
237
+ for (const candidate of found) {
238
+ info(describeCandidate(candidate.source, candidate.issue));
239
+ }
240
+ const target = policyTarget(config.conflictPolicy);
241
+ if (target !== null) {
242
+ const chosen = found.find((candidate) => candidate.source === target);
243
+ if (chosen !== void 0) {
244
+ info(`Conflict policy '${config.conflictPolicy}': using the ${sourceLabel(target)} version.`);
245
+ return build(chosen, true);
246
+ }
247
+ warn(
248
+ `Conflict policy '${config.conflictPolicy}' does not apply to Issue '${id}' (${sourceLabel(target)} has no version of it); using ${sourceLabel(preferred.source)}.`
249
+ );
250
+ return build(preferred, true);
251
+ }
252
+ const interactive = opts.interactive ?? isInteractiveByDefault();
253
+ if (!interactive) {
254
+ warn(
255
+ `Divergent Issue '${id}' and conflict policy 'ask' in a non-interactive environment; using the preferred provider (${sourceLabel(preferred.source)}).`
256
+ );
257
+ return build(preferred, true);
258
+ }
259
+ const choice = await promptChoice(
260
+ found.map((candidate) => candidate.source),
261
+ opts.stdin ?? process.stdin,
262
+ opts.stdout ?? process.stdout,
263
+ warn
264
+ );
265
+ if (choice === "cancel") {
266
+ throw new IssueResolutionError(
267
+ `Cancelled: Issue '${id}' diverges between ${listSources(found)}.`
268
+ );
269
+ }
270
+ const picked = found.find((candidate) => candidate.source === choice);
271
+ return build(picked, true);
272
+ }
273
+
274
+ export {
275
+ IssueResolutionError,
276
+ resolveIssue
277
+ };
278
+ //# sourceMappingURL=chunk-3JD25HHJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/issues/resolver.ts"],"sourcesContent":["import { createInterface, type Interface as ReadlineInterface } from 'node:readline';\nimport { loadIssuesConfig } from '../config.js';\nimport { actionOf, type ClassifiedFailure, failureOf } from '../resilience/errors.js';\nimport { printInfo, printWarning } from '../ui/logger.js';\nimport { ensureProvidersRegistered } from './bootstrap.js';\nimport type { ProviderAvailability } from './provider.js';\nimport { getProvider, getRegisteredSources } from './registry.js';\nimport type { Issue, IssueSource, IssuesConfig, ResolvedIssue } from './types.js';\n\n/**\n * Failure to settle on an Issue. Carries the exit code the CLI should return,\n * so callers propagate it instead of inventing their own.\n */\nexport class IssueResolutionError extends Error {\n readonly exitCode: number;\n /**\n * The verdict of the origin that failed, when one origin failed for a reason\n * worth naming. It is what tells \"this Issue does not exist\" apart from\n * \"GitHub could not be reached\", which are the same message today.\n */\n readonly failure: ClassifiedFailure | undefined;\n /** What a human has to do, for a failure that waiting cannot fix. */\n readonly action: string | undefined;\n\n constructor(\n message: string,\n exitCode = 1,\n details: { failure?: ClassifiedFailure; action?: string } = {},\n ) {\n super(message);\n this.name = 'IssueResolutionError';\n this.exitCode = exitCode;\n this.failure = details.failure;\n this.action = details.action;\n }\n}\n\nexport interface ResolveIssueOptions {\n /** Already-resolved provider configuration. Defaults to loadIssuesConfig(). */\n config?: IssuesConfig;\n /** Origins to query. Defaults to every registered source. */\n sources?: IssueSource[];\n /**\n * Whether a prompt may be shown. Defaults to a real TTY outside CI, which is\n * what keeps `ask` from ever blocking a headless run.\n */\n interactive?: boolean;\n /** Input stream for the conflict prompt. Defaults to process.stdin. */\n stdin?: NodeJS.ReadableStream;\n /** Output stream for the conflict prompt. Defaults to process.stdout. */\n stdout?: NodeJS.WritableStream;\n /** Informational sink. Defaults to printInfo. */\n info?: (message: string) => void;\n /** Warning sink. Defaults to printWarning. */\n warn?: (message: string) => void;\n}\n\n/** How many invalid answers are tolerated before the prompt gives up. */\nconst MAX_PROMPT_ATTEMPTS = 3;\n\n/**\n * Labels shown to users for the built-in origins. An origin registered from\n * outside this package is displayed under its own name, so a new provider needs\n * no entry here.\n */\nconst SOURCE_LABELS: Record<string, string> = {\n github: 'GitHub',\n local: 'local',\n};\n\nfunction sourceLabel(source: IssueSource): string {\n return SOURCE_LABELS[source] ?? source;\n}\n\n/** Same label, capitalized for the numbered prompt ('local' -> 'Local'). */\nfunction promptLabel(source: IssueSource): string {\n const label = sourceLabel(source);\n return label.charAt(0).toUpperCase() + label.slice(1);\n}\n\ninterface Candidate {\n source: IssueSource;\n issue: Issue | null;\n /** Why the origin produced nothing, used only to explain a total miss. */\n reason: string | null;\n /** The classified verdict behind `reason`, when the origin could give one. */\n failure?: ClassifiedFailure;\n /** What a human has to do about it (`gh auth login`), when that applies. */\n action?: string;\n}\n\n/** A candidate that actually produced an Issue. */\ninterface FoundCandidate extends Candidate {\n issue: Issue;\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Read one origin without letting it break the others.\n *\n * An unavailable provider or a failed read degrades to \"no candidate\" plus a\n * reason: as long as another origin has the Issue the pipeline keeps going, and\n * when nothing is found the reasons are surfaced in the final error so a broken\n * environment is never reported as a missing Issue.\n */\nasync function fetchCandidate(\n source: IssueSource,\n id: string,\n warn: (message: string) => void,\n): Promise<Candidate> {\n let provider: ReturnType<typeof getProvider>;\n try {\n provider = getProvider(source);\n } catch (err) {\n return { source, issue: null, reason: errorMessage(err) };\n }\n\n let availability: ProviderAvailability;\n try {\n availability = (await provider.checkAvailability?.()) ?? {\n available: await provider.isAvailable(),\n };\n } catch (err) {\n return { source, issue: null, reason: `provider unavailable (${errorMessage(err)})` };\n }\n if (!availability.available) {\n // The classified reason replaces the flat \"provider unavailable\": a\n // credential that expired and a network that blipped were indistinguishable\n // before, and only one of them is worth telling a human about.\n const reason = availability.failure?.message ?? 'provider unavailable';\n return {\n source,\n issue: null,\n reason,\n ...(availability.failure === undefined ? {} : { failure: availability.failure }),\n ...(availability.action === undefined ? {} : { action: availability.action }),\n };\n }\n\n try {\n const issue = await provider.get(id);\n return { source, issue, reason: issue === null ? 'not found' : null };\n } catch (err) {\n const reason = errorMessage(err);\n // A real failure (network, auth, corrupted metadata) must be visible even\n // when the other origin answers.\n warn(`Could not read issue '${id}' from ${sourceLabel(source)}: ${reason}`);\n const failure = failureOf(err);\n const action = actionOf(err);\n return {\n source,\n issue: null,\n reason,\n ...(failure === null ? {} : { failure }),\n ...(action === null ? {} : { action }),\n };\n }\n}\n\nfunction shortHash(hash: string): string {\n const hex = hash.startsWith('sha256:') ? hash.slice('sha256:'.length) : hash;\n return hex.slice(0, 12);\n}\n\n/** One line per origin describing what differs, without dumping every body. */\nfunction describeCandidate(source: IssueSource, issue: Issue): string {\n const lines = issue.body.length === 0 ? 0 : issue.body.split('\\n').length;\n return (\n ` ${sourceLabel(source).padEnd(6)} title: \"${issue.title}\" | body: ${lines} line(s), ` +\n `${issue.body.length} char(s) | updated: ${issue.updatedAt} | hash: ${shortHash(issue.contentHash)}`\n );\n}\n\n/** 'local and GitHub', 'local, GitHub and memory' — origins in query order. */\nfunction listSources(candidates: FoundCandidate[]): string {\n const labels = candidates.map((candidate) => sourceLabel(candidate.source));\n if (labels.length <= 1) {\n return labels.join('');\n }\n return `${labels.slice(0, -1).join(', ')} and ${labels[labels.length - 1]}`;\n}\n\n/**\n * Turn a readline interface into an awaitable line reader.\n *\n * Lines are queued instead of read on demand: `rl.question` drops whatever\n * arrives between two questions, which silently loses a piped answer typed\n * ahead of the prompt. Resolves `null` once the input is exhausted, so EOF ends\n * the prompt instead of hanging the process.\n */\nfunction createLineReader(\n rl: ReadlineInterface,\n output: NodeJS.WritableStream,\n): (query: string) => Promise<string | null> {\n const queued: string[] = [];\n let pending: ((line: string | null) => void) | null = null;\n let closed = false;\n\n rl.on('line', (line: string) => {\n if (pending !== null) {\n const resolve = pending;\n pending = null;\n resolve(line);\n return;\n }\n queued.push(line);\n });\n rl.on('close', () => {\n closed = true;\n if (pending !== null) {\n const resolve = pending;\n pending = null;\n resolve(null);\n }\n });\n\n return (query: string) =>\n new Promise<string | null>((resolve) => {\n const buffered = queued.shift();\n if (buffered !== undefined) {\n resolve(buffered);\n return;\n }\n if (closed) {\n resolve(null);\n return;\n }\n output.write(query);\n pending = resolve;\n });\n}\n\n/**\n * Interactive choice between the divergent versions.\n *\n * The options are numbered from the origins that actually answered, so a third\n * provider simply shows up as `[3] Memory` without this function knowing which\n * origins exist. Cancel is always the last option.\n */\nasync function promptChoice(\n sources: IssueSource[],\n stdin: NodeJS.ReadableStream,\n stdout: NodeJS.WritableStream,\n warn: (message: string) => void,\n): Promise<IssueSource | 'cancel'> {\n const options = sources.map((source, index) => `[${index + 1}] ${promptLabel(source)}`);\n const cancelOption = sources.length + 1;\n const query = `Which version should be used? ${options.join(' ')} [${cancelOption}] Cancel: `;\n const numbers = Array.from({ length: cancelOption }, (_, index) => String(index + 1));\n const accepted = `${numbers.slice(0, -1).join(', ')} or ${numbers[numbers.length - 1]}`;\n\n const rl = createInterface({ input: stdin, output: stdout });\n const ask = createLineReader(rl, stdout);\n try {\n for (let attempt = 0; attempt < MAX_PROMPT_ATTEMPTS; attempt++) {\n const answer = await ask(query);\n if (answer === null) {\n return 'cancel';\n }\n const choice = answer.trim();\n const picked = sources[Number(choice) - 1];\n if (choice.length > 0 && picked !== undefined) {\n return picked;\n }\n if (choice === String(cancelOption)) {\n return 'cancel';\n }\n warn(`Invalid choice: '${choice}'. Enter ${accepted}.`);\n }\n return 'cancel';\n } finally {\n rl.close();\n }\n}\n\nfunction isInteractiveByDefault(): boolean {\n const ci = process.env.CI;\n const inCi = ci !== undefined && ci !== '' && ci !== '0' && ci.toLowerCase() !== 'false';\n return Boolean(process.stdin.isTTY) && !inCi;\n}\n\nfunction buildResolved(\n issue: Issue,\n source: IssueSource,\n local: Issue | null,\n github: Issue | null,\n divergent: boolean,\n): ResolvedIssue {\n return { issue, source, local, github, divergent };\n}\n\n/** Origin `conflictPolicy` points at, or `null` for 'ask'. */\nfunction policyTarget(policy: IssuesConfig['conflictPolicy']): IssueSource | null {\n if (policy === 'prefer-local') {\n return 'local';\n }\n if (policy === 'prefer-github') {\n return 'github';\n }\n return null;\n}\n\n/**\n * Single entry point every command uses to decide which Issue the pipeline\n * works on.\n *\n * The logic is written against the origins that answered, never against a fixed\n * list of sources: registering a new provider is enough for it to take part in\n * the resolution, be reported in a divergence and appear in the prompt.\n *\n * Scenarios:\n * - only one origin has it -> that one, no questions asked;\n * - several, same contentHash -> equivalence is reported and the preferred\n * origin wins, without a prompt;\n * - several, different content -> the divergence is reported and\n * `conflictPolicy` decides (`ask` prompts on a TTY, falls back to\n * `preferredProvider` with a warning anywhere else);\n * - none -> IssueResolutionError, which carries the CLI exit code.\n */\nexport async function resolveIssue(\n id: string,\n opts: ResolveIssueOptions = {},\n): Promise<ResolvedIssue> {\n const info = opts.info ?? printInfo;\n const warn = opts.warn ?? printWarning;\n const config = opts.config ?? (await loadIssuesConfig({ warn }));\n\n ensureProvidersRegistered();\n const sources = opts.sources ?? getRegisteredSources();\n\n // Each origin is an independent I/O call (network for GitHub, disk for\n // local); querying them concurrently instead of one-by-one keeps the total\n // latency at the slowest origin instead of the sum of all of them, without\n // changing which candidate wins (order is preserved by Promise.all).\n const candidates: Candidate[] = await Promise.all(\n sources.map((source) => fetchCandidate(source, id, warn)),\n );\n\n const issueOf = (source: IssueSource): Issue | null =>\n candidates.find((candidate) => candidate.source === source)?.issue ?? null;\n const local = issueOf('local');\n const github = issueOf('github');\n\n const found = candidates.filter(\n (candidate): candidate is FoundCandidate => candidate.issue !== null,\n );\n\n if (found.length === 0) {\n const details = candidates\n .map((candidate) => `${sourceLabel(candidate.source)}: ${candidate.reason ?? 'not found'}`)\n .join('; ');\n const where = details.length > 0 ? ` (${details})` : '';\n\n // An origin that needs a human is the answer, not a footnote: reporting\n // \"Issue not found\" for an expired credential sends the user looking for\n // the wrong problem, and no amount of retrying would have helped.\n const blocking = candidates.find((candidate) => candidate.action !== undefined);\n if (blocking !== undefined) {\n throw new IssueResolutionError(\n `Cannot read issue '${id}' from ${sourceLabel(blocking.source)}: ${blocking.reason}`,\n 1,\n {\n ...(blocking.failure === undefined ? {} : { failure: blocking.failure }),\n ...(blocking.action === undefined ? {} : { action: blocking.action }),\n },\n );\n }\n\n throw new IssueResolutionError(`Issue '${id}' not found in any registered origin${where}`);\n }\n\n const build = (candidate: FoundCandidate, divergent: boolean): ResolvedIssue =>\n buildResolved(candidate.issue, candidate.source, local, github, divergent);\n\n const firstFound = found[0] as FoundCandidate;\n if (found.length === 1) {\n return build(firstFound, false);\n }\n\n // Several origins have it. The preferred one wins whenever it is among them;\n // otherwise the first origin queried does, so an unrelated preference never\n // leaves the pipeline without an Issue.\n const preferred =\n found.find((candidate) => candidate.source === config.preferredProvider) ?? firstFound;\n\n const distinct = new Set(found.map((candidate) => candidate.issue.contentHash));\n if (distinct.size === 1) {\n info(\n `Issue '${id}' has identical content in ${listSources(found)}; using ${sourceLabel(preferred.source)}.`,\n );\n return build(preferred, false);\n }\n\n info(`Issue '${id}' differs between origins:`);\n for (const candidate of found) {\n info(describeCandidate(candidate.source, candidate.issue));\n }\n\n const target = policyTarget(config.conflictPolicy);\n if (target !== null) {\n const chosen = found.find((candidate) => candidate.source === target);\n if (chosen !== undefined) {\n info(`Conflict policy '${config.conflictPolicy}': using the ${sourceLabel(target)} version.`);\n return build(chosen, true);\n }\n // The policy names an origin that has no version of this Issue: say so\n // instead of silently picking for the user under its name.\n warn(\n `Conflict policy '${config.conflictPolicy}' does not apply to Issue '${id}' ` +\n `(${sourceLabel(target)} has no version of it); using ${sourceLabel(preferred.source)}.`,\n );\n return build(preferred, true);\n }\n\n const interactive = opts.interactive ?? isInteractiveByDefault();\n if (!interactive) {\n warn(\n `Divergent Issue '${id}' and conflict policy 'ask' in a non-interactive environment; ` +\n `using the preferred provider (${sourceLabel(preferred.source)}).`,\n );\n return build(preferred, true);\n }\n\n const choice = await promptChoice(\n found.map((candidate) => candidate.source),\n opts.stdin ?? process.stdin,\n opts.stdout ?? process.stdout,\n warn,\n );\n if (choice === 'cancel') {\n throw new IssueResolutionError(\n `Cancelled: Issue '${id}' diverges between ${listSources(found)}.`,\n );\n }\n const picked = found.find((candidate) => candidate.source === choice) as FoundCandidate;\n return build(picked, true);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,uBAA4D;AAa9D,IAAM,uBAAN,cAAmC,MAAM;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA;AAAA,EAEA;AAAA,EAET,YACE,SACA,WAAW,GACX,UAA4D,CAAC,GAC7D;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,UAAU,QAAQ;AACvB,SAAK,SAAS,QAAQ;AAAA,EACxB;AACF;AAuBA,IAAM,sBAAsB;AAO5B,IAAM,gBAAwC;AAAA,EAC5C,QAAQ;AAAA,EACR,OAAO;AACT;AAEA,SAAS,YAAY,QAA6B;AAChD,SAAO,cAAc,MAAM,KAAK;AAClC;AAGA,SAAS,YAAY,QAA6B;AAChD,QAAM,QAAQ,YAAY,MAAM;AAChC,SAAO,MAAM,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,MAAM,CAAC;AACtD;AAkBA,SAAS,aAAa,KAAsB;AAC1C,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAUA,eAAe,eACb,QACA,IACA,MACoB;AACpB,MAAI;AACJ,MAAI;AACF,eAAW,YAAY,MAAM;AAAA,EAC/B,SAAS,KAAK;AACZ,WAAO,EAAE,QAAQ,OAAO,MAAM,QAAQ,aAAa,GAAG,EAAE;AAAA,EAC1D;AAEA,MAAI;AACJ,MAAI;AACF,mBAAgB,MAAM,SAAS,oBAAoB,KAAM;AAAA,MACvD,WAAW,MAAM,SAAS,YAAY;AAAA,IACxC;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,EAAE,QAAQ,OAAO,MAAM,QAAQ,yBAAyB,aAAa,GAAG,CAAC,IAAI;AAAA,EACtF;AACA,MAAI,CAAC,aAAa,WAAW;AAI3B,UAAM,SAAS,aAAa,SAAS,WAAW;AAChD,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA,GAAI,aAAa,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,aAAa,QAAQ;AAAA,MAC9E,GAAI,aAAa,WAAW,SAAY,CAAC,IAAI,EAAE,QAAQ,aAAa,OAAO;AAAA,IAC7E;AAAA,EACF;AAEA,MAAI;AACF,UAAM,QAAQ,MAAM,SAAS,IAAI,EAAE;AACnC,WAAO,EAAE,QAAQ,OAAO,QAAQ,UAAU,OAAO,cAAc,KAAK;AAAA,EACtE,SAAS,KAAK;AACZ,UAAM,SAAS,aAAa,GAAG;AAG/B,SAAK,yBAAyB,EAAE,UAAU,YAAY,MAAM,CAAC,KAAK,MAAM,EAAE;AAC1E,UAAM,UAAU,UAAU,GAAG;AAC7B,UAAM,SAAS,SAAS,GAAG;AAC3B,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA,GAAI,YAAY,OAAO,CAAC,IAAI,EAAE,QAAQ;AAAA,MACtC,GAAI,WAAW,OAAO,CAAC,IAAI,EAAE,OAAO;AAAA,IACtC;AAAA,EACF;AACF;AAEA,SAAS,UAAU,MAAsB;AACvC,QAAM,MAAM,KAAK,WAAW,SAAS,IAAI,KAAK,MAAM,UAAU,MAAM,IAAI;AACxE,SAAO,IAAI,MAAM,GAAG,EAAE;AACxB;AAGA,SAAS,kBAAkB,QAAqB,OAAsB;AACpE,QAAM,QAAQ,MAAM,KAAK,WAAW,IAAI,IAAI,MAAM,KAAK,MAAM,IAAI,EAAE;AACnE,SACE,KAAK,YAAY,MAAM,EAAE,OAAO,CAAC,CAAC,YAAY,MAAM,KAAK,aAAa,KAAK,aACxE,MAAM,KAAK,MAAM,uBAAuB,MAAM,SAAS,YAAY,UAAU,MAAM,WAAW,CAAC;AAEtG;AAGA,SAAS,YAAY,YAAsC;AACzD,QAAM,SAAS,WAAW,IAAI,CAAC,cAAc,YAAY,UAAU,MAAM,CAAC;AAC1E,MAAI,OAAO,UAAU,GAAG;AACtB,WAAO,OAAO,KAAK,EAAE;AAAA,EACvB;AACA,SAAO,GAAG,OAAO,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,QAAQ,OAAO,OAAO,SAAS,CAAC,CAAC;AAC3E;AAUA,SAAS,iBACP,IACA,QAC2C;AAC3C,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAkD;AACtD,MAAI,SAAS;AAEb,KAAG,GAAG,QAAQ,CAAC,SAAiB;AAC9B,QAAI,YAAY,MAAM;AACpB,YAAM,UAAU;AAChB,gBAAU;AACV,cAAQ,IAAI;AACZ;AAAA,IACF;AACA,WAAO,KAAK,IAAI;AAAA,EAClB,CAAC;AACD,KAAG,GAAG,SAAS,MAAM;AACnB,aAAS;AACT,QAAI,YAAY,MAAM;AACpB,YAAM,UAAU;AAChB,gBAAU;AACV,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,CAAC;AAED,SAAO,CAAC,UACN,IAAI,QAAuB,CAAC,YAAY;AACtC,UAAM,WAAW,OAAO,MAAM;AAC9B,QAAI,aAAa,QAAW;AAC1B,cAAQ,QAAQ;AAChB;AAAA,IACF;AACA,QAAI,QAAQ;AACV,cAAQ,IAAI;AACZ;AAAA,IACF;AACA,WAAO,MAAM,KAAK;AAClB,cAAU;AAAA,EACZ,CAAC;AACL;AASA,eAAe,aACb,SACA,OACA,QACA,MACiC;AACjC,QAAM,UAAU,QAAQ,IAAI,CAAC,QAAQ,UAAU,IAAI,QAAQ,CAAC,KAAK,YAAY,MAAM,CAAC,EAAE;AACtF,QAAM,eAAe,QAAQ,SAAS;AACtC,QAAM,QAAQ,iCAAiC,QAAQ,KAAK,IAAI,CAAC,MAAM,YAAY;AACnF,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,aAAa,GAAG,CAAC,GAAG,UAAU,OAAO,QAAQ,CAAC,CAAC;AACpF,QAAM,WAAW,GAAG,QAAQ,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC;AAErF,QAAM,KAAK,gBAAgB,EAAE,OAAO,OAAO,QAAQ,OAAO,CAAC;AAC3D,QAAM,MAAM,iBAAiB,IAAI,MAAM;AACvC,MAAI;AACF,aAAS,UAAU,GAAG,UAAU,qBAAqB,WAAW;AAC9D,YAAM,SAAS,MAAM,IAAI,KAAK;AAC9B,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AACA,YAAM,SAAS,OAAO,KAAK;AAC3B,YAAM,SAAS,QAAQ,OAAO,MAAM,IAAI,CAAC;AACzC,UAAI,OAAO,SAAS,KAAK,WAAW,QAAW;AAC7C,eAAO;AAAA,MACT;AACA,UAAI,WAAW,OAAO,YAAY,GAAG;AACnC,eAAO;AAAA,MACT;AACA,WAAK,oBAAoB,MAAM,YAAY,QAAQ,GAAG;AAAA,IACxD;AACA,WAAO;AAAA,EACT,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,SAAS,yBAAkC;AACzC,QAAM,KAAK,QAAQ,IAAI;AACvB,QAAM,OAAO,OAAO,UAAa,OAAO,MAAM,OAAO,OAAO,GAAG,YAAY,MAAM;AACjF,SAAO,QAAQ,QAAQ,MAAM,KAAK,KAAK,CAAC;AAC1C;AAEA,SAAS,cACP,OACA,QACA,OACA,QACA,WACe;AACf,SAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,UAAU;AACnD;AAGA,SAAS,aAAa,QAA4D;AAChF,MAAI,WAAW,gBAAgB;AAC7B,WAAO;AAAA,EACT;AACA,MAAI,WAAW,iBAAiB;AAC9B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAmBA,eAAsB,aACpB,IACA,OAA4B,CAAC,GACL;AACxB,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,SAAS,KAAK,UAAW,MAAM,iBAAiB,EAAE,KAAK,CAAC;AAE9D,4BAA0B;AAC1B,QAAM,UAAU,KAAK,WAAW,qBAAqB;AAMrD,QAAM,aAA0B,MAAM,QAAQ;AAAA,IAC5C,QAAQ,IAAI,CAAC,WAAW,eAAe,QAAQ,IAAI,IAAI,CAAC;AAAA,EAC1D;AAEA,QAAM,UAAU,CAAC,WACf,WAAW,KAAK,CAAC,cAAc,UAAU,WAAW,MAAM,GAAG,SAAS;AACxE,QAAM,QAAQ,QAAQ,OAAO;AAC7B,QAAM,SAAS,QAAQ,QAAQ;AAE/B,QAAM,QAAQ,WAAW;AAAA,IACvB,CAAC,cAA2C,UAAU,UAAU;AAAA,EAClE;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,UAAU,WACb,IAAI,CAAC,cAAc,GAAG,YAAY,UAAU,MAAM,CAAC,KAAK,UAAU,UAAU,WAAW,EAAE,EACzF,KAAK,IAAI;AACZ,UAAM,QAAQ,QAAQ,SAAS,IAAI,KAAK,OAAO,MAAM;AAKrD,UAAM,WAAW,WAAW,KAAK,CAAC,cAAc,UAAU,WAAW,MAAS;AAC9E,QAAI,aAAa,QAAW;AAC1B,YAAM,IAAI;AAAA,QACR,sBAAsB,EAAE,UAAU,YAAY,SAAS,MAAM,CAAC,KAAK,SAAS,MAAM;AAAA,QAClF;AAAA,QACA;AAAA,UACE,GAAI,SAAS,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,SAAS,QAAQ;AAAA,UACtE,GAAI,SAAS,WAAW,SAAY,CAAC,IAAI,EAAE,QAAQ,SAAS,OAAO;AAAA,QACrE;AAAA,MACF;AAAA,IACF;AAEA,UAAM,IAAI,qBAAqB,UAAU,EAAE,uCAAuC,KAAK,EAAE;AAAA,EAC3F;AAEA,QAAM,QAAQ,CAAC,WAA2B,cACxC,cAAc,UAAU,OAAO,UAAU,QAAQ,OAAO,QAAQ,SAAS;AAE3E,QAAM,aAAa,MAAM,CAAC;AAC1B,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,MAAM,YAAY,KAAK;AAAA,EAChC;AAKA,QAAM,YACJ,MAAM,KAAK,CAAC,cAAc,UAAU,WAAW,OAAO,iBAAiB,KAAK;AAE9E,QAAM,WAAW,IAAI,IAAI,MAAM,IAAI,CAAC,cAAc,UAAU,MAAM,WAAW,CAAC;AAC9E,MAAI,SAAS,SAAS,GAAG;AACvB;AAAA,MACE,UAAU,EAAE,8BAA8B,YAAY,KAAK,CAAC,WAAW,YAAY,UAAU,MAAM,CAAC;AAAA,IACtG;AACA,WAAO,MAAM,WAAW,KAAK;AAAA,EAC/B;AAEA,OAAK,UAAU,EAAE,4BAA4B;AAC7C,aAAW,aAAa,OAAO;AAC7B,SAAK,kBAAkB,UAAU,QAAQ,UAAU,KAAK,CAAC;AAAA,EAC3D;AAEA,QAAM,SAAS,aAAa,OAAO,cAAc;AACjD,MAAI,WAAW,MAAM;AACnB,UAAM,SAAS,MAAM,KAAK,CAAC,cAAc,UAAU,WAAW,MAAM;AACpE,QAAI,WAAW,QAAW;AACxB,WAAK,oBAAoB,OAAO,cAAc,gBAAgB,YAAY,MAAM,CAAC,WAAW;AAC5F,aAAO,MAAM,QAAQ,IAAI;AAAA,IAC3B;AAGA;AAAA,MACE,oBAAoB,OAAO,cAAc,8BAA8B,EAAE,MACnE,YAAY,MAAM,CAAC,iCAAiC,YAAY,UAAU,MAAM,CAAC;AAAA,IACzF;AACA,WAAO,MAAM,WAAW,IAAI;AAAA,EAC9B;AAEA,QAAM,cAAc,KAAK,eAAe,uBAAuB;AAC/D,MAAI,CAAC,aAAa;AAChB;AAAA,MACE,oBAAoB,EAAE,+FACa,YAAY,UAAU,MAAM,CAAC;AAAA,IAClE;AACA,WAAO,MAAM,WAAW,IAAI;AAAA,EAC9B;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB,MAAM,IAAI,CAAC,cAAc,UAAU,MAAM;AAAA,IACzC,KAAK,SAAS,QAAQ;AAAA,IACtB,KAAK,UAAU,QAAQ;AAAA,IACvB;AAAA,EACF;AACA,MAAI,WAAW,UAAU;AACvB,UAAM,IAAI;AAAA,MACR,qBAAqB,EAAE,sBAAsB,YAAY,KAAK,CAAC;AAAA,IACjE;AAAA,EACF;AACA,QAAM,SAAS,MAAM,KAAK,CAAC,cAAc,UAAU,WAAW,MAAM;AACpE,SAAO,MAAM,QAAQ,IAAI;AAC3B;","names":[]}
@@ -2,22 +2,23 @@
2
2
  import {
3
3
  getAgentCliOverrides,
4
4
  hasExplicitAgentSelection,
5
- loadAgentConfig
6
- } from "./chunk-HY5HLFDU.js";
5
+ loadAgentConfig,
6
+ loadRoutingConfig
7
+ } from "./chunk-UHDXXLYH.js";
7
8
  import {
8
9
  printError,
9
10
  printInfo,
10
11
  printSuccess,
11
12
  printWarning
12
- } from "./chunk-XZUEFTUP.js";
13
+ } from "./chunk-J6VJ5M2U.js";
13
14
  import {
14
15
  AGENT_PHASES
15
- } from "./chunk-ICM463EO.js";
16
+ } from "./chunk-IMB6FA2F.js";
16
17
 
17
18
  // src/commands/init.ts
18
- import { readFile } from "fs/promises";
19
- import { homedir } from "os";
20
- import { join } from "path";
19
+ import { readFile } from "node:fs/promises";
20
+ import { homedir } from "node:os";
21
+ import { join } from "node:path";
21
22
  import { execa } from "execa";
22
23
  async function checkClaude() {
23
24
  try {
@@ -193,6 +194,7 @@ async function runInit(source = "github", options = {}) {
193
194
  const json = options.json === true;
194
195
  const compact = options.compact === true;
195
196
  if (!json && !compact) {
197
+ printExperimentalNotice();
196
198
  printInfo("Checking prerequisites...\n");
197
199
  }
198
200
  const agent = await loadAgentConfig();
@@ -239,7 +241,7 @@ async function runInit(source = "github", options = {}) {
239
241
  }
240
242
  let plan;
241
243
  try {
242
- const { planRepositoryScaffold } = await import("./apply-ADB3ZK7W.js");
244
+ const { planRepositoryScaffold } = await import("./apply-VNSWQS6O.js");
243
245
  plan = await planRepositoryScaffold({ scope: options.scope ?? null });
244
246
  } catch (err) {
245
247
  if (json) {
@@ -259,7 +261,7 @@ async function runInit(source = "github", options = {}) {
259
261
  }
260
262
  let applied = null;
261
263
  if (options.apply === true) {
262
- const { applyScaffoldPlan } = await import("./apply-ADB3ZK7W.js");
264
+ const { applyScaffoldPlan } = await import("./apply-VNSWQS6O.js");
263
265
  applied = await applyScaffoldPlan(plan);
264
266
  }
265
267
  if (json) {
@@ -311,9 +313,9 @@ async function runInit(source = "github", options = {}) {
311
313
  await maybeOfferAgentChoice(options, agent);
312
314
  if (!json) {
313
315
  try {
314
- const { resolveContract } = await import("./contract-JH2WBIO6.js");
315
- const { getProjectRoot } = await import("./git-A2IP32PQ.js");
316
- const { loadVerifyConfig } = await import("./config-KGZUPAUP.js");
316
+ const { resolveContract } = await import("./contract-HWLFFL3F.js");
317
+ const { getProjectRoot } = await import("./git-LCG45YXD.js");
318
+ const { loadVerifyConfig } = await import("./config-56B4OHZE.js");
317
319
  const cwd = await getProjectRoot();
318
320
  const verify = await loadVerifyConfig({ projectRoot: cwd });
319
321
  const contract = await resolveContract({ cwd, declared: verify.contract });
@@ -327,6 +329,21 @@ async function runInit(source = "github", options = {}) {
327
329
  }
328
330
  return allPassed ? 0 : 1;
329
331
  }
332
+ function printExperimentalNotice() {
333
+ printWarning(
334
+ "Issue Flow is experimental and under active development, built mostly with AI coding agents and not audited."
335
+ );
336
+ for (const line of [
337
+ "Expect bugs, incomplete implementations, regressions and possibly undiscovered security flaws.",
338
+ "Not recommended for real projects, production environments, critical systems or repositories",
339
+ "with sensitive information. Keep backups, use a dedicated branch and review every change it makes.",
340
+ "Token consumption is not optimized yet: a run may use significantly more tokens than necessary.",
341
+ "https://github.com/fabioassuncao/issue-flow/blob/main/docs/project-status.md"
342
+ ]) {
343
+ console.log(` ${line}`);
344
+ }
345
+ console.log("");
346
+ }
330
347
  var ESCALATING_CODEX_KEYS = ["approvals_reviewer", "sandbox_mode", "sandbox_workspace_write"];
331
348
  async function warnEscalatingCodexConfig() {
332
349
  const configPath = join(homedir(), ".codex", "config.toml");
@@ -345,11 +362,17 @@ function isInteractiveInit() {
345
362
  const inCi = ci !== void 0 && ci !== "" && ci !== "0" && ci.toLowerCase() !== "false";
346
363
  return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && !inCi;
347
364
  }
365
+ function shouldOfferAgentPrompt(hasExplicitSelection, routingMode) {
366
+ return !hasExplicitSelection && routingMode !== "active";
367
+ }
348
368
  async function maybeOfferAgentChoice(options, agent) {
349
369
  if (options.json === true || options.noAgentPrompt === true) return;
350
370
  if (!isInteractiveInit()) return;
351
- if (hasExplicitAgentSelection(agent, getAgentCliOverrides())) return;
352
- const { createInterface } = await import("readline");
371
+ const routing = await loadRoutingConfig();
372
+ if (!shouldOfferAgentPrompt(hasExplicitAgentSelection(agent, getAgentCliOverrides()), routing.mode)) {
373
+ return;
374
+ }
375
+ const { createInterface } = await import("node:readline");
353
376
  const rl = createInterface({ input: process.stdin, output: process.stdout });
354
377
  const answer = await new Promise((resolve) => {
355
378
  rl.question("Agent [claude/codex] (default claude): ", resolve);
@@ -358,7 +381,7 @@ async function maybeOfferAgentChoice(options, agent) {
358
381
  const trimmed = answer.trim().toLowerCase();
359
382
  const chosen = trimmed === "codex" ? "codex" : "claude";
360
383
  if (options.apply === true) {
361
- const { persistFirstAgentChoice } = await import("./agent-TB6W7PT7.js");
384
+ const { persistFirstAgentChoice } = await import("./agent-EWKA7357.js");
362
385
  const path = await persistFirstAgentChoice(chosen);
363
386
  printSuccess(`Saved agent '${chosen}' to ${path}`);
364
387
  return;
@@ -370,6 +393,7 @@ async function maybeOfferAgentChoice(options, agent) {
370
393
 
371
394
  export {
372
395
  summarizePreflight,
373
- runInit
396
+ runInit,
397
+ shouldOfferAgentPrompt
374
398
  };
375
- //# sourceMappingURL=chunk-ISZ54SVE.js.map
399
+ //# sourceMappingURL=chunk-46S7APLQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { execa } from 'execa';\nimport { hasExplicitAgentSelection } from '../agents/resolve.js';\nimport { AGENT_PHASES } from '../agents/types.js';\nimport { getAgentCliOverrides, loadAgentConfig, loadRoutingConfig } from '../config.js';\nimport type { IssueSource } from '../issues/types.js';\nimport type { ScaffoldActionKind, ScaffoldPlan } from '../scaffold/plan.js';\nimport { printError, printInfo, printSuccess, printWarning } from '../ui/logger.js';\n\n/** Stable identifier of a check, used to decide whether it blocks. */\ntype CheckKey = 'claude' | 'codex' | 'gh' | 'git';\n\ninterface CheckResult {\n key: CheckKey;\n name: string;\n passed: boolean;\n detail: string;\n hint?: string;\n}\n\nasync function checkClaude(): Promise<CheckResult> {\n try {\n const proc = await execa('claude', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode === 0) {\n const version = proc.stdout?.toString().trim() ?? 'unknown';\n return { key: 'claude', name: 'claude CLI', passed: true, detail: version };\n }\n return {\n key: 'claude',\n name: 'claude CLI',\n passed: false,\n detail: 'claude command failed',\n hint: 'Install Claude Code: https://docs.anthropic.com/en/docs/claude-code',\n };\n } catch {\n return {\n key: 'claude',\n name: 'claude CLI',\n passed: false,\n detail: 'claude not found',\n hint: 'Install Claude Code: https://docs.anthropic.com/en/docs/claude-code',\n };\n }\n}\n\nasync function checkGh(): Promise<CheckResult> {\n try {\n const proc = await execa('gh', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode !== 0) {\n return {\n key: 'gh',\n name: 'gh CLI',\n passed: false,\n detail: 'gh command failed',\n hint: 'Install GitHub CLI: https://cli.github.com/',\n };\n }\n const version = proc.stdout?.toString().split('\\n')[0]?.trim() ?? 'unknown';\n\n // Check auth status\n const auth = await execa('gh', ['auth', 'status'], { reject: false, timeout: 10_000 });\n if (auth.exitCode !== 0) {\n return {\n key: 'gh',\n name: 'gh CLI',\n passed: false,\n detail: `${version} (not authenticated)`,\n hint: 'Run: gh auth login',\n };\n }\n return { key: 'gh', name: 'gh CLI', passed: true, detail: `${version} (authenticated)` };\n } catch {\n return {\n key: 'gh',\n name: 'gh CLI',\n passed: false,\n detail: 'gh not found',\n hint: 'Install GitHub CLI: https://cli.github.com/',\n };\n }\n}\n\nasync function checkCodex(): Promise<CheckResult> {\n try {\n const proc = await execa('codex', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode !== 0) {\n return {\n key: 'codex',\n name: 'codex CLI',\n passed: false,\n detail: 'codex command failed',\n hint: 'Install Codex CLI: https://developers.openai.com/codex/noninteractive',\n };\n }\n const version = proc.stdout?.toString().trim() ?? 'unknown';\n const auth = await execa('codex', ['login', 'status'], { reject: false, timeout: 10_000 });\n if (auth.exitCode !== 0) {\n return {\n key: 'codex',\n name: 'codex CLI',\n passed: false,\n detail: `${version} (not authenticated)`,\n hint: 'Run: codex login --with-api-key (or set CODEX_API_KEY)',\n };\n }\n return { key: 'codex', name: 'codex CLI', passed: true, detail: `${version} (authenticated)` };\n } catch {\n return {\n key: 'codex',\n name: 'codex CLI',\n passed: false,\n detail: 'codex not found',\n hint: 'Install Codex CLI: https://developers.openai.com/codex/noninteractive',\n };\n }\n}\n\nasync function checkGit(): Promise<CheckResult> {\n try {\n const proc = await execa('git', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode !== 0) {\n return {\n key: 'git',\n name: 'git',\n passed: false,\n detail: 'git command failed',\n hint: 'Install git: https://git-scm.com/',\n };\n }\n const version = proc.stdout?.toString().trim() ?? 'unknown';\n\n // Check if current directory is a git repo\n const repo = await execa('git', ['rev-parse', '--is-inside-work-tree'], {\n reject: false,\n timeout: 5_000,\n });\n if (repo.exitCode !== 0) {\n return {\n key: 'git',\n name: 'git',\n passed: false,\n detail: `${version} (not a git repository)`,\n hint: 'Run this command inside a git repository',\n };\n }\n return { key: 'git', name: 'git', passed: true, detail: `${version} (inside repo)` };\n } catch {\n return {\n key: 'git',\n name: 'git',\n passed: false,\n detail: 'git not found',\n hint: 'Install git: https://git-scm.com/',\n };\n }\n}\n\nexport interface InitOptions {\n /** Write the missing files instead of only reporting them. */\n apply?: boolean;\n /** Emit the plan as JSON — the bridge the initialization skill reads. */\n json?: boolean;\n /** Subdirectory the conventions apply to, in a monorepo. */\n scope?: string;\n /** Skip the convention report entirely and only check prerequisites. */\n checkOnly?: boolean;\n /** Skip the first-run agent choice. */\n noAgentPrompt?: boolean;\n /**\n * One-line preflight for `issue-flow run` in clean mode. `issue-flow init`\n * itself never passes this — its product is the full report.\n */\n compact?: boolean;\n}\n\nconst ACTION_ICON: Record<ScaffoldActionKind, string> = {\n create: '+',\n keep: '=',\n review: '!',\n};\n\n/**\n * One-line preflight for the clean terminal. The full report stays on\n * `issue-flow init` and on `--verbose`.\n */\nexport function summarizePreflight(\n results: ReadonlyArray<{ passed: boolean }>,\n plan: { actions: ReadonlyArray<{ kind: string }> },\n): string {\n const kept = plan.actions.filter((action) => action.kind === 'keep').length;\n const creates = plan.actions.filter((action) => action.kind === 'create').length;\n const env = results.every((result) => result.passed) ? 'environment ok' : 'environment failed';\n const createBit = creates === 0 ? 'nothing to create' : `${creates} to create`;\n return `Preflight: ${env} · ${kept} conventions kept · ${createBit}`;\n}\n\n/**\n * Render the plan for a human.\n *\n * `keep` lines are printed, not hidden: the most valuable thing this report\n * says is usually *what it is not going to touch*, and a list of only the\n * missing files would read as if the repository had nothing.\n */\nfunction renderPlan(plan: ScaffoldPlan, apply: boolean): void {\n const creates = plan.actions.filter((a) => a.kind === 'create');\n const reviews = plan.actions.filter((a) => a.kind === 'review');\n\n console.log('');\n printInfo(`Repository conventions (${plan.root})\\n`);\n\n for (const item of plan.actions) {\n console.log(` ${ACTION_ICON[item.kind]} ${item.path}`);\n console.log(` ${item.reason}`);\n }\n\n if (plan.notes.length > 0) {\n console.log('');\n for (const note of plan.notes) {\n printWarning(note);\n }\n }\n\n console.log('');\n if (creates.length === 0 && reviews.length === 0) {\n printSuccess('This repository already declares everything Issue Flow would add.');\n return;\n }\n if (creates.length === 0) {\n printSuccess('Nothing to create.');\n return;\n }\n if (!apply) {\n printInfo(`${creates.length} file(s) would be created. Re-run with --apply to write them.`);\n }\n}\n\n/**\n * Verify the prerequisites of the pipeline and report the repository's\n * conventions.\n *\n * `source` is the Issue origin the run is headed for. `gh` only blocks when\n * that origin is GitHub: no other origin shells out to it, so a missing or\n * unauthenticated gh is reported as a warning instead of failing the\n * environment. `claude` and `git` stay blocking for every origin, and the\n * default ('github') keeps the previous behaviour byte for byte.\n *\n * The convention report is additive: the prerequisite checks run first and still\n * decide the exit code, so an existing script calling `issue-flow init` sees the\n * same pass/fail it always did. Nothing is written without `--apply`.\n */\nexport async function runInit(\n source: IssueSource = 'github',\n options: InitOptions = {},\n): Promise<number> {\n const json = options.json === true;\n const compact = options.compact === true;\n\n if (!json && !compact) {\n printExperimentalNotice();\n printInfo('Checking prerequisites...\\n');\n }\n\n const agent = await loadAgentConfig();\n const usesCodex =\n agent.provider === 'codex' ||\n AGENT_PHASES.some((phase) => agent.phases[phase]?.provider === 'codex');\n\n const checks = [checkClaude(), checkGh(), checkGit()];\n if (usesCodex) checks.push(checkCodex());\n const results = await Promise.all(checks);\n const isBlocking = (r: CheckResult): boolean => {\n if (r.key === 'gh') return source === 'github';\n if (r.key === 'codex') return usesCodex;\n if (r.key === 'claude') return agent.provider === 'claude' || !usesCodex;\n return true;\n };\n\n if (!json && (!compact || !results.every((r) => r.passed || !isBlocking(r)))) {\n for (const r of results) {\n if (r.passed) {\n if (!compact) printSuccess(`${r.name}: ${r.detail}`);\n } else if (isBlocking(r)) {\n printError(`${r.name}: ${r.detail}`);\n if (r.hint) {\n console.log(` ${r.hint}`);\n }\n } else if (!compact) {\n printWarning(`${r.name}: ${r.detail} (not required for ${source} issues)`);\n }\n }\n }\n\n const allPassed = results.filter(isBlocking).every((r) => r.passed);\n\n if (!json && !compact) {\n console.log('');\n if (allPassed) {\n printSuccess('All prerequisites met. Ready to run the pipeline.');\n } else {\n printError('Some prerequisites are missing. Please fix the issues above.');\n }\n } else if (!json && compact && !allPassed) {\n printError('Prerequisites not met. Fix the issues above and try again.');\n }\n\n if (usesCodex) {\n await warnEscalatingCodexConfig();\n }\n\n if (options.checkOnly === true) {\n return allPassed ? 0 : 1;\n }\n\n // The conventions half. It never changes the exit code: a repository missing\n // a template is not a broken environment, and failing here would break every\n // script that treats `init` as a prerequisite gate.\n let plan: ScaffoldPlan;\n try {\n const { planRepositoryScaffold } = await import('../scaffold/apply.js');\n plan = await planRepositoryScaffold({ scope: options.scope ?? null });\n } catch (err) {\n if (json) {\n console.log(\n JSON.stringify(\n { schemaVersion: 1, error: err instanceof Error ? err.message : String(err) },\n null,\n 2,\n ),\n );\n return allPassed ? 0 : 1;\n }\n printWarning(\n `Could not inspect the repository conventions: ${err instanceof Error ? err.message : String(err)}`,\n );\n return allPassed ? 0 : 1;\n }\n\n let applied: { written: string[]; skipped: string[] } | null = null;\n if (options.apply === true) {\n const { applyScaffoldPlan } = await import('../scaffold/apply.js');\n applied = await applyScaffoldPlan(plan);\n }\n\n if (json) {\n console.log(\n JSON.stringify(\n {\n schemaVersion: 1,\n prerequisites: results.map((r) => ({\n key: r.key,\n passed: r.passed,\n detail: r.detail,\n blocking: isBlocking(r),\n })),\n root: plan.root,\n // Content is omitted: a skill decides from the actions, and the bodies\n // would make the payload unreadable for no gain.\n actions: plan.actions.map(({ path, kind, tier, reason }) => ({\n path,\n kind,\n tier,\n reason,\n })),\n notes: plan.notes,\n applied,\n },\n null,\n 2,\n ),\n );\n return allPassed ? 0 : 1;\n }\n\n if (compact) {\n printInfo(summarizePreflight(results, plan));\n } else {\n renderPlan(plan, options.apply === true);\n }\n\n if (applied !== null) {\n console.log('');\n for (const path of applied.written) {\n printSuccess(`Created ${path}`);\n }\n for (const path of applied.skipped) {\n printWarning(`Skipped ${path}: it already exists.`);\n }\n if (applied.written.length === 0) {\n printInfo('Nothing was written — the repository already had everything.');\n }\n }\n\n await maybeOfferAgentChoice(options, agent);\n\n if (!json) {\n try {\n const { resolveContract } = await import('../verify/contract.js');\n const { getProjectRoot } = await import('../utils/git.js');\n const { loadVerifyConfig } = await import('../config.js');\n const cwd = await getProjectRoot();\n const verify = await loadVerifyConfig({ projectRoot: cwd });\n const contract = await resolveContract({ cwd, declared: verify.contract });\n if (contract.source === 'empty') {\n printWarning(\n 'No acceptance contract declared or discovered — runs will finish unverified.',\n );\n }\n } catch {\n // Observational: init still reports prerequisites if discovery fails.\n }\n }\n\n return allPassed ? 0 : 1;\n}\n\n/**\n * `init` is the first command a new user runs, so it is where the maturity of\n * the project is stated. The full notice lives in `docs/project-status.md`; this\n * is the short form, printed only on the human path — `--json` is consumed by\n * tooling and `--compact` is the preflight inside `run`.\n */\nfunction printExperimentalNotice(): void {\n printWarning(\n 'Issue Flow is experimental and under active development, built mostly with AI coding agents and not audited.',\n );\n for (const line of [\n 'Expect bugs, incomplete implementations, regressions and possibly undiscovered security flaws.',\n 'Not recommended for real projects, production environments, critical systems or repositories',\n 'with sensitive information. Keep backups, use a dedicated branch and review every change it makes.',\n 'Token consumption is not optimized yet: a run may use significantly more tokens than necessary.',\n 'https://github.com/fabioassuncao/issue-flow/blob/main/docs/project-status.md',\n ]) {\n console.log(` ${line}`);\n }\n console.log('');\n}\n\nconst ESCALATING_CODEX_KEYS = ['approvals_reviewer', 'sandbox_mode', 'sandbox_workspace_write'];\n\nasync function warnEscalatingCodexConfig(): Promise<void> {\n const configPath = join(homedir(), '.codex', 'config.toml');\n try {\n const raw = await readFile(configPath, 'utf-8');\n const hits = ESCALATING_CODEX_KEYS.filter((key) => raw.includes(key));\n if (hits.length === 0) return;\n printWarning(\n `$CODEX_HOME/config.toml contains ${hits.join(', ')}, which can escalate --sandbox. Set agent.codex.ignoreUserConfig: true for reproducible runs (recommended in CI). Claude's equivalent is --setting-sources project (agent.claude.ignoreUserConfig).`,\n );\n } catch {\n // Absence is the common case.\n }\n}\n\nfunction isInteractiveInit(): boolean {\n const ci = process.env.CI;\n const inCi = ci !== undefined && ci !== '' && ci !== '0' && ci.toLowerCase() !== 'false';\n return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && !inCi;\n}\n\n/**\n * An active router owns the per-phase harness choice, so asking for one agent\n * at startup would immediately contradict the resolved configuration.\n */\nexport function shouldOfferAgentPrompt(\n hasExplicitSelection: boolean,\n routingMode: string,\n): boolean {\n return !hasExplicitSelection && routingMode !== 'active';\n}\n\nasync function maybeOfferAgentChoice(\n options: InitOptions,\n agent: Awaited<ReturnType<typeof loadAgentConfig>>,\n): Promise<void> {\n if (options.json === true || options.noAgentPrompt === true) return;\n if (!isInteractiveInit()) return;\n const routing = await loadRoutingConfig();\n if (\n !shouldOfferAgentPrompt(hasExplicitAgentSelection(agent, getAgentCliOverrides()), routing.mode)\n ) {\n return;\n }\n\n const { createInterface } = await import('node:readline');\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n rl.question('Agent [claude/codex] (default claude): ', resolve);\n });\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n const chosen = trimmed === 'codex' ? 'codex' : 'claude';\n\n if (options.apply === true) {\n const { persistFirstAgentChoice } = await import('./agent.js');\n const path = await persistFirstAgentChoice(chosen);\n printSuccess(`Saved agent '${chosen}' to ${path}`);\n return;\n }\n printInfo(\n `Using ${chosen} for this check. Persist with: issue-flow agent use ${chosen} --global`,\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/document-result.ts
4
+ function parseDocumentResult(output, tag) {
5
+ if (!/^[a-z][a-z0-9-]*$/.test(tag)) throw new Error(`Invalid document tag: ${tag}`);
6
+ const pattern = new RegExp(`<${tag}>\\s*([\\s\\S]*?)\\s*</${tag}>\\s*$`);
7
+ const matches = [...output.matchAll(new RegExp(`<${tag}>`, "g"))];
8
+ const match = output.match(pattern);
9
+ if (matches.length !== 1 || match?.[1] === void 0) {
10
+ throw new Error(`Expected exactly one final <${tag}> block.`);
11
+ }
12
+ const content = match[1].trim();
13
+ if (content.length < 10) throw new Error(`<${tag}> content is empty or too short.`);
14
+ return `${content}
15
+ `;
16
+ }
17
+
18
+ export {
19
+ parseDocumentResult
20
+ };
21
+ //# sourceMappingURL=chunk-6AOZCWN2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/document-result.ts"],"sourcesContent":["/** Extract one non-empty, final tagged document from a headless response. */\nexport function parseDocumentResult(output: string, tag: string): string {\n if (!/^[a-z][a-z0-9-]*$/.test(tag)) throw new Error(`Invalid document tag: ${tag}`);\n const pattern = new RegExp(`<${tag}>\\\\s*([\\\\s\\\\S]*?)\\\\s*</${tag}>\\\\s*$`);\n const matches = [...output.matchAll(new RegExp(`<${tag}>`, 'g'))];\n const match = output.match(pattern);\n if (matches.length !== 1 || match?.[1] === undefined) {\n throw new Error(`Expected exactly one final <${tag}> block.`);\n }\n const content = match[1].trim();\n if (content.length < 10) throw new Error(`<${tag}> content is empty or too short.`);\n return `${content}\\n`;\n}\n"],"mappings":";;;AACO,SAAS,oBAAoB,QAAgB,KAAqB;AACvE,MAAI,CAAC,oBAAoB,KAAK,GAAG,EAAG,OAAM,IAAI,MAAM,yBAAyB,GAAG,EAAE;AAClF,QAAM,UAAU,IAAI,OAAO,IAAI,GAAG,0BAA0B,GAAG,QAAQ;AACvE,QAAM,UAAU,CAAC,GAAG,OAAO,SAAS,IAAI,OAAO,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC;AAChE,QAAM,QAAQ,OAAO,MAAM,OAAO;AAClC,MAAI,QAAQ,WAAW,KAAK,QAAQ,CAAC,MAAM,QAAW;AACpD,UAAM,IAAI,MAAM,+BAA+B,GAAG,UAAU;AAAA,EAC9D;AACA,QAAM,UAAU,MAAM,CAAC,EAAE,KAAK;AAC9B,MAAI,QAAQ,SAAS,GAAI,OAAM,IAAI,MAAM,IAAI,GAAG,kCAAkC;AAClF,SAAO,GAAG,OAAO;AAAA;AACnB;","names":[]}
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- getDiagnosticsDir
4
- } from "./chunk-HAKDLR6N.js";
5
2
  import {
6
3
  redactSecrets
7
- } from "./chunk-PPZ3K4YM.js";
4
+ } from "./chunk-PRGPSNGA.js";
5
+ import {
6
+ getDiagnosticsDir
7
+ } from "./chunk-MAJ6QDRL.js";
8
8
 
9
9
  // src/storage/diagnostics.ts
10
- import { appendFile, mkdir, readdir, readFile, rename, stat, unlink } from "fs/promises";
11
- import { join } from "path";
10
+ import { appendFile, mkdir, readdir, readFile, rename, stat, unlink } from "node:fs/promises";
11
+ import { join } from "node:path";
12
12
  var MAX_FILE_BYTES = 10 * 1024 * 1024;
13
13
  var MAX_GENERATIONS = 5;
14
14
  var RETENTION_DAYS = 30;
@@ -160,4 +160,4 @@ export {
160
160
  resetDiagnosticsState,
161
161
  readDiagnostics
162
162
  };
163
- //# sourceMappingURL=chunk-QPGEFLMY.js.map
163
+ //# sourceMappingURL=chunk-7CMVL4X6.js.map