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.
- package/README.md +34 -100
- package/dist/agent-EWKA7357.js +32 -0
- package/dist/{analyze-6M3KEOX7.js → analyze-Z7ODC27F.js} +42 -41
- package/dist/analyze-Z7ODC27F.js.map +1 -0
- package/dist/{apply-ADB3ZK7W.js → apply-VNSWQS6O.js} +22 -24
- package/dist/apply-VNSWQS6O.js.map +1 -0
- package/dist/artifacts-AMJQJFFV.js +82 -0
- package/dist/artifacts-AMJQJFFV.js.map +1 -0
- package/dist/{bench-FM7I7EIV.js → bench-ZJEIACUK.js} +42 -42
- package/dist/{chunk-RYOUZ6EK.js → chunk-23VHVVKW.js} +3 -3
- package/dist/chunk-2EYLOS5I.js +99 -0
- package/dist/chunk-2EYLOS5I.js.map +1 -0
- package/dist/{chunk-ANI2NTR4.js → chunk-2EZW4X57.js} +28 -25
- package/dist/chunk-2EZW4X57.js.map +1 -0
- package/dist/{chunk-E6KXCV3T.js → chunk-2Q6PG3MT.js} +15 -15
- package/dist/chunk-2Q6PG3MT.js.map +1 -0
- package/dist/{chunk-C5TL7T4V.js → chunk-2YM2VH6R.js} +2 -2
- package/dist/{chunk-LPWPFIIB.js → chunk-2YYP7AE6.js} +14 -16
- package/dist/{chunk-LPWPFIIB.js.map → chunk-2YYP7AE6.js.map} +1 -1
- package/dist/chunk-3JD25HHJ.js +278 -0
- package/dist/chunk-3JD25HHJ.js.map +1 -0
- package/dist/{chunk-ISZ54SVE.js → chunk-46S7APLQ.js} +41 -17
- package/dist/chunk-46S7APLQ.js.map +1 -0
- package/dist/chunk-6AOZCWN2.js +21 -0
- package/dist/chunk-6AOZCWN2.js.map +1 -0
- package/dist/{chunk-QPGEFLMY.js → chunk-7CMVL4X6.js} +7 -7
- package/dist/chunk-7MJGYPHF.js +1571 -0
- package/dist/chunk-7MJGYPHF.js.map +1 -0
- package/dist/chunk-7ZZUSMBJ.js +35 -0
- package/dist/chunk-7ZZUSMBJ.js.map +1 -0
- package/dist/{chunk-DUS5ZNKL.js → chunk-DIMDAYZS.js} +7 -6
- package/dist/{chunk-DGRTJD5R.js → chunk-DXH4TQRB.js} +7 -7
- package/dist/{chunk-CRGIQFUQ.js → chunk-E2H3JV2E.js} +3 -1
- package/dist/chunk-E2H3JV2E.js.map +1 -0
- package/dist/{chunk-YUNBOUQH.js → chunk-ERAG5IQB.js} +8 -8
- package/dist/{chunk-D6ZBGJ3L.js → chunk-F2NFJ27Z.js} +130 -36
- package/dist/chunk-F2NFJ27Z.js.map +1 -0
- package/dist/{chunk-SOM4QA6C.js → chunk-GD7JZMSJ.js} +93 -18
- package/dist/chunk-GD7JZMSJ.js.map +1 -0
- package/dist/{chunk-YII4U7ZD.js → chunk-HEHHQU4G.js} +70 -81
- package/dist/chunk-HEHHQU4G.js.map +1 -0
- package/dist/{chunk-KZ5EY5XX.js → chunk-HHXWAWU6.js} +159 -114
- package/dist/chunk-HHXWAWU6.js.map +1 -0
- package/dist/{chunk-SGTTR4XW.js → chunk-HWWB22HC.js} +152 -100
- package/dist/chunk-HWWB22HC.js.map +1 -0
- package/dist/chunk-IMB6FA2F.js +673 -0
- package/dist/chunk-IMB6FA2F.js.map +1 -0
- package/dist/{chunk-6YQT3SSM.js → chunk-IQYXXFRC.js} +22 -6
- package/dist/chunk-IQYXXFRC.js.map +1 -0
- package/dist/chunk-J6VJ5M2U.js +1201 -0
- package/dist/chunk-J6VJ5M2U.js.map +1 -0
- package/dist/{chunk-B3TSS7BT.js → chunk-KDQRNVBZ.js} +3 -3
- package/dist/{chunk-A4H7P7T7.js → chunk-LRWIP7QS.js} +41 -26
- package/dist/chunk-LRWIP7QS.js.map +1 -0
- package/dist/{chunk-DV7HUC4Z.js → chunk-LYC57OXF.js} +20 -117
- package/dist/chunk-LYC57OXF.js.map +1 -0
- package/dist/{chunk-HAKDLR6N.js → chunk-MAJ6QDRL.js} +65 -78
- package/dist/chunk-MAJ6QDRL.js.map +1 -0
- package/dist/{chunk-HJBF4WD2.js → chunk-MHA7Q3KU.js} +288 -108
- package/dist/chunk-MHA7Q3KU.js.map +1 -0
- package/dist/{chunk-UK4E2FUA.js → chunk-MQTVMB7W.js} +103 -76
- package/dist/chunk-MQTVMB7W.js.map +1 -0
- package/dist/chunk-NP47HRX2.js +136 -0
- package/dist/chunk-NP47HRX2.js.map +1 -0
- package/dist/{chunk-MI5TREN3.js → chunk-NYDAHE3Y.js} +52 -46
- package/dist/chunk-NYDAHE3Y.js.map +1 -0
- package/dist/{chunk-UWDCIFTP.js → chunk-P7ZYOC23.js} +41 -57
- package/dist/chunk-P7ZYOC23.js.map +1 -0
- package/dist/{chunk-3R27MJ5R.js → chunk-PFWHHYEO.js} +3 -3
- package/dist/{chunk-2SUFAE6X.js → chunk-PHPAK2L6.js} +44 -33
- package/dist/{chunk-2SUFAE6X.js.map → chunk-PHPAK2L6.js.map} +1 -1
- package/dist/{chunk-PPZ3K4YM.js → chunk-PRGPSNGA.js} +2 -2
- package/dist/{chunk-32JSYXEK.js → chunk-RGFMQFX7.js} +9 -23
- package/dist/{chunk-32JSYXEK.js.map → chunk-RGFMQFX7.js.map} +1 -1
- package/dist/{chunk-YVPZUN6X.js → chunk-TJVX42KY.js} +335 -38
- package/dist/chunk-TJVX42KY.js.map +1 -0
- package/dist/{chunk-RCZJQUTC.js → chunk-UDRAD7MF.js} +60 -27
- package/dist/chunk-UDRAD7MF.js.map +1 -0
- package/dist/{chunk-HY5HLFDU.js → chunk-UHDXXLYH.js} +148 -135
- package/dist/chunk-UHDXXLYH.js.map +1 -0
- package/dist/{chunk-RNFUVKC3.js → chunk-UPAAZU4W.js} +30 -13
- package/dist/chunk-UPAAZU4W.js.map +1 -0
- package/dist/{chunk-L6HSKTQE.js → chunk-W7LZLJAF.js} +4 -4
- package/dist/{chunk-HLAPOUEM.js → chunk-WB2FVIUS.js} +44 -12
- package/dist/chunk-WB2FVIUS.js.map +1 -0
- package/dist/{chunk-VE3IYNDN.js → chunk-WPHC6JBR.js} +9 -9
- package/dist/chunk-WPHC6JBR.js.map +1 -0
- package/dist/{chunk-KZXQWC7U.js → chunk-XHCP6ZNO.js} +201 -64
- package/dist/chunk-XHCP6ZNO.js.map +1 -0
- package/dist/{chunk-7F7LGMVO.js → chunk-XKR3VD2Z.js} +12 -12
- package/dist/{chunk-7F7LGMVO.js.map → chunk-XKR3VD2Z.js.map} +1 -1
- package/dist/{chunk-ICM463EO.js → chunk-XNK5UN2D.js} +5 -649
- package/dist/chunk-XNK5UN2D.js.map +1 -0
- package/dist/{chunk-Z5O2XBWA.js → chunk-XQLNZW7I.js} +40 -30
- package/dist/chunk-XQLNZW7I.js.map +1 -0
- package/dist/{chunk-QMIQR7EE.js → chunk-ZAYATXJD.js} +32 -316
- package/dist/chunk-ZAYATXJD.js.map +1 -0
- package/dist/cli.js +128 -60
- package/dist/cli.js.map +1 -1
- package/dist/compat-YPDZB34U.js +24 -0
- package/dist/{config-KGZUPAUP.js → config-56B4OHZE.js} +20 -17
- package/dist/{contract-JH2WBIO6.js → contract-HWLFFL3F.js} +2 -2
- package/dist/{conventions-RYN7OXJN.js → conventions-DW2CM5I3.js} +20 -16
- package/dist/{conventions-RYN7OXJN.js.map → conventions-DW2CM5I3.js.map} +1 -1
- package/dist/db-5NU77PJA.js +264 -0
- package/dist/db-5NU77PJA.js.map +1 -0
- package/dist/diagnostics-UCTS2VUF.js +25 -0
- package/dist/execute-R7AVOLNG.js +38 -0
- package/dist/{generate-CX6KNHZS.js → generate-O7GUXLN4.js} +46 -43
- package/dist/generate-O7GUXLN4.js.map +1 -0
- package/dist/{git-A2IP32PQ.js → git-LCG45YXD.js} +2 -3
- package/dist/history-6GSURU3H.js +101 -0
- package/dist/history-6GSURU3H.js.map +1 -0
- package/dist/init-DD6DBRC5.js +28 -0
- package/dist/{operations-CAKIT2BO.js → operations-HU5ZFEBA.js} +78 -52
- package/dist/operations-HU5ZFEBA.js.map +1 -0
- package/dist/{permissions-G4XCAESF.js → permissions-NNNSRC4P.js} +5 -6
- package/dist/plan-XR6B7EM3.js +38 -0
- package/dist/{policy-WX6O2JID.js → policy-RFJOJQL6.js} +17 -15
- package/dist/{policy-WX6O2JID.js.map → policy-RFJOJQL6.js.map} +1 -1
- package/dist/pr-OSCNJCOO.js +42 -0
- package/dist/pr-review-IKIYHFBO.js +33 -0
- package/dist/prd-HFDZJJQR.js +37 -0
- package/dist/{ps-76L6ZAZL.js → ps-AKRHGPSR.js} +26 -15
- package/dist/ps-AKRHGPSR.js.map +1 -0
- package/dist/{recorder-N7U66HCO.js → recorder-T7426CKV.js} +11 -7
- package/dist/registry-DWRKD65A.js +17 -0
- package/dist/repository-TABBRW2C.js +80 -0
- package/dist/resume-GDXKK5XP.js +278 -0
- package/dist/resume-GDXKK5XP.js.map +1 -0
- package/dist/review-JJHMQA2O.js +38 -0
- package/dist/routing-FPNUVE7T.js +35 -0
- package/dist/run-RLCURLLL.js +61 -0
- package/dist/{session-publisher-KAJQRHNZ.js → session-publisher-Q5TNEZPX.js} +3 -3
- package/dist/session-publisher-Q5TNEZPX.js.map +1 -0
- package/dist/{state-manager-UPHO2EA2.js → state-manager-IGNTD4AB.js} +9 -4
- package/dist/state-manager-IGNTD4AB.js.map +1 -0
- package/dist/{usage-U7QNGCPK.js → usage-XW27AL5K.js} +69 -63
- package/dist/usage-XW27AL5K.js.map +1 -0
- package/dist/web-NCDBVZ5Y.js +205 -0
- package/dist/web-NCDBVZ5Y.js.map +1 -0
- package/package.json +20 -7
- package/prompts/analyze.md +16 -19
- package/prompts/execute.md +73 -144
- package/prompts/generate.md +23 -10
- package/prompts/plan.md +32 -60
- package/prompts/pr-review.md +58 -40
- package/prompts/pr.md +50 -16
- package/prompts/prd.md +26 -19
- package/prompts/review.md +41 -18
- package/web/AGENTS.md +115 -7
- package/web/public/app.css +393 -245
- package/web/public/app.js +212 -143
- package/web/public/index.html +106 -78
- package/dist/agent-TB6W7PT7.js +0 -30
- package/dist/analyze-6M3KEOX7.js.map +0 -1
- package/dist/apply-ADB3ZK7W.js.map +0 -1
- package/dist/chunk-6YQT3SSM.js.map +0 -1
- package/dist/chunk-A4H7P7T7.js.map +0 -1
- package/dist/chunk-ANI2NTR4.js.map +0 -1
- package/dist/chunk-CRGIQFUQ.js.map +0 -1
- package/dist/chunk-D6ZBGJ3L.js.map +0 -1
- package/dist/chunk-DV7HUC4Z.js.map +0 -1
- package/dist/chunk-E6KXCV3T.js.map +0 -1
- package/dist/chunk-HAKDLR6N.js.map +0 -1
- package/dist/chunk-HJBF4WD2.js.map +0 -1
- package/dist/chunk-HLAPOUEM.js.map +0 -1
- package/dist/chunk-HY5HLFDU.js.map +0 -1
- package/dist/chunk-ICM463EO.js.map +0 -1
- package/dist/chunk-ISZ54SVE.js.map +0 -1
- package/dist/chunk-KZ5EY5XX.js.map +0 -1
- package/dist/chunk-KZXQWC7U.js.map +0 -1
- package/dist/chunk-MI5TREN3.js.map +0 -1
- package/dist/chunk-QMIQR7EE.js.map +0 -1
- package/dist/chunk-RCZJQUTC.js.map +0 -1
- package/dist/chunk-RNFUVKC3.js.map +0 -1
- package/dist/chunk-SGTTR4XW.js.map +0 -1
- package/dist/chunk-SOM4QA6C.js.map +0 -1
- package/dist/chunk-UK4E2FUA.js.map +0 -1
- package/dist/chunk-UWDCIFTP.js.map +0 -1
- package/dist/chunk-VE3IYNDN.js.map +0 -1
- package/dist/chunk-WCCZJL7W.js +0 -141
- package/dist/chunk-WCCZJL7W.js.map +0 -1
- package/dist/chunk-XEYH6ZZI.js +0 -289
- package/dist/chunk-XEYH6ZZI.js.map +0 -1
- package/dist/chunk-XZUEFTUP.js +0 -243
- package/dist/chunk-XZUEFTUP.js.map +0 -1
- package/dist/chunk-YII4U7ZD.js.map +0 -1
- package/dist/chunk-YVPZUN6X.js.map +0 -1
- package/dist/chunk-Z5O2XBWA.js.map +0 -1
- package/dist/diagnostics-HTCWF7F4.js +0 -23
- package/dist/execute-QL3ZQIAP.js +0 -37
- package/dist/generate-CX6KNHZS.js.map +0 -1
- package/dist/init-PDVQDRS6.js +0 -24
- package/dist/operations-CAKIT2BO.js.map +0 -1
- package/dist/plan-PRORGS3L.js +0 -34
- package/dist/pr-27RMQI5F.js +0 -39
- package/dist/pr-review-VNESMAMT.js +0 -32
- package/dist/prd-ZNC4T5BS.js +0 -33
- package/dist/ps-76L6ZAZL.js.map +0 -1
- package/dist/registry-XLHHYLTR.js +0 -15
- package/dist/resume-VLWIE264.js +0 -244
- package/dist/resume-VLWIE264.js.map +0 -1
- package/dist/review-7CREJDKB.js +0 -35
- package/dist/routing-VCDYHSXA.js +0 -33
- package/dist/run-3MPORLF5.js +0 -56
- package/dist/usage-U7QNGCPK.js.map +0 -1
- package/dist/web-HUNDHCJ4.js +0 -172
- package/dist/web-HUNDHCJ4.js.map +0 -1
- /package/dist/{agent-TB6W7PT7.js.map → agent-EWKA7357.js.map} +0 -0
- /package/dist/{bench-FM7I7EIV.js.map → bench-ZJEIACUK.js.map} +0 -0
- /package/dist/{chunk-RYOUZ6EK.js.map → chunk-23VHVVKW.js.map} +0 -0
- /package/dist/{chunk-C5TL7T4V.js.map → chunk-2YM2VH6R.js.map} +0 -0
- /package/dist/{chunk-QPGEFLMY.js.map → chunk-7CMVL4X6.js.map} +0 -0
- /package/dist/{chunk-DUS5ZNKL.js.map → chunk-DIMDAYZS.js.map} +0 -0
- /package/dist/{chunk-DGRTJD5R.js.map → chunk-DXH4TQRB.js.map} +0 -0
- /package/dist/{chunk-YUNBOUQH.js.map → chunk-ERAG5IQB.js.map} +0 -0
- /package/dist/{chunk-B3TSS7BT.js.map → chunk-KDQRNVBZ.js.map} +0 -0
- /package/dist/{chunk-3R27MJ5R.js.map → chunk-PFWHHYEO.js.map} +0 -0
- /package/dist/{chunk-PPZ3K4YM.js.map → chunk-PRGPSNGA.js.map} +0 -0
- /package/dist/{chunk-L6HSKTQE.js.map → chunk-W7LZLJAF.js.map} +0 -0
- /package/dist/{config-KGZUPAUP.js.map → compat-YPDZB34U.js.map} +0 -0
- /package/dist/{contract-JH2WBIO6.js.map → config-56B4OHZE.js.map} +0 -0
- /package/dist/{diagnostics-HTCWF7F4.js.map → contract-HWLFFL3F.js.map} +0 -0
- /package/dist/{execute-QL3ZQIAP.js.map → diagnostics-UCTS2VUF.js.map} +0 -0
- /package/dist/{git-A2IP32PQ.js.map → execute-R7AVOLNG.js.map} +0 -0
- /package/dist/{init-PDVQDRS6.js.map → git-LCG45YXD.js.map} +0 -0
- /package/dist/{permissions-G4XCAESF.js.map → init-DD6DBRC5.js.map} +0 -0
- /package/dist/{plan-PRORGS3L.js.map → permissions-NNNSRC4P.js.map} +0 -0
- /package/dist/{pr-27RMQI5F.js.map → plan-XR6B7EM3.js.map} +0 -0
- /package/dist/{pr-review-VNESMAMT.js.map → pr-OSCNJCOO.js.map} +0 -0
- /package/dist/{prd-ZNC4T5BS.js.map → pr-review-IKIYHFBO.js.map} +0 -0
- /package/dist/{recorder-N7U66HCO.js.map → prd-HFDZJJQR.js.map} +0 -0
- /package/dist/{registry-XLHHYLTR.js.map → recorder-T7426CKV.js.map} +0 -0
- /package/dist/{review-7CREJDKB.js.map → registry-DWRKD65A.js.map} +0 -0
- /package/dist/{routing-VCDYHSXA.js.map → repository-TABBRW2C.js.map} +0 -0
- /package/dist/{run-3MPORLF5.js.map → review-JJHMQA2O.js.map} +0 -0
- /package/dist/{session-publisher-KAJQRHNZ.js.map → routing-FPNUVE7T.js.map} +0 -0
- /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":"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","names":[]}
|
|
@@ -2,22 +2,23 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getAgentCliOverrides,
|
|
4
4
|
hasExplicitAgentSelection,
|
|
5
|
-
loadAgentConfig
|
|
6
|
-
|
|
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-
|
|
13
|
+
} from "./chunk-J6VJ5M2U.js";
|
|
13
14
|
import {
|
|
14
15
|
AGENT_PHASES
|
|
15
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
315
|
-
const { getProjectRoot } = await import("./git-
|
|
316
|
-
const { loadVerifyConfig } = await import("./config-
|
|
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
|
-
|
|
352
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
163
|
+
//# sourceMappingURL=chunk-7CMVL4X6.js.map
|