issue-flow 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-TB6W7PT7.js +30 -0
- package/dist/{analyze-O2PYDIFK.js → analyze-6M3KEOX7.js} +22 -22
- package/dist/{apply-SSU6QCD4.js → apply-ADB3ZK7W.js} +14 -14
- package/dist/{bench-ODFPAGW6.js → bench-FM7I7EIV.js} +23 -23
- package/dist/{chunk-BMHGRYL6.js → chunk-2SUFAE6X.js} +67 -24
- package/dist/chunk-2SUFAE6X.js.map +1 -0
- package/dist/{chunk-QRCGW2ZO.js → chunk-32JSYXEK.js} +8 -8
- package/dist/chunk-3R27MJ5R.js +25 -0
- package/dist/chunk-3R27MJ5R.js.map +1 -0
- package/dist/{chunk-MGCOQU6N.js → chunk-6YQT3SSM.js} +4 -4
- package/dist/chunk-6YQT3SSM.js.map +1 -0
- package/dist/{chunk-A35HE4V7.js → chunk-7F7LGMVO.js} +3 -3
- package/dist/{chunk-RPXDBX6R.js → chunk-A4H7P7T7.js} +102 -102
- package/dist/chunk-A4H7P7T7.js.map +1 -0
- package/dist/{chunk-2ZAM45PZ.js → chunk-ANI2NTR4.js} +9 -9
- package/dist/{chunk-ZPIVCM7Q.js → chunk-B3TSS7BT.js} +3 -3
- package/dist/chunk-C5TL7T4V.js +27 -0
- package/dist/chunk-C5TL7T4V.js.map +1 -0
- package/dist/chunk-D6ZBGJ3L.js +321 -0
- package/dist/chunk-D6ZBGJ3L.js.map +1 -0
- package/dist/{chunk-ZXPASFHG.js → chunk-DGRTJD5R.js} +3 -3
- package/dist/{chunk-6QYMSNVA.js → chunk-DUS5ZNKL.js} +476 -362
- package/dist/chunk-DUS5ZNKL.js.map +1 -0
- package/dist/{chunk-VR34TXHF.js → chunk-DV7HUC4Z.js} +7 -7
- package/dist/{chunk-6SRE4KD7.js → chunk-E6KXCV3T.js} +4 -4
- package/dist/{chunk-HGSYD57B.js → chunk-HJBF4WD2.js} +1656 -1070
- package/dist/chunk-HJBF4WD2.js.map +1 -0
- package/dist/{chunk-PCN7ZQND.js → chunk-HLAPOUEM.js} +8 -8
- package/dist/{chunk-WM3Y7Q4A.js → chunk-HY5HLFDU.js} +630 -571
- package/dist/chunk-HY5HLFDU.js.map +1 -0
- package/dist/{chunk-XDKGA6UX.js → chunk-ICM463EO.js} +64 -14
- package/dist/chunk-ICM463EO.js.map +1 -0
- package/dist/{chunk-MEFY4B7T.js → chunk-ISZ54SVE.js} +9 -9
- package/dist/{chunk-ARWNYXFL.js → chunk-KZ5EY5XX.js} +12 -12
- package/dist/{chunk-ZFIXEYUJ.js → chunk-KZXQWC7U.js} +32 -20
- package/dist/chunk-KZXQWC7U.js.map +1 -0
- package/dist/{chunk-KAHQ6F5C.js → chunk-LPWPFIIB.js} +11 -11
- package/dist/{chunk-FK4IDY6P.js → chunk-MI5TREN3.js} +9 -9
- package/dist/{chunk-UNQYMR3H.js → chunk-PPZ3K4YM.js} +2 -2
- package/dist/{chunk-F4OK7KEP.js → chunk-QMIQR7EE.js} +10 -10
- package/dist/{chunk-AYYMW33T.js → chunk-QPGEFLMY.js} +2 -2
- package/dist/chunk-RCZJQUTC.js +196 -0
- package/dist/chunk-RCZJQUTC.js.map +1 -0
- package/dist/{chunk-MROS3SKL.js → chunk-RNFUVKC3.js} +3 -3
- package/dist/{chunk-RWEXK55D.js → chunk-RYOUZ6EK.js} +2 -2
- package/dist/{chunk-6E2PEBFT.js → chunk-SGTTR4XW.js} +10 -10
- package/dist/{chunk-XQHFC62U.js → chunk-SOM4QA6C.js} +3 -3
- package/dist/{chunk-5ZKBIXET.js → chunk-UK4E2FUA.js} +132 -34
- package/dist/chunk-UK4E2FUA.js.map +1 -0
- package/dist/{chunk-6FXOU2KR.js → chunk-UWDCIFTP.js} +7 -7
- package/dist/{chunk-XG2NYDRA.js → chunk-VE3IYNDN.js} +3 -3
- package/dist/{chunk-3BWZO4YI.js → chunk-WCCZJL7W.js} +2 -2
- package/dist/{chunk-NVZMBXTE.js → chunk-XZUEFTUP.js} +6 -6
- package/dist/{chunk-TBDP6QPJ.js → chunk-YII4U7ZD.js} +10 -10
- package/dist/{chunk-TBDP6QPJ.js.map → chunk-YII4U7ZD.js.map} +1 -1
- package/dist/{chunk-4RXYFWP7.js → chunk-YUNBOUQH.js} +6 -6
- package/dist/{chunk-GSZCQUCY.js → chunk-YVPZUN6X.js} +2 -2
- package/dist/{chunk-XRLRKTQA.js → chunk-Z5O2XBWA.js} +14 -14
- package/dist/cli.js +61 -52
- package/dist/cli.js.map +1 -1
- package/dist/{config-UMP6DBJB.js → config-KGZUPAUP.js} +13 -13
- package/dist/{conventions-DKH7AE4T.js → conventions-RYN7OXJN.js} +15 -15
- package/dist/{diagnostics-GOGCLTRS.js → diagnostics-HTCWF7F4.js} +7 -7
- package/dist/execute-QL3ZQIAP.js +37 -0
- package/dist/{generate-AGBT7EHX.js → generate-CX6KNHZS.js} +22 -22
- package/dist/{git-AC542GU2.js → git-A2IP32PQ.js} +2 -2
- package/dist/init-PDVQDRS6.js +24 -0
- package/dist/{operations-JQYK2EMT.js → operations-CAKIT2BO.js} +15 -15
- package/dist/{permissions-6HL52K54.js → permissions-G4XCAESF.js} +4 -4
- package/dist/plan-PRORGS3L.js +34 -0
- package/dist/{policy-IMRXGG7X.js → policy-WX6O2JID.js} +14 -14
- package/dist/pr-27RMQI5F.js +39 -0
- package/dist/pr-review-VNESMAMT.js +32 -0
- package/dist/prd-ZNC4T5BS.js +33 -0
- package/dist/{ps-KUUZ6TUL.js → ps-76L6ZAZL.js} +12 -12
- package/dist/{recorder-BJ2KIAWT.js → recorder-N7U66HCO.js} +7 -7
- package/dist/{registry-SB7XTMGE.js → registry-XLHHYLTR.js} +5 -5
- package/dist/{resume-QNLQSCR5.js → resume-VLWIE264.js} +40 -38
- package/dist/{resume-QNLQSCR5.js.map → resume-VLWIE264.js.map} +1 -1
- package/dist/review-7CREJDKB.js +35 -0
- package/dist/routing-VCDYHSXA.js +33 -0
- package/dist/run-3MPORLF5.js +56 -0
- package/dist/{session-publisher-T7EI3CIP.js → session-publisher-KAJQRHNZ.js} +3 -2
- package/dist/{state-manager-ZEPWTCA6.js → state-manager-UPHO2EA2.js} +4 -4
- package/dist/state-manager-UPHO2EA2.js.map +1 -0
- package/dist/{usage-QL563I7F.js → usage-U7QNGCPK.js} +12 -12
- package/dist/{web-PPSMNTQ6.js → web-HUNDHCJ4.js} +21 -18
- package/dist/{web-PPSMNTQ6.js.map → web-HUNDHCJ4.js.map} +1 -1
- package/package.json +6 -5
- package/web/AGENTS.md +15 -11
- package/web/public/app.css +51 -0
- package/web/public/app.js +325 -56
- package/web/public/index.html +6 -2
- package/dist/agent-AT64RXTA.js +0 -30
- package/dist/chunk-5ZKBIXET.js.map +0 -1
- package/dist/chunk-6QYMSNVA.js.map +0 -1
- package/dist/chunk-7Z4Y3VKM.js +0 -26
- package/dist/chunk-7Z4Y3VKM.js.map +0 -1
- package/dist/chunk-BMHGRYL6.js.map +0 -1
- package/dist/chunk-HGSYD57B.js.map +0 -1
- package/dist/chunk-MGCOQU6N.js.map +0 -1
- package/dist/chunk-RPXDBX6R.js.map +0 -1
- package/dist/chunk-WM3Y7Q4A.js.map +0 -1
- package/dist/chunk-XDKGA6UX.js.map +0 -1
- package/dist/chunk-XLVGPXNL.js +0 -180
- package/dist/chunk-XLVGPXNL.js.map +0 -1
- package/dist/chunk-ZFIXEYUJ.js.map +0 -1
- package/dist/execute-E3BRMDAQ.js +0 -35
- package/dist/init-LLBVJNPE.js +0 -24
- package/dist/plan-HSLWDJEZ.js +0 -34
- package/dist/pr-6TCZXG6K.js +0 -39
- package/dist/pr-review-6MWJO2NP.js +0 -32
- package/dist/prd-D6YDYAFX.js +0 -33
- package/dist/review-3KJ4BH2V.js +0 -35
- package/dist/routing-TN7GLMGU.js +0 -94
- package/dist/routing-TN7GLMGU.js.map +0 -1
- package/dist/run-5EHIFF2V.js +0 -54
- /package/dist/{agent-AT64RXTA.js.map → agent-TB6W7PT7.js.map} +0 -0
- /package/dist/{analyze-O2PYDIFK.js.map → analyze-6M3KEOX7.js.map} +0 -0
- /package/dist/{apply-SSU6QCD4.js.map → apply-ADB3ZK7W.js.map} +0 -0
- /package/dist/{bench-ODFPAGW6.js.map → bench-FM7I7EIV.js.map} +0 -0
- /package/dist/{chunk-QRCGW2ZO.js.map → chunk-32JSYXEK.js.map} +0 -0
- /package/dist/{chunk-A35HE4V7.js.map → chunk-7F7LGMVO.js.map} +0 -0
- /package/dist/{chunk-2ZAM45PZ.js.map → chunk-ANI2NTR4.js.map} +0 -0
- /package/dist/{chunk-ZPIVCM7Q.js.map → chunk-B3TSS7BT.js.map} +0 -0
- /package/dist/{chunk-ZXPASFHG.js.map → chunk-DGRTJD5R.js.map} +0 -0
- /package/dist/{chunk-VR34TXHF.js.map → chunk-DV7HUC4Z.js.map} +0 -0
- /package/dist/{chunk-6SRE4KD7.js.map → chunk-E6KXCV3T.js.map} +0 -0
- /package/dist/{chunk-PCN7ZQND.js.map → chunk-HLAPOUEM.js.map} +0 -0
- /package/dist/{chunk-MEFY4B7T.js.map → chunk-ISZ54SVE.js.map} +0 -0
- /package/dist/{chunk-ARWNYXFL.js.map → chunk-KZ5EY5XX.js.map} +0 -0
- /package/dist/{chunk-KAHQ6F5C.js.map → chunk-LPWPFIIB.js.map} +0 -0
- /package/dist/{chunk-FK4IDY6P.js.map → chunk-MI5TREN3.js.map} +0 -0
- /package/dist/{chunk-UNQYMR3H.js.map → chunk-PPZ3K4YM.js.map} +0 -0
- /package/dist/{chunk-F4OK7KEP.js.map → chunk-QMIQR7EE.js.map} +0 -0
- /package/dist/{chunk-AYYMW33T.js.map → chunk-QPGEFLMY.js.map} +0 -0
- /package/dist/{chunk-MROS3SKL.js.map → chunk-RNFUVKC3.js.map} +0 -0
- /package/dist/{chunk-RWEXK55D.js.map → chunk-RYOUZ6EK.js.map} +0 -0
- /package/dist/{chunk-6E2PEBFT.js.map → chunk-SGTTR4XW.js.map} +0 -0
- /package/dist/{chunk-XQHFC62U.js.map → chunk-SOM4QA6C.js.map} +0 -0
- /package/dist/{chunk-6FXOU2KR.js.map → chunk-UWDCIFTP.js.map} +0 -0
- /package/dist/{chunk-XG2NYDRA.js.map → chunk-VE3IYNDN.js.map} +0 -0
- /package/dist/{chunk-3BWZO4YI.js.map → chunk-WCCZJL7W.js.map} +0 -0
- /package/dist/{chunk-NVZMBXTE.js.map → chunk-XZUEFTUP.js.map} +0 -0
- /package/dist/{chunk-4RXYFWP7.js.map → chunk-YUNBOUQH.js.map} +0 -0
- /package/dist/{chunk-GSZCQUCY.js.map → chunk-YVPZUN6X.js.map} +0 -0
- /package/dist/{chunk-XRLRKTQA.js.map → chunk-Z5O2XBWA.js.map} +0 -0
- /package/dist/{config-UMP6DBJB.js.map → config-KGZUPAUP.js.map} +0 -0
- /package/dist/{conventions-DKH7AE4T.js.map → conventions-RYN7OXJN.js.map} +0 -0
- /package/dist/{diagnostics-GOGCLTRS.js.map → diagnostics-HTCWF7F4.js.map} +0 -0
- /package/dist/{execute-E3BRMDAQ.js.map → execute-QL3ZQIAP.js.map} +0 -0
- /package/dist/{generate-AGBT7EHX.js.map → generate-CX6KNHZS.js.map} +0 -0
- /package/dist/{git-AC542GU2.js.map → git-A2IP32PQ.js.map} +0 -0
- /package/dist/{init-LLBVJNPE.js.map → init-PDVQDRS6.js.map} +0 -0
- /package/dist/{operations-JQYK2EMT.js.map → operations-CAKIT2BO.js.map} +0 -0
- /package/dist/{permissions-6HL52K54.js.map → permissions-G4XCAESF.js.map} +0 -0
- /package/dist/{plan-HSLWDJEZ.js.map → plan-PRORGS3L.js.map} +0 -0
- /package/dist/{policy-IMRXGG7X.js.map → policy-WX6O2JID.js.map} +0 -0
- /package/dist/{pr-6TCZXG6K.js.map → pr-27RMQI5F.js.map} +0 -0
- /package/dist/{pr-review-6MWJO2NP.js.map → pr-review-VNESMAMT.js.map} +0 -0
- /package/dist/{prd-D6YDYAFX.js.map → prd-ZNC4T5BS.js.map} +0 -0
- /package/dist/{ps-KUUZ6TUL.js.map → ps-76L6ZAZL.js.map} +0 -0
- /package/dist/{recorder-BJ2KIAWT.js.map → recorder-N7U66HCO.js.map} +0 -0
- /package/dist/{registry-SB7XTMGE.js.map → registry-XLHHYLTR.js.map} +0 -0
- /package/dist/{review-3KJ4BH2V.js.map → review-7CREJDKB.js.map} +0 -0
- /package/dist/{run-5EHIFF2V.js.map → routing-VCDYHSXA.js.map} +0 -0
- /package/dist/{session-publisher-T7EI3CIP.js.map → run-3MPORLF5.js.map} +0 -0
- /package/dist/{state-manager-ZEPWTCA6.js.map → session-publisher-KAJQRHNZ.js.map} +0 -0
- /package/dist/{usage-QL563I7F.js.map → usage-U7QNGCPK.js.map} +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
publishIssueDetails,
|
|
4
|
+
publishStorySeed,
|
|
5
|
+
runPipeline
|
|
6
|
+
} from "./chunk-HJBF4WD2.js";
|
|
7
|
+
import "./chunk-B3TSS7BT.js";
|
|
8
|
+
import "./chunk-LPWPFIIB.js";
|
|
9
|
+
import "./chunk-Z5O2XBWA.js";
|
|
10
|
+
import "./chunk-YUNBOUQH.js";
|
|
11
|
+
import "./chunk-ANI2NTR4.js";
|
|
12
|
+
import "./chunk-MI5TREN3.js";
|
|
13
|
+
import "./chunk-A4H7P7T7.js";
|
|
14
|
+
import "./chunk-ISZ54SVE.js";
|
|
15
|
+
import "./chunk-KZXQWC7U.js";
|
|
16
|
+
import "./chunk-2SUFAE6X.js";
|
|
17
|
+
import "./chunk-6YQT3SSM.js";
|
|
18
|
+
import "./chunk-SGTTR4XW.js";
|
|
19
|
+
import "./chunk-CRGIQFUQ.js";
|
|
20
|
+
import "./chunk-L6HSKTQE.js";
|
|
21
|
+
import "./chunk-UWDCIFTP.js";
|
|
22
|
+
import "./chunk-RCZJQUTC.js";
|
|
23
|
+
import "./chunk-KZ5EY5XX.js";
|
|
24
|
+
import "./chunk-32JSYXEK.js";
|
|
25
|
+
import "./chunk-VE3IYNDN.js";
|
|
26
|
+
import "./chunk-QMIQR7EE.js";
|
|
27
|
+
import "./chunk-KMMASOA6.js";
|
|
28
|
+
import "./chunk-HLAPOUEM.js";
|
|
29
|
+
import "./chunk-7F7LGMVO.js";
|
|
30
|
+
import "./chunk-UK4E2FUA.js";
|
|
31
|
+
import "./chunk-WCCZJL7W.js";
|
|
32
|
+
import "./chunk-DGRTJD5R.js";
|
|
33
|
+
import "./chunk-D6ZBGJ3L.js";
|
|
34
|
+
import "./chunk-C5TL7T4V.js";
|
|
35
|
+
import "./chunk-3D4A4B46.js";
|
|
36
|
+
import "./chunk-E6KXCV3T.js";
|
|
37
|
+
import "./chunk-HY5HLFDU.js";
|
|
38
|
+
import "./chunk-DV7HUC4Z.js";
|
|
39
|
+
import "./chunk-XZUEFTUP.js";
|
|
40
|
+
import "./chunk-QPGEFLMY.js";
|
|
41
|
+
import "./chunk-HAKDLR6N.js";
|
|
42
|
+
import "./chunk-YVPZUN6X.js";
|
|
43
|
+
import "./chunk-DUS5ZNKL.js";
|
|
44
|
+
import "./chunk-YII4U7ZD.js";
|
|
45
|
+
import "./chunk-PPZ3K4YM.js";
|
|
46
|
+
import "./chunk-SOM4QA6C.js";
|
|
47
|
+
import "./chunk-ICM463EO.js";
|
|
48
|
+
import "./chunk-XEYH6ZZI.js";
|
|
49
|
+
import "./chunk-P3LRUNC6.js";
|
|
50
|
+
import "./chunk-3R27MJ5R.js";
|
|
51
|
+
export {
|
|
52
|
+
publishIssueDetails,
|
|
53
|
+
publishStorySeed,
|
|
54
|
+
runPipeline
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=run-3MPORLF5.js.map
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getSessionPublisher,
|
|
4
4
|
setSessionPublisher
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DUS5ZNKL.js";
|
|
6
6
|
import "./chunk-P3LRUNC6.js";
|
|
7
|
+
import "./chunk-3R27MJ5R.js";
|
|
7
8
|
export {
|
|
8
9
|
getSessionPublisher,
|
|
9
10
|
setSessionPublisher
|
|
10
11
|
};
|
|
11
|
-
//# sourceMappingURL=session-publisher-
|
|
12
|
+
//# sourceMappingURL=session-publisher-KAJQRHNZ.js.map
|
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
selectActiveStory,
|
|
15
15
|
setLastError,
|
|
16
16
|
trimErrorMessage
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-SOM4QA6C.js";
|
|
18
|
+
import "./chunk-ICM463EO.js";
|
|
19
|
+
import "./chunk-3R27MJ5R.js";
|
|
20
20
|
export {
|
|
21
21
|
allStoriesPass,
|
|
22
22
|
applyStoryMetrics,
|
|
@@ -33,4 +33,4 @@ export {
|
|
|
33
33
|
setLastError,
|
|
34
34
|
trimErrorMessage
|
|
35
35
|
};
|
|
36
|
-
//# sourceMappingURL=state-manager-
|
|
36
|
+
//# sourceMappingURL=state-manager-UPHO2EA2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -5,28 +5,28 @@ import {
|
|
|
5
5
|
} from "./chunk-CRGIQFUQ.js";
|
|
6
6
|
import {
|
|
7
7
|
resolveProjectPaths
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DV7HUC4Z.js";
|
|
9
9
|
import {
|
|
10
10
|
printError,
|
|
11
11
|
printInfo
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-AYYMW33T.js";
|
|
12
|
+
} from "./chunk-XZUEFTUP.js";
|
|
13
|
+
import "./chunk-QPGEFLMY.js";
|
|
15
14
|
import {
|
|
16
15
|
getIssuePaths
|
|
17
16
|
} from "./chunk-HAKDLR6N.js";
|
|
18
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-YVPZUN6X.js";
|
|
18
|
+
import "./chunk-DUS5ZNKL.js";
|
|
19
19
|
import {
|
|
20
20
|
discardedExecutionCount
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import "./chunk-
|
|
23
|
-
import "./chunk-UNQYMR3H.js";
|
|
21
|
+
} from "./chunk-YII4U7ZD.js";
|
|
22
|
+
import "./chunk-PPZ3K4YM.js";
|
|
24
23
|
import {
|
|
25
24
|
loadTaskPlan
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-7Z4Y3VKM.js";
|
|
25
|
+
} from "./chunk-SOM4QA6C.js";
|
|
26
|
+
import "./chunk-ICM463EO.js";
|
|
29
27
|
import "./chunk-XEYH6ZZI.js";
|
|
28
|
+
import "./chunk-P3LRUNC6.js";
|
|
29
|
+
import "./chunk-3R27MJ5R.js";
|
|
30
30
|
|
|
31
31
|
// src/commands/usage.ts
|
|
32
32
|
import { readdir } from "fs/promises";
|
|
@@ -174,4 +174,4 @@ export {
|
|
|
174
174
|
formatUsageReport,
|
|
175
175
|
runUsage
|
|
176
176
|
};
|
|
177
|
-
//# sourceMappingURL=usage-
|
|
177
|
+
//# sourceMappingURL=usage-U7QNGCPK.js.map
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
readSessionFile
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RYOUZ6EK.js";
|
|
5
5
|
import {
|
|
6
6
|
ensureSingleWebServer,
|
|
7
7
|
stopWebMonitor
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-2SUFAE6X.js";
|
|
9
|
+
import "./chunk-UWDCIFTP.js";
|
|
10
|
+
import "./chunk-RCZJQUTC.js";
|
|
11
|
+
import "./chunk-7F7LGMVO.js";
|
|
11
12
|
import {
|
|
12
13
|
parseJournal
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-WCCZJL7W.js";
|
|
15
|
+
import "./chunk-D6ZBGJ3L.js";
|
|
16
|
+
import "./chunk-C5TL7T4V.js";
|
|
14
17
|
import {
|
|
15
18
|
loadWebConfig
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-AYYMW33T.js";
|
|
19
|
+
} from "./chunk-HY5HLFDU.js";
|
|
20
|
+
import "./chunk-DV7HUC4Z.js";
|
|
21
|
+
import "./chunk-XZUEFTUP.js";
|
|
22
|
+
import "./chunk-QPGEFLMY.js";
|
|
21
23
|
import {
|
|
22
24
|
EVENTS_FILENAME,
|
|
23
25
|
ISSUES_DIR_NAME,
|
|
@@ -26,14 +28,15 @@ import {
|
|
|
26
28
|
SESSION_FILENAME,
|
|
27
29
|
getGlobalRoot
|
|
28
30
|
} from "./chunk-HAKDLR6N.js";
|
|
29
|
-
import "./chunk-
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-
|
|
32
|
-
import "./chunk-
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-
|
|
35
|
-
import "./chunk-7Z4Y3VKM.js";
|
|
31
|
+
import "./chunk-YVPZUN6X.js";
|
|
32
|
+
import "./chunk-DUS5ZNKL.js";
|
|
33
|
+
import "./chunk-YII4U7ZD.js";
|
|
34
|
+
import "./chunk-PPZ3K4YM.js";
|
|
35
|
+
import "./chunk-SOM4QA6C.js";
|
|
36
|
+
import "./chunk-ICM463EO.js";
|
|
36
37
|
import "./chunk-XEYH6ZZI.js";
|
|
38
|
+
import "./chunk-P3LRUNC6.js";
|
|
39
|
+
import "./chunk-3R27MJ5R.js";
|
|
37
40
|
|
|
38
41
|
// src/web/session-directory.ts
|
|
39
42
|
import { readdir, readFile } from "fs/promises";
|
|
@@ -166,4 +169,4 @@ export {
|
|
|
166
169
|
runWebServe,
|
|
167
170
|
runWebStop
|
|
168
171
|
};
|
|
169
|
-
//# sourceMappingURL=web-
|
|
172
|
+
//# sourceMappingURL=web-HUNDHCJ4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/web/session-directory.ts","../src/commands/web.ts"],"sourcesContent":["import { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { type JournalEntry, parseJournal } from '../core/journal.js';\nimport type { ValidatedSessionSnapshot } from '../schemas.js';\nimport {\n EVENTS_FILENAME,\n type GetGlobalRootOptions,\n getGlobalRoot,\n ISSUES_DIR_NAME,\n PROJECTS_DIR_NAME,\n ROTATED_EVENTS_FILENAME,\n SESSION_FILENAME,\n} from '../storage/paths.js';\nimport { readSessionFile } from '../storage/session-file.js';\n\n/**\n * Multi-session discovery for the web monitoring server (US-003).\n *\n * The single-instance server (`web/lock.ts`) runs decoupled from any one\n * pipeline invocation, so it cannot hold a `SessionPublisher` in memory for\n * \"the\" run being monitored — there may be zero, one or several running at\n * once, each in its own process. Instead this module polls every\n * `~/.issue-flow/projects/<project>/issues/<n>/session.json` on disk (the\n * same file `FilePublisher` already writes) and keeps an in-memory map of the\n * ones that are both well-formed and recently updated.\n *\n * Polling rather than `fs.watch` is a deliberate choice, not a placeholder:\n * `fs.watch`'s `recursive` option is only reliable on macOS and Windows (Linux\n * needs a manual per-directory watch tree, and the whole thing still degrades\n * to polling on network filesystems), while a `~/.issue-flow` tree is small\n * and local — a cheap poll of it is simpler and behaves identically on every\n * platform, which is what the US-003 acceptance criteria ask to validate.\n */\n\n/** How often the directory tree is rescanned, unless overridden. */\nexport const DEFAULT_POLL_INTERVAL_MS = 3000;\n\n/**\n * A session stops being reported once its file has gone this long without an\n * update. `FilePublisher` touches a live session every 10s without rewriting\n * its content. Three missed heartbeats plus ample scheduling/filesystem slack\n * avoid flapping while still removing an abruptly killed run promptly.\n */\nexport const DEFAULT_STALE_AFTER_MS = 90_000;\n\nexport interface ActiveSession {\n /** Directory the session.json was read from, one level up from the file. */\n issueDir: string;\n filePath: string;\n snapshot: ValidatedSessionSnapshot;\n /** mtime of the file at the last successful read, in epoch ms. */\n updatedAtMs: number;\n}\n\nexport interface SessionDirectoryOptions extends GetGlobalRootOptions {\n /** Poll interval, in ms. Default {@link DEFAULT_POLL_INTERVAL_MS}. */\n pollIntervalMs?: number;\n /** Staleness threshold, in ms. Default {@link DEFAULT_STALE_AFTER_MS}. */\n staleAfterMs?: number;\n /** Invoked (at most once) when a poll fails unexpectedly. Best-effort logging only. */\n onWarn?: (message: string) => void;\n}\n\nexport interface SessionDirectoryHandle {\n /** Snapshot of every session currently considered active. */\n sessions(): ActiveSession[];\n /** A single active session by id, or undefined. */\n getSession(sessionId: string): ActiveSession | undefined;\n /** Journal entries for one active session, oldest generation first. */\n events(sessionId: string): Promise<JournalEntry[] | undefined>;\n /** Force an immediate rescan instead of waiting for the next tick. Never throws. */\n refresh(): Promise<void>;\n /** Stop polling. Idempotent. */\n close(): void;\n}\n\nasync function listSubdirectories(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n // Symlinks are skipped on purpose, same discipline as the storage\n // migration walk: following one could pull session data from outside the\n // global tree into the listing.\n return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);\n } catch {\n // Missing/unreadable projects (or issues) directory reads as \"none yet\" —\n // this is routinely the case before the first --web run ever happens.\n return [];\n }\n}\n\n/** Every `session.json` path currently on disk, one per project/issue pair. */\nasync function discoverSessionFiles(root: string): Promise<string[]> {\n const projectsDir = join(root, PROJECTS_DIR_NAME);\n const projectIds = await listSubdirectories(projectsDir);\n\n const perProject = await Promise.all(\n projectIds.map(async (projectId) => {\n const issuesDir = join(projectsDir, projectId, ISSUES_DIR_NAME);\n const issueIds = await listSubdirectories(issuesDir);\n return issueIds.map((issueId) => join(issuesDir, issueId, SESSION_FILENAME));\n }),\n );\n\n return perProject.flat();\n}\n\nasync function readJournalFile(filePath: string): Promise<JournalEntry[]> {\n try {\n return parseJournal(await readFile(filePath, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Start polling the global storage tree for active sessions.\n *\n * Returns a handle immediately; the first scan happens synchronously in the\n * background and `sessions()` answers `[]` until it completes, same as \"no\n * sessions found yet\" — there is nothing to await here that would not also\n * delay the server's very first bind.\n */\nexport function watchSessionDirectory(\n options: SessionDirectoryOptions = {},\n): SessionDirectoryHandle {\n const root = getGlobalRoot(options);\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;\n const warn = options.onWarn;\n\n // Keyed by sessionId, not by file path: a session's identity is the id\n // inside its content, and that is what /api/status?session=<id> looks up.\n let sessions = new Map<string, ActiveSession>();\n let warned = false;\n\n async function scan(): Promise<void> {\n let files: string[];\n try {\n files = await discoverSessionFiles(root);\n } catch (err) {\n if (!warned) {\n warned = true;\n warn?.(\n `issue-flow: web monitor could not scan the session directory (will keep retrying silently): ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n return;\n }\n\n const now = Date.now();\n const next = new Map<string, ActiveSession>();\n for (const filePath of files) {\n const result = await readSessionFile(filePath);\n if (result === null) continue;\n if (now - result.updatedAtMs > staleAfterMs) continue; // stopped updating: not active\n const sessionId = result.snapshot.sessionId as string;\n next.set(sessionId, {\n issueDir: join(filePath, '..'),\n filePath,\n snapshot: result.snapshot,\n updatedAtMs: result.updatedAtMs,\n });\n }\n sessions = next;\n }\n\n // The first scan runs immediately (not just after the first interval tick)\n // so a server started right after a session begins does not report \"no\n // sessions\" for a whole poll interval.\n void scan();\n const timer = setInterval(() => void scan(), pollIntervalMs);\n timer.unref();\n\n return {\n sessions: () => [...sessions.values()],\n getSession: (sessionId: string) => sessions.get(sessionId),\n events: async (sessionId: string) => {\n const session = sessions.get(sessionId);\n if (session === undefined) return undefined;\n const [rotated, current] = await Promise.all([\n readJournalFile(join(session.issueDir, ROTATED_EVENTS_FILENAME)),\n readJournalFile(join(session.issueDir, EVENTS_FILENAME)),\n ]);\n return [...rotated, ...current];\n },\n refresh: scan,\n close: () => clearInterval(timer),\n };\n}\n","import { loadWebConfig } from '../config.js';\nimport type { WebConfig } from '../schemas.js';\nimport { ensureSingleWebServer, stopWebMonitor } from '../web/lock.js';\nimport { watchSessionDirectory } from '../web/session-directory.js';\n\n/**\n * `issue-flow web serve` / `issue-flow web stop` (US-002).\n *\n * `serve` is the process `ensureWebMonitor()` (`web/lock.ts`) spawns detached\n * — it is not meant to be run interactively, though nothing stops a user from\n * doing so to watch every session on the machine without going through a\n * pipeline command first. It never exits on its own: the server stays bound\n * (see `unref: false` in `web/server.ts`) until `stop` sends it `SIGTERM`, at\n * which point `startWebServer`'s own signal handler closes it (removing the\n * lock) and re-raises the signal for the default termination behavior.\n */\n\nexport interface RunWebServeOptions {\n port?: number;\n host?: string;\n refresh?: number;\n}\n\n/**\n * Bind (or defer to) the single web monitor instance and keep the process\n * alive for as long as it stays bound. Logging is intentionally silent on the\n * happy path: this process is spawned with `stdio: 'ignore'`, so anything\n * printed here goes nowhere — the caller (`ensureWebMonitor`) is the one that\n * tells the user the server is up.\n */\nexport async function runWebServe(options: RunWebServeOptions): Promise<number> {\n // Built conditionally, not `{ port: options.port, ... }`: loadWebConfig()\n // spreads this object directly over the lower-precedence layers, and an\n // explicit `undefined` value would overwrite an env/.issue-flow.json\n // setting instead of falling through to it (only an *absent* key does).\n const cli: Partial<WebConfig> = {};\n if (options.port !== undefined) cli.port = options.port;\n if (options.host !== undefined) cli.host = options.host;\n if (options.refresh !== undefined) cli.refreshSeconds = options.refresh;\n const webConfig = await loadWebConfig({ cli });\n\n const sessions = watchSessionDirectory();\n\n const noop = (): void => {};\n const handle = await ensureSingleWebServer({\n sessions,\n port: webConfig.port,\n host: webConfig.host,\n refreshSeconds: webConfig.refreshSeconds,\n unref: false,\n info: noop,\n warn: noop,\n });\n\n if (handle === null) {\n sessions.close();\n return 1;\n }\n\n if (handle.server === undefined) {\n // Another instance already won the race (or was already running by the\n // time this process got here): nothing to serve, so it exits right away\n // instead of idling as a redundant detached process.\n sessions.close();\n return 0;\n }\n\n const originalClose = handle.close;\n handle.close = async () => {\n await originalClose();\n sessions.close();\n };\n\n return 0;\n}\n\n/** Stop the single running web monitor instance, if any. */\nexport async function runWebStop(): Promise<number> {\n const webConfig = await loadWebConfig();\n const result = await stopWebMonitor({ port: webConfig.port, host: webConfig.host });\n return result === 'failed' || result === 'unowned' ? 1 : 0;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/web/session-directory.ts","../src/commands/web.ts"],"sourcesContent":["import { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { type JournalEntry, parseJournal } from '../core/journal.js';\nimport type { ValidatedSessionSnapshot } from '../schemas.js';\nimport {\n EVENTS_FILENAME,\n type GetGlobalRootOptions,\n getGlobalRoot,\n ISSUES_DIR_NAME,\n PROJECTS_DIR_NAME,\n ROTATED_EVENTS_FILENAME,\n SESSION_FILENAME,\n} from '../storage/paths.js';\nimport { readSessionFile } from '../storage/session-file.js';\n\n/**\n * Multi-session discovery for the web monitoring server (US-003).\n *\n * The single-instance server (`web/lock.ts`) runs decoupled from any one\n * pipeline invocation, so it cannot hold a `SessionPublisher` in memory for\n * \"the\" run being monitored — there may be zero, one or several running at\n * once, each in its own process. Instead this module polls every\n * `~/.issue-flow/projects/<project>/issues/<n>/session.json` on disk (the\n * same file `FilePublisher` already writes) and keeps an in-memory map of the\n * ones that are both well-formed and recently updated.\n *\n * Polling rather than `fs.watch` is a deliberate choice, not a placeholder:\n * `fs.watch`'s `recursive` option is only reliable on macOS and Windows (Linux\n * needs a manual per-directory watch tree, and the whole thing still degrades\n * to polling on network filesystems), while a `~/.issue-flow` tree is small\n * and local — a cheap poll of it is simpler and behaves identically on every\n * platform, which is what the US-003 acceptance criteria ask to validate.\n */\n\n/** How often the directory tree is rescanned, unless overridden. */\nexport const DEFAULT_POLL_INTERVAL_MS = 3000;\n\n/**\n * A session stops being reported once its file has gone this long without an\n * update. `FilePublisher` touches a live session every 10s without rewriting\n * its content. Three missed heartbeats plus ample scheduling/filesystem slack\n * avoid flapping while still removing an abruptly killed run promptly.\n */\nexport const DEFAULT_STALE_AFTER_MS = 90_000;\n\nexport interface ActiveSession {\n /** Directory the session.json was read from, one level up from the file. */\n issueDir: string;\n filePath: string;\n snapshot: ValidatedSessionSnapshot;\n /** mtime of the file at the last successful read, in epoch ms. */\n updatedAtMs: number;\n}\n\nexport interface SessionDirectoryOptions extends GetGlobalRootOptions {\n /** Poll interval, in ms. Default {@link DEFAULT_POLL_INTERVAL_MS}. */\n pollIntervalMs?: number;\n /** Staleness threshold, in ms. Default {@link DEFAULT_STALE_AFTER_MS}. */\n staleAfterMs?: number;\n /** Invoked (at most once) when a poll fails unexpectedly. Best-effort logging only. */\n onWarn?: (message: string) => void;\n}\n\nexport interface SessionDirectoryHandle {\n /** Snapshot of every session currently considered active. */\n sessions(): ActiveSession[];\n /** A single active session by id, or undefined. */\n getSession(sessionId: string): ActiveSession | undefined;\n /** Journal entries for one active session, oldest generation first. */\n events(sessionId: string): Promise<JournalEntry[] | undefined>;\n /** Force an immediate rescan instead of waiting for the next tick. Never throws. */\n refresh(): Promise<void>;\n /** Stop polling. Idempotent. */\n close(): void;\n}\n\nasync function listSubdirectories(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n // Symlinks are skipped on purpose, same discipline as the storage\n // migration walk: following one could pull session data from outside the\n // global tree into the listing.\n return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);\n } catch {\n // Missing/unreadable projects (or issues) directory reads as \"none yet\" —\n // this is routinely the case before the first --web run ever happens.\n return [];\n }\n}\n\n/** Every `session.json` path currently on disk, one per project/issue pair. */\nasync function discoverSessionFiles(root: string): Promise<string[]> {\n const projectsDir = join(root, PROJECTS_DIR_NAME);\n const projectIds = await listSubdirectories(projectsDir);\n\n const perProject = await Promise.all(\n projectIds.map(async (projectId) => {\n const issuesDir = join(projectsDir, projectId, ISSUES_DIR_NAME);\n const issueIds = await listSubdirectories(issuesDir);\n return issueIds.map((issueId) => join(issuesDir, issueId, SESSION_FILENAME));\n }),\n );\n\n return perProject.flat();\n}\n\nasync function readJournalFile(filePath: string): Promise<JournalEntry[]> {\n try {\n return parseJournal(await readFile(filePath, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Start polling the global storage tree for active sessions.\n *\n * Returns a handle immediately; the first scan happens synchronously in the\n * background and `sessions()` answers `[]` until it completes, same as \"no\n * sessions found yet\" — there is nothing to await here that would not also\n * delay the server's very first bind.\n */\nexport function watchSessionDirectory(\n options: SessionDirectoryOptions = {},\n): SessionDirectoryHandle {\n const root = getGlobalRoot(options);\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;\n const warn = options.onWarn;\n\n // Keyed by sessionId, not by file path: a session's identity is the id\n // inside its content, and that is what /api/status?session=<id> looks up.\n let sessions = new Map<string, ActiveSession>();\n let warned = false;\n\n async function scan(): Promise<void> {\n let files: string[];\n try {\n files = await discoverSessionFiles(root);\n } catch (err) {\n if (!warned) {\n warned = true;\n warn?.(\n `issue-flow: web monitor could not scan the session directory (will keep retrying silently): ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n return;\n }\n\n const now = Date.now();\n const next = new Map<string, ActiveSession>();\n for (const filePath of files) {\n const result = await readSessionFile(filePath);\n if (result === null) continue;\n if (now - result.updatedAtMs > staleAfterMs) continue; // stopped updating: not active\n const sessionId = result.snapshot.sessionId as string;\n next.set(sessionId, {\n issueDir: join(filePath, '..'),\n filePath,\n snapshot: result.snapshot,\n updatedAtMs: result.updatedAtMs,\n });\n }\n sessions = next;\n }\n\n // The first scan runs immediately (not just after the first interval tick)\n // so a server started right after a session begins does not report \"no\n // sessions\" for a whole poll interval.\n void scan();\n const timer = setInterval(() => void scan(), pollIntervalMs);\n timer.unref();\n\n return {\n sessions: () => [...sessions.values()],\n getSession: (sessionId: string) => sessions.get(sessionId),\n events: async (sessionId: string) => {\n const session = sessions.get(sessionId);\n if (session === undefined) return undefined;\n const [rotated, current] = await Promise.all([\n readJournalFile(join(session.issueDir, ROTATED_EVENTS_FILENAME)),\n readJournalFile(join(session.issueDir, EVENTS_FILENAME)),\n ]);\n return [...rotated, ...current];\n },\n refresh: scan,\n close: () => clearInterval(timer),\n };\n}\n","import { loadWebConfig } from '../config.js';\nimport type { WebConfig } from '../schemas.js';\nimport { ensureSingleWebServer, stopWebMonitor } from '../web/lock.js';\nimport { watchSessionDirectory } from '../web/session-directory.js';\n\n/**\n * `issue-flow web serve` / `issue-flow web stop` (US-002).\n *\n * `serve` is the process `ensureWebMonitor()` (`web/lock.ts`) spawns detached\n * — it is not meant to be run interactively, though nothing stops a user from\n * doing so to watch every session on the machine without going through a\n * pipeline command first. It never exits on its own: the server stays bound\n * (see `unref: false` in `web/server.ts`) until `stop` sends it `SIGTERM`, at\n * which point `startWebServer`'s own signal handler closes it (removing the\n * lock) and re-raises the signal for the default termination behavior.\n */\n\nexport interface RunWebServeOptions {\n port?: number;\n host?: string;\n refresh?: number;\n}\n\n/**\n * Bind (or defer to) the single web monitor instance and keep the process\n * alive for as long as it stays bound. Logging is intentionally silent on the\n * happy path: this process is spawned with `stdio: 'ignore'`, so anything\n * printed here goes nowhere — the caller (`ensureWebMonitor`) is the one that\n * tells the user the server is up.\n */\nexport async function runWebServe(options: RunWebServeOptions): Promise<number> {\n // Built conditionally, not `{ port: options.port, ... }`: loadWebConfig()\n // spreads this object directly over the lower-precedence layers, and an\n // explicit `undefined` value would overwrite an env/.issue-flow.json\n // setting instead of falling through to it (only an *absent* key does).\n const cli: Partial<WebConfig> = {};\n if (options.port !== undefined) cli.port = options.port;\n if (options.host !== undefined) cli.host = options.host;\n if (options.refresh !== undefined) cli.refreshSeconds = options.refresh;\n const webConfig = await loadWebConfig({ cli });\n\n const sessions = watchSessionDirectory();\n\n const noop = (): void => {};\n const handle = await ensureSingleWebServer({\n sessions,\n port: webConfig.port,\n host: webConfig.host,\n refreshSeconds: webConfig.refreshSeconds,\n unref: false,\n info: noop,\n warn: noop,\n });\n\n if (handle === null) {\n sessions.close();\n return 1;\n }\n\n if (handle.server === undefined) {\n // Another instance already won the race (or was already running by the\n // time this process got here): nothing to serve, so it exits right away\n // instead of idling as a redundant detached process.\n sessions.close();\n return 0;\n }\n\n const originalClose = handle.close;\n handle.close = async () => {\n await originalClose();\n sessions.close();\n };\n\n return 0;\n}\n\n/** Stop the single running web monitor instance, if any. */\nexport async function runWebStop(): Promise<number> {\n const webConfig = await loadWebConfig();\n const result = await stopWebMonitor({ port: webConfig.port, host: webConfig.host });\n return result === 'failed' || result === 'unowned' ? 1 : 0;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "issue-flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Unified CLI for orchestrating the full issue-flow pipeline via Claude Code, Codex CLI, Cursor CLI or Antigravity CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
"test:watch": "vitest",
|
|
15
15
|
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
16
16
|
"smoke": "bash scripts/smoke-issue-providers.sh",
|
|
17
|
-
"lint": "biome check
|
|
18
|
-
"lint:fix": "biome check --write
|
|
19
|
-
"format": "biome format --write
|
|
20
|
-
"check": "biome check
|
|
17
|
+
"lint": "biome check .",
|
|
18
|
+
"lint:fix": "biome check --write .",
|
|
19
|
+
"format": "biome format --write .",
|
|
20
|
+
"check": "biome check . && tsc --noEmit",
|
|
21
|
+
"fix": "biome check --write . && tsc --noEmit",
|
|
21
22
|
"prepack": "npm run build",
|
|
22
23
|
"prepublishOnly": "npm run lint && npm run typecheck && npm test",
|
|
23
24
|
"preversion": "node scripts/git-version.mjs check",
|
package/web/AGENTS.md
CHANGED
|
@@ -18,6 +18,11 @@ informado). Os dois significam "não informado" e nunca podem virar `0`,
|
|
|
18
18
|
qualquer coisa que não seja número finito para `null`. Prefira
|
|
19
19
|
`x !== null && x !== undefined` a `!x`: zero é um valor legítimo.
|
|
20
20
|
|
|
21
|
+
O chip de versão no header é a versão do **monitor** (`/api/health`), não a da
|
|
22
|
+
CLI que executa o pipeline: os assets desta página vêm da memória daquele
|
|
23
|
+
processo, então é ela que explica o que está na tela. As duas aparecem juntas no
|
|
24
|
+
card de configuração, e a divergência entre elas vira um aviso ali.
|
|
25
|
+
|
|
21
26
|
Toda resposta nova carrega `X-Issue-Flow-Instance`. O client guarda a primeira
|
|
22
27
|
identidade observada e chama `window.location.reload()` quando ela muda: isso é
|
|
23
28
|
o handoff de assets depois de `--restart-web`, não um estado de sessão.
|
|
@@ -212,17 +217,16 @@ o token que um tema herda do outro por engano.
|
|
|
212
217
|
|
|
213
218
|
## Escrita limitada a preferências futuras
|
|
214
219
|
|
|
215
|
-
O estado de execução continua somente leitura (`snapshot.readOnly === true`).
|
|
216
|
-
|
|
217
|
-
execuções **futuras**,
|
|
218
|
-
inferir permissão pela versão: o client
|
|
219
|
-
`/api/health.capabilities`
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
o efeito nem esperar o próximo poll. Enquanto `capabilities` estiver vazio,
|
|
220
|
+
O estado de execução continua somente leitura (`snapshot.readOnly === true`). As
|
|
221
|
+
únicas mutações são `POST /api/config/agent` e `POST /api/config/routing`, que
|
|
222
|
+
salvam preferências globais para execuções **futuras**, aparecem via capability
|
|
223
|
+
e só funcionam em loopback. Nunca inferir permissão pela versão: o client
|
|
224
|
+
renderiza os formulários apenas quando `/api/health.capabilities` anuncia as
|
|
225
|
+
duas capacidades correspondentes.
|
|
226
|
+
|
|
227
|
+
Cada mutação responde com `{ ok, file, appliesTo: 'future executions' }`; o
|
|
228
|
+
client atualiza a preferência viva no próximo `GET /api/config`, sem jamais
|
|
229
|
+
alterar o snapshot da execução. Enquanto as capabilities estiverem ausentes,
|
|
226
230
|
nenhum controle de escrita deve aparecer na tela.
|
|
227
231
|
|
|
228
232
|
## Métricas (tokens e custo)
|
package/web/public/app.css
CHANGED
|
@@ -266,6 +266,19 @@ a:hover {
|
|
|
266
266
|
font-weight: 700;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
/* Versão do processo que serve o painel. Par medido: --text-muted sobre
|
|
270
|
+
--surface-sunken (6,10 claro / 5,33 escuro). */
|
|
271
|
+
.app-version {
|
|
272
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
273
|
+
font-size: 0.72rem;
|
|
274
|
+
font-weight: 500;
|
|
275
|
+
padding: 1px 7px;
|
|
276
|
+
border-radius: 999px;
|
|
277
|
+
background: var(--surface-sunken);
|
|
278
|
+
color: var(--text-muted);
|
|
279
|
+
vertical-align: middle;
|
|
280
|
+
}
|
|
281
|
+
|
|
269
282
|
.issue-link {
|
|
270
283
|
font-weight: 600;
|
|
271
284
|
}
|
|
@@ -971,6 +984,25 @@ progress {
|
|
|
971
984
|
cursor: pointer;
|
|
972
985
|
}
|
|
973
986
|
|
|
987
|
+
.config-phase-row.is-editable {
|
|
988
|
+
grid-template-columns: minmax(5rem, 0.7fr) minmax(7rem, 1fr) minmax(13rem, 1.6fr) auto minmax(9rem, 1fr);
|
|
989
|
+
cursor: default;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.config-phase-open {
|
|
993
|
+
width: max-content;
|
|
994
|
+
border: 0;
|
|
995
|
+
padding: 0;
|
|
996
|
+
color: var(--accent);
|
|
997
|
+
background: transparent;
|
|
998
|
+
cursor: pointer;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.config-phase-open:focus-visible {
|
|
1002
|
+
outline: 2px solid var(--focus-ring);
|
|
1003
|
+
outline-offset: 3px;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
974
1006
|
.config-phase-row:hover {
|
|
975
1007
|
background: var(--surface-sunken);
|
|
976
1008
|
}
|
|
@@ -989,8 +1021,19 @@ progress {
|
|
|
989
1021
|
display: none;
|
|
990
1022
|
}
|
|
991
1023
|
|
|
1024
|
+
.config-routing-summary {
|
|
1025
|
+
margin-top: 1rem;
|
|
1026
|
+
padding-top: 1rem;
|
|
1027
|
+
border-top: 1px solid var(--border);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
.config-routing-form {
|
|
1031
|
+
grid-template-columns: minmax(8rem, 0.7fr) minmax(9rem, 0.7fr) auto auto minmax(10rem, 1fr);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
992
1034
|
.config-form select,
|
|
993
1035
|
.config-form input,
|
|
1036
|
+
.config-phase-row select,
|
|
994
1037
|
.config-save {
|
|
995
1038
|
min-height: 2.25rem;
|
|
996
1039
|
border: 1px solid var(--border-strong);
|
|
@@ -1000,6 +1043,10 @@ progress {
|
|
|
1000
1043
|
background: var(--surface);
|
|
1001
1044
|
}
|
|
1002
1045
|
|
|
1046
|
+
.config-phase-feedback {
|
|
1047
|
+
min-width: 0;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1003
1050
|
.config-save {
|
|
1004
1051
|
color: var(--accent-text);
|
|
1005
1052
|
border-color: var(--accent);
|
|
@@ -1064,6 +1111,10 @@ progress {
|
|
|
1064
1111
|
grid-template-columns: 1fr 1fr;
|
|
1065
1112
|
}
|
|
1066
1113
|
|
|
1114
|
+
.config-phase-row.is-editable {
|
|
1115
|
+
grid-template-columns: 1fr;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1067
1118
|
.config-form {
|
|
1068
1119
|
grid-template-columns: 1fr;
|
|
1069
1120
|
}
|