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
|
@@ -1,43 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
printInfo
|
|
4
|
-
} from "./chunk-XZUEFTUP.js";
|
|
5
|
-
import {
|
|
6
|
-
ISSUES_DIR_NAME,
|
|
7
|
-
PROVIDERS_HEALTH_FILENAME,
|
|
8
|
-
RUN_LOCK_FILENAME,
|
|
9
|
-
getGlobalRoot,
|
|
10
|
-
getIssuePaths,
|
|
11
|
-
getProjectDir,
|
|
12
|
-
getQueuePaths,
|
|
13
|
-
projectIdFromRemote
|
|
14
|
-
} from "./chunk-HAKDLR6N.js";
|
|
15
|
-
import {
|
|
16
|
-
getProjectRoot,
|
|
17
|
-
getRemoteUrl,
|
|
18
|
-
normalizeRemoteUrl
|
|
19
|
-
} from "./chunk-YVPZUN6X.js";
|
|
20
|
-
import {
|
|
21
|
-
bindTelemetry
|
|
22
|
-
} from "./chunk-YII4U7ZD.js";
|
|
23
2
|
import {
|
|
24
3
|
isoNow
|
|
25
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GD7JZMSJ.js";
|
|
26
5
|
import {
|
|
27
6
|
STORAGE_SCHEMA_VERSION,
|
|
28
7
|
projectMetadataSchema
|
|
29
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-IMB6FA2F.js";
|
|
9
|
+
import {
|
|
10
|
+
ISSUES_DIR_NAME,
|
|
11
|
+
getProjectDir
|
|
12
|
+
} from "./chunk-MAJ6QDRL.js";
|
|
13
|
+
import {
|
|
14
|
+
getRemoteUrl,
|
|
15
|
+
normalizeRemoteUrl,
|
|
16
|
+
projectIdFromRemote
|
|
17
|
+
} from "./chunk-TJVX42KY.js";
|
|
30
18
|
import {
|
|
31
19
|
writeFileAtomic
|
|
32
|
-
} from "./chunk-
|
|
33
|
-
|
|
34
|
-
// src/storage/resolve.ts
|
|
35
|
-
import { stat as stat2 } from "fs/promises";
|
|
36
|
-
import { basename, join as join2, resolve as resolve2 } from "path";
|
|
20
|
+
} from "./chunk-PFWHHYEO.js";
|
|
37
21
|
|
|
38
22
|
// src/storage/compat.ts
|
|
39
|
-
import { copyFile, mkdir, readdir, readFile, stat } from "fs/promises";
|
|
40
|
-
import { join, resolve, sep } from "path";
|
|
23
|
+
import { copyFile, mkdir, readdir, readFile, stat } from "node:fs/promises";
|
|
24
|
+
import { join, resolve, sep } from "node:path";
|
|
41
25
|
var LEGACY_ISSUES_DIR_NAME = "issues";
|
|
42
26
|
var METADATA_FILENAME = "metadata.json";
|
|
43
27
|
async function directoryExists(path) {
|
|
@@ -192,92 +176,11 @@ async function recordUserStoryNumbering(projectRoot, decision, options = {}) {
|
|
|
192
176
|
`);
|
|
193
177
|
}
|
|
194
178
|
|
|
195
|
-
// src/storage/resolve.ts
|
|
196
|
-
var projectCache = /* @__PURE__ */ new Map();
|
|
197
|
-
var checkedIssues = /* @__PURE__ */ new Set();
|
|
198
|
-
function resetStorageResolutionCache() {
|
|
199
|
-
projectCache.clear();
|
|
200
|
-
checkedIssues.clear();
|
|
201
|
-
}
|
|
202
|
-
async function directoryExists2(path) {
|
|
203
|
-
try {
|
|
204
|
-
return (await stat2(path)).isDirectory();
|
|
205
|
-
} catch {
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
function announceMigration(result) {
|
|
210
|
-
if (result.copied.length === 0) return;
|
|
211
|
-
const count = result.copied.length;
|
|
212
|
-
const target = join2(result.globalDir, ISSUES_DIR_NAME);
|
|
213
|
-
printInfo(
|
|
214
|
-
`Migrated ${count} file${count === 1 ? "" : "s"} from ${result.legacyDir} to ${target}`
|
|
215
|
-
);
|
|
216
|
-
printInfo(
|
|
217
|
-
`The legacy directory was not modified or removed \u2014 ${result.legacyDir} is kept as-is, read-only, for compatibility.`
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
async function resolveProject(projectRoot, options) {
|
|
221
|
-
const status = await resolveStorageMode(projectRoot, options);
|
|
222
|
-
if (status.mode === "needs-migration") {
|
|
223
|
-
announceMigration(await migrateLegacyStorage(projectRoot, options));
|
|
224
|
-
}
|
|
225
|
-
return {
|
|
226
|
-
projectId: status.projectId,
|
|
227
|
-
legacyDir: status.legacyDir
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
function getProjectResolution(projectRoot, options) {
|
|
231
|
-
const key = `${getGlobalRoot(options)}::${projectRoot}`;
|
|
232
|
-
const cached = projectCache.get(key);
|
|
233
|
-
if (cached) return cached;
|
|
234
|
-
const pending = resolveProject(projectRoot, options).catch((err) => {
|
|
235
|
-
projectCache.delete(key);
|
|
236
|
-
throw err;
|
|
237
|
-
});
|
|
238
|
-
projectCache.set(key, pending);
|
|
239
|
-
return pending;
|
|
240
|
-
}
|
|
241
|
-
async function resolveProjectPaths(options = {}) {
|
|
242
|
-
const { projectRoot: projectRootOption, ...rootOptions } = options;
|
|
243
|
-
const projectRoot = resolve2(projectRootOption ?? await getProjectRoot());
|
|
244
|
-
const project = await getProjectResolution(projectRoot, rootOptions);
|
|
245
|
-
const projectDir = getProjectDir(project.projectId, rootOptions);
|
|
246
|
-
return {
|
|
247
|
-
projectId: project.projectId,
|
|
248
|
-
projectDir,
|
|
249
|
-
issuesDir: join2(projectDir, ISSUES_DIR_NAME),
|
|
250
|
-
runLockFile: join2(projectDir, RUN_LOCK_FILENAME),
|
|
251
|
-
providersHealthFile: join2(projectDir, PROVIDERS_HEALTH_FILENAME)
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
async function resolveQueuePaths(queueId, options = {}) {
|
|
255
|
-
const { projectRoot: projectRootOption, ...rootOptions } = options;
|
|
256
|
-
const projectRoot = resolve2(projectRootOption ?? await getProjectRoot());
|
|
257
|
-
const project = await getProjectResolution(projectRoot, rootOptions);
|
|
258
|
-
return getQueuePaths(project.projectId, queueId, rootOptions);
|
|
259
|
-
}
|
|
260
|
-
async function resolveIssuePaths(issueNumber, options = {}) {
|
|
261
|
-
const { projectRoot: projectRootOption, ...rootOptions } = options;
|
|
262
|
-
const projectRoot = resolve2(projectRootOption ?? await getProjectRoot());
|
|
263
|
-
const project = await getProjectResolution(projectRoot, rootOptions);
|
|
264
|
-
const paths = getIssuePaths(project.projectId, issueNumber, rootOptions);
|
|
265
|
-
if (!checkedIssues.has(paths.issueDir)) {
|
|
266
|
-
const legacyIssueDir = join2(project.legacyDir, basename(paths.issueDir));
|
|
267
|
-
if (!await directoryExists2(paths.issueDir) && await directoryExists2(legacyIssueDir)) {
|
|
268
|
-
announceMigration(await migrateLegacyStorage(projectRoot, rootOptions));
|
|
269
|
-
}
|
|
270
|
-
checkedIssues.add(paths.issueDir);
|
|
271
|
-
}
|
|
272
|
-
bindTelemetry({ tasksPath: paths.tasksFile });
|
|
273
|
-
return paths;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
179
|
export {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
180
|
+
LEGACY_ISSUES_DIR_NAME,
|
|
181
|
+
METADATA_FILENAME,
|
|
182
|
+
resolveStorageMode,
|
|
183
|
+
migrateLegacyStorage,
|
|
184
|
+
recordUserStoryNumbering
|
|
282
185
|
};
|
|
283
|
-
//# sourceMappingURL=chunk-
|
|
186
|
+
//# sourceMappingURL=chunk-LYC57OXF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/compat.ts"],"sourcesContent":["import type { Dirent } from 'node:fs';\nimport { copyFile, mkdir, readdir, readFile, stat } from 'node:fs/promises';\nimport { join, resolve, sep } from 'node:path';\nimport { isoNow } from '../core/state-manager.js';\nimport { writeFileAtomic } from '../utils/fs.js';\nimport { getRemoteUrl, normalizeRemoteUrl } from '../utils/git.js';\nimport {\n type GetGlobalRootOptions,\n getProjectDir,\n ISSUES_DIR_NAME,\n projectIdFromRemote,\n} from './paths.js';\nimport {\n type ProjectMetadata,\n projectMetadataSchema,\n STORAGE_SCHEMA_VERSION,\n type UserStoryNumberingDecision,\n} from './schemas.js';\n\n/**\n * Coexistence between the legacy per-project layout (`<projectRoot>/issues/`)\n * and the global storage tree (`~/.issue-flow/projects/<project-id>/`).\n *\n * The single rule of this module: **the legacy directory is read-only.** Nothing\n * here removes or rewrites a byte under `<projectRoot>/issues/` — not even\n * behind an opt-in flag — so adopting the global storage can never cost a user\n * their history. Migration is a copy, and a copy that refuses to overwrite.\n */\n\n/** Directory holding the legacy per-project artifacts. */\nexport const LEGACY_ISSUES_DIR_NAME = 'issues';\n\n/** File holding the per-project metadata inside the global storage. */\nexport const METADATA_FILENAME = 'metadata.json';\n\n/**\n * - `global`: the global directory is the source of truth (it already exists,\n * or neither layout does and a fresh project starts there).\n * - `needs-migration`: only the legacy directory exists, so its content has not\n * been copied over yet.\n */\nexport type StorageMode = 'global' | 'needs-migration';\n\nexport interface StorageStatus {\n mode: StorageMode;\n /** Deterministic id, derived from {@link projectIdFromRemote}. */\n projectId: string;\n /** `<globalRoot>/projects/<projectId>`. */\n globalDir: string;\n /** `<projectRoot>/issues`. */\n legacyDir: string;\n /** Whether `globalDir` currently exists on disk. */\n globalExists: boolean;\n /** Whether `legacyDir` currently exists on disk. */\n legacyExists: boolean;\n /**\n * Normalized `origin` remote of `projectRoot`, or `null` without one.\n * Resolved once here so `migrateLegacyStorage` can reuse it instead of\n * shelling out to git a second time for `metadata.json`.\n */\n remoteUrl: string | null;\n}\n\nexport interface MigrationResult {\n projectId: string;\n globalDir: string;\n legacyDir: string;\n /** Mode observed *before* the migration ran. */\n previousMode: StorageMode;\n /** Paths copied, relative to `legacyDir`. */\n copied: string[];\n /** Paths left alone because the destination already had a file. */\n skipped: string[];\n /** Absolute path of the metadata file written by this run. */\n metadataFile: string;\n /** Metadata as persisted. */\n metadata: ProjectMetadata;\n}\n\nexport interface MigrateLegacyStorageOptions extends GetGlobalRootOptions {\n /** Timestamp source, injectable so tests can assert createdAt/updatedAt. */\n now?: () => string;\n}\n\nasync function directoryExists(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Describe where a project's artifacts live, without touching anything.\n *\n * Read-only by construction: it stats the two candidate directories and reports\n * what it found. It never creates a directory, so a caller can ask the question\n * (a status command, a warning banner) without committing the user to the new\n * layout.\n *\n * An existing global directory always wins: once artifacts are there, that is\n * the source of truth, and a legacy directory left behind is simply preserved\n * rather than re-read or re-migrated.\n */\nexport async function resolveStorageMode(\n projectRoot: string,\n options: GetGlobalRootOptions = {},\n): Promise<StorageStatus> {\n const remoteUrl = normalizeRemoteUrl(await getRemoteUrl(projectRoot));\n const projectId = projectIdFromRemote(remoteUrl, projectRoot);\n const globalDir = getProjectDir(projectId, options);\n const legacyDir = join(resolve(projectRoot), LEGACY_ISSUES_DIR_NAME);\n\n const [globalExists, legacyExists] = await Promise.all([\n directoryExists(globalDir),\n directoryExists(legacyDir),\n ]);\n\n return {\n // Only one state calls for work: legacy content that has nowhere to be read\n // from yet. Everything else — including \"neither exists\" — is already the\n // global layout.\n mode: legacyExists && !globalExists ? 'needs-migration' : 'global',\n projectId,\n globalDir,\n legacyDir,\n globalExists,\n legacyExists,\n remoteUrl,\n };\n}\n\n/**\n * Wrap an IO failure with the issue and file it happened on, so a partial\n * migration is diagnosable instead of a bare `EACCES`.\n */\nfunction migrationFailure(issue: string, relativePath: string, err: unknown): Error {\n const reason = err instanceof Error ? err.message : String(err);\n\n return new Error(\n `Failed to migrate '${relativePath}' (issue '${issue}'): ${reason}. ` +\n 'The legacy directory was left untouched — fix the cause and run the migration again.',\n );\n}\n\n/** First path segment of a relative path — the issue a file belongs to. */\nfunction issueOf(relativePath: string): string {\n return relativePath.split(sep)[0] || '.';\n}\n\n/**\n * Copy one directory level, recursing into subdirectories.\n *\n * Symlinks and other non-regular entries are skipped on purpose: following them\n * could copy content from outside the legacy directory into the global tree.\n */\nasync function copyTree(\n sourceRoot: string,\n targetRoot: string,\n relativeDir: string,\n result: { copied: string[]; skipped: string[] },\n): Promise<void> {\n const source = join(sourceRoot, relativeDir);\n const target = join(targetRoot, relativeDir);\n\n let entries: Dirent[];\n try {\n entries = await readdir(source, { withFileTypes: true });\n } catch (err) {\n throw migrationFailure(issueOf(relativeDir), relativeDir || '.', err);\n }\n\n try {\n // recursive: true also makes re-running a no-op on an existing directory.\n await mkdir(target, { recursive: true });\n } catch (err) {\n throw migrationFailure(issueOf(relativeDir), relativeDir || '.', err);\n }\n\n for (const entry of entries) {\n const relativePath = join(relativeDir, entry.name);\n\n if (entry.isDirectory()) {\n await copyTree(sourceRoot, targetRoot, relativePath, result);\n continue;\n }\n if (!entry.isFile()) {\n continue;\n }\n\n const to = join(targetRoot, relativePath);\n try {\n // Idempotency and safety in one check: a destination that already exists\n // is never overwritten, so a second run copies nothing and a hand-edited\n // artifact in the global tree survives.\n if (await pathExists(to)) {\n result.skipped.push(relativePath);\n continue;\n }\n await copyFile(join(sourceRoot, relativePath), to);\n result.copied.push(relativePath);\n } catch (err) {\n throw migrationFailure(issueOf(relativePath), relativePath, err);\n }\n }\n}\n\n/**\n * Read `metadata.json`, tolerating every failure.\n *\n * A missing, corrupt or schema-invalid file must not block a migration: the\n * caller simply writes a fresh one. The only thing worth recovering is\n * `createdAt`/`lastAttemptAt` from a valid previous file.\n */\nasync function readExistingMetadata(file: string): Promise<ProjectMetadata | null> {\n let raw: string;\n try {\n raw = await readFile(file, 'utf-8');\n } catch {\n return null;\n }\n\n try {\n const parsed = projectMetadataSchema.safeParse(JSON.parse(raw));\n return parsed.success ? parsed.data : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Copy `<projectRoot>/issues/` into the project's global directory and stamp\n * its `metadata.json`.\n *\n * Non-destructive and idempotent, by design rather than by convention:\n *\n * - the source is only ever read — there is no removal option in this release;\n * - a destination file that already exists is skipped, never overwritten, so a\n * second run reports everything as skipped and changes nothing but\n * `updatedAt`;\n * - a failure mid-way leaves the legacy directory intact and names the issue\n * and file that failed, so the run can be repeated after the fix and will\n * resume by skipping what already made it across.\n *\n * Called on a project with no legacy directory it is still useful: it creates\n * the project directory and its metadata.\n */\nexport async function migrateLegacyStorage(\n projectRoot: string,\n options: MigrateLegacyStorageOptions = {},\n): Promise<MigrationResult> {\n const status = await resolveStorageMode(projectRoot, { env: options.env });\n const result = { copied: [] as string[], skipped: [] as string[] };\n\n try {\n await mkdir(status.globalDir, { recursive: true });\n } catch (err) {\n throw migrationFailure('.', status.globalDir, err);\n }\n\n if (status.legacyExists) {\n await copyTree(status.legacyDir, join(status.globalDir, ISSUES_DIR_NAME), '', result);\n }\n\n const now = (options.now ?? isoNow)();\n const metadataFile = join(status.globalDir, METADATA_FILENAME);\n const existing = await readExistingMetadata(metadataFile);\n\n const metadata: ProjectMetadata = {\n schemaVersion: STORAGE_SCHEMA_VERSION,\n projectId: status.projectId,\n root: resolve(projectRoot),\n remoteUrl: status.remoteUrl,\n // The project was first seen whenever it was first written, not now.\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n lastAttemptAt: existing?.lastAttemptAt ?? null,\n };\n\n await writeFileAtomic(metadataFile, `${JSON.stringify(metadata, null, 2)}\\n`);\n\n return {\n projectId: status.projectId,\n globalDir: status.globalDir,\n legacyDir: status.legacyDir,\n previousMode: status.mode,\n ...result,\n metadataFile,\n metadata,\n };\n}\n\nexport interface RecordUserStoryNumberingOptions extends GetGlobalRootOptions {\n /** Timestamp source, injectable so tests can assert `updatedAt`. */\n now?: () => string;\n}\n\n/**\n * Persist a `plan` run's User Story numbering decision into the project's\n * `metadata.json`, for audit (issue #36) — this is a read-modify-write on the\n * same file {@link migrateLegacyStorage} stamps, never a second file.\n *\n * The decision itself is never read back to resolve a *later* one: the next\n * `plan` run always re-scans `tasks.json` from scratch (see\n * `storage/user-story-numbering.ts`). Losing this field only costs an audit\n * trail entry, never correctness.\n *\n * A missing `metadata.json` (a project whose first command ever is a numbered\n * `plan`, with no legacy directory to trigger a migration) is created here\n * from scratch, exactly like {@link migrateLegacyStorage} would.\n */\nexport async function recordUserStoryNumbering(\n projectRoot: string,\n decision: UserStoryNumberingDecision,\n options: RecordUserStoryNumberingOptions = {},\n): Promise<void> {\n const status = await resolveStorageMode(projectRoot, { env: options.env });\n const metadataFile = join(status.globalDir, METADATA_FILENAME);\n const existing = await readExistingMetadata(metadataFile);\n const now = (options.now ?? isoNow)();\n\n const metadata: ProjectMetadata = {\n schemaVersion: STORAGE_SCHEMA_VERSION,\n projectId: status.projectId,\n root: resolve(projectRoot),\n remoteUrl: status.remoteUrl,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n lastAttemptAt: existing?.lastAttemptAt ?? null,\n userStoryNumbering: decision,\n };\n\n await mkdir(status.globalDir, { recursive: true });\n await writeFileAtomic(metadataFile, `${JSON.stringify(metadata, null, 2)}\\n`);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,23 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/storage/artifact-paths.ts
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
var ISSUES_DIR_NAME = "issues";
|
|
6
|
+
var SESSION_FILENAME = "session.json";
|
|
7
|
+
var EVENTS_FILENAME = "events.jsonl";
|
|
8
|
+
var ROTATED_EVENTS_FILENAME = "events.1.jsonl";
|
|
9
|
+
var PRD_FILENAME = "prd.md";
|
|
10
|
+
var TASKS_FILENAME = "tasks.json";
|
|
11
|
+
var VERIFY_FILENAME = "verify.json";
|
|
12
|
+
var RUN_LOG_FILENAME = "run.log";
|
|
13
|
+
var ROTATED_RUN_LOG_FILENAME = "run.log.1";
|
|
14
|
+
function normalizeIssueIdentifier(issueNumber) {
|
|
15
|
+
const normalized = String(issueNumber).trim().replace(/^#/, "");
|
|
16
|
+
if (normalized.length === 0) throw new Error("Issue identifier cannot be empty");
|
|
17
|
+
if (/[/\\]/.test(normalized) || normalized === "." || normalized === "..") {
|
|
18
|
+
throw new Error(`Invalid issue identifier: '${issueNumber}'`);
|
|
19
|
+
}
|
|
20
|
+
return normalized;
|
|
21
|
+
}
|
|
22
|
+
function resolveIssueArtifactPaths(projectDir, issueNumber) {
|
|
23
|
+
const issueDir = join(projectDir, ISSUES_DIR_NAME, normalizeIssueIdentifier(issueNumber));
|
|
24
|
+
return {
|
|
25
|
+
issueDir,
|
|
26
|
+
issueFile: join(issueDir, "issue.md"),
|
|
27
|
+
metadataFile: join(issueDir, "metadata.json"),
|
|
28
|
+
prdFile: join(issueDir, PRD_FILENAME),
|
|
29
|
+
tasksFile: join(issueDir, TASKS_FILENAME),
|
|
30
|
+
progressFile: join(issueDir, "progress.txt"),
|
|
31
|
+
analysisFile: join(issueDir, "analysis.md"),
|
|
32
|
+
sessionFile: join(issueDir, SESSION_FILENAME),
|
|
33
|
+
eventsFile: join(issueDir, EVENTS_FILENAME),
|
|
34
|
+
rotatedEventsFile: join(issueDir, ROTATED_EVENTS_FILENAME),
|
|
35
|
+
runLogFile: join(issueDir, RUN_LOG_FILENAME),
|
|
36
|
+
rotatedRunLogFile: join(issueDir, ROTATED_RUN_LOG_FILENAME),
|
|
37
|
+
decompositionFile: join(issueDir, "decomposition.md"),
|
|
38
|
+
verifyFile: join(issueDir, VERIFY_FILENAME),
|
|
39
|
+
lastBranchFile: join(issueDir, ".last-branch"),
|
|
40
|
+
archiveDir: join(issueDir, "archive"),
|
|
41
|
+
prReviewDir: join(issueDir, "pr-review")
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
3
45
|
// src/storage/paths.ts
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { basename, join, resolve } from "path";
|
|
46
|
+
import { homedir } from "node:os";
|
|
47
|
+
import { join as join2, resolve } from "node:path";
|
|
7
48
|
var GLOBAL_DIR_NAME = ".issue-flow";
|
|
8
49
|
var GLOBAL_ROOT_ENV = "ISSUE_FLOW_HOME";
|
|
9
50
|
var PROJECTS_DIR_NAME = "projects";
|
|
10
51
|
var LOGS_DIR_NAME = "logs";
|
|
11
|
-
var ISSUES_DIR_NAME = "issues";
|
|
12
52
|
var QUEUES_DIR_NAME = "queues";
|
|
13
53
|
var RUN_LOCK_FILENAME = "run.lock";
|
|
14
54
|
var PROVIDERS_HEALTH_FILENAME = "providers.json";
|
|
15
|
-
var SESSION_FILENAME = "session.json";
|
|
16
|
-
var EVENTS_FILENAME = "events.jsonl";
|
|
17
|
-
var ROTATED_EVENTS_FILENAME = "events.1.jsonl";
|
|
18
|
-
var VERIFY_FILENAME = "verify.json";
|
|
19
|
-
var RUN_LOG_FILENAME = "run.log";
|
|
20
|
-
var ROTATED_RUN_LOG_FILENAME = "run.log.1";
|
|
21
55
|
function getGlobalRoot(options = {}) {
|
|
22
56
|
const env = options.env ?? process.env;
|
|
23
57
|
const override = env[GLOBAL_ROOT_ENV]?.trim();
|
|
@@ -35,42 +69,17 @@ function getGlobalRoot(options = {}) {
|
|
|
35
69
|
`Unable to resolve the home directory for the global storage. Set ${GLOBAL_ROOT_ENV} to an explicit path.`
|
|
36
70
|
);
|
|
37
71
|
}
|
|
38
|
-
return
|
|
72
|
+
return join2(home, GLOBAL_DIR_NAME);
|
|
39
73
|
}
|
|
40
74
|
function getDiagnosticsDir(options = {}) {
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
var SLUG_MAX_LENGTH = 32;
|
|
44
|
-
var HASH_LENGTH = 12;
|
|
45
|
-
var FALLBACK_SLUG = "project";
|
|
46
|
-
function slugify(name) {
|
|
47
|
-
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, SLUG_MAX_LENGTH).replace(/-+$/g, "");
|
|
48
|
-
return slug === "" ? FALLBACK_SLUG : slug;
|
|
49
|
-
}
|
|
50
|
-
function projectIdFromRemote(remote, projectRoot) {
|
|
51
|
-
let seed;
|
|
52
|
-
let name;
|
|
53
|
-
if (remote) {
|
|
54
|
-
seed = `remote:${remote}`;
|
|
55
|
-
name = remote.split("/").pop() ?? "";
|
|
56
|
-
} else {
|
|
57
|
-
const absolute = resolve(projectRoot);
|
|
58
|
-
seed = `path:${absolute}`;
|
|
59
|
-
name = basename(absolute);
|
|
60
|
-
}
|
|
61
|
-
const hash = createHash("sha256").update(seed).digest("hex").slice(0, HASH_LENGTH);
|
|
62
|
-
return `${slugify(name)}-${hash}`;
|
|
75
|
+
return join2(getGlobalRoot(options), LOGS_DIR_NAME);
|
|
63
76
|
}
|
|
64
77
|
function getProjectDir(projectId, options = {}) {
|
|
65
|
-
return
|
|
78
|
+
return join2(getGlobalRoot(options), PROJECTS_DIR_NAME, projectId);
|
|
66
79
|
}
|
|
67
|
-
function
|
|
68
|
-
const queueDir =
|
|
69
|
-
|
|
70
|
-
QUEUES_DIR_NAME,
|
|
71
|
-
normalizeIssueNumber(queueId)
|
|
72
|
-
);
|
|
73
|
-
return { queueDir, planFile: join(queueDir, "execution-plan.json") };
|
|
80
|
+
function getQueuePathsAt(projectDir, queueId) {
|
|
81
|
+
const queueDir = join2(projectDir, QUEUES_DIR_NAME, normalizeIssueNumber(queueId));
|
|
82
|
+
return { queueDir, planFile: join2(queueDir, "execution-plan.json") };
|
|
74
83
|
}
|
|
75
84
|
function normalizeIssueNumber(issueNumber) {
|
|
76
85
|
const normalized = String(issueNumber).trim().replace(/^#/, "");
|
|
@@ -83,52 +92,30 @@ function normalizeIssueNumber(issueNumber) {
|
|
|
83
92
|
return normalized;
|
|
84
93
|
}
|
|
85
94
|
function getIssuePaths(projectId, issueNumber, options = {}) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
);
|
|
91
|
-
return {
|
|
92
|
-
issueDir,
|
|
93
|
-
issueFile: join(issueDir, "issue.md"),
|
|
94
|
-
metadataFile: join(issueDir, "metadata.json"),
|
|
95
|
-
prdFile: join(issueDir, "prd.md"),
|
|
96
|
-
tasksFile: join(issueDir, "tasks.json"),
|
|
97
|
-
progressFile: join(issueDir, "progress.txt"),
|
|
98
|
-
analysisFile: join(issueDir, "analysis.md"),
|
|
99
|
-
sessionFile: join(issueDir, SESSION_FILENAME),
|
|
100
|
-
// The journal, and the single generation kept when it rotates. The
|
|
101
|
-
// snapshot above is the projection; this pair is the history.
|
|
102
|
-
eventsFile: join(issueDir, EVENTS_FILENAME),
|
|
103
|
-
rotatedEventsFile: join(issueDir, ROTATED_EVENTS_FILENAME),
|
|
104
|
-
runLogFile: join(issueDir, RUN_LOG_FILENAME),
|
|
105
|
-
rotatedRunLogFile: join(issueDir, ROTATED_RUN_LOG_FILENAME),
|
|
106
|
-
// "This issue looks larger than one run": written only when the signals
|
|
107
|
-
// agree, and read by a person rather than by the pipeline.
|
|
108
|
-
decompositionFile: join(issueDir, "decomposition.md"),
|
|
109
|
-
verifyFile: join(issueDir, VERIFY_FILENAME),
|
|
110
|
-
lastBranchFile: join(issueDir, ".last-branch"),
|
|
111
|
-
archiveDir: join(issueDir, "archive"),
|
|
112
|
-
prReviewDir: join(issueDir, "pr-review")
|
|
113
|
-
};
|
|
95
|
+
return getIssuePathsAt(getProjectDir(projectId, options), issueNumber);
|
|
96
|
+
}
|
|
97
|
+
function getIssuePathsAt(projectDir, issueNumber) {
|
|
98
|
+
return resolveIssueArtifactPaths(projectDir, issueNumber);
|
|
114
99
|
}
|
|
115
100
|
|
|
116
101
|
export {
|
|
117
|
-
GLOBAL_ROOT_ENV,
|
|
118
|
-
PROJECTS_DIR_NAME,
|
|
119
102
|
ISSUES_DIR_NAME,
|
|
120
|
-
QUEUES_DIR_NAME,
|
|
121
|
-
RUN_LOCK_FILENAME,
|
|
122
|
-
PROVIDERS_HEALTH_FILENAME,
|
|
123
103
|
SESSION_FILENAME,
|
|
124
104
|
EVENTS_FILENAME,
|
|
125
105
|
ROTATED_EVENTS_FILENAME,
|
|
106
|
+
PRD_FILENAME,
|
|
107
|
+
TASKS_FILENAME,
|
|
126
108
|
VERIFY_FILENAME,
|
|
109
|
+
GLOBAL_ROOT_ENV,
|
|
110
|
+
PROJECTS_DIR_NAME,
|
|
111
|
+
QUEUES_DIR_NAME,
|
|
112
|
+
RUN_LOCK_FILENAME,
|
|
113
|
+
PROVIDERS_HEALTH_FILENAME,
|
|
127
114
|
getGlobalRoot,
|
|
128
115
|
getDiagnosticsDir,
|
|
129
|
-
projectIdFromRemote,
|
|
130
116
|
getProjectDir,
|
|
131
|
-
|
|
132
|
-
getIssuePaths
|
|
117
|
+
getQueuePathsAt,
|
|
118
|
+
getIssuePaths,
|
|
119
|
+
getIssuePathsAt
|
|
133
120
|
};
|
|
134
|
-
//# sourceMappingURL=chunk-
|
|
121
|
+
//# sourceMappingURL=chunk-MAJ6QDRL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/artifact-paths.ts","../src/storage/paths.ts"],"sourcesContent":["import { join } from 'node:path';\n\nexport const ISSUES_DIR_NAME = 'issues';\nexport const SESSION_FILENAME = 'session.json';\nexport const EVENTS_FILENAME = 'events.jsonl';\nexport const ROTATED_EVENTS_FILENAME = 'events.1.jsonl';\nexport const PRD_FILENAME = 'prd.md';\nexport const TASKS_FILENAME = 'tasks.json';\nexport const VERIFY_FILENAME = 'verify.json';\nexport const RUN_LOG_FILENAME = 'run.log';\nexport const ROTATED_RUN_LOG_FILENAME = 'run.log.1';\n\nexport interface IssuePaths {\n issueDir: string;\n issueFile: string;\n metadataFile: string;\n prdFile: string;\n tasksFile: string;\n progressFile: string;\n analysisFile: string;\n sessionFile: string;\n eventsFile: string;\n rotatedEventsFile: string;\n runLogFile: string;\n rotatedRunLogFile: string;\n decompositionFile: string;\n verifyFile: string;\n lastBranchFile: string;\n archiveDir: string;\n prReviewDir: string;\n}\n\n/** Validate an identifier before it becomes a path segment. */\nexport function normalizeIssueIdentifier(issueNumber: string | number): string {\n const normalized = String(issueNumber).trim().replace(/^#/, '');\n if (normalized.length === 0) throw new Error('Issue identifier cannot be empty');\n if (/[/\\\\]/.test(normalized) || normalized === '.' || normalized === '..') {\n throw new Error(`Invalid issue identifier: '${issueNumber}'`);\n }\n return normalized;\n}\n\n/** The single canonical layout shared by CLI and portable Skill helpers. */\nexport function resolveIssueArtifactPaths(\n projectDir: string,\n issueNumber: string | number,\n): IssuePaths {\n const issueDir = join(projectDir, ISSUES_DIR_NAME, normalizeIssueIdentifier(issueNumber));\n return {\n issueDir,\n issueFile: join(issueDir, 'issue.md'),\n metadataFile: join(issueDir, 'metadata.json'),\n prdFile: join(issueDir, PRD_FILENAME),\n tasksFile: join(issueDir, TASKS_FILENAME),\n progressFile: join(issueDir, 'progress.txt'),\n analysisFile: join(issueDir, 'analysis.md'),\n sessionFile: join(issueDir, SESSION_FILENAME),\n eventsFile: join(issueDir, EVENTS_FILENAME),\n rotatedEventsFile: join(issueDir, ROTATED_EVENTS_FILENAME),\n runLogFile: join(issueDir, RUN_LOG_FILENAME),\n rotatedRunLogFile: join(issueDir, ROTATED_RUN_LOG_FILENAME),\n decompositionFile: join(issueDir, 'decomposition.md'),\n verifyFile: join(issueDir, VERIFY_FILENAME),\n lastBranchFile: join(issueDir, '.last-branch'),\n archiveDir: join(issueDir, 'archive'),\n prReviewDir: join(issueDir, 'pr-review'),\n };\n}\n","import { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { getRemoteUrl, normalizeRemoteUrl } from '../utils/git.js';\nimport { ISSUES_DIR_NAME, type IssuePaths, resolveIssueArtifactPaths } from './artifact-paths.js';\nimport { projectIdFromRemote } from './project-identity.js';\n\nexport {\n EVENTS_FILENAME,\n ISSUES_DIR_NAME,\n type IssuePaths,\n PRD_FILENAME,\n ROTATED_EVENTS_FILENAME,\n ROTATED_RUN_LOG_FILENAME,\n RUN_LOG_FILENAME,\n SESSION_FILENAME,\n TASKS_FILENAME,\n VERIFY_FILENAME,\n} from './artifact-paths.js';\nexport { projectIdFromRemote } from './project-identity.js';\n\n/** Directory created under the user's home when no override is provided. */\nexport const GLOBAL_DIR_NAME = '.issue-flow';\n\n/** Environment variable that relocates the whole global storage tree. */\nexport const GLOBAL_ROOT_ENV = 'ISSUE_FLOW_HOME';\n\n/** Directory under the global root holding one folder per project. */\nexport const PROJECTS_DIR_NAME = 'projects';\n\n/** Machine-wide structured diagnostic logs, independent of any project. */\nexport const LOGS_DIR_NAME = 'logs';\n\n/** Directory under a project holding one folder per issue. */\n/** Directory under a project holding one folder per multi-issue execution queue. */\nexport const QUEUES_DIR_NAME = 'queues';\n\n/** Run-ownership lock, a sibling of `issues/` inside the project directory. */\nexport const RUN_LOCK_FILENAME = 'run.lock';\n\n/** Persisted agent-provider health, shared by every run of one project. */\nexport const PROVIDERS_HEALTH_FILENAME = 'providers.json';\n\n/** Projection and journal filenames shared by storage and the web reader. */\n/**\n * Acceptance-contract evidence. Named here — and not at the call site — because\n * `verify/run-issue.ts` also writes it in standalone mode, where there is no\n * `IssuePaths` to ask: one constant, two callers, no second spelling.\n */\n\nexport interface GetGlobalRootOptions {\n /** Environment source. Defaults to process.env. */\n env?: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve the root directory of the global storage tree (`~/.issue-flow`).\n *\n * Every path under the global storage MUST be derived from this function: it is\n * the single seam where `ISSUE_FLOW_HOME` takes effect, which is what lets\n * tests, CI and sandboxes point the whole tree at a temporary directory instead\n * of touching the real `$HOME`. A call site that joins `homedir()` by hand\n * silently opts out of that isolation.\n *\n * Pure and synchronous: it never creates directories nor touches the\n * filesystem — callers decide when (and whether) the directory should exist.\n */\nexport function getGlobalRoot(options: GetGlobalRootOptions = {}): string {\n const env = options.env ?? process.env;\n\n const override = env[GLOBAL_ROOT_ENV]?.trim();\n if (override) {\n // A relative override is resolved against the CWD so that every consumer\n // gets an absolute path, exactly as in the homedir() branch below.\n return resolve(override);\n }\n\n // The only failure mode: an environment where the home directory cannot be\n // determined (some containers and CI images). The override is the escape\n // hatch, so the error names it.\n let home: string;\n try {\n home = homedir();\n } catch {\n home = '';\n }\n if (home.trim() === '') {\n throw new Error(\n `Unable to resolve the home directory for the global storage. Set ${GLOBAL_ROOT_ENV} to an explicit path.`,\n );\n }\n\n return join(home, GLOBAL_DIR_NAME);\n}\n\nexport function getDiagnosticsDir(options: GetGlobalRootOptions = {}): string {\n return join(getGlobalRoot(options), LOGS_DIR_NAME);\n}\n\n/**\n * Derive a stable identifier for a project, in the form `<slug>-<hash12>`.\n *\n * The seed is canonical rather than local, so the same repository yields the\n * same id on any machine: `remote:<normalizedRemote>` whenever an `origin`\n * remote exists, falling back to `path:<absoluteProjectRoot>` when it does not.\n * That is what makes moving or renaming the local folder harmless for a cloned\n * repository — the history stays attached to the remote identity.\n *\n * The `remote:` / `path:` prefix is part of the hashed seed, so a project\n * identified by path can never collide with one identified by remote even if\n * the two strings were to coincide. The slug is only cosmetic (it makes the\n * directory recognizable); the hash carries the identity.\n *\n * Known limitation of the path fallback: for a repository with no `origin`\n * remote, the absolute path *is* the identity. Moving or renaming that folder\n * produces a different id, and the previous history is left behind under the\n * old one. Configuring a remote before adopting the global storage avoids it.\n */\nexport async function getProjectId(projectRoot: string): Promise<string> {\n const remote = normalizeRemoteUrl(await getRemoteUrl(projectRoot));\n return projectIdFromRemote(remote, projectRoot);\n}\n\n/**\n * Pure half of {@link getProjectId}: derive the id once the normalized remote\n * (or `null`) is already known.\n *\n * Exported so a caller that already resolved the remote for another reason\n * (e.g. {@link resolveStorageMode} in `compat.ts`, which also persists it into\n * `metadata.json`) can compute the id without shelling out to git a second\n * time.\n */\n/**\n * Absolute directory of a project inside the global storage tree.\n *\n * `projectId` is expected to come from {@link getProjectId}, which already\n * guarantees a single safe path segment.\n */\nexport function getProjectDir(projectId: string, options: GetGlobalRootOptions = {}): string {\n return join(getGlobalRoot(options), PROJECTS_DIR_NAME, projectId);\n}\n\n/**\n * Every artifact of one issue, resolved from a single place.\n *\n * Note that `prdFile` is the requirements document (`prd.md`) and `tasksFile`\n * is the task plan (`tasks.json`) — unlike `ResolvedPaths.prdFile` in\n * `types.ts`, which historically points at `tasks.json`.\n */\n/**\n * Artifacts of one multi-issue execution queue.\n *\n * A queue coordinates several issues that share a branch; each issue keeps its\n * own directory under {@link ISSUES_DIR_NAME} exactly as before, and only the\n * coordination state (order, per-issue status, shared branch, consolidated Pull\n * Request) lives here. That is what keeps a single-issue run byte-identical: it\n * never creates a queue at all.\n */\nexport interface QueuePaths {\n queueDir: string;\n planFile: string;\n}\n\n/**\n * Resolve the paths of one execution queue under the global storage.\n *\n * The queue id is the identifier of the primary issue — the first one the user\n * asked for — so re-running the same command finds the same queue and resumes\n * it instead of starting a parallel one.\n *\n * Pure and synchronous like every other helper here: nothing is created.\n */\nexport function getQueuePaths(\n projectId: string,\n queueId: string | number,\n options: GetGlobalRootOptions = {},\n): QueuePaths {\n return getQueuePathsAt(getProjectDir(projectId, options), queueId);\n}\n\n/** Resolve one queue below an already selected project storage directory. */\nexport function getQueuePathsAt(projectDir: string, queueId: string | number): QueuePaths {\n const queueDir = join(projectDir, QUEUES_DIR_NAME, normalizeIssueNumber(queueId));\n\n return { queueDir, planFile: join(queueDir, 'execution-plan.json') };\n}\n\n/**\n * Issue identifiers become path segments, so anything that could escape the\n * issues directory is rejected before it reaches `join` — the same discipline\n * as `normalizeId()` in `issues/providers/local.ts`.\n *\n * Non-numeric identifiers (`auth-refactor`, `pr-184`) are first-class here:\n * the local provider already supports them, so the global storage must too.\n */\nfunction normalizeIssueNumber(issueNumber: string | number): string {\n const normalized = String(issueNumber).trim().replace(/^#/, '');\n\n if (normalized.length === 0) {\n throw new Error('Issue identifier cannot be empty');\n }\n if (/[/\\\\]/.test(normalized) || normalized === '.' || normalized === '..') {\n throw new Error(`Invalid issue identifier: '${issueNumber}'`);\n }\n\n return normalized;\n}\n\n/**\n * Resolve every path of a single issue under the global storage.\n *\n * This is the only place allowed to know the layout of an issue directory:\n * call sites ask for the artifact they need instead of joining names by hand,\n * so renaming or adding an artifact stays a one-file change.\n *\n * Pure and synchronous: nothing here creates directories or touches the\n * filesystem.\n */\nexport function getIssuePaths(\n projectId: string,\n issueNumber: string | number,\n options: GetGlobalRootOptions = {},\n): IssuePaths {\n return getIssuePathsAt(getProjectDir(projectId, options), issueNumber);\n}\n\n/** Resolve one issue below an already selected project storage directory. */\nexport function getIssuePathsAt(projectDir: string, issueNumber: string | number): IssuePaths {\n return resolveIssueArtifactPaths(projectDir, issueNumber);\n}\n"],"mappings":"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","names":["join","join","join","join"]}
|