plan-assistant 1.4.5 → 1.6.0-alpha.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 +31 -12
- package/build/client/_app/immutable/assets/0.Bc8GgJCN.css +1 -0
- package/build/client/_app/immutable/chunks/{ABPQj_I6.js → 5KpFG7Y3.js} +1 -1
- package/build/client/_app/immutable/chunks/AMggw8F_.js +1 -0
- package/build/client/_app/immutable/chunks/{DCP7pAeu.js → B0Br67Jm.js} +22 -22
- package/build/client/_app/immutable/chunks/{D01yuNpr.js → BGuzEOEG.js} +1 -1
- package/build/client/_app/immutable/chunks/{x3JJhWj3.js → BPC30ZkC.js} +1 -1
- package/build/client/_app/immutable/chunks/{Dn9lSg-V.js → BT-rYp04.js} +1 -1
- package/build/client/_app/immutable/chunks/{B25Hd2fz.js → BXachUYg.js} +1 -1
- package/build/client/_app/immutable/chunks/{CKmKshyc.js → BdxNwExl.js} +1 -1
- package/build/client/_app/immutable/chunks/{BcKBKgcZ.js → Bdy6d4YN.js} +1 -1
- package/build/client/_app/immutable/chunks/{ClpyzqjJ.js → BkkBWYh4.js} +1 -1
- package/build/client/_app/immutable/chunks/{yS11QLaS.js → Bmsplg2Y.js} +1 -1
- package/build/client/_app/immutable/chunks/{hHxGQnZj.js → Bn0XLNse.js} +1 -1
- package/build/client/_app/immutable/chunks/{J7DoTV-B.js → BuxFeYZa.js} +1 -1
- package/build/client/_app/immutable/chunks/{DJ4AwCJ9.js → C2OLrLCJ.js} +1 -1
- package/build/client/_app/immutable/chunks/C88p_n1l.js +3 -0
- package/build/client/_app/immutable/chunks/{DVaxwt1f.js → CFomSPzW.js} +1 -1
- package/build/client/_app/immutable/chunks/{BRUI-1NR.js → CFx4VLDw.js} +1 -1
- package/build/client/_app/immutable/chunks/{DBY3mAk0.js → CRUUUIwS.js} +1 -1
- package/build/client/_app/immutable/chunks/{CfICpXHz.js → CRuCklg1.js} +1 -1
- package/build/client/_app/immutable/chunks/{DmuA5Qzn.js → CUHvMbx0.js} +1 -1
- package/build/client/_app/immutable/chunks/{CadzRJ6P.js → C_5efo6-.js} +1 -1
- package/build/client/_app/immutable/chunks/{BierXyQN.js → Cb177sm-.js} +1 -1
- package/build/client/_app/immutable/chunks/{CWe1fJjs.js → Cb9ekAV0.js} +1 -1
- package/build/client/_app/immutable/chunks/{DnZ1MoPi.js → CdiHcRRA.js} +1 -1
- package/build/client/_app/immutable/chunks/{BAG_vqoT.js → CgIOtWnS.js} +1 -1
- package/build/client/_app/immutable/chunks/{CbLSu4oh.js → CghPw8Xx.js} +1 -1
- package/build/client/_app/immutable/chunks/{Uy7RUP_Z.js → Cmym6IfH.js} +1 -1
- package/build/client/_app/immutable/chunks/{C7FtzXJF.js → Cvpo3167.js} +1 -1
- package/build/client/_app/immutable/chunks/{4dPdNqKL.js → Cy0JyQfD.js} +1 -1
- package/build/client/_app/immutable/chunks/{DXnvq78W.js → D-0mahX7.js} +1 -1
- package/build/client/_app/immutable/chunks/{gqH_bfvs.js → D8823oiN.js} +1 -1
- package/build/client/_app/immutable/chunks/{BCxCv_4N.js → DIjXbgfi.js} +1 -1
- package/build/client/_app/immutable/chunks/{CF3N_j1A.js → DUIJXtzj.js} +1 -1
- package/build/client/_app/immutable/chunks/{DbxsK-Gw.js → DXlvMCt6.js} +1 -1
- package/build/client/_app/immutable/chunks/{DA_DyJGG.js → Dffa6mVP.js} +1 -1
- package/build/client/_app/immutable/chunks/{D8aFfI6M.js → DgzenGSu.js} +1 -1
- package/build/client/_app/immutable/chunks/{1hoFVlwF.js → DqaGnr0n.js} +4 -4
- package/build/client/_app/immutable/chunks/{CE48ZXRi.js → Drf3oeac.js} +1 -1
- package/build/client/_app/immutable/chunks/{CmCoqHco.js → EBvoHjF1.js} +1 -1
- package/build/client/_app/immutable/chunks/{Cumsgxi9.js → Fqo6xqtt.js} +1 -1
- package/build/client/_app/immutable/chunks/QDQklEwM.js +1 -0
- package/build/client/_app/immutable/chunks/{zMHAx0Qk.js → Q_gMk3PN.js} +1 -1
- package/build/client/_app/immutable/chunks/{Wv7WdG7L.js → XHkT9caI.js} +1 -1
- package/build/client/_app/immutable/chunks/{B3CyqxdZ.js → Zxe_1j0j.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bysr0gLv.js → czbfzgw3.js} +1 -1
- package/build/client/_app/immutable/chunks/{BdcZtdph.js → mlMCI16G.js} +1 -1
- package/build/client/_app/immutable/chunks/{BEobpKJ6.js → qFFXeAvY.js} +1 -1
- package/build/client/_app/immutable/entry/{app.BdutA_ce.js → app.C_pWXh8R.js} +2 -2
- package/build/client/_app/immutable/entry/start.DfbNX0kb.js +1 -0
- package/build/client/_app/immutable/nodes/{1.BBwvzKm5.js → 1.DN_p76XO.js} +1 -1
- package/build/client/_app/immutable/nodes/3.B0eShLlr.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-CEFNOpUG.js} +2 -2
- package/build/server/chunks/{1-DLxoXeuD.js.map → 1-CEFNOpUG.js.map} +1 -1
- package/build/server/chunks/{2-Cy9PX2zK.js → 2-qGWtN2-9.js} +3 -2
- package/build/server/chunks/{2-Cy9PX2zK.js.map → 2-qGWtN2-9.js.map} +1 -1
- package/build/server/chunks/{3-DIjT-1nt.js → 3-OUU46X3b.js} +6 -7
- package/build/server/chunks/3-OUU46X3b.js.map +1 -0
- package/build/server/chunks/{_page.svelte-XhzZOhTs.js → _page.svelte-DcqVlJwT.js} +13 -14
- package/build/server/chunks/_page.svelte-DcqVlJwT.js.map +1 -0
- package/build/server/chunks/_server.ts-BOWeAYnM.js +13 -0
- package/build/server/chunks/_server.ts-BOWeAYnM.js.map +1 -0
- package/build/server/chunks/{_server.ts-CEtvrNT1.js → _server.ts-BbMhMik1.js} +4 -3
- package/build/server/chunks/_server.ts-BbMhMik1.js.map +1 -0
- package/build/server/chunks/{_server.ts-b4ynvdd4.js → _server.ts-BhmTJZsP.js} +3 -4
- package/build/server/chunks/_server.ts-BhmTJZsP.js.map +1 -0
- package/build/server/chunks/{_server.ts-DCa-_qe7.js → _server.ts-BjSxqsHx.js} +5 -7
- package/build/server/chunks/_server.ts-BjSxqsHx.js.map +1 -0
- package/build/server/chunks/{_server.ts-sRxHDQa9.js → _server.ts-C7d7vtwn.js} +3 -2
- package/build/server/chunks/_server.ts-C7d7vtwn.js.map +1 -0
- package/build/server/chunks/{_server.ts-aWI5JVTi.js → _server.ts-D9nBKsq1.js} +2 -2
- package/build/server/chunks/{_server.ts-aWI5JVTi.js.map → _server.ts-D9nBKsq1.js.map} +1 -1
- package/build/server/chunks/_server.ts-DTGwAmWz.js +14 -0
- package/build/server/chunks/_server.ts-DTGwAmWz.js.map +1 -0
- package/build/server/chunks/{_server.ts-DVH0ejp0.js → _server.ts-DiMlmks0.js} +2 -2
- package/build/server/chunks/{_server.ts-DVH0ejp0.js.map → _server.ts-DiMlmks0.js.map} +1 -1
- package/build/server/chunks/{hooks.server-sBuk2932.js → hooks.server-DigZpFgW.js} +4 -3
- package/build/server/chunks/{hooks.server-sBuk2932.js.map → hooks.server-DigZpFgW.js.map} +1 -1
- package/build/server/chunks/{idle-timer-CouFy_WF.js → idle-timer-BshN5Svi.js} +3 -2
- package/build/server/chunks/idle-timer-BshN5Svi.js.map +1 -0
- package/build/server/chunks/{session-manager-Vdr0BxAo.js → session-manager-scAVAR7S.js} +39 -41
- package/build/server/chunks/session-manager-scAVAR7S.js.map +1 -0
- package/build/server/index.js +2 -2
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +13 -20
- package/build/server/manifest.js.map +1 -1
- package/dist/cli/commands/clean.js +33 -29
- package/dist/cli/commands/export.js +5 -8
- package/dist/cli/commands/feedback.js +5 -8
- package/dist/cli/commands/init.js +2 -1
- package/dist/cli/commands/review.js +18 -121
- package/dist/cli/commands/status.js +20 -23
- package/dist/cli/commands/stop.js +7 -15
- 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 +117 -132
- package/dist/cli/markdown-to-plan.js +1 -50
- package/dist/cli/review-session.js +130 -0
- package/dist/cli/server-client.js +20 -33
- package/dist/cli/session-reader.js +20 -47
- package/dist/cli/session-resolver.js +10 -0
- package/dist/lib/server/session-manager.js +115 -0
- package/dist/lib/utils/status.js +53 -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/3-DIjT-1nt.js.map +0 -1
- package/build/server/chunks/_page.svelte-XhzZOhTs.js.map +0 -1
- package/build/server/chunks/_server.ts-BkcqX0Gw.js +0 -14
- package/build/server/chunks/_server.ts-BkcqX0Gw.js.map +0 -1
- package/build/server/chunks/_server.ts-C9Rz5pqb.js +0 -15
- package/build/server/chunks/_server.ts-C9Rz5pqb.js.map +0 -1
- package/build/server/chunks/_server.ts-CEtvrNT1.js.map +0 -1
- package/build/server/chunks/_server.ts-DCa-_qe7.js.map +0 -1
- package/build/server/chunks/_server.ts-b4ynvdd4.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/_server.ts-sRxHDQa9.js.map +0 -1
- package/build/server/chunks/idle-timer-CouFy_WF.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.DfbNX0kb.js",app:"_app/immutable/entry/app.C_pWXh8R.js",imports:["_app/immutable/entry/start.DfbNX0kb.js","_app/immutable/chunks/C88p_n1l.js","_app/immutable/chunks/CobJHAAW.js","_app/immutable/chunks/DGn8gxMH.js","_app/immutable/entry/app.C_pWXh8R.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-CEFNOpUG.js')),
|
|
17
|
+
__memo(() => import('./chunks/2-qGWtN2-9.js')),
|
|
18
|
+
__memo(() => import('./chunks/3-OUU46X3b.js'))
|
|
19
19
|
],
|
|
20
20
|
remotes: {
|
|
21
21
|
|
|
@@ -33,63 +33,56 @@ 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-DiMlmks0.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-D9nBKsq1.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-DTGwAmWz.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-BjSxqsHx.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-BOWeAYnM.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-BhmTJZsP.js'))
|
|
79
72
|
},
|
|
80
73
|
{
|
|
81
74
|
id: "/api/shutdown",
|
|
82
75
|
pattern: /^\/api\/shutdown\/?$/,
|
|
83
76
|
params: [],
|
|
84
77
|
page: null,
|
|
85
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
78
|
+
endpoint: __memo(() => import('./chunks/_server.ts-BbMhMik1.js'))
|
|
86
79
|
},
|
|
87
80
|
{
|
|
88
81
|
id: "/api/sse/[sessionId]",
|
|
89
82
|
pattern: /^\/api\/sse\/([^/]+?)\/?$/,
|
|
90
83
|
params: [{"name":"sessionId","optional":false,"rest":false,"chained":false}],
|
|
91
84
|
page: null,
|
|
92
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
85
|
+
endpoint: __memo(() => import('./chunks/_server.ts-C7d7vtwn.js'))
|
|
93
86
|
},
|
|
94
87
|
{
|
|
95
88
|
id: "/plan/[sessionId]",
|
|
@@ -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.DfbNX0kb.js\",app:\"_app/immutable/entry/app.C_pWXh8R.js\",imports:[\"_app/immutable/entry/start.DfbNX0kb.js\",\"_app/immutable/chunks/C88p_n1l.js\",\"_app/immutable/chunks/CobJHAAW.js\",\"_app/immutable/chunks/DGn8gxMH.js\",\"_app/immutable/entry/app.C_pWXh8R.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({
|
|
@@ -16,33 +17,14 @@ async function confirm(message) {
|
|
|
16
17
|
});
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
|
-
export
|
|
20
|
-
const dir = typeof args.flags.dir === "string"
|
|
21
|
-
? resolve(args.flags.dir)
|
|
22
|
-
: process.cwd();
|
|
23
|
-
const all = args.flags.all === true;
|
|
24
|
-
const dryRun = args.flags["dry-run"] === true;
|
|
25
|
-
const force = args.flags.force === true;
|
|
26
|
-
const olderThanStr = typeof args.flags["older-than"] === "string"
|
|
27
|
-
? args.flags["older-than"]
|
|
28
|
-
: undefined;
|
|
29
|
-
let olderThanMs = null;
|
|
30
|
-
if (olderThanStr) {
|
|
31
|
-
olderThanMs = parseDuration(olderThanStr);
|
|
32
|
-
if (olderThanMs === null) {
|
|
33
|
-
outputError(`Invalid duration: ${olderThanStr}. Use format like 7d, 24h, 2w`, "INVALID_DURATION");
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
const sessions = findSessionDirs(dir);
|
|
38
|
-
const now = Date.now();
|
|
20
|
+
export function selectSessionsToClean(sessions, options, now = Date.now()) {
|
|
39
21
|
const toRemove = [];
|
|
40
22
|
for (const entry of sessions) {
|
|
41
23
|
const meta = entry.meta;
|
|
42
24
|
if (!meta)
|
|
43
25
|
continue;
|
|
44
26
|
// Orphan detection: markdown file no longer exists
|
|
45
|
-
if (!all && !existsSync(meta.markdownPath)) {
|
|
27
|
+
if (!options.all && !existsSync(meta.markdownPath)) {
|
|
46
28
|
toRemove.push({
|
|
47
29
|
sessionId: entry.sessionId,
|
|
48
30
|
sessionDir: entry.sessionDir,
|
|
@@ -51,14 +33,14 @@ export async function clean(args) {
|
|
|
51
33
|
continue;
|
|
52
34
|
}
|
|
53
35
|
// All flag: remove everything
|
|
54
|
-
if (all) {
|
|
55
|
-
if (olderThanMs) {
|
|
36
|
+
if (options.all) {
|
|
37
|
+
if (options.olderThanMs) {
|
|
56
38
|
const updatedAt = new Date(meta.updatedAt).getTime();
|
|
57
|
-
if (now - updatedAt > olderThanMs) {
|
|
39
|
+
if (now - updatedAt > options.olderThanMs) {
|
|
58
40
|
toRemove.push({
|
|
59
41
|
sessionId: entry.sessionId,
|
|
60
42
|
sessionDir: entry.sessionDir,
|
|
61
|
-
reason: `older than ${olderThanStr}`,
|
|
43
|
+
reason: `older than ${options.olderThanStr}`,
|
|
62
44
|
});
|
|
63
45
|
}
|
|
64
46
|
}
|
|
@@ -72,17 +54,39 @@ export async function clean(args) {
|
|
|
72
54
|
continue;
|
|
73
55
|
}
|
|
74
56
|
// Age-based cleanup
|
|
75
|
-
if (olderThanMs) {
|
|
57
|
+
if (options.olderThanMs) {
|
|
76
58
|
const updatedAt = new Date(meta.updatedAt).getTime();
|
|
77
|
-
if (now - updatedAt > olderThanMs) {
|
|
59
|
+
if (now - updatedAt > options.olderThanMs) {
|
|
78
60
|
toRemove.push({
|
|
79
61
|
sessionId: entry.sessionId,
|
|
80
62
|
sessionDir: entry.sessionDir,
|
|
81
|
-
reason: `older than ${olderThanStr}`,
|
|
63
|
+
reason: `older than ${options.olderThanStr}`,
|
|
82
64
|
});
|
|
83
65
|
}
|
|
84
66
|
}
|
|
85
67
|
}
|
|
68
|
+
return toRemove;
|
|
69
|
+
}
|
|
70
|
+
export async function clean(args) {
|
|
71
|
+
const dir = typeof args.flags.dir === "string"
|
|
72
|
+
? resolve(args.flags.dir)
|
|
73
|
+
: process.cwd();
|
|
74
|
+
const all = args.flags.all === true;
|
|
75
|
+
const dryRun = args.flags["dry-run"] === true;
|
|
76
|
+
const force = args.flags.force === true;
|
|
77
|
+
const olderThanStr = typeof args.flags["older-than"] === "string"
|
|
78
|
+
? args.flags["older-than"]
|
|
79
|
+
: undefined;
|
|
80
|
+
let olderThanMs = null;
|
|
81
|
+
if (olderThanStr) {
|
|
82
|
+
olderThanMs = parseDuration(olderThanStr);
|
|
83
|
+
if (olderThanMs === null) {
|
|
84
|
+
outputError(`Invalid duration: ${olderThanStr}. Use format like 7d, 24h, 2w`, "INVALID_DURATION");
|
|
85
|
+
throw new CliError(`Invalid duration: ${olderThanStr}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const sessions = findSessionDirs(dir);
|
|
89
|
+
const toRemove = selectSessionsToClean(sessions, { all, olderThanMs, olderThanStr });
|
|
86
90
|
if (toRemove.length === 0) {
|
|
87
91
|
outputJson({ removed: 0, sessions: [] }, args.flags.pretty === true);
|
|
88
92
|
return;
|
|
@@ -106,7 +110,7 @@ export async function clean(args) {
|
|
|
106
110
|
const ok = await confirm("Continue?");
|
|
107
111
|
if (!ok) {
|
|
108
112
|
console.error("Aborted.");
|
|
109
|
-
|
|
113
|
+
throw new CliExitCode(0);
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
let removed = 0;
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { requireSession } 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
|
-
}
|
|
13
|
-
const resolved = resolveSession(idOrFile);
|
|
14
|
-
if (!resolved) {
|
|
15
|
-
outputError(`Session not found for: ${idOrFile}`, "NOT_FOUND");
|
|
16
|
-
process.exit(1);
|
|
12
|
+
throw new CliError("Missing session ID or file path");
|
|
17
13
|
}
|
|
14
|
+
const resolved = requireSession(idOrFile);
|
|
18
15
|
const plan = readPlan(resolved.sessionDir);
|
|
19
16
|
if (!plan) {
|
|
20
17
|
outputError("Could not read plan data", "READ_ERROR");
|
|
21
|
-
|
|
18
|
+
throw new CliError("Could not read plan data");
|
|
22
19
|
}
|
|
23
20
|
const feedback = readFeedback(resolved.sessionDir);
|
|
24
21
|
const html = renderPlanToHtml(plan, feedback);
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireSession } 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
|
-
}
|
|
10
|
-
const resolved = resolveSession(idOrFile);
|
|
11
|
-
if (!resolved) {
|
|
12
|
-
outputError(`Session not found for: ${idOrFile}`, "NOT_FOUND");
|
|
13
|
-
process.exit(1);
|
|
9
|
+
throw new CliError("Missing session ID or file path");
|
|
14
10
|
}
|
|
11
|
+
const resolved = requireSession(idOrFile);
|
|
15
12
|
const feedback = readFeedback(resolved.sessionDir);
|
|
16
13
|
if (!feedback) {
|
|
17
14
|
outputError("No feedback found for this session", "NO_FEEDBACK");
|
|
18
|
-
|
|
15
|
+
throw new CliError("No feedback found for this session");
|
|
19
16
|
}
|
|
20
17
|
const phaseFilter = typeof args.flags.phase === "string" ? args.flags.phase : undefined;
|
|
21
18
|
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
|
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireSession } 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,13 @@ 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
|
-
}
|
|
51
|
-
const resolved = resolveSession(idOrFile);
|
|
52
|
-
if (!resolved) {
|
|
53
|
-
outputError(`Session not found for: ${idOrFile}`, "NOT_FOUND");
|
|
54
|
-
process.exit(EXIT_ERROR);
|
|
50
|
+
throw new CliError("Missing session ID or file path");
|
|
55
51
|
}
|
|
52
|
+
const resolved = requireSession(idOrFile);
|
|
56
53
|
const meta = readMeta(resolved.sessionDir);
|
|
57
54
|
if (!meta) {
|
|
58
55
|
outputError(`Could not read session metadata`, "READ_ERROR");
|
|
59
|
-
|
|
56
|
+
throw new CliError("Could not read session metadata");
|
|
60
57
|
}
|
|
61
58
|
const shouldWait = args.flags.wait === true;
|
|
62
59
|
if (shouldWait) {
|
|
@@ -64,8 +61,8 @@ export async function status(args) {
|
|
|
64
61
|
const timeoutMs = typeof timeoutStr === "string"
|
|
65
62
|
? (parseDuration(timeoutStr) ?? 30 * 60 * 1000)
|
|
66
63
|
: 30 * 60 * 1000; // 30 min default
|
|
67
|
-
await
|
|
68
|
-
|
|
64
|
+
const exitCode = await pollFeedbackStatus(resolved.sessionDir, resolved.sessionId, meta, timeoutMs);
|
|
65
|
+
throw new CliExitCode(exitCode);
|
|
69
66
|
}
|
|
70
67
|
const rawFeedback = readFeedback(resolved.sessionDir);
|
|
71
68
|
// Discard feedback from a previous plan version (stale cycle)
|
|
@@ -83,9 +80,9 @@ export async function status(args) {
|
|
|
83
80
|
phaseSummary,
|
|
84
81
|
commentSummary,
|
|
85
82
|
}, args.flags.pretty === true);
|
|
86
|
-
|
|
83
|
+
throw new CliExitCode(exitCode);
|
|
87
84
|
}
|
|
88
|
-
async function
|
|
85
|
+
async function pollFeedbackStatus(sessionDir, sessionId, meta, timeoutMs) {
|
|
89
86
|
// Check current state first (ignore stale feedback from older plan versions)
|
|
90
87
|
const current = readFeedback(sessionDir);
|
|
91
88
|
if (current &&
|
|
@@ -101,15 +98,15 @@ async function waitForFeedback(sessionDir, sessionId, meta, timeoutMs) {
|
|
|
101
98
|
phaseSummary,
|
|
102
99
|
commentSummary,
|
|
103
100
|
});
|
|
104
|
-
|
|
101
|
+
return exitCode;
|
|
105
102
|
}
|
|
106
103
|
// Watch for changes
|
|
107
104
|
const feedbackPath = join(sessionDir, "feedback.json");
|
|
108
|
-
return new Promise((resolve) => {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
109
106
|
const timer = setTimeout(() => {
|
|
110
107
|
watcher.close();
|
|
111
108
|
outputError("Timed out waiting for feedback", "TIMEOUT");
|
|
112
|
-
|
|
109
|
+
reject(new CliError("Timed out waiting for feedback"));
|
|
113
110
|
}, timeoutMs);
|
|
114
111
|
const checkFile = () => {
|
|
115
112
|
try {
|
|
@@ -130,7 +127,7 @@ async function waitForFeedback(sessionDir, sessionId, meta, timeoutMs) {
|
|
|
130
127
|
phaseSummary,
|
|
131
128
|
commentSummary,
|
|
132
129
|
});
|
|
133
|
-
|
|
130
|
+
resolve(exitCode);
|
|
134
131
|
}
|
|
135
132
|
}
|
|
136
133
|
catch {
|