plan-assistant 1.4.5 → 1.5.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/build/client/_app/immutable/assets/0.Bc8GgJCN.css +1 -0
- package/build/client/_app/immutable/chunks/{DbxsK-Gw.js → 2MoIsY2H.js} +1 -1
- package/build/client/_app/immutable/chunks/{DCP7pAeu.js → 3hcDFuOy.js} +3 -3
- package/build/client/_app/immutable/chunks/{CF3N_j1A.js → 5WozQe49.js} +1 -1
- package/build/client/_app/immutable/chunks/{BCxCv_4N.js → 60AcEuIB.js} +1 -1
- package/build/client/_app/immutable/chunks/{BEobpKJ6.js → B5dqmTZt.js} +1 -1
- package/build/client/_app/immutable/chunks/{hHxGQnZj.js → B7UpUR8j.js} +1 -1
- package/build/client/_app/immutable/chunks/{CKmKshyc.js → BBUci_EN.js} +1 -1
- package/build/client/_app/immutable/chunks/{CadzRJ6P.js → BE3d1fW1.js} +1 -1
- package/build/client/_app/immutable/chunks/{BcKBKgcZ.js → BExD98LF.js} +1 -1
- package/build/client/_app/immutable/chunks/{Cumsgxi9.js → BHlQNJvI.js} +1 -1
- package/build/client/_app/immutable/chunks/{BdcZtdph.js → BLkN0eN4.js} +1 -1
- package/build/client/_app/immutable/chunks/{DBY3mAk0.js → BP4iV_qy.js} +1 -1
- package/build/client/_app/immutable/chunks/{B3CyqxdZ.js → BSmyMpB9.js} +1 -1
- package/build/client/_app/immutable/chunks/{CbLSu4oh.js → Bc8p4D6T.js} +1 -1
- package/build/client/_app/immutable/chunks/{Uy7RUP_Z.js → BkgST-kf.js} +1 -1
- package/build/client/_app/immutable/chunks/{BAG_vqoT.js → Bp1XOP3Y.js} +1 -1
- package/build/client/_app/immutable/chunks/{ClpyzqjJ.js → Bv2Fvpd-.js} +1 -1
- package/build/client/_app/immutable/chunks/{4dPdNqKL.js → BxbztSiI.js} +1 -1
- package/build/client/_app/immutable/chunks/{Dn9lSg-V.js → C6lsq0EQ.js} +1 -1
- package/build/client/_app/immutable/chunks/{x3JJhWj3.js → C8APfMGc.js} +1 -1
- package/build/client/_app/immutable/chunks/{C7FtzXJF.js → C8TKm6Ie.js} +1 -1
- package/build/client/_app/immutable/chunks/{zMHAx0Qk.js → CDe5iF5C.js} +1 -1
- package/build/client/_app/immutable/chunks/{ABPQj_I6.js → CLpU78SQ.js} +1 -1
- package/build/client/_app/immutable/chunks/{CWe1fJjs.js → CMEI75WY.js} +1 -1
- package/build/client/_app/immutable/chunks/{CfICpXHz.js → CQ3jUvrt.js} +1 -1
- package/build/client/_app/immutable/chunks/CT9cgEPP.js +1 -0
- package/build/client/_app/immutable/chunks/{DVaxwt1f.js → CaQ_EsFt.js} +1 -1
- package/build/client/_app/immutable/chunks/{DnZ1MoPi.js → Cklag6Wh.js} +1 -1
- package/build/client/_app/immutable/chunks/{J7DoTV-B.js → CsdQ7qj_.js} +1 -1
- package/build/client/_app/immutable/chunks/{yS11QLaS.js → CtvUvmNP.js} +1 -1
- package/build/client/_app/immutable/chunks/{B25Hd2fz.js → CuTEmqRN.js} +1 -1
- package/build/client/_app/immutable/chunks/{DXnvq78W.js → D-JOz5e5.js} +1 -1
- package/build/client/_app/immutable/chunks/{CE48ZXRi.js → D82Ibe2Y.js} +1 -1
- package/build/client/_app/immutable/chunks/{D01yuNpr.js → DAI9x5pz.js} +1 -1
- package/build/client/_app/immutable/chunks/{BRUI-1NR.js → DVyAwrwy.js} +1 -1
- package/build/client/_app/immutable/chunks/{gqH_bfvs.js → DX0JfudZ.js} +1 -1
- package/build/client/_app/immutable/chunks/DcuidqoU.js +1 -0
- package/build/client/_app/immutable/chunks/{BierXyQN.js → DgYrUT6W.js} +1 -1
- package/build/client/_app/immutable/chunks/{DA_DyJGG.js → DhhVlT3Q.js} +1 -1
- package/build/client/_app/immutable/chunks/{DJ4AwCJ9.js → DmIm4oWg.js} +1 -1
- package/build/client/_app/immutable/chunks/{D8aFfI6M.js → DmJI01FD.js} +1 -1
- package/build/client/_app/immutable/chunks/{1hoFVlwF.js → DwtNXPRV.js} +4 -4
- package/build/client/_app/immutable/chunks/JT4pIYWD.js +3 -0
- package/build/client/_app/immutable/chunks/{Bysr0gLv.js → NVoraB0x.js} +1 -1
- package/build/client/_app/immutable/chunks/{DmuA5Qzn.js → PzsI0t6r.js} +1 -1
- package/build/client/_app/immutable/chunks/{CmCoqHco.js → v0r-xbZ1.js} +1 -1
- package/build/client/_app/immutable/chunks/{Wv7WdG7L.js → wkCjElX-.js} +1 -1
- package/build/client/_app/immutable/entry/{app.BdutA_ce.js → app.3iFud8JB.js} +2 -2
- package/build/client/_app/immutable/entry/start.CfusPEA0.js +1 -0
- package/build/client/_app/immutable/nodes/{1.BBwvzKm5.js → 1.DV_Uu5i6.js} +1 -1
- package/build/client/_app/immutable/nodes/3.C2JIlVS_.js +1 -0
- package/build/client/_app/version.json +1 -1
- package/build/server/chunks/{0-DpiwjOzQ.js → 0-rXmaDIAK.js} +3 -3
- package/build/server/chunks/{0-DpiwjOzQ.js.map → 0-rXmaDIAK.js.map} +1 -1
- package/build/server/chunks/{1-DLxoXeuD.js → 1-B0EuD9t5.js} +2 -2
- package/build/server/chunks/{1-DLxoXeuD.js.map → 1-B0EuD9t5.js.map} +1 -1
- package/build/server/chunks/{2-Cy9PX2zK.js → 2-1iFROMDo.js} +2 -2
- package/build/server/chunks/{2-Cy9PX2zK.js.map → 2-1iFROMDo.js.map} +1 -1
- package/build/server/chunks/{3-DIjT-1nt.js → 3-ChRVOXhH.js} +4 -4
- package/build/server/chunks/{3-DIjT-1nt.js.map → 3-ChRVOXhH.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-XhzZOhTs.js → _page.svelte-Bg0Bxr-a.js} +1 -4
- package/build/server/chunks/_page.svelte-Bg0Bxr-a.js.map +1 -0
- package/build/server/chunks/{_server.ts-C9Rz5pqb.js → _server.ts-5D7tHXsH.js} +2 -2
- package/build/server/chunks/{_server.ts-C9Rz5pqb.js.map → _server.ts-5D7tHXsH.js.map} +1 -1
- package/build/server/chunks/{_server.ts-aWI5JVTi.js → _server.ts-B-Tg4-8H.js} +2 -2
- package/build/server/chunks/{_server.ts-aWI5JVTi.js.map → _server.ts-B-Tg4-8H.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DCa-_qe7.js → _server.ts-C91-5A8I.js} +2 -2
- package/build/server/chunks/{_server.ts-DCa-_qe7.js.map → _server.ts-C91-5A8I.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DVH0ejp0.js → _server.ts-DLQy-IHP.js} +2 -2
- package/build/server/chunks/{_server.ts-DVH0ejp0.js.map → _server.ts-DLQy-IHP.js.map} +1 -1
- package/build/server/chunks/{_server.ts-b4ynvdd4.js → _server.ts-H4qb8W_h.js} +2 -2
- package/build/server/chunks/{_server.ts-b4ynvdd4.js.map → _server.ts-H4qb8W_h.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BkcqX0Gw.js → _server.ts-VG3gOvsa.js} +2 -2
- package/build/server/chunks/{_server.ts-BkcqX0Gw.js.map → _server.ts-VG3gOvsa.js.map} +1 -1
- package/build/server/chunks/{hooks.server-sBuk2932.js → hooks.server-rdEjirDu.js} +2 -2
- package/build/server/chunks/{hooks.server-sBuk2932.js.map → hooks.server-rdEjirDu.js.map} +1 -1
- package/build/server/chunks/{session-manager-Vdr0BxAo.js → session-manager-Df52lIJ3.js} +30 -30
- package/build/server/chunks/session-manager-Df52lIJ3.js.map +1 -0
- package/build/server/index.js +2 -2
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +11 -18
- package/build/server/manifest.js.map +1 -1
- package/dist/cli/commands/clean.js +3 -2
- package/dist/cli/commands/export.js +4 -3
- package/dist/cli/commands/feedback.js +4 -3
- package/dist/cli/commands/init.js +2 -1
- package/dist/cli/commands/review.js +18 -121
- package/dist/cli/commands/status.js +18 -17
- package/dist/cli/commands/stop.js +2 -1
- package/dist/cli/errors.js +23 -0
- package/dist/cli/export-html.js +1 -1
- package/dist/cli/index.js +21 -5
- package/dist/cli/markdown-parser.js +125 -132
- package/dist/cli/markdown-to-plan.js +2 -10
- package/dist/cli/review-session.js +130 -0
- package/dist/cli/server-client.js +5 -12
- package/dist/cli/session-reader.js +8 -39
- package/dist/lib/server/session-manager.js +116 -0
- package/package.json +1 -1
- package/build/client/_app/immutable/assets/0.CltY7ilp.css +0 -1
- package/build/client/_app/immutable/chunks/CULHGyhn.js +0 -1
- package/build/client/_app/immutable/chunks/DPsQ0F5p.js +0 -3
- package/build/client/_app/immutable/chunks/UfQGGAux.js +0 -1
- package/build/client/_app/immutable/entry/start.CTfow7ge.js +0 -1
- package/build/client/_app/immutable/nodes/3.DHzG8WJV.js +0 -1
- package/build/server/chunks/_page.svelte-XhzZOhTs.js.map +0 -1
- package/build/server/chunks/_server.ts-r59HGb-h.js +0 -22
- package/build/server/chunks/_server.ts-r59HGb-h.js.map +0 -1
- package/build/server/chunks/session-manager-Vdr0BxAo.js.map +0 -1
- /package/build/client/_app/immutable/nodes/{0.D7ZwKtS7.js → 0.CuhA9F69.js} +0 -0
package/build/server/manifest.js
CHANGED
|
@@ -10,12 +10,12 @@ return {
|
|
|
10
10
|
assets: new Set(["favicon.png"]),
|
|
11
11
|
mimeTypes: {".png":"image/png"},
|
|
12
12
|
_: {
|
|
13
|
-
client: {start:"_app/immutable/entry/start.
|
|
13
|
+
client: {start:"_app/immutable/entry/start.CfusPEA0.js",app:"_app/immutable/entry/app.3iFud8JB.js",imports:["_app/immutable/entry/start.CfusPEA0.js","_app/immutable/chunks/JT4pIYWD.js","_app/immutable/chunks/CobJHAAW.js","_app/immutable/chunks/DGn8gxMH.js","_app/immutable/entry/app.3iFud8JB.js","_app/immutable/chunks/Dj-iuGW3.js","_app/immutable/chunks/CobJHAAW.js","_app/immutable/chunks/dRJp6O4P.js","_app/immutable/chunks/BrjAEg64.js","_app/immutable/chunks/DGn8gxMH.js","_app/immutable/chunks/BZQqs639.js","_app/immutable/chunks/C5wtj4WQ.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
|
14
14
|
nodes: [
|
|
15
|
-
__memo(() => import('./chunks/0-
|
|
16
|
-
__memo(() => import('./chunks/1-
|
|
17
|
-
__memo(() => import('./chunks/2-
|
|
18
|
-
__memo(() => import('./chunks/3-
|
|
15
|
+
__memo(() => import('./chunks/0-rXmaDIAK.js')),
|
|
16
|
+
__memo(() => import('./chunks/1-B0EuD9t5.js')),
|
|
17
|
+
__memo(() => import('./chunks/2-1iFROMDo.js')),
|
|
18
|
+
__memo(() => import('./chunks/3-ChRVOXhH.js'))
|
|
19
19
|
],
|
|
20
20
|
remotes: {
|
|
21
21
|
|
|
@@ -33,49 +33,42 @@ return {
|
|
|
33
33
|
pattern: /^\/api\/health\/?$/,
|
|
34
34
|
params: [],
|
|
35
35
|
page: null,
|
|
36
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
36
|
+
endpoint: __memo(() => import('./chunks/_server.ts-DLQy-IHP.js'))
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
id: "/api/sessions",
|
|
40
40
|
pattern: /^\/api\/sessions\/?$/,
|
|
41
41
|
params: [],
|
|
42
42
|
page: null,
|
|
43
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
43
|
+
endpoint: __memo(() => import('./chunks/_server.ts-B-Tg4-8H.js'))
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
id: "/api/sessions/[sessionId]",
|
|
47
47
|
pattern: /^\/api\/sessions\/([^/]+?)\/?$/,
|
|
48
48
|
params: [{"name":"sessionId","optional":false,"rest":false,"chained":false}],
|
|
49
49
|
page: null,
|
|
50
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
id: "/api/sessions/[sessionId]/approve",
|
|
54
|
-
pattern: /^\/api\/sessions\/([^/]+?)\/approve\/?$/,
|
|
55
|
-
params: [{"name":"sessionId","optional":false,"rest":false,"chained":false}],
|
|
56
|
-
page: null,
|
|
57
|
-
endpoint: __memo(() => import('./chunks/_server.ts-r59HGb-h.js'))
|
|
50
|
+
endpoint: __memo(() => import('./chunks/_server.ts-5D7tHXsH.js'))
|
|
58
51
|
},
|
|
59
52
|
{
|
|
60
53
|
id: "/api/sessions/[sessionId]/feedback",
|
|
61
54
|
pattern: /^\/api\/sessions\/([^/]+?)\/feedback\/?$/,
|
|
62
55
|
params: [{"name":"sessionId","optional":false,"rest":false,"chained":false}],
|
|
63
56
|
page: null,
|
|
64
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
57
|
+
endpoint: __memo(() => import('./chunks/_server.ts-C91-5A8I.js'))
|
|
65
58
|
},
|
|
66
59
|
{
|
|
67
60
|
id: "/api/sessions/[sessionId]/versions",
|
|
68
61
|
pattern: /^\/api\/sessions\/([^/]+?)\/versions\/?$/,
|
|
69
62
|
params: [{"name":"sessionId","optional":false,"rest":false,"chained":false}],
|
|
70
63
|
page: null,
|
|
71
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
64
|
+
endpoint: __memo(() => import('./chunks/_server.ts-VG3gOvsa.js'))
|
|
72
65
|
},
|
|
73
66
|
{
|
|
74
67
|
id: "/api/sessions/[sessionId]/versions/[version]",
|
|
75
68
|
pattern: /^\/api\/sessions\/([^/]+?)\/versions\/([^/]+?)\/?$/,
|
|
76
69
|
params: [{"name":"sessionId","optional":false,"rest":false,"chained":false},{"name":"version","optional":false,"rest":false,"chained":false}],
|
|
77
70
|
page: null,
|
|
78
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
71
|
+
endpoint: __memo(() => import('./chunks/_server.ts-H4qb8W_h.js'))
|
|
79
72
|
},
|
|
80
73
|
{
|
|
81
74
|
id: "/api/shutdown",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sources":["../../.svelte-kit/adapter-node/manifest.js"],"sourcesContent":["export const manifest = (() => {\nfunction __memo(fn) {\n\tlet value;\n\treturn () => value ??= (value = fn());\n}\n\nreturn {\n\tappDir: \"_app\",\n\tappPath: \"_app\",\n\tassets: new Set([\"favicon.png\"]),\n\tmimeTypes: {\".png\":\"image/png\"},\n\t_: {\n\t\tclient: {start:\"_app/immutable/entry/start.
|
|
1
|
+
{"version":3,"file":"manifest.js","sources":["../../.svelte-kit/adapter-node/manifest.js"],"sourcesContent":["export const manifest = (() => {\nfunction __memo(fn) {\n\tlet value;\n\treturn () => value ??= (value = fn());\n}\n\nreturn {\n\tappDir: \"_app\",\n\tappPath: \"_app\",\n\tassets: new Set([\"favicon.png\"]),\n\tmimeTypes: {\".png\":\"image/png\"},\n\t_: {\n\t\tclient: {start:\"_app/immutable/entry/start.CfusPEA0.js\",app:\"_app/immutable/entry/app.3iFud8JB.js\",imports:[\"_app/immutable/entry/start.CfusPEA0.js\",\"_app/immutable/chunks/JT4pIYWD.js\",\"_app/immutable/chunks/CobJHAAW.js\",\"_app/immutable/chunks/DGn8gxMH.js\",\"_app/immutable/entry/app.3iFud8JB.js\",\"_app/immutable/chunks/Dj-iuGW3.js\",\"_app/immutable/chunks/CobJHAAW.js\",\"_app/immutable/chunks/dRJp6O4P.js\",\"_app/immutable/chunks/BrjAEg64.js\",\"_app/immutable/chunks/DGn8gxMH.js\",\"_app/immutable/chunks/BZQqs639.js\",\"_app/immutable/chunks/C5wtj4WQ.js\"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},\n\t\tnodes: [\n\t\t\t__memo(() => import('./nodes/0.js')),\n\t\t\t__memo(() => import('./nodes/1.js')),\n\t\t\t__memo(() => import('./nodes/2.js')),\n\t\t\t__memo(() => import('./nodes/3.js'))\n\t\t],\n\t\tremotes: {\n\t\t\t\n\t\t},\n\t\troutes: [\n\t\t\t{\n\t\t\t\tid: \"/\",\n\t\t\t\tpattern: /^\\/$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: { layouts: [0,], errors: [1,], leaf: 2 },\n\t\t\t\tendpoint: null\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/health\",\n\t\t\t\tpattern: /^\\/api\\/health\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/health/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]/feedback\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/feedback\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/feedback/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]/versions\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/versions\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/versions/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]/versions/[version]\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/versions\\/([^/]+?)\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false},{\"name\":\"version\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/versions/_version_/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/shutdown\",\n\t\t\t\tpattern: /^\\/api\\/shutdown\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/shutdown/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sse/[sessionId]\",\n\t\t\t\tpattern: /^\\/api\\/sse\\/([^/]+?)\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sse/_sessionId_/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/plan/[sessionId]\",\n\t\t\t\tpattern: /^\\/plan\\/([^/]+?)\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: { layouts: [0,], errors: [1,], leaf: 3 },\n\t\t\t\tendpoint: null\n\t\t\t}\n\t\t],\n\t\tprerendered_routes: new Set([]),\n\t\tmatchers: async () => {\n\t\t\t\n\t\t\treturn { };\n\t\t},\n\t\tserver_assets: {}\n\t}\n}\n})();\n\nexport const prerendered = new Set([]);\n\nexport const base = \"\";"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { findSessionDirs } from "../session-reader.js";
|
|
4
4
|
import { outputJson, outputError } from "../output.js";
|
|
5
5
|
import { parseDuration } from "../utils.js";
|
|
6
|
+
import { CliError, CliExitCode } from "../errors.js";
|
|
6
7
|
import * as readline from "node:readline";
|
|
7
8
|
async function confirm(message) {
|
|
8
9
|
const rl = readline.createInterface({
|
|
@@ -31,7 +32,7 @@ export async function clean(args) {
|
|
|
31
32
|
olderThanMs = parseDuration(olderThanStr);
|
|
32
33
|
if (olderThanMs === null) {
|
|
33
34
|
outputError(`Invalid duration: ${olderThanStr}. Use format like 7d, 24h, 2w`, "INVALID_DURATION");
|
|
34
|
-
|
|
35
|
+
throw new CliError(`Invalid duration: ${olderThanStr}`);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
const sessions = findSessionDirs(dir);
|
|
@@ -106,7 +107,7 @@ export async function clean(args) {
|
|
|
106
107
|
const ok = await confirm("Continue?");
|
|
107
108
|
if (!ok) {
|
|
108
109
|
console.error("Aborted.");
|
|
109
|
-
|
|
110
|
+
throw new CliExitCode(0);
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
let removed = 0;
|
|
@@ -4,21 +4,22 @@ import { resolveSession } from "../session-resolver.js";
|
|
|
4
4
|
import { readPlan, readFeedback } from "../session-reader.js";
|
|
5
5
|
import { renderPlanToHtml } from "../export-html.js";
|
|
6
6
|
import { outputError } from "../output.js";
|
|
7
|
+
import { CliError } from "../errors.js";
|
|
7
8
|
export async function exportCmd(args) {
|
|
8
9
|
const idOrFile = args.positional[0];
|
|
9
10
|
if (!idOrFile) {
|
|
10
11
|
outputError("Please provide a session ID or markdown file path", "MISSING_ARG");
|
|
11
|
-
|
|
12
|
+
throw new CliError("Missing session ID or file path");
|
|
12
13
|
}
|
|
13
14
|
const resolved = resolveSession(idOrFile);
|
|
14
15
|
if (!resolved) {
|
|
15
16
|
outputError(`Session not found for: ${idOrFile}`, "NOT_FOUND");
|
|
16
|
-
|
|
17
|
+
throw new CliError(`Session not found for: ${idOrFile}`);
|
|
17
18
|
}
|
|
18
19
|
const plan = readPlan(resolved.sessionDir);
|
|
19
20
|
if (!plan) {
|
|
20
21
|
outputError("Could not read plan data", "READ_ERROR");
|
|
21
|
-
|
|
22
|
+
throw new CliError("Could not read plan data");
|
|
22
23
|
}
|
|
23
24
|
const feedback = readFeedback(resolved.sessionDir);
|
|
24
25
|
const html = renderPlanToHtml(plan, feedback);
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { resolveSession } from "../session-resolver.js";
|
|
2
2
|
import { readFeedback } from "../session-reader.js";
|
|
3
3
|
import { outputJson, outputError } from "../output.js";
|
|
4
|
+
import { CliError } from "../errors.js";
|
|
4
5
|
export async function feedback(args) {
|
|
5
6
|
const idOrFile = args.positional[0];
|
|
6
7
|
if (!idOrFile) {
|
|
7
8
|
outputError("Please provide a session ID or markdown file path", "MISSING_ARG");
|
|
8
|
-
|
|
9
|
+
throw new CliError("Missing session ID or file path");
|
|
9
10
|
}
|
|
10
11
|
const resolved = resolveSession(idOrFile);
|
|
11
12
|
if (!resolved) {
|
|
12
13
|
outputError(`Session not found for: ${idOrFile}`, "NOT_FOUND");
|
|
13
|
-
|
|
14
|
+
throw new CliError(`Session not found for: ${idOrFile}`);
|
|
14
15
|
}
|
|
15
16
|
const feedback = readFeedback(resolved.sessionDir);
|
|
16
17
|
if (!feedback) {
|
|
17
18
|
outputError("No feedback found for this session", "NO_FEEDBACK");
|
|
18
|
-
|
|
19
|
+
throw new CliError("No feedback found for this session");
|
|
19
20
|
}
|
|
20
21
|
const phaseFilter = typeof args.flags.phase === "string" ? args.flags.phase : undefined;
|
|
21
22
|
const unresolvedOnly = args.flags.unresolved === true;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { writeFileSync, existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { outputError } from "../output.js";
|
|
4
|
+
import { CliError } from "../errors.js";
|
|
4
5
|
const TEMPLATE = `# [Feature Name] Implementation Plan
|
|
5
6
|
|
|
6
7
|
<!-- Plan Assistant template. Replace bracketed placeholders with your content. -->
|
|
@@ -113,7 +114,7 @@ export async function init(args) {
|
|
|
113
114
|
const path = resolve(outputFile);
|
|
114
115
|
if (existsSync(path)) {
|
|
115
116
|
outputError(`File already exists: ${path}`, "FILE_EXISTS");
|
|
116
|
-
|
|
117
|
+
throw new CliError(`File already exists: ${path}`);
|
|
117
118
|
}
|
|
118
119
|
writeFileSync(path, TEMPLATE, "utf-8");
|
|
119
120
|
console.error(`Template written to ${path}`);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { watch } from "chokidar";
|
|
4
|
-
import { parseMarkdownToPlan, sessionIdFromPath } from "../markdown-to-plan.js";
|
|
1
|
+
import { existsSync, mkdirSync, symlinkSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
5
3
|
import { outputJson } from "../output.js";
|
|
6
|
-
import {
|
|
4
|
+
import { awaitReviewFeedback } from "../session-reader.js";
|
|
5
|
+
import { prepareSession, watchMarkdownFile } from "../review-session.js";
|
|
7
6
|
import { DEFAULT_BASE_PORT, MAX_PORT, findExistingServer, checkHealth, isPortFree, findFreePort, launchServer, openBrowser, } from "../server-client.js";
|
|
7
|
+
import { CliError } from "../errors.js";
|
|
8
8
|
export async function review(args) {
|
|
9
9
|
const markdownFile = args.positional[0];
|
|
10
10
|
if (!markdownFile) {
|
|
11
11
|
console.error("Error: Please provide a markdown file path");
|
|
12
12
|
console.error("Usage: plan-assistant review <markdown-file>");
|
|
13
|
-
|
|
13
|
+
throw new CliError("Missing markdown file path");
|
|
14
14
|
}
|
|
15
15
|
// Parse host configuration (for Docker/remote sandbox environments)
|
|
16
16
|
const hostFlag = args.flags.host;
|
|
@@ -24,103 +24,27 @@ export async function review(args) {
|
|
|
24
24
|
if (typeof portFlag === "string") {
|
|
25
25
|
requestedPort = parseInt(portFlag, 10);
|
|
26
26
|
if (isNaN(requestedPort)) {
|
|
27
|
-
|
|
28
|
-
process.exit(1);
|
|
27
|
+
throw new CliError(`Invalid port number: ${portFlag}`);
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
else if (envPort) {
|
|
32
31
|
requestedPort = parseInt(envPort, 10);
|
|
33
32
|
if (isNaN(requestedPort)) {
|
|
34
|
-
|
|
35
|
-
process.exit(1);
|
|
33
|
+
throw new CliError(`Invalid PLAN_ASSISTANT_PORT: ${envPort}`);
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
const sessionDir = join(dirname(absolutePath), ".plan-sessions");
|
|
44
|
-
const sessionId = sessionIdFromPath(absolutePath);
|
|
45
|
-
const sessionPath = join(sessionDir, sessionId);
|
|
46
|
-
// Parse markdown
|
|
47
|
-
const markdown = readFileSync(absolutePath, "utf-8");
|
|
48
|
-
const projectDir = process.cwd();
|
|
49
|
-
let version = 1;
|
|
50
|
-
const existingPlanPath = join(sessionPath, "plan.json");
|
|
51
|
-
if (existsSync(existingPlanPath)) {
|
|
52
|
-
try {
|
|
53
|
-
const existing = JSON.parse(readFileSync(existingPlanPath, "utf-8"));
|
|
54
|
-
version = (existing.meta?.version ?? 0) + 1;
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// ignore, start at 1
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const { plan, warnings } = parseMarkdownToPlan(markdown, absolutePath, projectDir, version);
|
|
61
|
-
// Display any parser warnings
|
|
62
|
-
for (const warning of warnings) {
|
|
63
|
-
console.error(`Warning: ${warning}`);
|
|
64
|
-
}
|
|
65
|
-
if (plan.phases.length === 0) {
|
|
66
|
-
console.error(`
|
|
67
|
-
⚠ No phases found — plan will appear empty in the browser.
|
|
68
|
-
|
|
69
|
-
Use the correct format for phases:
|
|
70
|
-
|
|
71
|
-
## Phase 1: Phase Name
|
|
72
|
-
|
|
73
|
-
### Changes Required:
|
|
74
|
-
|
|
75
|
-
#### 1. Component Name
|
|
76
|
-
**File**: \`path/to/file.ext\`
|
|
77
|
-
Description of what to change.
|
|
78
|
-
|
|
79
|
-
### Success Criteria:
|
|
80
|
-
- [ ] \`npm test\`
|
|
81
|
-
|
|
82
|
-
Accepted phase keywords: "Phase N:", "Step N:", "Task N:" (H2 headings)
|
|
83
|
-
Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatted template.
|
|
84
|
-
`);
|
|
85
|
-
}
|
|
86
|
-
// Write session files
|
|
87
|
-
mkdirSync(sessionPath, { recursive: true });
|
|
88
|
-
mkdirSync(join(sessionPath, "versions"), { recursive: true });
|
|
89
|
-
// Clear stale feedback from previous review cycle
|
|
90
|
-
const oldFeedbackPath = join(sessionPath, "feedback.json");
|
|
91
|
-
if (existsSync(oldFeedbackPath)) {
|
|
92
|
-
try {
|
|
93
|
-
unlinkSync(oldFeedbackPath);
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// ignore
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
const meta = {
|
|
100
|
-
id: sessionId,
|
|
101
|
-
planTitle: plan.meta.title,
|
|
102
|
-
markdownPath: absolutePath,
|
|
103
|
-
projectDir,
|
|
104
|
-
status: "active",
|
|
105
|
-
planVersion: version,
|
|
106
|
-
createdAt: plan.meta.createdAt,
|
|
107
|
-
updatedAt: plan.meta.updatedAt,
|
|
108
|
-
};
|
|
109
|
-
writeFileSync(join(sessionPath, "meta.json"), JSON.stringify(meta, null, 2));
|
|
110
|
-
writeFileSync(join(sessionPath, "plan.json"), JSON.stringify(plan, null, 2));
|
|
111
|
-
writeFileSync(join(sessionPath, "versions", `v${version}.json`), JSON.stringify(plan, null, 2));
|
|
112
|
-
// Find existing server for this session dir or start a new one
|
|
36
|
+
// --- Session setup (extracted to review-session.ts) ---
|
|
37
|
+
const session = prepareSession(markdownFile);
|
|
38
|
+
const { sessionId, sessionPath, sessionDir, plan, version, absolutePath, existingPlanPath, } = session;
|
|
39
|
+
// --- Server lifecycle ---
|
|
113
40
|
const reuse = args.flags.reuse === true;
|
|
114
41
|
const basePort = requestedPort ?? DEFAULT_BASE_PORT;
|
|
115
42
|
let port = await findExistingServer(sessionDir, basePort);
|
|
116
43
|
if (!port && reuse) {
|
|
117
|
-
// --reuse: find any running plan-assistant server and reuse it
|
|
118
44
|
for (let p = basePort; p <= MAX_PORT; p++) {
|
|
119
45
|
const health = await checkHealth(p);
|
|
120
46
|
if (health) {
|
|
121
47
|
port = p;
|
|
122
|
-
// Cross-project reuse: the server only knows its own SESSION_DIR.
|
|
123
|
-
// Symlink our session into the server's directory so it can find it.
|
|
124
48
|
if (health.sessionDir !== sessionDir) {
|
|
125
49
|
const linkPath = join(health.sessionDir, sessionId);
|
|
126
50
|
if (!existsSync(linkPath)) {
|
|
@@ -137,7 +61,6 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
137
61
|
if (!port) {
|
|
138
62
|
if (requestedPort) {
|
|
139
63
|
if (!(await isPortFree(requestedPort))) {
|
|
140
|
-
// Check if it's a plan-assistant server for a better error message
|
|
141
64
|
const health = await checkHealth(requestedPort);
|
|
142
65
|
if (health) {
|
|
143
66
|
console.error(`Error: Port ${requestedPort} is already used by Plan Assistant (session dir: ${health.sessionDir}).` +
|
|
@@ -146,7 +69,7 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
146
69
|
else {
|
|
147
70
|
console.error(`Error: Port ${requestedPort} is already in use by another process.`);
|
|
148
71
|
}
|
|
149
|
-
|
|
72
|
+
throw new CliError(`Port ${requestedPort} is already in use`);
|
|
150
73
|
}
|
|
151
74
|
port = requestedPort;
|
|
152
75
|
}
|
|
@@ -155,9 +78,7 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
155
78
|
}
|
|
156
79
|
await launchServer(sessionDir, port);
|
|
157
80
|
}
|
|
158
|
-
// At this point port is always assigned (server found or started above)
|
|
159
81
|
const serverPort = port;
|
|
160
|
-
// Helper: check server health and restart if it died (e.g. idle timeout)
|
|
161
82
|
async function ensureServer() {
|
|
162
83
|
const health = await checkHealth(serverPort);
|
|
163
84
|
if (!health) {
|
|
@@ -165,7 +86,7 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
165
86
|
await launchServer(sessionDir, serverPort);
|
|
166
87
|
}
|
|
167
88
|
}
|
|
168
|
-
// Keepalive
|
|
89
|
+
// Keepalive
|
|
169
90
|
const KEEPALIVE_MS = 2 * 60 * 1000;
|
|
170
91
|
const keepaliveInterval = setInterval(async () => {
|
|
171
92
|
try {
|
|
@@ -177,7 +98,7 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
177
98
|
}, KEEPALIVE_MS);
|
|
178
99
|
const url = `http://${displayHost}:${serverPort}/plan/${sessionId}`;
|
|
179
100
|
const feedbackPath = join(sessionPath, "feedback.json");
|
|
180
|
-
// Machine-readable ready event
|
|
101
|
+
// Machine-readable ready event
|
|
181
102
|
outputJson({
|
|
182
103
|
event: "ready",
|
|
183
104
|
sessionId,
|
|
@@ -203,35 +124,11 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
203
124
|
console.error(`\nWatching for changes and waiting for your feedback (Approve / Request Changes)...`);
|
|
204
125
|
console.error(`Press Ctrl+C to stop without waiting.`);
|
|
205
126
|
}
|
|
206
|
-
//
|
|
207
|
-
const mdWatcher =
|
|
208
|
-
awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 },
|
|
209
|
-
});
|
|
210
|
-
mdWatcher.on("change", async () => {
|
|
211
|
-
try {
|
|
212
|
-
// Ensure server is alive before writing updated plan
|
|
213
|
-
await ensureServer();
|
|
214
|
-
const updated = readFileSync(absolutePath, "utf-8");
|
|
215
|
-
const existingPlan = JSON.parse(readFileSync(existingPlanPath, "utf-8"));
|
|
216
|
-
const newVersion = (existingPlan.meta?.version ?? 0) + 1;
|
|
217
|
-
const { plan: newPlan, warnings: newWarnings } = parseMarkdownToPlan(updated, absolutePath, projectDir, newVersion);
|
|
218
|
-
for (const warning of newWarnings) {
|
|
219
|
-
console.error(`Warning: ${warning}`);
|
|
220
|
-
}
|
|
221
|
-
if (newPlan.phases.length === 0) {
|
|
222
|
-
console.error(`⚠ No phases found after reload — check format. Run \`npx plan-assistant init\` for a template.`);
|
|
223
|
-
}
|
|
224
|
-
writeFileSync(join(sessionPath, "plan.json"), JSON.stringify(newPlan, null, 2));
|
|
225
|
-
writeFileSync(join(sessionPath, "versions", `v${newVersion}.json`), JSON.stringify(newPlan, null, 2));
|
|
226
|
-
console.error(`[${new Date().toLocaleTimeString()}] Plan updated (v${newVersion})`);
|
|
227
|
-
}
|
|
228
|
-
catch (err) {
|
|
229
|
-
console.error(`Error re-parsing markdown: ${err}`);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
127
|
+
// --- File watching (extracted to review-session.ts) ---
|
|
128
|
+
const mdWatcher = watchMarkdownFile(absolutePath, sessionPath, session.projectDir, existingPlanPath, ensureServer);
|
|
232
129
|
// Wait for feedback unless --no-wait
|
|
233
130
|
if (!noWait) {
|
|
234
|
-
await
|
|
131
|
+
await awaitReviewFeedback(feedbackPath, sessionId, plan.meta.title, mdWatcher, sessionPath);
|
|
235
132
|
clearInterval(keepaliveInterval);
|
|
236
133
|
return;
|
|
237
134
|
}
|
|
@@ -2,16 +2,17 @@ import { resolveSession } from "../session-resolver.js";
|
|
|
2
2
|
import { readMeta, readFeedback } from "../session-reader.js";
|
|
3
3
|
import { outputJson, outputError } from "../output.js";
|
|
4
4
|
import { parseDuration } from "../utils.js";
|
|
5
|
+
import { CliError, CliExitCode } from "../errors.js";
|
|
5
6
|
import { watch } from "chokidar";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
9
|
// Exit codes
|
|
9
|
-
const EXIT_APPROVED = 0;
|
|
10
|
-
const EXIT_ERROR = 1;
|
|
11
|
-
const EXIT_NEEDS_WORK = 3;
|
|
12
|
-
const EXIT_REVIEWING = 4;
|
|
13
|
-
const EXIT_NO_FEEDBACK = 5;
|
|
14
|
-
function computeStatus(feedback) {
|
|
10
|
+
export const EXIT_APPROVED = 0;
|
|
11
|
+
export const EXIT_ERROR = 1;
|
|
12
|
+
export const EXIT_NEEDS_WORK = 3;
|
|
13
|
+
export const EXIT_REVIEWING = 4;
|
|
14
|
+
export const EXIT_NO_FEEDBACK = 5;
|
|
15
|
+
export function computeStatus(feedback) {
|
|
15
16
|
if (!feedback)
|
|
16
17
|
return { feedbackStatus: "none", exitCode: EXIT_NO_FEEDBACK };
|
|
17
18
|
switch (feedback.status) {
|
|
@@ -24,7 +25,7 @@ function computeStatus(feedback) {
|
|
|
24
25
|
return { feedbackStatus: feedback.status, exitCode: EXIT_REVIEWING };
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
|
-
function computeSummary(feedback) {
|
|
28
|
+
export function computeSummary(feedback) {
|
|
28
29
|
const phaseSummary = { total: 0, approved: 0, needsWork: 0, pending: 0 };
|
|
29
30
|
const commentSummary = { total: 0, unresolved: 0 };
|
|
30
31
|
if (feedback) {
|
|
@@ -46,17 +47,17 @@ export async function status(args) {
|
|
|
46
47
|
const idOrFile = args.positional[0];
|
|
47
48
|
if (!idOrFile) {
|
|
48
49
|
outputError("Please provide a session ID or markdown file path", "MISSING_ARG");
|
|
49
|
-
|
|
50
|
+
throw new CliError("Missing session ID or file path");
|
|
50
51
|
}
|
|
51
52
|
const resolved = resolveSession(idOrFile);
|
|
52
53
|
if (!resolved) {
|
|
53
54
|
outputError(`Session not found for: ${idOrFile}`, "NOT_FOUND");
|
|
54
|
-
|
|
55
|
+
throw new CliError(`Session not found for: ${idOrFile}`);
|
|
55
56
|
}
|
|
56
57
|
const meta = readMeta(resolved.sessionDir);
|
|
57
58
|
if (!meta) {
|
|
58
59
|
outputError(`Could not read session metadata`, "READ_ERROR");
|
|
59
|
-
|
|
60
|
+
throw new CliError("Could not read session metadata");
|
|
60
61
|
}
|
|
61
62
|
const shouldWait = args.flags.wait === true;
|
|
62
63
|
if (shouldWait) {
|
|
@@ -64,7 +65,7 @@ export async function status(args) {
|
|
|
64
65
|
const timeoutMs = typeof timeoutStr === "string"
|
|
65
66
|
? (parseDuration(timeoutStr) ?? 30 * 60 * 1000)
|
|
66
67
|
: 30 * 60 * 1000; // 30 min default
|
|
67
|
-
await
|
|
68
|
+
await pollFeedbackStatus(resolved.sessionDir, resolved.sessionId, meta, timeoutMs);
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
70
71
|
const rawFeedback = readFeedback(resolved.sessionDir);
|
|
@@ -83,9 +84,9 @@ export async function status(args) {
|
|
|
83
84
|
phaseSummary,
|
|
84
85
|
commentSummary,
|
|
85
86
|
}, args.flags.pretty === true);
|
|
86
|
-
|
|
87
|
+
throw new CliExitCode(exitCode);
|
|
87
88
|
}
|
|
88
|
-
async function
|
|
89
|
+
async function pollFeedbackStatus(sessionDir, sessionId, meta, timeoutMs) {
|
|
89
90
|
// Check current state first (ignore stale feedback from older plan versions)
|
|
90
91
|
const current = readFeedback(sessionDir);
|
|
91
92
|
if (current &&
|
|
@@ -101,15 +102,15 @@ async function waitForFeedback(sessionDir, sessionId, meta, timeoutMs) {
|
|
|
101
102
|
phaseSummary,
|
|
102
103
|
commentSummary,
|
|
103
104
|
});
|
|
104
|
-
|
|
105
|
+
throw new CliExitCode(exitCode);
|
|
105
106
|
}
|
|
106
107
|
// Watch for changes
|
|
107
108
|
const feedbackPath = join(sessionDir, "feedback.json");
|
|
108
|
-
return new Promise((resolve) => {
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
109
110
|
const timer = setTimeout(() => {
|
|
110
111
|
watcher.close();
|
|
111
112
|
outputError("Timed out waiting for feedback", "TIMEOUT");
|
|
112
|
-
|
|
113
|
+
reject(new CliError("Timed out waiting for feedback"));
|
|
113
114
|
}, timeoutMs);
|
|
114
115
|
const checkFile = () => {
|
|
115
116
|
try {
|
|
@@ -130,7 +131,7 @@ async function waitForFeedback(sessionDir, sessionId, meta, timeoutMs) {
|
|
|
130
131
|
phaseSummary,
|
|
131
132
|
commentSummary,
|
|
132
133
|
});
|
|
133
|
-
|
|
134
|
+
reject(new CliExitCode(exitCode));
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
catch {
|
|
@@ -2,6 +2,7 @@ import { dirname } from "node:path";
|
|
|
2
2
|
import { resolveSession } from "../session-resolver.js";
|
|
3
3
|
import { stopServer, checkHealth, DEFAULT_BASE_PORT, MAX_PORT, } from "../server-client.js";
|
|
4
4
|
import { outputJson, outputError } from "../output.js";
|
|
5
|
+
import { CliError } from "../errors.js";
|
|
5
6
|
export async function stop(args) {
|
|
6
7
|
const target = args.positional[0];
|
|
7
8
|
if (target) {
|
|
@@ -9,7 +10,7 @@ export async function stop(args) {
|
|
|
9
10
|
const session = resolveSession(target);
|
|
10
11
|
if (!session) {
|
|
11
12
|
outputError(`Session not found: ${target}`, "NOT_FOUND");
|
|
12
|
-
|
|
13
|
+
throw new CliError(`Session not found: ${target}`);
|
|
13
14
|
}
|
|
14
15
|
const parentDir = dirname(session.sessionDir);
|
|
15
16
|
const stopped = await stopServer(parentDir);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown by CLI commands to signal an error exit.
|
|
3
|
+
* Caught by main() which writes message to stderr and calls process.exit(exitCode).
|
|
4
|
+
*/
|
|
5
|
+
export class CliError extends Error {
|
|
6
|
+
exitCode;
|
|
7
|
+
constructor(message, exitCode = 1) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.exitCode = exitCode;
|
|
10
|
+
this.name = "CliError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Thrown by CLI commands to signal intentional non-error exit with a specific code.
|
|
15
|
+
* For example, `status` exits with code 3 for "needs-work" — not an error, just a signal.
|
|
16
|
+
* Caught by main() which calls process.exit(exitCode) without printing an error.
|
|
17
|
+
*/
|
|
18
|
+
export class CliExitCode {
|
|
19
|
+
exitCode;
|
|
20
|
+
constructor(exitCode) {
|
|
21
|
+
this.exitCode = exitCode;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/dist/cli/export-html.js
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -130,8 +130,8 @@ Optional top-level sections (all H2):
|
|
|
130
130
|
## Testing Strategy
|
|
131
131
|
## References`);
|
|
132
132
|
}
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
import { CliError, CliExitCode } from "./errors.js";
|
|
134
|
+
async function dispatch(parsed) {
|
|
135
135
|
switch (parsed.command) {
|
|
136
136
|
case "review": {
|
|
137
137
|
const { review } = await import("./commands/review.js");
|
|
@@ -176,8 +176,24 @@ export async function main(args) {
|
|
|
176
176
|
}
|
|
177
177
|
return;
|
|
178
178
|
default:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
throw new CliError(`Unknown command: ${parsed.command}`, 1);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export async function main(args) {
|
|
183
|
+
const parsed = parseArgs(args);
|
|
184
|
+
try {
|
|
185
|
+
await dispatch(parsed);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
if (err instanceof CliExitCode) {
|
|
189
|
+
process.exit(err.exitCode);
|
|
190
|
+
}
|
|
191
|
+
if (err instanceof CliError) {
|
|
192
|
+
console.error(err.message);
|
|
193
|
+
process.exit(err.exitCode);
|
|
194
|
+
}
|
|
195
|
+
// Unexpected error
|
|
196
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
197
|
+
process.exit(1);
|
|
182
198
|
}
|
|
183
199
|
}
|