@dendelion/paper-camp 0.3.0 → 0.4.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/app/assets/main-6nYButky.js +232 -0
- package/dist/app/assets/main-6nYButky.js.map +1 -0
- package/dist/app/assets/main-DUnpdvQF.css +1 -0
- package/dist/app/index.html +2 -2
- package/dist/chunks/{serializer.DL7H4ZTd.js → serializer.B8u-L8BI.js} +18 -18
- package/dist/chunks/serializer.B8u-L8BI.js.map +1 -0
- package/dist/cli/index.js +1892 -1457
- package/dist/cli/index.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/parser.d.ts +1 -25
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/readers.d.ts +27 -0
- package/dist/core/readers.d.ts.map +1 -0
- package/dist/core/readers.test.d.ts +2 -0
- package/dist/core/readers.test.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/app/assets/main-8b5RQGnS.js +0 -208
- package/dist/app/assets/main-8b5RQGnS.js.map +0 -1
- package/dist/app/assets/main-B61tEYvn.css +0 -1
- package/dist/chunks/serializer.DL7H4ZTd.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,90 +1,95 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn as
|
|
3
|
-
import { stat as
|
|
4
|
-
import { join as
|
|
5
|
-
import { createInterface as
|
|
6
|
-
import { Command as
|
|
7
|
-
import { AGENT_OPTIONS as
|
|
8
|
-
import { K as
|
|
9
|
-
import { createServer as
|
|
10
|
-
import { fileURLToPath as
|
|
11
|
-
import { watch as
|
|
12
|
-
function
|
|
13
|
-
const
|
|
14
|
-
`),
|
|
15
|
-
`) : "(none)",
|
|
2
|
+
import { spawn as it, spawnSync as It } from "node:child_process";
|
|
3
|
+
import { stat as ht, readFile as at, mkdir as ut, writeFile as q, lstat as ce, unlink as Xt } from "node:fs/promises";
|
|
4
|
+
import { join as $, dirname as de, sep as le, extname as ue, basename as pe, resolve as ft } from "node:path";
|
|
5
|
+
import { createInterface as Tt } from "node:readline";
|
|
6
|
+
import { Command as fe } from "commander";
|
|
7
|
+
import { AGENT_OPTIONS as qt, coerceAgentConfig as z, DEFAULT_AGENTS as _t, AGENT_IDS as Mt, PLAN_KINDS as Et } from "../types/index.js";
|
|
8
|
+
import { K as lt, D as mt, L as Ct, M as gt, q as Lt, k as Zt, o as xt, O as ot, E as Vt, J as te, B as ee, C as jt, i as he, b as me, m as ge, x as Jt, c as we, j as ne, e as ae, d as ye, F as $e, w as Kt, h as ke, P as be, t as Se, A as ve } from "../chunks/serializer.B8u-L8BI.js";
|
|
9
|
+
import { createServer as xe } from "node:http";
|
|
10
|
+
import { fileURLToPath as Ie } from "node:url";
|
|
11
|
+
import { watch as Ot, readFileSync as Pe } from "node:fs";
|
|
12
|
+
function ie(t) {
|
|
13
|
+
const a = t.phases.map((s, e) => `${e + 1}. [${s.done ? "x" : " "}] ${s.text}`).join(`
|
|
14
|
+
`), i = t.log && t.log.length > 0 ? t.log.map((s) => `- ${s.date}: ${s.text}`).join(`
|
|
15
|
+
`) : "(none)", r = t.clarifications && t.clarifications.length > 0 ? t.clarifications.map((s) => `- ${s.date}: ${s.text}`).join(`
|
|
16
16
|
`) : "(none)";
|
|
17
|
-
return `You
|
|
17
|
+
return `You are auditing the plan "${t.title}" (${t.id ?? "no id"}) for missing phases. The plan is a single file at papercamp/plans/${t.id ?? "<ID>"}.md — if it is not there, it is archived at papercamp/plans/archive/${t.id ?? "<ID>"}.md. Edit only that file.
|
|
18
18
|
|
|
19
|
-
Plan body: ${
|
|
19
|
+
Plan body: ${t.body}
|
|
20
20
|
|
|
21
21
|
Current phases:
|
|
22
|
-
${i}
|
|
23
|
-
|
|
24
|
-
Log entries (issues, bugs, and review notes documented so far):
|
|
25
22
|
${a}
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
${
|
|
24
|
+
Log entries (issues, bugs, and review notes recorded so far):
|
|
25
|
+
${i}
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
Clarifications (answered scope/design questions):
|
|
28
|
+
${r}
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
Task:
|
|
31
|
+
1. Read the plan above, then inspect the relevant code in this repo.
|
|
32
|
+
2. Identify work that is clearly required to fulfil this plan but is covered by no existing phase. The Log entries are the best source — they often record bugs or gaps that were never turned into phases.
|
|
33
|
+
3. If you found missing work: append one \`- [ ] <short phase title>\` line per item at the end of the \`### Phases\` list. You may add detail below a checkbox line as continuation lines indented with 6 spaces. Then append exactly one line to the \`### Log\` section, formatted \`- YYYY-MM-DD: <what was found and appended>\`, creating that section after the Phases list if it does not exist.
|
|
34
|
+
4. If nothing is missing: make no edits at all — no Log line, no empty heading, no "nothing found" note. This audit re-runs regularly and must leave no trace when there is nothing to add.
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
Rules:
|
|
37
|
+
- Never modify, reorder, check, uncheck, or delete any existing line, even if it looks stale, wrong, or redundant.
|
|
38
|
+
- Never touch the YAML frontmatter.
|
|
39
|
+
- Append only: new unchecked phases at the end of the list, plus the single Log line.`;
|
|
35
40
|
}
|
|
36
|
-
function
|
|
37
|
-
const
|
|
41
|
+
function Ae(t, a) {
|
|
42
|
+
const i = [
|
|
38
43
|
"-p",
|
|
39
|
-
|
|
44
|
+
t,
|
|
40
45
|
"--output-format",
|
|
41
46
|
"stream-json",
|
|
42
47
|
"--verbose",
|
|
43
48
|
"--permission-mode",
|
|
44
49
|
"auto"
|
|
45
50
|
];
|
|
46
|
-
return
|
|
51
|
+
return a != null && a.model && i.push("--model", a.model), a != null && a.effort && i.push("--effort", a.effort), i;
|
|
47
52
|
}
|
|
48
|
-
function
|
|
49
|
-
let
|
|
53
|
+
function Te(t) {
|
|
54
|
+
let a;
|
|
50
55
|
try {
|
|
51
|
-
|
|
56
|
+
a = JSON.parse(t);
|
|
52
57
|
} catch {
|
|
53
58
|
return null;
|
|
54
59
|
}
|
|
55
|
-
switch (
|
|
60
|
+
switch (a.type) {
|
|
56
61
|
case "system":
|
|
57
|
-
return
|
|
62
|
+
return a.subtype === "init" ? { text: "Agent session started" } : a.subtype === "post_turn_summary" && typeof a.status_detail == "string" ? { text: a.status_detail } : null;
|
|
58
63
|
case "rate_limit_event":
|
|
59
64
|
return null;
|
|
60
65
|
case "assistant": {
|
|
61
|
-
const
|
|
62
|
-
for (const
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
65
|
-
return { text: `Running ${
|
|
66
|
-
if (
|
|
67
|
-
return { text:
|
|
66
|
+
const i = a.message, r = (i == null ? void 0 : i.content) ?? [];
|
|
67
|
+
for (const s of r) {
|
|
68
|
+
const e = s;
|
|
69
|
+
if (e.type === "tool_use")
|
|
70
|
+
return { text: `Running ${e.name ?? "a tool"}…` };
|
|
71
|
+
if (e.type === "text" && typeof e.text == "string" && e.text.trim())
|
|
72
|
+
return { text: e.text.trim() };
|
|
68
73
|
}
|
|
69
74
|
return null;
|
|
70
75
|
}
|
|
71
76
|
case "user": {
|
|
72
|
-
const
|
|
73
|
-
return
|
|
77
|
+
const i = a.message, r = i == null ? void 0 : i.content, s = Array.isArray(r) ? r[0] : void 0;
|
|
78
|
+
return s != null && s.is_error ? { text: `Error: ${typeof s.content == "string" ? s.content : "Tool call failed"}`, error: !0 } : { text: "Tool finished" };
|
|
74
79
|
}
|
|
75
80
|
case "result": {
|
|
76
|
-
const
|
|
77
|
-
return { text: (typeof
|
|
81
|
+
const i = !!a.is_error;
|
|
82
|
+
return { text: (typeof a.result == "string" ? a.result.trim() : "") || (i ? "Agent run failed" : "Agent run finished"), done: !0, error: i };
|
|
78
83
|
}
|
|
79
84
|
default:
|
|
80
85
|
return { text: "Agent is working…" };
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
|
-
function
|
|
84
|
-
const
|
|
85
|
-
return
|
|
88
|
+
function Ee(t, a) {
|
|
89
|
+
const i = ["run", t, "--format", "json"];
|
|
90
|
+
return a != null && a.model && i.push("-m", a.model), a != null && a.effort && i.push("--variant", a.effort), i;
|
|
86
91
|
}
|
|
87
|
-
const
|
|
92
|
+
const De = {
|
|
88
93
|
bash: "Running command",
|
|
89
94
|
read: "Reading file",
|
|
90
95
|
edit: "Editing file",
|
|
@@ -95,465 +100,589 @@ const fe = {
|
|
|
95
100
|
webfetch: "Fetching URL",
|
|
96
101
|
question: "Asking for input"
|
|
97
102
|
};
|
|
98
|
-
function
|
|
99
|
-
let
|
|
103
|
+
function Oe(t) {
|
|
104
|
+
let a;
|
|
100
105
|
try {
|
|
101
|
-
|
|
106
|
+
a = JSON.parse(t);
|
|
102
107
|
} catch {
|
|
103
108
|
return null;
|
|
104
109
|
}
|
|
105
|
-
const
|
|
106
|
-
if (!
|
|
107
|
-
switch (
|
|
110
|
+
const i = a.type, r = a.part;
|
|
111
|
+
if (!i || !r) return null;
|
|
112
|
+
switch (i) {
|
|
108
113
|
case "step_start":
|
|
109
114
|
return null;
|
|
110
115
|
case "text": {
|
|
111
|
-
const
|
|
112
|
-
return
|
|
116
|
+
const s = r.text;
|
|
117
|
+
return s != null && s.trim() ? { text: s.trim() } : null;
|
|
113
118
|
}
|
|
114
119
|
case "tool_use": {
|
|
115
|
-
const
|
|
116
|
-
return
|
|
120
|
+
const s = r.tool, e = r.state ? r.state.input : void 0, n = e ? e.description : void 0, l = s ? De[s] : "Running tool", c = typeof n == "string" && n.trim() ? `: ${n.trim()}` : "";
|
|
121
|
+
return s ? { text: `${l}${c}…` } : null;
|
|
117
122
|
}
|
|
118
123
|
case "step_finish": {
|
|
119
|
-
const
|
|
120
|
-
return
|
|
124
|
+
const s = r.reason, e = s === "tool-calls" ? null : s === "stop" ? "Done" : "Step finished";
|
|
125
|
+
return e ? { text: e } : null;
|
|
121
126
|
}
|
|
122
127
|
default:
|
|
123
128
|
return null;
|
|
124
129
|
}
|
|
125
130
|
}
|
|
126
|
-
const
|
|
131
|
+
const Gt = "claude-code", bt = {
|
|
127
132
|
"claude-code": {
|
|
128
133
|
command: "claude",
|
|
129
|
-
buildArgs:
|
|
130
|
-
parseLine:
|
|
131
|
-
options:
|
|
134
|
+
buildArgs: Ae,
|
|
135
|
+
parseLine: Te,
|
|
136
|
+
options: qt["claude-code"]
|
|
132
137
|
},
|
|
133
138
|
opencode: {
|
|
134
139
|
command: "opencode",
|
|
135
|
-
buildArgs:
|
|
136
|
-
parseLine:
|
|
137
|
-
options:
|
|
140
|
+
buildArgs: Ee,
|
|
141
|
+
parseLine: Oe,
|
|
142
|
+
options: qt.opencode
|
|
138
143
|
}
|
|
139
|
-
},
|
|
144
|
+
}, Ne = {
|
|
140
145
|
phase: "phase",
|
|
141
146
|
audit: "phase",
|
|
142
147
|
"batch-audit": "phase",
|
|
148
|
+
"run-all": "phase",
|
|
149
|
+
sync: "phase",
|
|
150
|
+
reconcile: "phase",
|
|
143
151
|
draft: "planDraft",
|
|
144
152
|
extend: "ideaExtend",
|
|
145
153
|
"commit-suggest": "commitSuggest"
|
|
146
154
|
};
|
|
147
|
-
function St(
|
|
148
|
-
const { agentId:
|
|
149
|
-
if (
|
|
150
|
-
if (
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
return { id:
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return { id:
|
|
155
|
+
function St(t) {
|
|
156
|
+
const { agentId: a, defaultAgents: i, taskKind: r } = t;
|
|
157
|
+
if (a && a in bt) return { id: a, adapter: bt[a] };
|
|
158
|
+
if (r && i) {
|
|
159
|
+
const s = Ne[r], e = i[s];
|
|
160
|
+
if (e) {
|
|
161
|
+
const n = e.agent;
|
|
162
|
+
if (n in bt)
|
|
163
|
+
return { id: n, adapter: bt[n], model: e.model, effort: e.effort };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { id: Gt, adapter: bt[Gt] };
|
|
159
167
|
}
|
|
160
|
-
function
|
|
161
|
-
return
|
|
162
|
-
if (!
|
|
163
|
-
return { ...
|
|
164
|
-
const
|
|
165
|
-
if (
|
|
166
|
-
return { ...
|
|
167
|
-
const
|
|
168
|
-
return { ...
|
|
168
|
+
function se(t, a) {
|
|
169
|
+
return t.map((i) => {
|
|
170
|
+
if (!i.id)
|
|
171
|
+
return { ...i, status: "planned" };
|
|
172
|
+
const r = a.filter((e) => e.idea === i.id);
|
|
173
|
+
if (r.length === 0)
|
|
174
|
+
return { ...i, status: "planned" };
|
|
175
|
+
const s = r.every((e) => e.status === "done" || e.status === "dropped");
|
|
176
|
+
return { ...i, status: s ? "done" : "planned" };
|
|
169
177
|
});
|
|
170
178
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const i = /* @__PURE__ */ new Set();
|
|
177
|
-
let a = null;
|
|
178
|
-
function p() {
|
|
179
|
-
const n = `data: ${JSON.stringify({
|
|
179
|
+
function Re(t) {
|
|
180
|
+
const a = /* @__PURE__ */ new Set();
|
|
181
|
+
let i = null;
|
|
182
|
+
function r() {
|
|
183
|
+
const s = `data: ${JSON.stringify({
|
|
180
184
|
message: "changed",
|
|
181
185
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
182
186
|
})}
|
|
183
187
|
|
|
184
188
|
`;
|
|
185
|
-
for (const
|
|
189
|
+
for (const e of a)
|
|
186
190
|
try {
|
|
187
|
-
|
|
191
|
+
e.write(s);
|
|
188
192
|
} catch {
|
|
189
|
-
|
|
193
|
+
a.delete(e);
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
try {
|
|
193
|
-
|
|
194
|
-
|
|
197
|
+
Ot($(t, "papercamp"), { recursive: !0 }, () => {
|
|
198
|
+
i && clearTimeout(i), i = setTimeout(r, 300);
|
|
195
199
|
});
|
|
196
200
|
} catch {
|
|
197
201
|
}
|
|
198
202
|
return {
|
|
199
|
-
subscribe(
|
|
200
|
-
|
|
201
|
-
const
|
|
203
|
+
subscribe(s) {
|
|
204
|
+
a.add(s);
|
|
205
|
+
const e = JSON.stringify({
|
|
202
206
|
message: "Watching for changes…",
|
|
203
207
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
204
208
|
});
|
|
205
|
-
|
|
209
|
+
s.write(`data: ${e}
|
|
206
210
|
|
|
207
|
-
`),
|
|
211
|
+
`), s.on("close", () => a.delete(s));
|
|
208
212
|
}
|
|
209
213
|
};
|
|
210
214
|
}
|
|
211
|
-
const
|
|
212
|
-
function
|
|
215
|
+
const Be = 50, Ht = 30 * 60 * 1e3;
|
|
216
|
+
function Pt(t) {
|
|
213
217
|
try {
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
218
|
+
const a = Pe($(t, "papercamp", "config.json"), "utf-8"), i = JSON.parse(a), r = i.defaultAgents;
|
|
219
|
+
if (r)
|
|
216
220
|
return {
|
|
217
|
-
phase:
|
|
218
|
-
planDraft:
|
|
219
|
-
ideaExtend:
|
|
220
|
-
commitSuggest:
|
|
221
|
+
phase: z(r.phase),
|
|
222
|
+
planDraft: z(r.planDraft),
|
|
223
|
+
ideaExtend: z(r.ideaExtend),
|
|
224
|
+
commitSuggest: z(r.commitSuggest)
|
|
221
225
|
};
|
|
222
|
-
if (
|
|
223
|
-
const
|
|
226
|
+
if (i.defaultAgent) {
|
|
227
|
+
const s = i.defaultAgent;
|
|
224
228
|
return {
|
|
225
|
-
phase: { agent:
|
|
226
|
-
planDraft: { agent:
|
|
227
|
-
ideaExtend: { agent:
|
|
228
|
-
commitSuggest: { agent:
|
|
229
|
+
phase: { agent: s },
|
|
230
|
+
planDraft: { agent: s },
|
|
231
|
+
ideaExtend: { agent: s },
|
|
232
|
+
commitSuggest: { agent: s }
|
|
229
233
|
};
|
|
230
234
|
}
|
|
231
|
-
return
|
|
235
|
+
return _t;
|
|
232
236
|
} catch {
|
|
233
|
-
return
|
|
237
|
+
return _t;
|
|
234
238
|
}
|
|
235
239
|
}
|
|
236
|
-
function
|
|
237
|
-
|
|
240
|
+
function Wt(t, a, i) {
|
|
241
|
+
const r = a.description ? `Phase details:
|
|
242
|
+
${a.description}
|
|
238
243
|
|
|
239
|
-
|
|
244
|
+
` : "";
|
|
245
|
+
return `You are executing exactly one phase of the plan "${t.title}" (${t.id ?? "no id"}): phase ${i + 1}, "${a.text}". The plan is a single file at papercamp/plans/${t.id ?? "<ID>"}.md.
|
|
240
246
|
|
|
241
|
-
Plan context: ${
|
|
247
|
+
${r}Plan context: ${t.body}
|
|
242
248
|
|
|
243
|
-
Do only this phase
|
|
249
|
+
Do only this phase — do not start any other phase, even if it looks quick.
|
|
250
|
+
|
|
251
|
+
When the work is done:
|
|
252
|
+
1. In the plan file's \`### Phases\` list, change this phase's checkbox from \`- [ ]\` to \`- [x]\`. Do not change any other line.
|
|
253
|
+
2. Add one bullet describing what you did under today's \`## YYYY-MM-DD\` heading at the top of progress.md (create the heading at the top if today's is not there yet — newest day stays first).
|
|
254
|
+
3. If every phase in the list is now checked, set the plan's \`status:\` frontmatter field to \`review\` — never \`done\`; per this repo's AGENTS.md a human promotes plans to done.`;
|
|
244
255
|
}
|
|
245
|
-
function Ce(
|
|
246
|
-
},
|
|
247
|
-
const
|
|
256
|
+
function Ce(t, a = () => {
|
|
257
|
+
}, i, r, s) {
|
|
258
|
+
const e = /* @__PURE__ */ new Set();
|
|
248
259
|
let n = null;
|
|
249
|
-
function
|
|
250
|
-
const
|
|
260
|
+
function l(o) {
|
|
261
|
+
const k = `data: ${JSON.stringify({ message: o, timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "agent" })}
|
|
251
262
|
|
|
252
263
|
`;
|
|
253
|
-
for (const
|
|
264
|
+
for (const v of e)
|
|
254
265
|
try {
|
|
255
|
-
|
|
266
|
+
v.write(k);
|
|
256
267
|
} catch {
|
|
257
|
-
|
|
268
|
+
e.delete(v);
|
|
258
269
|
}
|
|
259
270
|
}
|
|
260
|
-
function
|
|
261
|
-
|
|
271
|
+
function c(o, k) {
|
|
272
|
+
o.lines.push(k), o.lines.length > Be && o.lines.shift(), l(k);
|
|
262
273
|
}
|
|
263
|
-
function
|
|
264
|
-
|
|
274
|
+
function d(o, k) {
|
|
275
|
+
o.status = k, l(`agent: ${k}`);
|
|
265
276
|
}
|
|
266
|
-
async function u(
|
|
267
|
-
var
|
|
277
|
+
async function u(o) {
|
|
278
|
+
var k, v;
|
|
268
279
|
try {
|
|
269
|
-
if (
|
|
270
|
-
const
|
|
271
|
-
return !
|
|
280
|
+
if (o.taskKind === "extend") {
|
|
281
|
+
const x = $(t, "papercamp", "ideas"), { entries: W } = await Ct(x, $(t, "papercamp", "ideas.md")), Q = W.find((b) => b.id === o.ideaId);
|
|
282
|
+
return !Q || o.ideaBodyBaseline === void 0 ? null : Q.body !== o.ideaBodyBaseline;
|
|
283
|
+
}
|
|
284
|
+
if (o.taskKind === "reconcile") {
|
|
285
|
+
const { entries: x } = await gt(
|
|
286
|
+
$(t, "papercamp", "plans"),
|
|
287
|
+
$(t, "papercamp", "plans.md")
|
|
288
|
+
), W = x.find((Q) => Q.id === o.planId);
|
|
289
|
+
return !W || o.reconcileBaseline === void 0 ? null : JSON.stringify({ body: W.body, phases: W.phases.map((Q) => Q.text) }) !== o.reconcileBaseline;
|
|
272
290
|
}
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
275
|
-
return
|
|
276
|
-
const
|
|
277
|
-
return
|
|
291
|
+
const N = $(t, "papercamp", "plans"), { entries: X } = await gt(N, $(t, "papercamp", "plans.md"));
|
|
292
|
+
if (o.ideaId !== void 0)
|
|
293
|
+
return X.some((x) => x.idea === o.ideaId);
|
|
294
|
+
const G = X.find((x) => x.id === o.planId) ?? X.find((x) => x.title === o.planTitle);
|
|
295
|
+
return G ? o.phaseIndex !== void 0 ? ((k = G.phases[o.phaseIndex]) == null ? void 0 : k.done) ?? null : o.planBaseline ? G.phases.length > o.planBaseline.phases || (((v = G.log) == null ? void 0 : v.length) ?? 0) > o.planBaseline.log : null : null;
|
|
278
296
|
} catch {
|
|
279
297
|
return null;
|
|
280
298
|
}
|
|
281
299
|
}
|
|
282
|
-
function
|
|
283
|
-
|
|
284
|
-
if (n ===
|
|
285
|
-
const
|
|
286
|
-
|
|
300
|
+
function f(o, k) {
|
|
301
|
+
d(o, k ? "error" : "done"), !k && u(o).then((v) => {
|
|
302
|
+
if (n === o && v === !1) {
|
|
303
|
+
const N = o.taskKind === "extend" ? `Warning: agent finished but the idea body for ${o.ideaId} did not change — verify manually` : o.taskKind === "reconcile" ? "Warning: agent finished but the plan body and phase text did not change — verify manually" : o.ideaId !== void 0 ? `Warning: agent finished but no plan linking idea: ${o.ideaId} appeared in papercamp/plans/ — verify manually` : o.phaseIndex !== void 0 ? "Warning: agent finished but did not check off this phase in the plan file — verify manually" : "Warning: agent finished but appended nothing to Phases or Log — verify manually";
|
|
304
|
+
c(o, N);
|
|
287
305
|
}
|
|
288
|
-
n ===
|
|
306
|
+
n === o && o.taskKind === "audit" && o.planId && v === !0 && (i == null || i(o.planId, 0).catch(() => {
|
|
289
307
|
}));
|
|
290
308
|
});
|
|
291
309
|
}
|
|
292
|
-
function
|
|
293
|
-
if (!
|
|
294
|
-
|
|
295
|
-
if (n !==
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
}),
|
|
299
|
-
n ===
|
|
300
|
-
}),
|
|
301
|
-
n ===
|
|
310
|
+
function h(o) {
|
|
311
|
+
if (!o.proc.stdout) return;
|
|
312
|
+
Tt({ input: o.proc.stdout }).on("line", (v) => {
|
|
313
|
+
if (n !== o || !v.trim()) return;
|
|
314
|
+
const N = o.adapter.parseLine(v);
|
|
315
|
+
N && (c(o, N.text), N.done && f(o, !!N.error));
|
|
316
|
+
}), o.proc.on("close", (v) => {
|
|
317
|
+
n === o && (o.status === "starting" || o.status === "running" ? f(o, v !== 0) : o.status === "stopping" && d(o, "done"));
|
|
318
|
+
}), o.proc.on("error", (v) => {
|
|
319
|
+
n === o && (c(o, `Failed to spawn agent: ${v.message}`), d(o, "error"));
|
|
302
320
|
});
|
|
303
321
|
}
|
|
304
|
-
function
|
|
305
|
-
return
|
|
306
|
-
cwd:
|
|
322
|
+
function y(o, k) {
|
|
323
|
+
return it(o.command, k, {
|
|
324
|
+
cwd: t,
|
|
307
325
|
stdio: ["ignore", "pipe", "pipe"]
|
|
308
326
|
});
|
|
309
327
|
}
|
|
310
|
-
function
|
|
328
|
+
function g() {
|
|
311
329
|
return n !== null && n.status !== "done" && n.status !== "error";
|
|
312
330
|
}
|
|
313
|
-
function
|
|
314
|
-
if (
|
|
331
|
+
function E(o, k, v) {
|
|
332
|
+
if (g())
|
|
315
333
|
return { ok: !1, error: "An agent task is already running" };
|
|
316
|
-
const
|
|
317
|
-
id:
|
|
318
|
-
adapter:
|
|
319
|
-
model:
|
|
334
|
+
const N = Pt(t), {
|
|
335
|
+
id: X,
|
|
336
|
+
adapter: G,
|
|
337
|
+
model: x,
|
|
320
338
|
effort: W
|
|
321
339
|
} = St({
|
|
322
|
-
agentId:
|
|
323
|
-
defaultAgents:
|
|
324
|
-
taskKind:
|
|
325
|
-
}),
|
|
326
|
-
planTitle:
|
|
327
|
-
planId:
|
|
340
|
+
agentId: o.agentOverride,
|
|
341
|
+
defaultAgents: N,
|
|
342
|
+
taskKind: v.taskKind
|
|
343
|
+
}), Q = y(G, G.buildArgs(k, { model: x, effort: W })), b = {
|
|
344
|
+
planTitle: o.planTitle,
|
|
345
|
+
planId: o.planId,
|
|
328
346
|
status: "starting",
|
|
329
|
-
agentId:
|
|
330
|
-
adapter:
|
|
331
|
-
proc:
|
|
347
|
+
agentId: X,
|
|
348
|
+
adapter: G,
|
|
349
|
+
proc: Q,
|
|
332
350
|
lines: [],
|
|
333
|
-
...
|
|
351
|
+
...v
|
|
334
352
|
};
|
|
335
|
-
return n =
|
|
353
|
+
return n = b, h(b), d(b, "running"), { ok: !0 };
|
|
336
354
|
}
|
|
337
|
-
function
|
|
338
|
-
if (
|
|
355
|
+
function F(o, k) {
|
|
356
|
+
if (g())
|
|
339
357
|
return { ok: !1, error: "An agent task is already running" };
|
|
340
|
-
const
|
|
341
|
-
if (!
|
|
358
|
+
const v = o.phases[k];
|
|
359
|
+
if (!v)
|
|
342
360
|
return { ok: !1, error: "Phase not found" };
|
|
343
|
-
|
|
344
|
-
const
|
|
345
|
-
return
|
|
361
|
+
a(o);
|
|
362
|
+
const N = Wt(o, v, k);
|
|
363
|
+
return E({ planTitle: o.title, planId: o.id, agentOverride: o.agent }, N, {
|
|
346
364
|
taskKind: "phase",
|
|
347
|
-
phaseIndex:
|
|
365
|
+
phaseIndex: k
|
|
348
366
|
});
|
|
349
367
|
}
|
|
350
|
-
function
|
|
351
|
-
var
|
|
352
|
-
return
|
|
353
|
-
taskKind:
|
|
354
|
-
|
|
368
|
+
function D(o, k, v = "audit") {
|
|
369
|
+
var N;
|
|
370
|
+
return E({ planTitle: o.title, planId: o.id, agentOverride: o.agent }, k, {
|
|
371
|
+
taskKind: v,
|
|
372
|
+
...v === "reconcile" ? {
|
|
373
|
+
reconcileBaseline: JSON.stringify({
|
|
374
|
+
body: o.body,
|
|
375
|
+
phases: o.phases.map((X) => X.text)
|
|
376
|
+
})
|
|
377
|
+
} : { planBaseline: { phases: o.phases.length, log: ((N = o.log) == null ? void 0 : N.length) ?? 0 } }
|
|
355
378
|
});
|
|
356
379
|
}
|
|
357
|
-
function
|
|
358
|
-
return
|
|
380
|
+
function S(o, k) {
|
|
381
|
+
return o.id ? E({ planTitle: `Draft plan for ${o.id}` }, k, {
|
|
359
382
|
taskKind: "draft",
|
|
360
|
-
ideaId:
|
|
383
|
+
ideaId: o.id
|
|
361
384
|
}) : { ok: !1, error: "Idea has no id to link a drafted plan back to" };
|
|
362
385
|
}
|
|
363
|
-
function
|
|
364
|
-
return
|
|
386
|
+
function m(o, k) {
|
|
387
|
+
return o.id ? E({ planTitle: `Extend ${o.id}` }, k, {
|
|
365
388
|
taskKind: "extend",
|
|
366
|
-
ideaId:
|
|
367
|
-
ideaBodyBaseline:
|
|
389
|
+
ideaId: o.id,
|
|
390
|
+
ideaBodyBaseline: o.body
|
|
368
391
|
}) : { ok: !1, error: "Idea has no id to extend" };
|
|
369
392
|
}
|
|
370
|
-
|
|
371
|
-
|
|
393
|
+
function w(o) {
|
|
394
|
+
return E({ planTitle: "Sync to main" }, o, { taskKind: "sync" });
|
|
395
|
+
}
|
|
396
|
+
async function I(o, k) {
|
|
397
|
+
const v = $(o, `${k}.md`);
|
|
372
398
|
try {
|
|
373
|
-
return await
|
|
399
|
+
return await ht(v), v;
|
|
374
400
|
} catch {
|
|
375
401
|
}
|
|
376
|
-
const
|
|
402
|
+
const N = $(o, "archive", `${k}.md`);
|
|
377
403
|
try {
|
|
378
|
-
return await
|
|
404
|
+
return await ht(N), N;
|
|
379
405
|
} catch {
|
|
380
406
|
}
|
|
381
407
|
return null;
|
|
382
408
|
}
|
|
383
|
-
function
|
|
384
|
-
if (
|
|
409
|
+
function T() {
|
|
410
|
+
if (g())
|
|
385
411
|
return { ok: !1, error: "An agent task is already running" };
|
|
386
|
-
const
|
|
387
|
-
id:
|
|
388
|
-
adapter:
|
|
389
|
-
model:
|
|
390
|
-
effort:
|
|
391
|
-
} = St({ defaultAgents:
|
|
392
|
-
cwd:
|
|
412
|
+
const o = Pt(t), {
|
|
413
|
+
id: k,
|
|
414
|
+
adapter: v,
|
|
415
|
+
model: N,
|
|
416
|
+
effort: X
|
|
417
|
+
} = St({ defaultAgents: o, taskKind: "audit" }), G = it("sh", ["-c", "exit 0"], {
|
|
418
|
+
cwd: t,
|
|
393
419
|
stdio: "ignore"
|
|
394
|
-
}),
|
|
420
|
+
}), x = {
|
|
395
421
|
taskKind: "batch-audit",
|
|
396
422
|
planTitle: "Batch audit",
|
|
397
423
|
status: "starting",
|
|
398
|
-
agentId:
|
|
399
|
-
adapter:
|
|
400
|
-
proc:
|
|
424
|
+
agentId: k,
|
|
425
|
+
adapter: v,
|
|
426
|
+
proc: G,
|
|
401
427
|
lines: []
|
|
402
428
|
};
|
|
403
|
-
return n =
|
|
404
|
-
var W,
|
|
429
|
+
return n = x, d(x, "running"), (async () => {
|
|
430
|
+
var W, Q;
|
|
405
431
|
try {
|
|
406
|
-
const
|
|
407
|
-
if (
|
|
408
|
-
n ===
|
|
432
|
+
const b = $(t, "papercamp", "plans"), { entries: K } = await lt(b), C = K.filter((A) => A.status === "review" || A.status === "done");
|
|
433
|
+
if (C.length === 0) {
|
|
434
|
+
n === x && (c(x, 'No plans with status "review" or "done" to audit.'), d(x, "done"));
|
|
409
435
|
return;
|
|
410
436
|
}
|
|
411
|
-
|
|
412
|
-
let
|
|
413
|
-
for (const
|
|
414
|
-
if (n !==
|
|
415
|
-
if (!
|
|
416
|
-
|
|
437
|
+
c(x, `Auditing ${C.length} plan(s)…`);
|
|
438
|
+
let st = 0, Z = 0, tt = 0;
|
|
439
|
+
for (const A of C) {
|
|
440
|
+
if (n !== x || x.status === "stopping") break;
|
|
441
|
+
if (!A.id) {
|
|
442
|
+
Z++;
|
|
417
443
|
continue;
|
|
418
444
|
}
|
|
419
|
-
const
|
|
420
|
-
if (!
|
|
421
|
-
|
|
445
|
+
const ct = await I(b, A.id);
|
|
446
|
+
if (!ct) {
|
|
447
|
+
Z++;
|
|
422
448
|
continue;
|
|
423
449
|
}
|
|
424
|
-
if (
|
|
425
|
-
const
|
|
426
|
-
if (
|
|
427
|
-
|
|
450
|
+
if (A.audited) {
|
|
451
|
+
const H = await ht(ct).catch(() => null), V = H ? H.mtime.toISOString().slice(0, 10) : null;
|
|
452
|
+
if (V && A.audited >= V) {
|
|
453
|
+
c(x, `[skip] ${A.id} — up to date`), Z++;
|
|
428
454
|
continue;
|
|
429
455
|
}
|
|
430
456
|
}
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
cwd:
|
|
457
|
+
c(x, `[audit] ${A.id} ${A.title}`);
|
|
458
|
+
const U = ie(A), et = it(v.command, v.buildArgs(U, { model: N, effort: X }), {
|
|
459
|
+
cwd: t,
|
|
434
460
|
stdio: ["ignore", "pipe", "pipe"]
|
|
435
461
|
});
|
|
436
|
-
|
|
437
|
-
if (n !==
|
|
438
|
-
const L =
|
|
439
|
-
L != null && L.text && L.text !== "Agent is working…" &&
|
|
440
|
-
}), (W =
|
|
462
|
+
x.proc = et, et.stdout && Tt({ input: et.stdout }).on("line", (V) => {
|
|
463
|
+
if (n !== x) return;
|
|
464
|
+
const L = v.parseLine(V);
|
|
465
|
+
L != null && L.text && L.text !== "Agent is working…" && c(x, ` ${L.text}`);
|
|
466
|
+
}), (W = et.stderr) == null || W.on("data", () => {
|
|
441
467
|
});
|
|
442
|
-
const
|
|
443
|
-
|
|
468
|
+
const $t = await new Promise((H) => {
|
|
469
|
+
et.on("close", (V) => H(V === 0)), et.on("error", () => H(!1));
|
|
444
470
|
});
|
|
445
|
-
if (n !==
|
|
446
|
-
if (
|
|
447
|
-
let
|
|
471
|
+
if (n !== x) return;
|
|
472
|
+
if ($t) {
|
|
473
|
+
let H = 0;
|
|
448
474
|
try {
|
|
449
|
-
const
|
|
450
|
-
|
|
475
|
+
const V = await at(ct, "utf-8"), nt = ((Q = mt(V).entries[0]) == null ? void 0 : Q.phases.length) ?? A.phases.length;
|
|
476
|
+
H = Math.max(0, nt - A.phases.length);
|
|
451
477
|
} catch {
|
|
452
478
|
}
|
|
453
|
-
await (
|
|
454
|
-
|
|
455
|
-
|
|
479
|
+
await (i == null ? void 0 : i(A.id, H)), st++, c(
|
|
480
|
+
x,
|
|
481
|
+
H > 0 ? `[done] ${A.id} — ${H} gap phase${H === 1 ? "" : "s"} added` : `[done] ${A.id}`
|
|
456
482
|
);
|
|
457
483
|
} else
|
|
458
|
-
|
|
484
|
+
tt++, c(x, `[fail] ${A.id} — agent error`);
|
|
485
|
+
}
|
|
486
|
+
if (n !== x) return;
|
|
487
|
+
if (x.status === "stopping") {
|
|
488
|
+
d(x, "done");
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
c(x, `Audit complete — ${st} audited, ${Z} skipped, ${tt} failed`), d(x, tt > 0 ? "error" : "done");
|
|
492
|
+
} catch (b) {
|
|
493
|
+
n === x && (c(x, `Batch audit failed: ${b.message}`), d(x, "error"));
|
|
494
|
+
}
|
|
495
|
+
})(), { ok: !0 };
|
|
496
|
+
}
|
|
497
|
+
function O(o, k) {
|
|
498
|
+
if (g())
|
|
499
|
+
return { ok: !1, error: "An agent task is already running" };
|
|
500
|
+
const v = o.phases.map((K, C) => ({ phase: K, i: C })).filter(({ phase: K }) => !K.done);
|
|
501
|
+
if (v.length === 0)
|
|
502
|
+
return { ok: !1, error: "No unchecked phases to run" };
|
|
503
|
+
a(o);
|
|
504
|
+
const N = Pt(t), {
|
|
505
|
+
id: X,
|
|
506
|
+
adapter: G,
|
|
507
|
+
model: x,
|
|
508
|
+
effort: W
|
|
509
|
+
} = St({ agentId: o.agent, defaultAgents: N, taskKind: "run-all" }), Q = it("sh", ["-c", "exit 0"], { cwd: t, stdio: "ignore" }), b = {
|
|
510
|
+
taskKind: "run-all",
|
|
511
|
+
planTitle: o.title,
|
|
512
|
+
planId: o.id,
|
|
513
|
+
status: "starting",
|
|
514
|
+
agentId: X,
|
|
515
|
+
adapter: G,
|
|
516
|
+
proc: Q,
|
|
517
|
+
lines: []
|
|
518
|
+
};
|
|
519
|
+
return n = b, d(b, "running"), (async () => {
|
|
520
|
+
var K;
|
|
521
|
+
try {
|
|
522
|
+
const C = o.phases.length;
|
|
523
|
+
let st = 0, Z = 0;
|
|
524
|
+
for (const { phase: tt, i: A } of v) {
|
|
525
|
+
if (n !== b || b.status === "stopping") break;
|
|
526
|
+
b.phaseIndex = A, c(b, `[phase ${A + 1}/${C}] ${tt.text}`);
|
|
527
|
+
const ct = Wt(o, tt, A), U = it(G.command, G.buildArgs(ct, { model: x, effort: W }), {
|
|
528
|
+
cwd: t,
|
|
529
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
530
|
+
});
|
|
531
|
+
b.proc = U, U.stdout && Tt({ input: U.stdout }).on("line", (kt) => {
|
|
532
|
+
if (n !== b) return;
|
|
533
|
+
const dt = G.parseLine(kt);
|
|
534
|
+
dt != null && dt.text && dt.text !== "Agent is working…" && c(b, ` ${dt.text}`);
|
|
535
|
+
}), (K = U.stderr) == null || K.on("data", () => {
|
|
536
|
+
});
|
|
537
|
+
let et = !1;
|
|
538
|
+
const $t = new Promise((nt) => {
|
|
539
|
+
U.on("close", (kt) => nt(kt === 0)), U.on("error", () => nt(!1));
|
|
540
|
+
}), H = setTimeout(() => {
|
|
541
|
+
et = !0, U.killed || U.kill("SIGTERM"), setTimeout(() => {
|
|
542
|
+
U.exitCode === null && U.signalCode === null && U.kill("SIGKILL");
|
|
543
|
+
}, 5e3);
|
|
544
|
+
}, Ht), V = await $t;
|
|
545
|
+
if (clearTimeout(H), n !== b) return;
|
|
546
|
+
if (et) {
|
|
547
|
+
Z++, c(
|
|
548
|
+
b,
|
|
549
|
+
`[timeout] phase ${A + 1} — no progress for ${Ht / 6e4}min, stopping`
|
|
550
|
+
);
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
if (!V) {
|
|
554
|
+
Z++, c(b, `[fail] phase ${A + 1} — agent error, stopping`);
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
const L = await u(b);
|
|
558
|
+
if (!L) {
|
|
559
|
+
Z++, c(
|
|
560
|
+
b,
|
|
561
|
+
L === null ? `[fail] phase ${A + 1} — could not read plan after run, stopping` : `[fail] phase ${A + 1} — phase checkbox did not flip, stopping`
|
|
562
|
+
);
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
if (k) {
|
|
566
|
+
c(b, `[verify] phase ${A + 1} — running lint/format/test`);
|
|
567
|
+
const nt = await k();
|
|
568
|
+
if (n !== b) return;
|
|
569
|
+
if (!nt) {
|
|
570
|
+
Z++, c(b, `[fail] phase ${A + 1} — project checks failed, stopping`);
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
st++, r && (c(b, `[commit] phase ${A + 1} — ${tt.text}`), await r(o, tt, A));
|
|
459
575
|
}
|
|
460
|
-
if (n !==
|
|
461
|
-
if (
|
|
462
|
-
|
|
576
|
+
if (n !== b) return;
|
|
577
|
+
if (b.status === "stopping") {
|
|
578
|
+
d(b, "done");
|
|
463
579
|
return;
|
|
464
580
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
581
|
+
if (Z > 0)
|
|
582
|
+
c(b, `Run stopped after ${st} phase(s) completed, 1 failed`), d(b, "error");
|
|
583
|
+
else {
|
|
584
|
+
if (c(b, `All ${st} phase(s) completed`), s)
|
|
585
|
+
try {
|
|
586
|
+
c(b, "[review] setting plan status to review"), await s(o);
|
|
587
|
+
} catch (tt) {
|
|
588
|
+
c(
|
|
589
|
+
b,
|
|
590
|
+
`Warning: could not set plan status to review: ${tt.message}`
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
d(b, "done");
|
|
594
|
+
}
|
|
595
|
+
} catch (C) {
|
|
596
|
+
n === b && (c(b, `Run all phases failed: ${C.message}`), d(b, "error"));
|
|
468
597
|
}
|
|
469
598
|
})(), { ok: !0 };
|
|
470
599
|
}
|
|
471
|
-
const
|
|
472
|
-
function
|
|
473
|
-
if (
|
|
600
|
+
const M = 6e4, B = 10 * 1024 * 1024;
|
|
601
|
+
function J(o) {
|
|
602
|
+
if (g())
|
|
474
603
|
return Promise.reject(new Error("An agent task is already running"));
|
|
475
|
-
if (Buffer.byteLength(
|
|
604
|
+
if (Buffer.byteLength(o, "utf-8") > B)
|
|
476
605
|
return Promise.reject(new Error("Commit suggestion prompt exceeds the 10MB stdin limit"));
|
|
477
|
-
const
|
|
478
|
-
id:
|
|
479
|
-
adapter:
|
|
480
|
-
model:
|
|
481
|
-
effort:
|
|
606
|
+
const k = Pt(t), {
|
|
607
|
+
id: v,
|
|
608
|
+
adapter: N,
|
|
609
|
+
model: X,
|
|
610
|
+
effort: G
|
|
482
611
|
} = St({
|
|
483
|
-
defaultAgents:
|
|
612
|
+
defaultAgents: k,
|
|
484
613
|
taskKind: "commit-suggest"
|
|
485
|
-
}),
|
|
486
|
-
return
|
|
487
|
-
var
|
|
488
|
-
const K =
|
|
489
|
-
cwd:
|
|
614
|
+
}), x = v === "claude-code", W = x ? ["-p", "--output-format", "json"] : ["run", "--format", "json"];
|
|
615
|
+
return X && W.push(x ? "--model" : "-m", X), G && W.push(x ? "--effort" : "--variant", G), new Promise((Q, b) => {
|
|
616
|
+
var U, et, $t, H, V;
|
|
617
|
+
const K = it(N.command, W, {
|
|
618
|
+
cwd: t,
|
|
490
619
|
stdio: ["pipe", "pipe", "pipe"]
|
|
491
|
-
}),
|
|
620
|
+
}), C = {
|
|
492
621
|
taskKind: "commit-suggest",
|
|
493
622
|
planTitle: "Suggest commit message",
|
|
494
623
|
status: "starting",
|
|
495
|
-
agentId:
|
|
496
|
-
adapter:
|
|
624
|
+
agentId: v,
|
|
625
|
+
adapter: N,
|
|
497
626
|
proc: K,
|
|
498
627
|
lines: []
|
|
499
628
|
};
|
|
500
|
-
n =
|
|
501
|
-
let
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
},
|
|
505
|
-
|
|
506
|
-
n ===
|
|
629
|
+
n = C, d(C, "running");
|
|
630
|
+
let st = !1;
|
|
631
|
+
const Z = (L) => {
|
|
632
|
+
st || (st = !0, clearTimeout(tt), L());
|
|
633
|
+
}, tt = setTimeout(() => {
|
|
634
|
+
Z(() => {
|
|
635
|
+
n === C && (c(C, "Commit suggestion timed out"), d(C, "error"), K.killed || K.kill("SIGTERM")), b(new Error("Commit suggestion timed out"));
|
|
507
636
|
});
|
|
508
|
-
},
|
|
509
|
-
(
|
|
510
|
-
}), (
|
|
511
|
-
let
|
|
512
|
-
(
|
|
513
|
-
|
|
514
|
-
}), (
|
|
515
|
-
|
|
637
|
+
}, M);
|
|
638
|
+
(U = K.stdin) == null || U.on("error", () => {
|
|
639
|
+
}), (et = K.stdin) == null || et.write(o), ($t = K.stdin) == null || $t.end();
|
|
640
|
+
let A = "", ct = "";
|
|
641
|
+
(H = K.stdout) == null || H.on("data", (L) => {
|
|
642
|
+
A += L.toString();
|
|
643
|
+
}), (V = K.stderr) == null || V.on("data", (L) => {
|
|
644
|
+
ct += L.toString();
|
|
516
645
|
}), K.on("close", (L) => {
|
|
517
|
-
n ===
|
|
518
|
-
if (
|
|
519
|
-
|
|
646
|
+
n === C && Z(() => {
|
|
647
|
+
if (C.status === "stopping") {
|
|
648
|
+
d(C, "done"), b(new Error("Stopped"));
|
|
520
649
|
return;
|
|
521
650
|
}
|
|
522
651
|
if (L === 0) {
|
|
523
|
-
|
|
524
|
-
const
|
|
525
|
-
`).map((
|
|
526
|
-
var
|
|
652
|
+
d(C, "done");
|
|
653
|
+
const nt = x ? A : A.split(`
|
|
654
|
+
`).map((kt) => {
|
|
655
|
+
var dt;
|
|
527
656
|
try {
|
|
528
|
-
const
|
|
529
|
-
if ((
|
|
657
|
+
const pt = JSON.parse(kt);
|
|
658
|
+
if ((pt == null ? void 0 : pt.type) === "text" && ((dt = pt == null ? void 0 : pt.part) != null && dt.text)) return pt.part.text;
|
|
530
659
|
} catch {
|
|
531
660
|
}
|
|
532
661
|
return null;
|
|
533
662
|
}).filter(Boolean).join(`
|
|
534
663
|
`);
|
|
535
|
-
|
|
664
|
+
Q(nt);
|
|
536
665
|
} else {
|
|
537
|
-
const
|
|
538
|
-
|
|
666
|
+
const nt = ct || `${N.command} exited with code ${L}`;
|
|
667
|
+
c(C, nt), d(C, "error"), b(new Error(nt));
|
|
539
668
|
}
|
|
540
669
|
});
|
|
541
670
|
}), K.on("error", (L) => {
|
|
542
|
-
n ===
|
|
543
|
-
|
|
671
|
+
n === C && Z(() => {
|
|
672
|
+
c(C, `Failed to spawn agent: ${L.message}`), d(C, "error"), b(L);
|
|
544
673
|
});
|
|
545
674
|
});
|
|
546
675
|
});
|
|
547
676
|
}
|
|
548
|
-
function
|
|
677
|
+
function _() {
|
|
549
678
|
if (!n)
|
|
550
679
|
return { ok: !1, error: "No agent task running" };
|
|
551
|
-
const
|
|
552
|
-
return
|
|
553
|
-
n ===
|
|
680
|
+
const o = n;
|
|
681
|
+
return d(o, "stopping"), o.proc.killed || o.proc.kill("SIGTERM"), setTimeout(() => {
|
|
682
|
+
n === o && o.status === "stopping" && o.proc.kill("SIGKILL");
|
|
554
683
|
}, 5e3), { ok: !0 };
|
|
555
684
|
}
|
|
556
|
-
function
|
|
685
|
+
function R() {
|
|
557
686
|
return n ? {
|
|
558
687
|
status: n.status,
|
|
559
688
|
taskKind: n.taskKind,
|
|
@@ -566,1161 +695,1464 @@ function Ce(e, i = () => {
|
|
|
566
695
|
} : null;
|
|
567
696
|
}
|
|
568
697
|
return {
|
|
569
|
-
start:
|
|
570
|
-
startForPlan:
|
|
571
|
-
startForIdea:
|
|
572
|
-
startForIdeaExtend:
|
|
573
|
-
startBatchAudit:
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
698
|
+
start: F,
|
|
699
|
+
startForPlan: D,
|
|
700
|
+
startForIdea: S,
|
|
701
|
+
startForIdeaExtend: m,
|
|
702
|
+
startBatchAudit: T,
|
|
703
|
+
startRunAllPhases: O,
|
|
704
|
+
startSync: w,
|
|
705
|
+
runCommitSuggest: J,
|
|
706
|
+
stop: _,
|
|
707
|
+
getStatus: R,
|
|
708
|
+
subscribe(o) {
|
|
709
|
+
e.add(o), o.on("close", () => e.delete(o));
|
|
579
710
|
},
|
|
580
711
|
killCurrent() {
|
|
581
712
|
n != null && n.proc && !n.proc.killed && n.proc.kill();
|
|
582
713
|
}
|
|
583
714
|
};
|
|
584
715
|
}
|
|
585
|
-
function
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
{"title": "type(scope): Description", "message": "${i ? `longer body describing what changed and why, ending with a \`Refs: ${i}\` line` : "optional longer body describing what changed and why, or an empty string if the title alone is clear enough"}"}
|
|
593
|
-
|
|
594
|
-
Diff:
|
|
595
|
-
${e}`;
|
|
716
|
+
async function j(t) {
|
|
717
|
+
try {
|
|
718
|
+
return await at(t, "utf-8");
|
|
719
|
+
} catch (a) {
|
|
720
|
+
if (a.code === "ENOENT") return "";
|
|
721
|
+
throw a;
|
|
722
|
+
}
|
|
596
723
|
}
|
|
597
|
-
async function
|
|
598
|
-
if (!e.trim())
|
|
599
|
-
throw new Error("No changes to summarize — select at least one file first");
|
|
600
|
-
const p = $e(e, i), n = await a(p);
|
|
601
|
-
let h = n;
|
|
724
|
+
async function wt(t) {
|
|
602
725
|
try {
|
|
603
|
-
|
|
604
|
-
typeof t.result == "string" && (h = t.result);
|
|
726
|
+
return await ht(t), !0;
|
|
605
727
|
} catch {
|
|
728
|
+
return !1;
|
|
606
729
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
730
|
+
}
|
|
731
|
+
const P = (t, a) => $(t, "papercamp", a);
|
|
732
|
+
function Nt(t, a = {}) {
|
|
733
|
+
return {
|
|
734
|
+
id: t.id ?? "",
|
|
735
|
+
title: t.title,
|
|
736
|
+
kind: t.kind ?? "feat",
|
|
737
|
+
status: t.status,
|
|
738
|
+
idea: t.idea,
|
|
739
|
+
agent: t.agent,
|
|
740
|
+
created: t.created,
|
|
741
|
+
updated: t.updated,
|
|
742
|
+
audited: t.audited,
|
|
743
|
+
tags: t.tags,
|
|
744
|
+
body: t.body,
|
|
745
|
+
phases: t.phases,
|
|
746
|
+
log: t.log,
|
|
747
|
+
clarifications: t.clarifications,
|
|
748
|
+
...a
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
async function Rt(t, a) {
|
|
752
|
+
await q(t, `${xt(a)}
|
|
753
|
+
`, "utf-8");
|
|
754
|
+
}
|
|
755
|
+
async function Dt(t) {
|
|
756
|
+
const a = P(t, "plans"), i = P(t, "ideas"), [r, s] = await Promise.all([
|
|
757
|
+
gt(a, P(t, "plans.md")),
|
|
758
|
+
Ct(i, P(t, "ideas.md"))
|
|
759
|
+
]), e = se(s.entries, r.entries);
|
|
760
|
+
await ut(a, { recursive: !0 }), await ut(i, { recursive: !0 }), await Promise.all([
|
|
761
|
+
q($(a, "index.md"), Lt(r.entries)),
|
|
762
|
+
q($(i, "index.md"), Zt(e))
|
|
763
|
+
]);
|
|
764
|
+
}
|
|
765
|
+
async function rt(t, a, i) {
|
|
766
|
+
const r = a.getFeatureBranchPlanId();
|
|
767
|
+
if (i && r === i || !r) return null;
|
|
768
|
+
if (await a.getBranchHygieneStatus() === "stale-merged")
|
|
769
|
+
return "You're on a merged branch — switch to main before starting another plan";
|
|
770
|
+
const e = P(t, "plans"), { entries: n } = await gt(e, P(t, "plans.md")), l = n.find((c) => c.id === r);
|
|
771
|
+
return !l || l.status === "done" || l.status === "dropped" ? null : `Finish \`${r}\` — ${l.title} — before starting another plan`;
|
|
772
|
+
}
|
|
773
|
+
function Fe(t, a) {
|
|
774
|
+
async function i(n) {
|
|
775
|
+
const l = P(t, "progress.md"), d = `## ${ot()}`, u = await j(l);
|
|
776
|
+
if (u.startsWith(`${d}
|
|
777
|
+
`))
|
|
778
|
+
await q(
|
|
779
|
+
l,
|
|
780
|
+
`${d}
|
|
781
|
+
- ${n}
|
|
782
|
+
${u.slice(d.length + 1)}`,
|
|
783
|
+
"utf-8"
|
|
784
|
+
);
|
|
785
|
+
else {
|
|
786
|
+
const f = u.trimEnd(), h = f ? `${d}
|
|
787
|
+
- ${n}
|
|
613
788
|
|
|
614
|
-
|
|
789
|
+
${f}
|
|
790
|
+
` : `${d}
|
|
791
|
+
- ${n}
|
|
792
|
+
`;
|
|
793
|
+
await q(l, h, "utf-8");
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
async function r(n, l) {
|
|
797
|
+
const c = P(t, "plans"), d = $(c, `${n}.md`), u = $(c, "archive", `${n}.md`), f = await wt(d) ? d : await wt(u) ? u : null;
|
|
798
|
+
if (!f) return;
|
|
799
|
+
const h = await j(f);
|
|
800
|
+
if (!h) return;
|
|
801
|
+
const y = mt(h);
|
|
802
|
+
if (y.entries.length === 0) return;
|
|
803
|
+
const g = y.entries[0];
|
|
804
|
+
if (await Rt(f, Nt(g, { id: n, audited: ot() })), l > 0) {
|
|
805
|
+
const E = `gap phase${l === 1 ? "" : "s"}`;
|
|
806
|
+
await i(
|
|
807
|
+
`Batch audit of ${n} (${g.title}) — ${l} ${E} appended`
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
async function s(n, l) {
|
|
812
|
+
var f;
|
|
813
|
+
const c = ((f = n.tags) == null ? void 0 : f[0]) ?? "plans", d = `${n.kind ?? "feat"}(${c}): ${l.text}`, u = n.id ? `Refs: ${n.id}` : void 0;
|
|
814
|
+
await a.stageAll(), await a.commit([], d, u);
|
|
815
|
+
}
|
|
816
|
+
async function e(n) {
|
|
817
|
+
var g;
|
|
818
|
+
if (!n.id) return;
|
|
819
|
+
const l = P(t, "plans"), c = $(l, `${n.id}.md`), d = await j(c);
|
|
820
|
+
if (!d) return;
|
|
821
|
+
const u = mt(d);
|
|
822
|
+
if (u.entries.length === 0) return;
|
|
823
|
+
const f = u.entries[0];
|
|
824
|
+
if (f.status === "review" || f.status === "done" || f.status === "dropped") return;
|
|
825
|
+
await Rt(
|
|
826
|
+
c,
|
|
827
|
+
Nt(f, {
|
|
828
|
+
id: f.id ?? n.id,
|
|
829
|
+
status: "review",
|
|
830
|
+
updated: ot()
|
|
831
|
+
})
|
|
832
|
+
);
|
|
833
|
+
const h = ((g = n.tags) == null ? void 0 : g[0]) ?? "plans", y = n.id ? `Refs: ${n.id}` : void 0;
|
|
834
|
+
await a.stageAll(), await a.commit([], `${f.kind ?? "feat"}(${h}): mark ${n.id} review`, y);
|
|
835
|
+
}
|
|
836
|
+
return { stampAuditDate: r, commitPhase: s, setRunReview: e };
|
|
615
837
|
}
|
|
616
|
-
const
|
|
617
|
-
function
|
|
838
|
+
const Ut = [/(^|\/)\.env(\.|$)/i, /\.(pem|key|p12|crt)$/i], Ft = (t) => `:(literal)${t}`;
|
|
839
|
+
function _e(t, a = {}) {
|
|
618
840
|
const i = /* @__PURE__ */ new Set();
|
|
619
|
-
function
|
|
620
|
-
const
|
|
841
|
+
function r(w) {
|
|
842
|
+
const I = `data: ${JSON.stringify(w)}
|
|
621
843
|
|
|
622
844
|
`;
|
|
623
|
-
for (const
|
|
845
|
+
for (const T of i)
|
|
624
846
|
try {
|
|
625
|
-
|
|
847
|
+
T.write(I);
|
|
626
848
|
} catch {
|
|
627
|
-
i.delete(
|
|
849
|
+
i.delete(T);
|
|
628
850
|
}
|
|
629
851
|
}
|
|
630
|
-
function
|
|
631
|
-
const
|
|
632
|
-
for (const
|
|
852
|
+
function s(w) {
|
|
853
|
+
const I = [];
|
|
854
|
+
for (const T of w.split(`
|
|
633
855
|
`)) {
|
|
634
|
-
if (!
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
path:
|
|
638
|
-
status: `${
|
|
639
|
-
staged:
|
|
640
|
-
renameSource: (
|
|
856
|
+
if (!T.trim()) continue;
|
|
857
|
+
const O = T[0] ?? " ", M = T[1] ?? " ", B = T.slice(3), J = B.split(" -> "), _ = J.pop() ?? B;
|
|
858
|
+
I.push({
|
|
859
|
+
path: _,
|
|
860
|
+
status: `${O}${M}`,
|
|
861
|
+
staged: O !== " " && O !== "?",
|
|
862
|
+
renameSource: (O === "R" || O === "C") && J.length > 0 ? J[0] : void 0
|
|
641
863
|
});
|
|
642
864
|
}
|
|
643
|
-
return
|
|
865
|
+
return I;
|
|
644
866
|
}
|
|
645
|
-
function
|
|
646
|
-
return new Promise((
|
|
647
|
-
var
|
|
648
|
-
const
|
|
649
|
-
cwd:
|
|
867
|
+
function e(w) {
|
|
868
|
+
return new Promise((I, T) => {
|
|
869
|
+
var J, _;
|
|
870
|
+
const O = it("git", ["-c", "core.quotepath=off", ...w], {
|
|
871
|
+
cwd: t,
|
|
650
872
|
stdio: ["ignore", "pipe", "pipe"]
|
|
651
873
|
});
|
|
652
|
-
let
|
|
653
|
-
(
|
|
654
|
-
|
|
655
|
-
}), (
|
|
656
|
-
|
|
657
|
-
}),
|
|
658
|
-
|
|
659
|
-
}),
|
|
874
|
+
let M = "", B = "";
|
|
875
|
+
(J = O.stdout) == null || J.on("data", (R) => {
|
|
876
|
+
M += R.toString();
|
|
877
|
+
}), (_ = O.stderr) == null || _.on("data", (R) => {
|
|
878
|
+
B += R.toString();
|
|
879
|
+
}), O.on("close", (R) => {
|
|
880
|
+
R === 0 ? I(M) : T(new Error(B || `git ${w[0]} exited with code ${R}`));
|
|
881
|
+
}), O.on("error", T);
|
|
660
882
|
});
|
|
661
883
|
}
|
|
662
|
-
function
|
|
663
|
-
return
|
|
884
|
+
function n() {
|
|
885
|
+
return e(["status", "--porcelain=v1"]).then(s);
|
|
664
886
|
}
|
|
665
|
-
async function
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
887
|
+
async function l(w, I, T) {
|
|
888
|
+
let O = /* @__PURE__ */ new Map();
|
|
889
|
+
if (w.length > 0) {
|
|
890
|
+
const B = await n(), J = new Map(B.map((R) => [R.path, R.status]));
|
|
891
|
+
O = new Map(
|
|
892
|
+
B.filter((R) => !!R.renameSource).map((R) => [R.path, R.renameSource])
|
|
893
|
+
);
|
|
894
|
+
const _ = w.filter((R) => {
|
|
895
|
+
const o = J.get(R);
|
|
896
|
+
return o === void 0 || o[1] !== " ";
|
|
670
897
|
});
|
|
671
|
-
|
|
898
|
+
_.length > 0 && await e(["add", "--", ..._.map(Ft)]);
|
|
672
899
|
}
|
|
673
|
-
const
|
|
674
|
-
|
|
900
|
+
const M = ["commit", "-m", I];
|
|
901
|
+
if (T && M.push("-m", T), w.length > 0) {
|
|
902
|
+
const B = w.flatMap((J) => {
|
|
903
|
+
const _ = O.get(J);
|
|
904
|
+
return _ ? [J, _] : [J];
|
|
905
|
+
});
|
|
906
|
+
M.push("--", ...B.map(Ft));
|
|
907
|
+
}
|
|
908
|
+
await e(M);
|
|
675
909
|
}
|
|
676
|
-
function
|
|
677
|
-
if (!
|
|
678
|
-
const
|
|
679
|
-
if (
|
|
910
|
+
function c(w) {
|
|
911
|
+
if (!w.kind || !w.id) return;
|
|
912
|
+
const I = w.kind.toLowerCase(), T = w.id.toLowerCase(), O = w.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), M = `${I}/${T}-${O}`, B = It("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: t });
|
|
913
|
+
if (B.status !== 0)
|
|
680
914
|
throw new Error(
|
|
681
|
-
|
|
915
|
+
B.stderr.toString().trim() || "Unable to read current git branch"
|
|
682
916
|
);
|
|
683
|
-
if (
|
|
684
|
-
const
|
|
685
|
-
if (
|
|
686
|
-
const
|
|
687
|
-
if (
|
|
917
|
+
if (B.stdout.toString().trim() === M) return;
|
|
918
|
+
const _ = It("git", ["checkout", "-b", M, "main"], { cwd: t });
|
|
919
|
+
if (_.status !== 0) {
|
|
920
|
+
const R = It("git", ["checkout", M], { cwd: t });
|
|
921
|
+
if (R.status !== 0)
|
|
688
922
|
throw new Error(
|
|
689
|
-
|
|
923
|
+
R.stderr.toString().trim() || _.stderr.toString().trim() || `Unable to check out ${M}`
|
|
690
924
|
);
|
|
691
925
|
}
|
|
692
926
|
}
|
|
693
|
-
async function
|
|
927
|
+
async function d() {
|
|
694
928
|
try {
|
|
695
|
-
await
|
|
929
|
+
await n(), r({
|
|
696
930
|
message: "Working tree status updated",
|
|
697
931
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
698
932
|
});
|
|
699
933
|
} catch {
|
|
700
934
|
}
|
|
701
935
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
936
|
+
if (a.watch !== !1) {
|
|
937
|
+
const w = $(t, ".git");
|
|
938
|
+
let I = null;
|
|
939
|
+
try {
|
|
940
|
+
Ot(w, { recursive: !0 }, (M, B) => {
|
|
941
|
+
B === "index" && (I && clearTimeout(I), I = setTimeout(d, 500));
|
|
942
|
+
});
|
|
943
|
+
} catch {
|
|
944
|
+
}
|
|
945
|
+
const T = $(t, "src");
|
|
946
|
+
let O = null;
|
|
947
|
+
try {
|
|
948
|
+
Ot(T, { recursive: !0 }, () => {
|
|
949
|
+
O && clearTimeout(O), O = setTimeout(d, 500);
|
|
950
|
+
});
|
|
951
|
+
} catch {
|
|
952
|
+
}
|
|
717
953
|
}
|
|
718
|
-
async function
|
|
954
|
+
async function u() {
|
|
719
955
|
try {
|
|
720
|
-
return await
|
|
956
|
+
return await e(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]), !0;
|
|
721
957
|
} catch {
|
|
722
958
|
return !1;
|
|
723
959
|
}
|
|
724
960
|
}
|
|
725
|
-
async function
|
|
961
|
+
async function f() {
|
|
726
962
|
try {
|
|
727
|
-
const
|
|
728
|
-
return Number.parseInt(
|
|
963
|
+
const w = await u() ? ["rev-list", "--count", "@{u}..HEAD"] : ["rev-list", "--count", "HEAD", "--not", "--remotes"], I = await e(w);
|
|
964
|
+
return Number.parseInt(I.trim(), 10) || 0;
|
|
729
965
|
} catch {
|
|
730
966
|
return 0;
|
|
731
967
|
}
|
|
732
968
|
}
|
|
733
|
-
async function
|
|
734
|
-
await
|
|
969
|
+
async function h() {
|
|
970
|
+
await u() ? await e(["push"]) : await e(["push", "--set-upstream", "origin", y()]);
|
|
735
971
|
}
|
|
736
|
-
function
|
|
737
|
-
return
|
|
972
|
+
function y() {
|
|
973
|
+
return It("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: t }).stdout.toString().trim();
|
|
738
974
|
}
|
|
739
|
-
function
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
),
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
975
|
+
async function g() {
|
|
976
|
+
const w = y();
|
|
977
|
+
if (w === "main" || w === "master") return !1;
|
|
978
|
+
try {
|
|
979
|
+
return (await e(["branch", "--merged", "main"])).split(`
|
|
980
|
+
`).some((T) => T.trim().replace(/^\*\s+/, "") === w);
|
|
981
|
+
} catch {
|
|
982
|
+
return !1;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
async function E() {
|
|
986
|
+
const w = y(), T = (await n()).length > 0;
|
|
987
|
+
return w === "main" || w === "master" ? T ? "dirty" : "clean-on-main" : await g() && await e(["rev-list", "--count", "HEAD..main"]).then((M) => Number.parseInt(M.trim(), 10) || 0).catch(() => 0) > 0 ? "stale-merged" : T ? "dirty" : "fine";
|
|
988
|
+
}
|
|
989
|
+
function F() {
|
|
990
|
+
const I = y().match(/^[a-z]+\/([a-z]+-\d+)-/);
|
|
991
|
+
return I ? I[1].toUpperCase() : null;
|
|
992
|
+
}
|
|
993
|
+
async function D(w, I = 12e3) {
|
|
994
|
+
if (w.length === 0) return "";
|
|
995
|
+
const T = w.find((o) => Ut.some((k) => k.test(o)));
|
|
996
|
+
if (T)
|
|
997
|
+
throw new Error(`Refusing to send sensitive file "${T}" to commit suggestion`);
|
|
998
|
+
const O = await n(), M = new Set(O.filter((o) => o.status === "??").map((o) => o.path)), B = new Map(
|
|
999
|
+
O.filter((o) => !!o.renameSource).map((o) => [o.path, o.renameSource])
|
|
1000
|
+
), J = w.filter((o) => {
|
|
1001
|
+
if (M.has(o)) return !1;
|
|
1002
|
+
const k = B.get(o);
|
|
1003
|
+
return !(k && Ut.some((v) => v.test(k)));
|
|
1004
|
+
}), _ = [];
|
|
1005
|
+
if (J.length > 0) {
|
|
1006
|
+
const o = await e([
|
|
757
1007
|
"diff",
|
|
758
1008
|
"HEAD",
|
|
759
1009
|
"--",
|
|
760
|
-
...
|
|
1010
|
+
...J.map(Ft)
|
|
761
1011
|
]).catch(() => "");
|
|
762
|
-
|
|
763
|
-
}
|
|
764
|
-
for (const
|
|
765
|
-
const
|
|
766
|
-
if (!
|
|
767
|
-
if (
|
|
768
|
-
|
|
769
|
-
+++ b/${
|
|
1012
|
+
o && _.push(o);
|
|
1013
|
+
}
|
|
1014
|
+
for (const o of w.filter((k) => M.has(k))) {
|
|
1015
|
+
const k = $(t, o), v = await ce(k).catch(() => null);
|
|
1016
|
+
if (!v) continue;
|
|
1017
|
+
if (v.isSymbolicLink()) {
|
|
1018
|
+
_.push(`--- /dev/null
|
|
1019
|
+
+++ b/${o}
|
|
770
1020
|
(new file omitted: symlink)`);
|
|
771
1021
|
continue;
|
|
772
1022
|
}
|
|
773
|
-
if (
|
|
774
|
-
|
|
775
|
-
+++ b/${
|
|
1023
|
+
if (v.size > I) {
|
|
1024
|
+
_.push(`--- /dev/null
|
|
1025
|
+
+++ b/${o}
|
|
776
1026
|
(new file omitted: exceeds diff size cap)`);
|
|
777
1027
|
continue;
|
|
778
1028
|
}
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
+++ b/${
|
|
1029
|
+
const N = await at(k, "utf-8").catch(() => "");
|
|
1030
|
+
N && _.push(`--- /dev/null
|
|
1031
|
+
+++ b/${o}
|
|
782
1032
|
(new file)
|
|
783
|
-
${
|
|
1033
|
+
${N}`);
|
|
784
1034
|
}
|
|
785
|
-
const
|
|
1035
|
+
const R = _.join(`
|
|
786
1036
|
|
|
787
1037
|
`);
|
|
788
|
-
return
|
|
789
|
-
... (truncated)` :
|
|
790
|
-
}
|
|
791
|
-
return {
|
|
792
|
-
async getStatus() {
|
|
793
|
-
return h();
|
|
794
|
-
},
|
|
795
|
-
getCurrentBranch: l,
|
|
796
|
-
commit: m,
|
|
797
|
-
diff: c,
|
|
798
|
-
ensureBranch: $,
|
|
799
|
-
getFeatureBranchPlanId: T,
|
|
800
|
-
getAheadCount: d,
|
|
801
|
-
push: y,
|
|
802
|
-
subscribe(o) {
|
|
803
|
-
i.add(o), o.on("close", () => i.delete(o));
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
const Ne = {
|
|
808
|
-
lint: "npx biome lint .",
|
|
809
|
-
format: "npx biome format .",
|
|
810
|
-
test: "npx vitest run"
|
|
811
|
-
};
|
|
812
|
-
function be(e) {
|
|
813
|
-
const i = /* @__PURE__ */ new Set(), a = {
|
|
814
|
-
lint: { status: "stale", lastRun: null, output: "" },
|
|
815
|
-
format: { status: "stale", lastRun: null, output: "" },
|
|
816
|
-
test: { status: "stale", lastRun: null, output: "" }
|
|
817
|
-
}, p = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set();
|
|
818
|
-
function h(g) {
|
|
819
|
-
const j = `data: ${JSON.stringify(g)}
|
|
820
|
-
|
|
821
|
-
`;
|
|
822
|
-
for (const r of i)
|
|
823
|
-
try {
|
|
824
|
-
r.write(j);
|
|
825
|
-
} catch {
|
|
826
|
-
i.delete(r);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
function m(g, j, r) {
|
|
830
|
-
a[g] = { status: j, lastRun: (/* @__PURE__ */ new Date()).toISOString(), output: r }, h({
|
|
831
|
-
message: `${g}: ${j}`,
|
|
832
|
-
timestamp: a[g].lastRun
|
|
833
|
-
}), j !== "running" && n.has(g) && (n.delete(g), $(g));
|
|
834
|
-
}
|
|
835
|
-
function $(g) {
|
|
836
|
-
var l, T;
|
|
837
|
-
if (p.has(g)) {
|
|
838
|
-
n.add(g);
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
p.add(g), m(g, "running", "");
|
|
842
|
-
const j = Ne[g], r = V(j, {
|
|
843
|
-
cwd: e,
|
|
844
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
845
|
-
shell: !0
|
|
846
|
-
});
|
|
847
|
-
let d = "", y = "";
|
|
848
|
-
(l = r.stdout) == null || l.on("data", (c) => {
|
|
849
|
-
d += c.toString();
|
|
850
|
-
}), (T = r.stderr) == null || T.on("data", (c) => {
|
|
851
|
-
y += c.toString();
|
|
852
|
-
}), r.on("close", (c) => {
|
|
853
|
-
p.delete(g);
|
|
854
|
-
const o = d + y;
|
|
855
|
-
c === 0 ? m(g, "pass", o) : m(g, "fail", o);
|
|
856
|
-
}), r.on("error", (c) => {
|
|
857
|
-
p.delete(g), m(g, "fail", `Failed to spawn process: ${c.message}`);
|
|
858
|
-
});
|
|
1038
|
+
return R.length > I ? `${R.slice(0, I)}
|
|
1039
|
+
... (truncated)` : R;
|
|
859
1040
|
}
|
|
860
|
-
function
|
|
861
|
-
|
|
862
|
-
m("lint", "running", "Applying automatic fixes…"), m("format", "running", "Applying automatic fixes…");
|
|
863
|
-
const g = V("npx biome check . --write", {
|
|
864
|
-
cwd: e,
|
|
865
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
866
|
-
shell: !0
|
|
867
|
-
});
|
|
868
|
-
g.on("close", () => {
|
|
869
|
-
$("lint"), $("format");
|
|
870
|
-
}), g.on("error", (j) => {
|
|
871
|
-
const r = `Failed to spawn fix process: ${j.message}`;
|
|
872
|
-
m("lint", "fail", r), m("format", "fail", r);
|
|
873
|
-
});
|
|
1041
|
+
async function S() {
|
|
1042
|
+
await e(["add", "-A"]);
|
|
874
1043
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
try {
|
|
878
|
-
Ct(t, { recursive: !0 }, () => {
|
|
879
|
-
J && clearTimeout(J), J = setTimeout(() => {
|
|
880
|
-
$("lint"), $("format");
|
|
881
|
-
}, 1e3);
|
|
882
|
-
});
|
|
883
|
-
} catch {
|
|
1044
|
+
async function m() {
|
|
1045
|
+
await e(["checkout", "main"]), await e(["fetch", "--prune"]), await e(["merge", "--ff-only", "origin/main"]);
|
|
884
1046
|
}
|
|
885
1047
|
return {
|
|
886
|
-
getStatus() {
|
|
887
|
-
return
|
|
888
|
-
lint: { ...a.lint },
|
|
889
|
-
format: { ...a.format },
|
|
890
|
-
test: { ...a.test }
|
|
891
|
-
};
|
|
1048
|
+
async getStatus() {
|
|
1049
|
+
return n();
|
|
892
1050
|
},
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1051
|
+
getCurrentBranch: y,
|
|
1052
|
+
commit: l,
|
|
1053
|
+
stageAll: S,
|
|
1054
|
+
diff: D,
|
|
1055
|
+
ensureBranch: c,
|
|
1056
|
+
getFeatureBranchPlanId: F,
|
|
1057
|
+
getAheadCount: f,
|
|
1058
|
+
push: h,
|
|
1059
|
+
isMergedIntoMain: g,
|
|
1060
|
+
getBranchHygieneStatus: E,
|
|
1061
|
+
runGitSync: m,
|
|
1062
|
+
subscribe(w) {
|
|
1063
|
+
i.add(w), w.on("close", () => i.delete(w));
|
|
906
1064
|
}
|
|
907
1065
|
};
|
|
908
1066
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
return await U(e, "utf-8");
|
|
912
|
-
} catch (i) {
|
|
913
|
-
if (i.code === "ENOENT") return "";
|
|
914
|
-
throw i;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
async function ft(e) {
|
|
918
|
-
try {
|
|
919
|
-
return await it(e), !0;
|
|
920
|
-
} catch {
|
|
921
|
-
return !1;
|
|
922
|
-
}
|
|
1067
|
+
function p(t, a, i) {
|
|
1068
|
+
t.statusCode = a, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(i));
|
|
923
1069
|
}
|
|
924
|
-
async function
|
|
925
|
-
return new Promise((
|
|
926
|
-
let
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
}),
|
|
1070
|
+
async function Y(t) {
|
|
1071
|
+
return new Promise((a, i) => {
|
|
1072
|
+
let r = "";
|
|
1073
|
+
t.on("data", (s) => {
|
|
1074
|
+
r += s;
|
|
1075
|
+
}), t.on("end", () => a(r)), t.on("error", i);
|
|
930
1076
|
});
|
|
931
1077
|
}
|
|
932
|
-
|
|
933
|
-
"
|
|
934
|
-
"tsconfig.json",
|
|
935
|
-
"tailwind.config.ts",
|
|
936
|
-
"vite.config.ts",
|
|
937
|
-
"vite.app.config.ts",
|
|
938
|
-
"postcss.config.js",
|
|
939
|
-
"package.json"
|
|
940
|
-
];
|
|
941
|
-
async function et(e, i, a) {
|
|
942
|
-
const p = i.getFeatureBranchPlanId();
|
|
943
|
-
if (!p || a && p === a) return null;
|
|
944
|
-
const n = x(e, "plans"), { entries: h } = await ct(n, x(e, "plans.md")), m = h.find(($) => $.id === p);
|
|
945
|
-
return !m || m.status === "done" || m.status === "dropped" ? null : `Finish \`${p}\` — ${m.title} — before starting another plan`;
|
|
1078
|
+
function vt(t) {
|
|
1079
|
+
return new URL(t.url ?? "", `http://${t.headers.host ?? "localhost"}`);
|
|
946
1080
|
}
|
|
947
|
-
async function
|
|
948
|
-
const i =
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
await q(i, { recursive: !0 }), await q(a, { recursive: !0 }), await Promise.all([
|
|
953
|
-
E(N(i, "index.md"), Ht(p.entries)),
|
|
954
|
-
E(N(a, "index.md"), _t(h))
|
|
955
|
-
]);
|
|
1081
|
+
async function Me(t, a) {
|
|
1082
|
+
const i = $(P(t, "plans"), `${a}.md`);
|
|
1083
|
+
if (await wt(i)) return i;
|
|
1084
|
+
const r = $(P(t, "plans"), "archive", `${a}.md`);
|
|
1085
|
+
return await wt(r) ? r : null;
|
|
956
1086
|
}
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
} catch {
|
|
966
|
-
return null;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
},
|
|
970
|
-
{
|
|
971
|
-
path: "/api/plans",
|
|
972
|
-
handler: async (e) => ct(x(e, "plans"), x(e, "plans.md"))
|
|
973
|
-
},
|
|
974
|
-
{
|
|
975
|
-
path: "/api/progress",
|
|
976
|
-
handler: async (e) => ({
|
|
977
|
-
entries: ne(await P(x(e, "progress.md")))
|
|
978
|
-
})
|
|
979
|
-
},
|
|
980
|
-
{
|
|
981
|
-
path: "/api/decisions",
|
|
982
|
-
handler: async (e) => Dt(await P(x(e, "decisions.md")))
|
|
983
|
-
},
|
|
984
|
-
{
|
|
985
|
-
path: "/api/open-questions",
|
|
986
|
-
handler: async (e) => It(await P(x(e, "open-questions.md")))
|
|
987
|
-
},
|
|
988
|
-
{
|
|
989
|
-
path: "/api/ideas",
|
|
990
|
-
handler: async (e) => Tt(x(e, "ideas"), x(e, "ideas.md"))
|
|
991
|
-
},
|
|
992
|
-
{
|
|
993
|
-
path: "/api/consistency",
|
|
994
|
-
handler: async (e) => {
|
|
995
|
-
const [i, a, p] = await Promise.all([
|
|
996
|
-
P(x(e, "decisions.md")),
|
|
997
|
-
P(x(e, "open-questions.md")),
|
|
998
|
-
ct(x(e, "plans"), x(e, "plans.md"))
|
|
999
|
-
]), n = Dt(i), h = It(a);
|
|
1000
|
-
return ae(n.entries, h.entries, p.entries);
|
|
1001
|
-
}
|
|
1002
|
-
},
|
|
1003
|
-
{
|
|
1004
|
-
path: "/api/config",
|
|
1005
|
-
handler: async (e) => {
|
|
1006
|
-
const i = await P(N(e, "papercamp", "config.json"));
|
|
1007
|
-
if (!i) return null;
|
|
1008
|
-
const a = JSON.parse(i);
|
|
1009
|
-
return a != null && a.defaultAgents && (a.defaultAgents = {
|
|
1010
|
-
phase: B(a.defaultAgents.phase),
|
|
1011
|
-
planDraft: B(a.defaultAgents.planDraft),
|
|
1012
|
-
ideaExtend: B(a.defaultAgents.ideaExtend),
|
|
1013
|
-
commitSuggest: B(a.defaultAgents.commitSuggest)
|
|
1014
|
-
}), a;
|
|
1015
|
-
}
|
|
1016
|
-
},
|
|
1017
|
-
{
|
|
1018
|
-
path: "/api/docs",
|
|
1019
|
-
handler: async (e) => {
|
|
1020
|
-
const i = ["MAIN.md", "README.md", "CHANGELOG.md", "LICENSE"], a = [];
|
|
1021
|
-
for (const p of i) {
|
|
1022
|
-
const n = await P(N(e, p));
|
|
1023
|
-
n && a.push({ name: p, content: n });
|
|
1024
|
-
}
|
|
1025
|
-
return { files: a };
|
|
1026
|
-
}
|
|
1027
|
-
},
|
|
1028
|
-
{
|
|
1029
|
-
path: "/api/configs",
|
|
1030
|
-
handler: async (e) => {
|
|
1031
|
-
const i = [
|
|
1032
|
-
"biome.json",
|
|
1033
|
-
"tsconfig.json",
|
|
1034
|
-
"tailwind.config.ts",
|
|
1035
|
-
"vite.config.ts",
|
|
1036
|
-
"vite.app.config.ts",
|
|
1037
|
-
"postcss.config.js",
|
|
1038
|
-
"package.json"
|
|
1039
|
-
], a = [];
|
|
1040
|
-
for (const p of i)
|
|
1041
|
-
await P(N(e, p)) && a.push(p);
|
|
1042
|
-
return { files: a };
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1087
|
+
const je = [
|
|
1088
|
+
["`plans.md`", "`papercamp/plans/`"],
|
|
1089
|
+
["`ideas.md`", "`papercamp/ideas/`"],
|
|
1090
|
+
[".paper-camp/", "papercamp/"],
|
|
1091
|
+
["FocusTaskItem", "FocusPhaseItem"],
|
|
1092
|
+
["TaskItem", "PhaseItem"],
|
|
1093
|
+
["taskProgress", "phaseProgress"],
|
|
1094
|
+
["taskPercentage", "phasePercentage"]
|
|
1045
1095
|
];
|
|
1046
|
-
function
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
"utf-8"
|
|
1058
|
-
);
|
|
1059
|
-
else {
|
|
1060
|
-
const r = j.trimEnd(), d = r ? `${g}
|
|
1061
|
-
- ${u}
|
|
1062
|
-
|
|
1063
|
-
${r}
|
|
1064
|
-
` : `${g}
|
|
1065
|
-
- ${u}
|
|
1066
|
-
`;
|
|
1067
|
-
await E(t, d, "utf-8");
|
|
1096
|
+
function Le(t) {
|
|
1097
|
+
const a = t.startsWith(`---
|
|
1098
|
+
`) ? t.indexOf(`
|
|
1099
|
+
---`, 4) + 4 : -1, i = a >= 4 ? t.slice(0, a) : "", s = (a >= 4 ? t.slice(a) : t).split(`
|
|
1100
|
+
`);
|
|
1101
|
+
let e = !1, n = !1;
|
|
1102
|
+
for (let l = 0; l < s.length; l++) {
|
|
1103
|
+
const c = s[l];
|
|
1104
|
+
if (/^- \[x\]/.test(c)) {
|
|
1105
|
+
n = !0;
|
|
1106
|
+
continue;
|
|
1068
1107
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
if (!r) return;
|
|
1073
|
-
const d = await P(r);
|
|
1074
|
-
if (!d) return;
|
|
1075
|
-
const y = dt(d);
|
|
1076
|
-
if (y.entries.length === 0) return;
|
|
1077
|
-
const l = y.entries[0], T = {
|
|
1078
|
-
id: u,
|
|
1079
|
-
title: l.title,
|
|
1080
|
-
kind: l.kind ?? "feat",
|
|
1081
|
-
status: l.status,
|
|
1082
|
-
idea: l.idea,
|
|
1083
|
-
agent: l.agent,
|
|
1084
|
-
created: l.created,
|
|
1085
|
-
updated: l.updated,
|
|
1086
|
-
audited: Z(),
|
|
1087
|
-
tags: l.tags,
|
|
1088
|
-
body: l.body,
|
|
1089
|
-
phases: l.phases,
|
|
1090
|
-
log: l.log,
|
|
1091
|
-
clarifications: l.clarifications
|
|
1092
|
-
};
|
|
1093
|
-
if (await E(r, `${at(T)}
|
|
1094
|
-
`, "utf-8"), t > 0) {
|
|
1095
|
-
const c = `gap phase${t === 1 ? "" : "s"}`;
|
|
1096
|
-
await n(
|
|
1097
|
-
`Batch audit of ${u} (${l.title}) — ${t} ${c} appended`
|
|
1098
|
-
);
|
|
1108
|
+
if (n) {
|
|
1109
|
+
if (c.trim() === "" || /^\s+\S/.test(c)) continue;
|
|
1110
|
+
n = !1;
|
|
1099
1111
|
}
|
|
1112
|
+
let d = c;
|
|
1113
|
+
for (const [u, f] of je)
|
|
1114
|
+
d.includes(u) && (d = d.split(u).join(f));
|
|
1115
|
+
d !== c && (e = !0, s[l] = d);
|
|
1100
1116
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1117
|
+
return { content: i + s.join(`
|
|
1118
|
+
`), changed: e };
|
|
1119
|
+
}
|
|
1120
|
+
async function At(t, a) {
|
|
1121
|
+
const { entries: i } = await lt(P(t, "plans")), r = i.find((e) => e.id === a);
|
|
1122
|
+
return r || Vt(await j(P(t, "plans.md"))).entries.find((e) => e.id === a);
|
|
1123
|
+
}
|
|
1124
|
+
async function Yt(t, a) {
|
|
1125
|
+
const { entries: i } = await te(P(t, "ideas")), r = i.find((e) => e.id === a);
|
|
1126
|
+
return r || ee(await j(P(t, "ideas.md"))).find((e) => e.id === a);
|
|
1127
|
+
}
|
|
1128
|
+
function qe({ root: t, git: a, status: i, agent: r }) {
|
|
1129
|
+
return [
|
|
1130
|
+
// GET /api/agent/status — current agent task state, if any
|
|
1131
|
+
{
|
|
1132
|
+
method: "GET",
|
|
1133
|
+
path: "/api/agent/status",
|
|
1134
|
+
handle: (s, e) => {
|
|
1135
|
+
p(e, 200, r.getStatus());
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
// POST /api/agent/launch — start a headless agent on one plan phase
|
|
1139
|
+
{
|
|
1140
|
+
method: "POST",
|
|
1141
|
+
path: "/api/agent/launch",
|
|
1142
|
+
handle: async (s, e) => {
|
|
1143
|
+
const n = await Y(s), { planId: l, phaseIndex: c } = JSON.parse(n);
|
|
1144
|
+
if (!l || typeof c != "number") {
|
|
1145
|
+
p(e, 400, { error: "planId and phaseIndex are required" });
|
|
1108
1146
|
return;
|
|
1109
1147
|
}
|
|
1110
|
-
const
|
|
1111
|
-
if (!
|
|
1112
|
-
|
|
1148
|
+
const d = await At(t, l);
|
|
1149
|
+
if (!d) {
|
|
1150
|
+
p(e, 404, { error: "plan not found" });
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
const u = await rt(t, a, d.id);
|
|
1154
|
+
if (u) {
|
|
1155
|
+
p(e, 409, { error: u });
|
|
1113
1156
|
return;
|
|
1114
1157
|
}
|
|
1115
|
-
const
|
|
1116
|
-
if (!
|
|
1117
|
-
|
|
1158
|
+
const f = r.start(d, c);
|
|
1159
|
+
if (!f.ok) {
|
|
1160
|
+
p(e, 409, { error: f.error });
|
|
1118
1161
|
return;
|
|
1119
1162
|
}
|
|
1120
|
-
|
|
1121
|
-
} catch (r) {
|
|
1122
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1163
|
+
p(e, 202, { ok: !0 });
|
|
1123
1164
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1165
|
+
},
|
|
1166
|
+
// POST /api/agent/launch-audit — start a headless agent on a plan-scoped convergence audit
|
|
1167
|
+
{
|
|
1168
|
+
method: "POST",
|
|
1169
|
+
path: "/api/agent/launch-audit",
|
|
1170
|
+
handle: async (s, e) => {
|
|
1171
|
+
const n = await Y(s), { planId: l, prompt: c } = JSON.parse(n);
|
|
1172
|
+
if (!l || !c) {
|
|
1173
|
+
p(e, 400, { error: "planId and prompt are required" });
|
|
1131
1174
|
return;
|
|
1132
1175
|
}
|
|
1133
|
-
const
|
|
1134
|
-
if (!
|
|
1135
|
-
|
|
1176
|
+
const d = await At(t, l);
|
|
1177
|
+
if (!d) {
|
|
1178
|
+
p(e, 404, { error: "plan not found" });
|
|
1136
1179
|
return;
|
|
1137
1180
|
}
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
id: o,
|
|
1142
|
-
title: d.trim(),
|
|
1143
|
-
kind: T,
|
|
1144
|
-
status: "idea",
|
|
1145
|
-
created: Z(),
|
|
1146
|
-
body: y == null ? void 0 : y.trim()
|
|
1147
|
-
});
|
|
1148
|
-
await E(N(f, `${o}.md`), `${S}
|
|
1149
|
-
`, "utf-8"), await yt(e), t.statusCode = 201, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0, id: o }));
|
|
1150
|
-
} catch (r) {
|
|
1151
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1152
|
-
}
|
|
1153
|
-
return;
|
|
1154
|
-
}
|
|
1155
|
-
if (u.method === "PATCH" && g === "/api/plans") {
|
|
1156
|
-
try {
|
|
1157
|
-
const d = new URL(u.url ?? "", `http://${u.headers.host ?? "localhost"}`).searchParams.get("title");
|
|
1158
|
-
if (!(d != null && d.trim())) {
|
|
1159
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "title is required" }));
|
|
1181
|
+
const u = await rt(t, a, d.id);
|
|
1182
|
+
if (u) {
|
|
1183
|
+
p(e, 409, { error: u });
|
|
1160
1184
|
return;
|
|
1161
1185
|
}
|
|
1162
|
-
const
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
1186
|
+
const f = r.startForPlan(d, c);
|
|
1187
|
+
if (!f.ok) {
|
|
1188
|
+
p(e, 409, { error: f.error });
|
|
1165
1189
|
return;
|
|
1166
1190
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1191
|
+
p(e, 202, { ok: !0 });
|
|
1192
|
+
}
|
|
1193
|
+
},
|
|
1194
|
+
// POST /api/agent/launch-reconcile — start a headless agent that reconciles stale prose in a plan
|
|
1195
|
+
{
|
|
1196
|
+
method: "POST",
|
|
1197
|
+
path: "/api/agent/launch-reconcile",
|
|
1198
|
+
handle: async (s, e) => {
|
|
1199
|
+
const n = await Y(s), { planId: l, prompt: c } = JSON.parse(n);
|
|
1200
|
+
if (!l || !c) {
|
|
1201
|
+
p(e, 400, { error: "planId and prompt are required" });
|
|
1170
1202
|
return;
|
|
1171
1203
|
}
|
|
1172
|
-
const
|
|
1173
|
-
if (!
|
|
1174
|
-
|
|
1204
|
+
const d = await At(t, l);
|
|
1205
|
+
if (!d) {
|
|
1206
|
+
p(e, 404, { error: "plan not found" });
|
|
1175
1207
|
return;
|
|
1176
1208
|
}
|
|
1177
|
-
const
|
|
1178
|
-
if (
|
|
1179
|
-
|
|
1209
|
+
const u = await rt(t, a, d.id);
|
|
1210
|
+
if (u) {
|
|
1211
|
+
p(e, 409, { error: u });
|
|
1180
1212
|
return;
|
|
1181
1213
|
}
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
...l.log !== void 0 && { log: l.log },
|
|
1187
|
-
...l.agent !== void 0 && { agent: l.agent ?? void 0 },
|
|
1188
|
-
updated: Z()
|
|
1189
|
-
};
|
|
1190
|
-
if (l.status === "done" || l.status === "dropped") {
|
|
1191
|
-
const w = await et(e, a, f.id);
|
|
1192
|
-
if (w) {
|
|
1193
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: w }));
|
|
1194
|
-
return;
|
|
1195
|
-
}
|
|
1214
|
+
const f = await Me(t, l);
|
|
1215
|
+
if (f) {
|
|
1216
|
+
const y = await at(f, "utf-8"), { content: g, changed: E } = Le(y);
|
|
1217
|
+
E && await q(f, g, "utf-8");
|
|
1196
1218
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
const O = N(T, `${C.id}.md`), A = await P(O);
|
|
1202
|
-
if (A) {
|
|
1203
|
-
const H = dt(A);
|
|
1204
|
-
if (H.entries.length > 0) {
|
|
1205
|
-
const k = {
|
|
1206
|
-
id: H.entries[0].id ?? C.id,
|
|
1207
|
-
title: H.entries[0].title,
|
|
1208
|
-
kind: H.entries[0].kind ?? "feat",
|
|
1209
|
-
status: "planned",
|
|
1210
|
-
created: H.entries[0].created,
|
|
1211
|
-
updated: Z(),
|
|
1212
|
-
audited: H.entries[0].audited,
|
|
1213
|
-
body: H.entries[0].body,
|
|
1214
|
-
phases: H.entries[0].phases,
|
|
1215
|
-
log: H.entries[0].log,
|
|
1216
|
-
clarifications: H.entries[0].clarifications
|
|
1217
|
-
};
|
|
1218
|
-
await E(O, `${at(k)}
|
|
1219
|
-
`, "utf-8");
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1219
|
+
const h = r.startForPlan(d, c, "reconcile");
|
|
1220
|
+
if (!h.ok) {
|
|
1221
|
+
p(e, 409, { error: h.error });
|
|
1222
|
+
return;
|
|
1223
1223
|
}
|
|
1224
|
-
|
|
1225
|
-
id: v.id ?? f.id,
|
|
1226
|
-
title: v.title,
|
|
1227
|
-
kind: v.kind ?? "feat",
|
|
1228
|
-
status: v.status,
|
|
1229
|
-
idea: v.idea,
|
|
1230
|
-
agent: v.agent,
|
|
1231
|
-
created: v.created,
|
|
1232
|
-
updated: v.updated,
|
|
1233
|
-
audited: v.audited,
|
|
1234
|
-
tags: v.tags,
|
|
1235
|
-
body: v.body,
|
|
1236
|
-
phases: v.phases,
|
|
1237
|
-
log: v.log,
|
|
1238
|
-
clarifications: v.clarifications
|
|
1239
|
-
};
|
|
1240
|
-
if (await E(S, `${at(s)}
|
|
1241
|
-
`, "utf-8"), await yt(e), l.status === "done" || l.status === "dropped") {
|
|
1242
|
-
await Zt(e, f.id);
|
|
1243
|
-
try {
|
|
1244
|
-
a.ensureBranch(v);
|
|
1245
|
-
} catch {
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1249
|
-
} catch (r) {
|
|
1250
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1224
|
+
p(e, 202, { ok: !0 });
|
|
1251
1225
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1226
|
+
},
|
|
1227
|
+
// POST /api/agent/launch-draft — start a headless agent that drafts a new plan from an idea
|
|
1228
|
+
{
|
|
1229
|
+
method: "POST",
|
|
1230
|
+
path: "/api/agent/launch-draft",
|
|
1231
|
+
handle: async (s, e) => {
|
|
1232
|
+
const n = await Y(s), { ideaId: l, prompt: c } = JSON.parse(n);
|
|
1233
|
+
if (!l || !c) {
|
|
1234
|
+
p(e, 400, { error: "ideaId and prompt are required" });
|
|
1259
1235
|
return;
|
|
1260
1236
|
}
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1237
|
+
const d = await Yt(t, l);
|
|
1238
|
+
if (!d) {
|
|
1239
|
+
p(e, 404, { error: "idea not found" });
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
const u = await rt(t, a);
|
|
1243
|
+
if (u) {
|
|
1244
|
+
p(e, 409, { error: u });
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
const f = r.startForIdea(d, c);
|
|
1248
|
+
if (!f.ok) {
|
|
1249
|
+
p(e, 409, { error: f.error });
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
p(e, 202, { ok: !0 });
|
|
1278
1253
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
webp: "image/webp"
|
|
1289
|
-
};
|
|
1290
|
-
for (const [y, l] of Object.entries(d))
|
|
1291
|
-
try {
|
|
1292
|
-
const T = await U(N(r, `icon.${y}`));
|
|
1293
|
-
t.statusCode = 200, t.setHeader("Content-Type", l), t.setHeader("Cache-Control", "no-cache"), t.end(T);
|
|
1254
|
+
},
|
|
1255
|
+
// POST /api/agent/launch-extend — start a headless agent to extend an idea's body
|
|
1256
|
+
{
|
|
1257
|
+
method: "POST",
|
|
1258
|
+
path: "/api/agent/launch-extend",
|
|
1259
|
+
handle: async (s, e) => {
|
|
1260
|
+
const n = await Y(s), { ideaId: l, prompt: c } = JSON.parse(n);
|
|
1261
|
+
if (!l || !c) {
|
|
1262
|
+
p(e, 400, { error: "ideaId and prompt are required" });
|
|
1294
1263
|
return;
|
|
1295
|
-
} catch {
|
|
1296
1264
|
}
|
|
1297
|
-
|
|
1298
|
-
return;
|
|
1299
|
-
}
|
|
1300
|
-
if (u.method === "POST" && g === "/api/icon") {
|
|
1301
|
-
try {
|
|
1302
|
-
const r = await M(u), { dataUri: d } = JSON.parse(r);
|
|
1265
|
+
const d = await Yt(t, l);
|
|
1303
1266
|
if (!d) {
|
|
1304
|
-
|
|
1267
|
+
p(e, 404, { error: "idea not found" });
|
|
1305
1268
|
return;
|
|
1306
1269
|
}
|
|
1307
|
-
const
|
|
1308
|
-
if (
|
|
1309
|
-
|
|
1270
|
+
const u = await rt(t, a);
|
|
1271
|
+
if (u) {
|
|
1272
|
+
p(e, 409, { error: u });
|
|
1310
1273
|
return;
|
|
1311
1274
|
}
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1316
|
-
}
|
|
1317
|
-
return;
|
|
1318
|
-
}
|
|
1319
|
-
if (u.method === "GET" && g === "/api/git/status") {
|
|
1320
|
-
try {
|
|
1321
|
-
const r = await a.getStatus(), d = a.getCurrentBranch(), y = await a.getAheadCount();
|
|
1322
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ branch: d, entries: r, ahead: y }));
|
|
1323
|
-
} catch (r) {
|
|
1324
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1325
|
-
}
|
|
1326
|
-
return;
|
|
1327
|
-
}
|
|
1328
|
-
if (u.method === "POST" && g === "/api/git/push") {
|
|
1329
|
-
try {
|
|
1330
|
-
await a.push(), t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1331
|
-
} catch (r) {
|
|
1332
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1333
|
-
}
|
|
1334
|
-
return;
|
|
1335
|
-
}
|
|
1336
|
-
if (u.method === "POST" && g === "/api/git/commit") {
|
|
1337
|
-
try {
|
|
1338
|
-
const r = await M(u), { files: d, title: y, message: l } = JSON.parse(r);
|
|
1339
|
-
if (!(y != null && y.trim())) {
|
|
1340
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "title is required" }));
|
|
1275
|
+
const f = r.startForIdeaExtend(d, c);
|
|
1276
|
+
if (!f.ok) {
|
|
1277
|
+
p(e, 409, { error: f.error });
|
|
1341
1278
|
return;
|
|
1342
1279
|
}
|
|
1343
|
-
|
|
1344
|
-
} catch (r) {
|
|
1345
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1280
|
+
p(e, 202, { ok: !0 });
|
|
1346
1281
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1282
|
+
},
|
|
1283
|
+
// POST /api/agent/launch-audit-all — start a batch convergence audit across all review/done plans
|
|
1284
|
+
{
|
|
1285
|
+
method: "POST",
|
|
1286
|
+
path: "/api/agent/launch-audit-all",
|
|
1287
|
+
handle: async (s, e) => {
|
|
1288
|
+
const n = await rt(t, a);
|
|
1289
|
+
if (n) {
|
|
1290
|
+
p(e, 409, { error: n });
|
|
1354
1291
|
return;
|
|
1355
1292
|
}
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
m.runCommitSuggest
|
|
1363
|
-
);
|
|
1364
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(c));
|
|
1365
|
-
} catch (r) {
|
|
1366
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1367
|
-
}
|
|
1368
|
-
return;
|
|
1369
|
-
}
|
|
1370
|
-
if (u.method === "GET" && g === "/api/status") {
|
|
1371
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(p.getStatus()));
|
|
1372
|
-
return;
|
|
1373
|
-
}
|
|
1374
|
-
if (u.method === "POST" && g === "/api/status/check") {
|
|
1375
|
-
const d = new URL(u.url ?? "", `http://${u.headers.host ?? "localhost"}`).searchParams.get("name");
|
|
1376
|
-
if (d !== "lint" && d !== "format" && d !== "test") {
|
|
1377
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "name must be lint, format, or test" }));
|
|
1378
|
-
return;
|
|
1293
|
+
const l = r.startBatchAudit();
|
|
1294
|
+
if (!l.ok) {
|
|
1295
|
+
p(e, 409, { error: l.error });
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
p(e, 202, { ok: !0 });
|
|
1379
1299
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
return;
|
|
1390
|
-
}
|
|
1391
|
-
if (u.method === "GET" && g === "/api/agent/status") {
|
|
1392
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(m.getStatus()));
|
|
1393
|
-
return;
|
|
1394
|
-
}
|
|
1395
|
-
if (u.method === "POST" && g === "/api/agent/launch") {
|
|
1396
|
-
try {
|
|
1397
|
-
const r = await M(u), { planId: d, phaseIndex: y } = JSON.parse(r);
|
|
1398
|
-
if (!d || typeof y != "number") {
|
|
1399
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "planId and phaseIndex are required" }));
|
|
1300
|
+
},
|
|
1301
|
+
// POST /api/agent/launch-run-all — run every unchecked phase sequentially with per-phase commits
|
|
1302
|
+
{
|
|
1303
|
+
method: "POST",
|
|
1304
|
+
path: "/api/agent/launch-run-all",
|
|
1305
|
+
handle: async (s, e) => {
|
|
1306
|
+
const n = await Y(s), { planId: l } = JSON.parse(n);
|
|
1307
|
+
if (!l) {
|
|
1308
|
+
p(e, 400, { error: "planId is required" });
|
|
1400
1309
|
return;
|
|
1401
1310
|
}
|
|
1402
|
-
const
|
|
1311
|
+
const c = await At(t, l);
|
|
1403
1312
|
if (!c) {
|
|
1404
|
-
|
|
1405
|
-
if (!b) {
|
|
1406
|
-
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "plan not found" }));
|
|
1407
|
-
return;
|
|
1408
|
-
}
|
|
1409
|
-
const I = await et(e, a, b.id);
|
|
1410
|
-
if (I) {
|
|
1411
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: I }));
|
|
1412
|
-
return;
|
|
1413
|
-
}
|
|
1414
|
-
const v = await m.start(b, y);
|
|
1415
|
-
if (!v.ok) {
|
|
1416
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: v.error }));
|
|
1417
|
-
return;
|
|
1418
|
-
}
|
|
1419
|
-
t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1313
|
+
p(e, 404, { error: "plan not found" });
|
|
1420
1314
|
return;
|
|
1421
1315
|
}
|
|
1422
|
-
const
|
|
1423
|
-
if (
|
|
1424
|
-
|
|
1316
|
+
const d = await rt(t, a, c.id);
|
|
1317
|
+
if (d) {
|
|
1318
|
+
p(e, 409, { error: d });
|
|
1425
1319
|
return;
|
|
1426
1320
|
}
|
|
1427
|
-
const
|
|
1428
|
-
if (!
|
|
1429
|
-
|
|
1321
|
+
const u = r.startRunAllPhases(c, () => i.runChecksAndWait());
|
|
1322
|
+
if (!u.ok) {
|
|
1323
|
+
p(e, 409, { error: u.error });
|
|
1430
1324
|
return;
|
|
1431
1325
|
}
|
|
1432
|
-
|
|
1433
|
-
} catch (r) {
|
|
1434
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1326
|
+
p(e, 202, { ok: !0 });
|
|
1435
1327
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1328
|
+
},
|
|
1329
|
+
// POST /api/agent/stop — kill the running agent task
|
|
1330
|
+
{
|
|
1331
|
+
method: "POST",
|
|
1332
|
+
path: "/api/agent/stop",
|
|
1333
|
+
handle: (s, e) => {
|
|
1334
|
+
const n = r.stop();
|
|
1335
|
+
if (!n.ok) {
|
|
1336
|
+
p(e, 409, { error: n.error });
|
|
1443
1337
|
return;
|
|
1444
1338
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1339
|
+
p(e, 202, { ok: !0 });
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
];
|
|
1343
|
+
}
|
|
1344
|
+
const re = [
|
|
1345
|
+
"biome.json",
|
|
1346
|
+
"tsconfig.json",
|
|
1347
|
+
"tailwind.config.ts",
|
|
1348
|
+
"vite.config.ts",
|
|
1349
|
+
"vite.app.config.ts",
|
|
1350
|
+
"postcss.config.js",
|
|
1351
|
+
"package.json"
|
|
1352
|
+
];
|
|
1353
|
+
async function oe(t) {
|
|
1354
|
+
const a = [];
|
|
1355
|
+
for (const i of re)
|
|
1356
|
+
await j($(t, i)) && a.push(i);
|
|
1357
|
+
return { files: a };
|
|
1358
|
+
}
|
|
1359
|
+
function Je({ root: t }) {
|
|
1360
|
+
return [
|
|
1361
|
+
// POST /api/config — update editable fields in papercamp/config.json (port, projectName)
|
|
1362
|
+
{
|
|
1363
|
+
method: "POST",
|
|
1364
|
+
path: "/api/config",
|
|
1365
|
+
handle: async (a, i) => {
|
|
1366
|
+
const r = $(t, "papercamp", "config.json"), s = await j(r);
|
|
1367
|
+
if (!s) {
|
|
1368
|
+
p(i, 404, { error: "config not found" });
|
|
1449
1369
|
return;
|
|
1450
1370
|
}
|
|
1451
|
-
const
|
|
1452
|
-
if (
|
|
1453
|
-
|
|
1371
|
+
const e = await Y(a), n = JSON.parse(e), { port: l, projectName: c, defaultAgent: d } = n, u = n.defaultAgents;
|
|
1372
|
+
if (l !== void 0 && (!Number.isInteger(l) || l <= 0)) {
|
|
1373
|
+
p(i, 400, { error: "port must be a positive integer" });
|
|
1454
1374
|
return;
|
|
1455
1375
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: f.error }));
|
|
1376
|
+
if (c !== void 0 && c.trim().length === 0) {
|
|
1377
|
+
p(i, 400, { error: "projectName must not be empty" });
|
|
1459
1378
|
return;
|
|
1460
1379
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1464
|
-
}
|
|
1465
|
-
return;
|
|
1466
|
-
}
|
|
1467
|
-
if (u.method === "POST" && g === "/api/agent/launch-draft") {
|
|
1468
|
-
try {
|
|
1469
|
-
const r = await M(u), { ideaId: d, prompt: y } = JSON.parse(r);
|
|
1470
|
-
if (!d || !y) {
|
|
1471
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "ideaId and prompt are required" }));
|
|
1380
|
+
if (d !== void 0 && !Mt.includes(d)) {
|
|
1381
|
+
p(i, 400, { error: "defaultAgent must be a known agent id" });
|
|
1472
1382
|
return;
|
|
1473
1383
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1384
|
+
if (u !== void 0)
|
|
1385
|
+
for (const S of ["phase", "planDraft", "ideaExtend", "commitSuggest"]) {
|
|
1386
|
+
const m = u[S], w = typeof m == "string" ? m : m == null ? void 0 : m.agent;
|
|
1387
|
+
if (!w || !Mt.includes(w)) {
|
|
1388
|
+
p(i, 400, { error: `defaultAgents.${S} must be a known agent id` });
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
const f = JSON.parse(s), y = (u ? {
|
|
1393
|
+
phase: z(u.phase),
|
|
1394
|
+
planDraft: z(u.planDraft),
|
|
1395
|
+
ideaExtend: z(u.ideaExtend),
|
|
1396
|
+
commitSuggest: z(u.commitSuggest)
|
|
1397
|
+
} : void 0) ?? (d !== void 0 ? {
|
|
1398
|
+
phase: { agent: d },
|
|
1399
|
+
planDraft: { agent: d },
|
|
1400
|
+
ideaExtend: { agent: d },
|
|
1401
|
+
commitSuggest: { agent: d }
|
|
1402
|
+
} : void 0), g = f, { defaultAgent: E, ...F } = g, D = {
|
|
1403
|
+
...F,
|
|
1404
|
+
...l !== void 0 && { port: l },
|
|
1405
|
+
...c !== void 0 && { projectName: c.trim() },
|
|
1406
|
+
...y && { defaultAgents: y }
|
|
1407
|
+
};
|
|
1408
|
+
await q(r, `${JSON.stringify(D, null, 2)}
|
|
1409
|
+
`), p(i, 200, { ok: !0 });
|
|
1410
|
+
}
|
|
1411
|
+
},
|
|
1412
|
+
// GET /api/configs — list readable config files, or ?name=... for one file's content
|
|
1413
|
+
{
|
|
1414
|
+
method: "GET",
|
|
1415
|
+
path: "/api/configs",
|
|
1416
|
+
handle: async (a, i) => {
|
|
1417
|
+
const r = vt(a).searchParams.get("name");
|
|
1418
|
+
if (!r) {
|
|
1419
|
+
p(i, 200, await oe(t));
|
|
1478
1420
|
return;
|
|
1479
1421
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: o }));
|
|
1422
|
+
if (!re.includes(r)) {
|
|
1423
|
+
p(i, 400, { error: "invalid config file name" });
|
|
1483
1424
|
return;
|
|
1484
1425
|
}
|
|
1485
|
-
const
|
|
1486
|
-
if (!
|
|
1487
|
-
|
|
1426
|
+
const s = await j($(t, r));
|
|
1427
|
+
if (!s) {
|
|
1428
|
+
p(i, 404, { error: "config file not found" });
|
|
1488
1429
|
return;
|
|
1489
1430
|
}
|
|
1490
|
-
|
|
1491
|
-
} catch (r) {
|
|
1492
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1431
|
+
p(i, 200, { name: r, content: s });
|
|
1493
1432
|
}
|
|
1494
|
-
return;
|
|
1495
1433
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1434
|
+
];
|
|
1435
|
+
}
|
|
1436
|
+
function Ke({ root: t }) {
|
|
1437
|
+
return [
|
|
1438
|
+
// POST /api/open-questions/resolve?title=... — resolve an open question with a new decision
|
|
1439
|
+
{
|
|
1440
|
+
method: "POST",
|
|
1441
|
+
path: "/api/open-questions/resolve",
|
|
1442
|
+
handle: async (a, i) => {
|
|
1443
|
+
const r = vt(a).searchParams.get("title");
|
|
1444
|
+
if (!(r != null && r.trim())) {
|
|
1445
|
+
p(i, 400, { error: "title is required" });
|
|
1501
1446
|
return;
|
|
1502
1447
|
}
|
|
1503
|
-
const
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "idea not found" }));
|
|
1448
|
+
const s = await Y(a), { decision: e, rationale: n } = JSON.parse(s);
|
|
1449
|
+
if (!(e != null && e.trim())) {
|
|
1450
|
+
p(i, 400, { error: "decision is required" });
|
|
1507
1451
|
return;
|
|
1508
1452
|
}
|
|
1509
|
-
const
|
|
1510
|
-
if (
|
|
1511
|
-
|
|
1453
|
+
const l = P(t, "open-questions.md"), c = await j(l);
|
|
1454
|
+
if (!c) {
|
|
1455
|
+
p(i, 404, { error: "open-questions.md not found" });
|
|
1512
1456
|
return;
|
|
1513
1457
|
}
|
|
1514
|
-
const
|
|
1515
|
-
if (
|
|
1516
|
-
|
|
1458
|
+
const d = jt(c);
|
|
1459
|
+
if (d.warnings.length > 0) {
|
|
1460
|
+
p(i, 409, {
|
|
1461
|
+
error: "open-questions.md has parse warnings — resolve them before updating to avoid data loss",
|
|
1462
|
+
warnings: d.warnings.map((g) => `${g.title}: ${g.message}`)
|
|
1463
|
+
});
|
|
1517
1464
|
return;
|
|
1518
1465
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1466
|
+
const u = r.trim(), f = d.entries.find((g) => g.title === u);
|
|
1467
|
+
if (!f) {
|
|
1468
|
+
p(i, 404, { error: `open question "${u}" not found` });
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
if (f.status !== "open") {
|
|
1472
|
+
p(i, 409, { error: `open question "${u}" is already ${f.status}` });
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
const h = he({
|
|
1476
|
+
title: e.trim(),
|
|
1477
|
+
date: ot(),
|
|
1478
|
+
status: "decided",
|
|
1479
|
+
body: n == null ? void 0 : n.trim()
|
|
1480
|
+
});
|
|
1481
|
+
await me(P(t, "decisions.md"), h), f.status = "resolved", f.resolvedBy = e.trim();
|
|
1482
|
+
const y = ge(d.entries);
|
|
1483
|
+
await q(l, `${y}
|
|
1484
|
+
`, "utf-8"), p(i, 200, { ok: !0 });
|
|
1522
1485
|
}
|
|
1523
|
-
return;
|
|
1524
1486
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1487
|
+
];
|
|
1488
|
+
}
|
|
1489
|
+
function Ge({ root: t }) {
|
|
1490
|
+
return [
|
|
1491
|
+
// GET /api/env — read the project root's .env, masked client-side only
|
|
1492
|
+
{
|
|
1493
|
+
method: "GET",
|
|
1494
|
+
path: "/api/env",
|
|
1495
|
+
handle: async (a, i) => {
|
|
1496
|
+
const r = $(t, ".env"), s = $(t, ".env.example"), [e, n] = await Promise.all([
|
|
1497
|
+
wt(r),
|
|
1498
|
+
wt(s)
|
|
1499
|
+
]), l = e ? Jt(await j(r)) : [], c = n ? Jt(await j(s)).map((f) => f.key) : [], d = new Set(l.map((f) => f.key)), u = c.filter((f) => !d.has(f));
|
|
1500
|
+
p(i, 200, { exists: e, exampleExists: n, entries: l, missingKeys: u });
|
|
1530
1501
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1502
|
+
},
|
|
1503
|
+
// POST /api/env — write the full desired entry set back to .env
|
|
1504
|
+
{
|
|
1505
|
+
method: "POST",
|
|
1506
|
+
path: "/api/env",
|
|
1507
|
+
handle: async (a, i) => {
|
|
1508
|
+
const r = await Y(a), { entries: s } = JSON.parse(r);
|
|
1509
|
+
if (!Array.isArray(s)) {
|
|
1510
|
+
p(i, 400, { error: "entries is required" });
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
const e = /* @__PURE__ */ new Set();
|
|
1514
|
+
for (const c of s) {
|
|
1515
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(c.key)) {
|
|
1516
|
+
p(i, 400, { error: `invalid key: ${c.key}` });
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
if (e.has(c.key)) {
|
|
1520
|
+
p(i, 400, { error: `duplicate key: ${c.key}` });
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
e.add(c.key);
|
|
1524
|
+
}
|
|
1525
|
+
const n = $(t, ".env"), l = await j(n);
|
|
1526
|
+
await q(n, we(l, s)), p(i, 200, { ok: !0 });
|
|
1535
1527
|
}
|
|
1536
|
-
t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1537
|
-
return;
|
|
1538
1528
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1529
|
+
];
|
|
1530
|
+
}
|
|
1531
|
+
const He = (t, a) => {
|
|
1532
|
+
if (t)
|
|
1533
|
+
return t.find((i) => i.status === "in-progress" || i.status === "review");
|
|
1534
|
+
};
|
|
1535
|
+
function We(t, a) {
|
|
1536
|
+
return `You are writing a single git commit message for the diff below. Do not use any tools, do not read or edit any files — base your answer only on the diff text given.
|
|
1537
|
+
|
|
1538
|
+
Follow this repo's commit convention: \`type(scope): Description\`, where type is one of feat|fix|chore|docs|refactor and scope is a subsystem area from this fixed list: core, cli, app, server, agent, plans, ideas, docs, settings, stack, ui, ci, config, deps, repo. Pick the area the diff most affects. Keep the whole title under 100 characters and do not end it with a period.${a ? `
|
|
1539
|
+
This work belongs to plan ${a}. Do NOT put the plan id in the scope — instead end the message body with a \`Refs: ${a}\` line as its final line.` : ""}
|
|
1540
|
+
|
|
1541
|
+
Respond with ONLY a single JSON object, no prose, no code fences, no markdown — exactly this shape:
|
|
1542
|
+
{"title": "type(scope): Description", "message": "${a ? `longer body describing what changed and why, ending with a \`Refs: ${a}\` line` : "optional longer body describing what changed and why, or an empty string if the title alone is clear enough"}"}
|
|
1543
|
+
|
|
1544
|
+
Diff:
|
|
1545
|
+
${t}`;
|
|
1546
|
+
}
|
|
1547
|
+
async function Ue(t, a, i) {
|
|
1548
|
+
if (!t.trim())
|
|
1549
|
+
throw new Error("No changes to summarize — select at least one file first");
|
|
1550
|
+
const r = We(t, a), s = await i(r);
|
|
1551
|
+
let e = s;
|
|
1552
|
+
try {
|
|
1553
|
+
const d = JSON.parse(s);
|
|
1554
|
+
typeof d.result == "string" && (e = d.result);
|
|
1555
|
+
} catch {
|
|
1556
|
+
}
|
|
1557
|
+
const n = e.match(/\{[\s\S]*\}/);
|
|
1558
|
+
if (!n) throw new Error("Agent did not return a parseable commit message");
|
|
1559
|
+
const l = JSON.parse(n[0]);
|
|
1560
|
+
if (!l.title) throw new Error("Agent response missing a title");
|
|
1561
|
+
let c = l.message ?? "";
|
|
1562
|
+
return a && !/Refs:\s*\S/.test(c) && (c = c ? `${c}
|
|
1563
|
+
|
|
1564
|
+
Refs: ${a}` : `Refs: ${a}`), { title: l.title, message: c };
|
|
1565
|
+
}
|
|
1566
|
+
const Ye = "Sync the current branch to main by:\n1. Stashing or committing any uncommitted changes (do not use `git reset --hard` or `git clean -fd` without an explicit confirmation step)\n2. Relocating any mis-filed content (e.g., any new plans written to the legacy `papercamp/plans.md` instead of per-file `papercamp/plans/*.md`)\n3. Checking out main: `git checkout main`\n4. Fetching from origin: `git fetch --prune`\n5. Fast-forwarding the merge: `git merge --ff-only origin/main`\n6. Confirming success\n\nReport the final branch and status to verify the sync completed.";
|
|
1567
|
+
function ze({ root: t, git: a, agent: i }) {
|
|
1568
|
+
return [
|
|
1569
|
+
// GET /api/git/status — return working tree status
|
|
1570
|
+
{
|
|
1571
|
+
method: "GET",
|
|
1572
|
+
path: "/api/git/status",
|
|
1573
|
+
handle: async (r, s) => {
|
|
1574
|
+
const e = await a.getStatus(), n = a.getCurrentBranch(), l = await a.getAheadCount(), c = await a.getBranchHygieneStatus();
|
|
1575
|
+
p(s, 200, { branch: n, entries: e, ahead: l, branchHygiene: c });
|
|
1544
1576
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1577
|
+
},
|
|
1578
|
+
// POST /api/git/push — push the current branch to its upstream
|
|
1579
|
+
{
|
|
1580
|
+
method: "POST",
|
|
1581
|
+
path: "/api/git/push",
|
|
1582
|
+
handle: async (r, s) => {
|
|
1583
|
+
try {
|
|
1584
|
+
await a.push(), p(s, 200, { ok: !0 });
|
|
1585
|
+
} catch (e) {
|
|
1586
|
+
p(s, 400, { error: e.message });
|
|
1554
1587
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1588
|
+
}
|
|
1589
|
+
},
|
|
1590
|
+
// POST /api/git/commit — stage files and create a commit
|
|
1591
|
+
{
|
|
1592
|
+
method: "POST",
|
|
1593
|
+
path: "/api/git/commit",
|
|
1594
|
+
handle: async (r, s) => {
|
|
1595
|
+
try {
|
|
1596
|
+
const e = await Y(r), { files: n, title: l, message: c } = JSON.parse(e);
|
|
1597
|
+
if (!(l != null && l.trim())) {
|
|
1598
|
+
p(s, 400, { error: "title is required" });
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
await a.commit(n ?? [], l.trim(), c == null ? void 0 : c.trim()), p(s, 200, { ok: !0 });
|
|
1602
|
+
} catch (e) {
|
|
1603
|
+
p(s, 400, { error: e.message });
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
},
|
|
1607
|
+
// POST /api/git/sync — sync to main (clean: inline, dirty: agent task)
|
|
1608
|
+
{
|
|
1609
|
+
method: "POST",
|
|
1610
|
+
path: "/api/git/sync",
|
|
1611
|
+
handle: async (r, s) => {
|
|
1612
|
+
const e = await Y(r), { mode: n } = JSON.parse(e);
|
|
1613
|
+
if (n !== "clean" && n !== "dirty") {
|
|
1614
|
+
p(s, 400, { error: "mode must be 'clean' or 'dirty'" });
|
|
1558
1615
|
return;
|
|
1559
1616
|
}
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1617
|
+
if (n === "clean") {
|
|
1618
|
+
if ((await a.getStatus()).length > 0) {
|
|
1619
|
+
p(s, 409, { error: "Working tree is no longer clean — refresh and retry" });
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
await a.runGitSync(), p(s, 200, { ok: !0 });
|
|
1562
1623
|
return;
|
|
1563
1624
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1625
|
+
const l = i.startSync(Ye);
|
|
1626
|
+
if (!l.ok) {
|
|
1627
|
+
p(s, 409, { error: l.error });
|
|
1566
1628
|
return;
|
|
1567
1629
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1630
|
+
p(s, 202, { ok: !0 });
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
// POST /api/git/suggest-commit-message — agent-written title/body from the actual diff
|
|
1634
|
+
{
|
|
1635
|
+
method: "POST",
|
|
1636
|
+
path: "/api/git/suggest-commit-message",
|
|
1637
|
+
handle: async (r, s) => {
|
|
1638
|
+
try {
|
|
1639
|
+
const e = await Y(r), { files: n } = JSON.parse(e);
|
|
1640
|
+
if (!(n != null && n.length)) {
|
|
1641
|
+
p(s, 400, { error: "files is required" });
|
|
1642
|
+
return;
|
|
1575
1643
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
...T !== void 0 && { port: T },
|
|
1589
|
-
...c !== void 0 && { projectName: c.trim() },
|
|
1590
|
-
...I && { defaultAgents: I }
|
|
1591
|
-
};
|
|
1592
|
-
await E(r, JSON.stringify(C, null, 2)), t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1593
|
-
} catch (r) {
|
|
1594
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1644
|
+
const l = await a.diff(n), { entries: c } = await gt(
|
|
1645
|
+
P(t, "plans"),
|
|
1646
|
+
P(t, "plans.md")
|
|
1647
|
+
), d = He(c), u = await Ue(
|
|
1648
|
+
l,
|
|
1649
|
+
d == null ? void 0 : d.id,
|
|
1650
|
+
i.runCommitSuggest
|
|
1651
|
+
);
|
|
1652
|
+
p(s, 200, u);
|
|
1653
|
+
} catch (e) {
|
|
1654
|
+
p(s, 400, { error: e.message });
|
|
1655
|
+
}
|
|
1595
1656
|
}
|
|
1596
|
-
return;
|
|
1597
1657
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1658
|
+
];
|
|
1659
|
+
}
|
|
1660
|
+
const zt = {
|
|
1661
|
+
svg: "image/svg+xml",
|
|
1662
|
+
png: "image/png",
|
|
1663
|
+
jpg: "image/jpeg",
|
|
1664
|
+
jpeg: "image/jpeg",
|
|
1665
|
+
gif: "image/gif",
|
|
1666
|
+
webp: "image/webp"
|
|
1667
|
+
};
|
|
1668
|
+
function Qe({ root: t }) {
|
|
1669
|
+
return [
|
|
1670
|
+
// GET /api/icon — serve project icon
|
|
1671
|
+
{
|
|
1672
|
+
method: "GET",
|
|
1673
|
+
path: "/api/icon",
|
|
1674
|
+
handle: async (a, i) => {
|
|
1675
|
+
const r = $(t, "papercamp", "assets");
|
|
1676
|
+
for (const [s, e] of Object.entries(zt))
|
|
1677
|
+
try {
|
|
1678
|
+
const n = await at($(r, `icon.${s}`));
|
|
1679
|
+
i.statusCode = 200, i.setHeader("Content-Type", e), i.setHeader("Cache-Control", "no-cache"), i.end(n);
|
|
1680
|
+
return;
|
|
1681
|
+
} catch {
|
|
1682
|
+
}
|
|
1683
|
+
p(i, 404, { error: "no icon uploaded" });
|
|
1684
|
+
}
|
|
1685
|
+
},
|
|
1686
|
+
// POST /api/icon — upload project icon
|
|
1687
|
+
{
|
|
1688
|
+
method: "POST",
|
|
1689
|
+
path: "/api/icon",
|
|
1690
|
+
handle: async (a, i) => {
|
|
1691
|
+
const r = await Y(a), { dataUri: s } = JSON.parse(r);
|
|
1692
|
+
if (!s) {
|
|
1693
|
+
p(i, 400, { error: "dataUri is required" });
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
const e = s.match(/^data:(image\/[a-z0-9+.-]+);base64,(.+)$/);
|
|
1697
|
+
if (!e) {
|
|
1698
|
+
p(i, 400, { error: "invalid data URI" });
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
const n = e[1], l = n === "image/svg+xml" ? "svg" : n.split("/")[1], c = Buffer.from(e[2], "base64"), d = $(t, "papercamp", "assets");
|
|
1702
|
+
await ut(d, { recursive: !0 }), await Promise.all(
|
|
1703
|
+
Object.keys(zt).filter((u) => u !== l).map((u) => Xt($(d, `icon.${u}`)).catch(() => {
|
|
1704
|
+
}))
|
|
1705
|
+
), await q($(d, `icon.${l}`), c), p(i, 200, { ok: !0 });
|
|
1607
1706
|
}
|
|
1608
|
-
return;
|
|
1609
1707
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1708
|
+
];
|
|
1709
|
+
}
|
|
1710
|
+
function Xe({ root: t }) {
|
|
1711
|
+
return [
|
|
1712
|
+
// POST /api/ideas — create a new per-file idea entry
|
|
1713
|
+
{
|
|
1714
|
+
method: "POST",
|
|
1715
|
+
path: "/api/ideas",
|
|
1716
|
+
handle: async (a, i) => {
|
|
1717
|
+
const r = await Y(a), { title: s, content: e } = JSON.parse(r);
|
|
1718
|
+
if (!(s != null && s.trim())) {
|
|
1719
|
+
p(i, 400, { error: "title is required" });
|
|
1615
1720
|
return;
|
|
1616
1721
|
}
|
|
1617
|
-
const
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
return;
|
|
1622
|
-
}
|
|
1623
|
-
if (y.has(c.key)) {
|
|
1624
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: `duplicate key: ${c.key}` }));
|
|
1625
|
-
return;
|
|
1722
|
+
const n = P(t, "ideas"), d = `IDEA-${(await Ct(n, P(t, "ideas.md"))).entries.reduce((f, h) => {
|
|
1723
|
+
if (h.id) {
|
|
1724
|
+
const y = Number.parseInt(h.id.replace("IDEA-", ""), 10);
|
|
1725
|
+
return Number.isNaN(y) ? f : Math.max(f, y);
|
|
1626
1726
|
}
|
|
1627
|
-
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1727
|
+
return f;
|
|
1728
|
+
}, 0) + 1}`;
|
|
1729
|
+
await ut(n, { recursive: !0 });
|
|
1730
|
+
const u = ne({
|
|
1731
|
+
id: d,
|
|
1732
|
+
title: s.trim(),
|
|
1733
|
+
body: e == null ? void 0 : e.trim()
|
|
1734
|
+
});
|
|
1735
|
+
await q($(n, `${d}.md`), `${u}
|
|
1736
|
+
`, "utf-8"), await Dt(t), p(i, 201, { ok: !0, id: d });
|
|
1633
1737
|
}
|
|
1634
|
-
return;
|
|
1635
1738
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1739
|
+
];
|
|
1740
|
+
}
|
|
1741
|
+
function Ze({ root: t, git: a }) {
|
|
1742
|
+
return [
|
|
1743
|
+
// DELETE /api/plans?title=... — remove a plan entry from per-file storage
|
|
1744
|
+
{
|
|
1745
|
+
method: "DELETE",
|
|
1746
|
+
path: "/api/plans",
|
|
1747
|
+
handle: async (i, r) => {
|
|
1748
|
+
const s = vt(i).searchParams.get("title");
|
|
1749
|
+
if (!(s != null && s.trim())) {
|
|
1750
|
+
p(r, 400, { error: "title is required" });
|
|
1641
1751
|
return;
|
|
1642
1752
|
}
|
|
1643
|
-
const
|
|
1644
|
-
if (!(
|
|
1645
|
-
|
|
1753
|
+
const e = P(t, "plans"), n = s.trim(), { entries: l } = await lt(e), c = l.find((u) => u.title === n || u.id === n);
|
|
1754
|
+
if (!(c != null && c.id)) {
|
|
1755
|
+
p(r, 404, { error: "plan not found in per-file storage" });
|
|
1646
1756
|
return;
|
|
1647
1757
|
}
|
|
1648
|
-
const
|
|
1649
|
-
if (!
|
|
1650
|
-
|
|
1758
|
+
const d = $(e, `${c.id}.md`);
|
|
1759
|
+
if (!await wt(d)) {
|
|
1760
|
+
p(r, 404, { error: "plan file not found" });
|
|
1651
1761
|
return;
|
|
1652
1762
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1763
|
+
await Xt(d), await Dt(t), p(r, 200, { ok: !0 });
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
// POST /api/plans — create a new per-file plan entry
|
|
1767
|
+
{
|
|
1768
|
+
method: "POST",
|
|
1769
|
+
path: "/api/plans",
|
|
1770
|
+
handle: async (i, r) => {
|
|
1771
|
+
const s = await Y(i), { title: e, content: n, kind: l } = JSON.parse(s);
|
|
1772
|
+
if (!(e != null && e.trim())) {
|
|
1773
|
+
p(r, 400, { error: "title is required" });
|
|
1661
1774
|
return;
|
|
1662
1775
|
}
|
|
1663
|
-
const
|
|
1664
|
-
if (
|
|
1665
|
-
|
|
1776
|
+
const c = await rt(t, a);
|
|
1777
|
+
if (c) {
|
|
1778
|
+
p(r, 409, { error: c });
|
|
1666
1779
|
return;
|
|
1667
1780
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
);
|
|
1781
|
+
const d = l && Et.includes(l) ? l : "feat", u = $(t, "papercamp", "config.json"), f = await ae(u, d);
|
|
1782
|
+
if (!f) {
|
|
1783
|
+
p(r, 500, { error: "could not assign plan ID" });
|
|
1672
1784
|
return;
|
|
1673
1785
|
}
|
|
1674
|
-
const
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1786
|
+
const h = P(t, "plans");
|
|
1787
|
+
await ut(h, { recursive: !0 });
|
|
1788
|
+
const y = xt({
|
|
1789
|
+
id: f,
|
|
1790
|
+
title: e.trim(),
|
|
1791
|
+
kind: d,
|
|
1792
|
+
status: "idea",
|
|
1793
|
+
created: ot(),
|
|
1794
|
+
body: n == null ? void 0 : n.trim()
|
|
1679
1795
|
});
|
|
1680
|
-
await
|
|
1681
|
-
|
|
1682
|
-
await E(c, `${v}
|
|
1683
|
-
`, "utf-8"), t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1684
|
-
} catch (r) {
|
|
1685
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1796
|
+
await q($(h, `${f}.md`), `${y}
|
|
1797
|
+
`, "utf-8"), await Dt(t), p(r, 201, { ok: !0, id: f });
|
|
1686
1798
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1799
|
+
},
|
|
1800
|
+
// PATCH /api/plans?title=... — update an existing plan entry
|
|
1801
|
+
{
|
|
1802
|
+
method: "PATCH",
|
|
1803
|
+
path: "/api/plans",
|
|
1804
|
+
handle: async (i, r) => {
|
|
1805
|
+
const s = vt(i).searchParams.get("title");
|
|
1806
|
+
if (!(s != null && s.trim())) {
|
|
1807
|
+
p(r, 400, { error: "title is required" });
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
const e = await Y(i), n = JSON.parse(e);
|
|
1811
|
+
if (n.agent && !Mt.includes(n.agent)) {
|
|
1812
|
+
p(r, 400, { error: "agent must be a known agent id" });
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
const l = P(t, "plans"), { entries: c } = await lt(l), d = s.trim(), u = c.find((E) => E.title === d || E.id === d);
|
|
1816
|
+
if (!(u != null && u.id)) {
|
|
1817
|
+
p(r, 404, { error: "plan not found" });
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
const f = $(l, `${u.id}.md`), h = await j(f);
|
|
1821
|
+
if (!h) {
|
|
1822
|
+
p(r, 404, { error: "plan file not found" });
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
const y = mt(h);
|
|
1826
|
+
if (y.entries.length === 0) {
|
|
1827
|
+
p(r, 500, { error: "failed to parse plan file" });
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
const g = {
|
|
1831
|
+
...y.entries[0],
|
|
1832
|
+
...n.body !== void 0 && { body: n.body },
|
|
1833
|
+
...n.status !== void 0 && { status: n.status },
|
|
1834
|
+
...n.phases !== void 0 && { phases: n.phases },
|
|
1835
|
+
...n.log !== void 0 && { log: n.log },
|
|
1836
|
+
...n.agent !== void 0 && { agent: n.agent ?? void 0 },
|
|
1837
|
+
updated: ot()
|
|
1838
|
+
};
|
|
1839
|
+
if (n.status === "done" || n.status === "dropped") {
|
|
1840
|
+
const E = await rt(t, a, u.id);
|
|
1841
|
+
if (E) {
|
|
1842
|
+
p(r, 409, { error: E });
|
|
1695
1843
|
return;
|
|
1696
1844
|
}
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1845
|
+
}
|
|
1846
|
+
if (n.status === "in-progress") {
|
|
1847
|
+
const E = await lt(l);
|
|
1848
|
+
for (const F of E.entries)
|
|
1849
|
+
if (F.id !== u.id && F.status === "in-progress") {
|
|
1850
|
+
const D = $(l, `${F.id}.md`), S = await j(D);
|
|
1851
|
+
if (S) {
|
|
1852
|
+
const m = mt(S);
|
|
1853
|
+
m.entries.length > 0 && await Rt(
|
|
1854
|
+
D,
|
|
1855
|
+
Nt(m.entries[0], {
|
|
1856
|
+
id: m.entries[0].id ?? F.id,
|
|
1857
|
+
status: "planned",
|
|
1858
|
+
updated: ot()
|
|
1859
|
+
})
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
if (await Rt(
|
|
1865
|
+
f,
|
|
1866
|
+
Nt(g, { id: g.id ?? u.id })
|
|
1867
|
+
), await Dt(t), n.status === "done" || n.status === "dropped") {
|
|
1868
|
+
await ye(t, u.id);
|
|
1869
|
+
try {
|
|
1870
|
+
a.ensureBranch(g);
|
|
1871
|
+
} catch {
|
|
1701
1872
|
}
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1873
|
+
}
|
|
1874
|
+
p(r, 200, { ok: !0 });
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
];
|
|
1878
|
+
}
|
|
1879
|
+
function Ve({ activity: t, agent: a, git: i, status: r }) {
|
|
1880
|
+
return [
|
|
1881
|
+
// GET /api/status — return current lint/format/test check results
|
|
1882
|
+
{
|
|
1883
|
+
method: "GET",
|
|
1884
|
+
path: "/api/status",
|
|
1885
|
+
handle: (s, e) => {
|
|
1886
|
+
p(e, 200, r.getStatus());
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
// POST /api/status/check?name=lint|format|test — trigger a one-off check run
|
|
1890
|
+
{
|
|
1891
|
+
method: "POST",
|
|
1892
|
+
path: "/api/status/check",
|
|
1893
|
+
handle: (s, e) => {
|
|
1894
|
+
const n = vt(s).searchParams.get("name");
|
|
1895
|
+
if (n !== "lint" && n !== "format" && n !== "test") {
|
|
1896
|
+
p(e, 400, { error: "name must be lint, format, or test" });
|
|
1706
1897
|
return;
|
|
1707
1898
|
}
|
|
1899
|
+
r.runCheck(n), p(e, 202, { ok: !0 });
|
|
1900
|
+
}
|
|
1901
|
+
},
|
|
1902
|
+
// POST /api/status/fix — run `biome check . --write` to auto-fix lint/format issues
|
|
1903
|
+
{
|
|
1904
|
+
method: "POST",
|
|
1905
|
+
path: "/api/status/fix",
|
|
1906
|
+
handle: (s, e) => {
|
|
1907
|
+
r.runQualityFix(), p(e, 202, { ok: !0 });
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1910
|
+
// GET /api/activity/stream — SSE endpoint for live activity events
|
|
1911
|
+
{
|
|
1912
|
+
method: "GET",
|
|
1913
|
+
path: "/api/activity/stream",
|
|
1914
|
+
handle: (s, e) => {
|
|
1915
|
+
e.statusCode = 200, e.setHeader("Content-Type", "text/event-stream"), e.setHeader("Cache-Control", "no-cache"), e.setHeader("Connection", "keep-alive"), e.flushHeaders(), t.subscribe(e), i.subscribe(e), r.subscribe(e), a.subscribe(e);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
];
|
|
1919
|
+
}
|
|
1920
|
+
const tn = [
|
|
1921
|
+
{
|
|
1922
|
+
path: "/api/package-name",
|
|
1923
|
+
handler: async (t) => {
|
|
1924
|
+
const a = await j($(t, "package.json"));
|
|
1925
|
+
if (!a) return null;
|
|
1926
|
+
try {
|
|
1927
|
+
return JSON.parse(a).name ?? null;
|
|
1928
|
+
} catch {
|
|
1929
|
+
return null;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
},
|
|
1933
|
+
{
|
|
1934
|
+
path: "/api/plans",
|
|
1935
|
+
handler: async (t) => gt(P(t, "plans"), P(t, "plans.md"))
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
path: "/api/progress",
|
|
1939
|
+
handler: async (t) => ({
|
|
1940
|
+
entries: $e(await j(P(t, "progress.md")))
|
|
1941
|
+
})
|
|
1942
|
+
},
|
|
1943
|
+
{
|
|
1944
|
+
path: "/api/decisions",
|
|
1945
|
+
handler: async (t) => Kt(await j(P(t, "decisions.md")))
|
|
1946
|
+
},
|
|
1947
|
+
{
|
|
1948
|
+
path: "/api/open-questions",
|
|
1949
|
+
handler: async (t) => jt(await j(P(t, "open-questions.md")))
|
|
1950
|
+
},
|
|
1951
|
+
{
|
|
1952
|
+
path: "/api/ideas",
|
|
1953
|
+
handler: async (t) => Ct(P(t, "ideas"), P(t, "ideas.md"))
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
path: "/api/consistency",
|
|
1957
|
+
handler: async (t) => {
|
|
1958
|
+
const [a, i, r] = await Promise.all([
|
|
1959
|
+
j(P(t, "decisions.md")),
|
|
1960
|
+
j(P(t, "open-questions.md")),
|
|
1961
|
+
gt(P(t, "plans"), P(t, "plans.md"))
|
|
1962
|
+
]), s = Kt(a), e = jt(i);
|
|
1963
|
+
return ke(s.entries, e.entries, r.entries);
|
|
1964
|
+
}
|
|
1965
|
+
},
|
|
1966
|
+
{
|
|
1967
|
+
path: "/api/config",
|
|
1968
|
+
handler: async (t) => {
|
|
1969
|
+
const a = await j($(t, "papercamp", "config.json"));
|
|
1970
|
+
if (!a) return null;
|
|
1971
|
+
const i = JSON.parse(a);
|
|
1972
|
+
return i != null && i.defaultAgents && (i.defaultAgents = {
|
|
1973
|
+
phase: z(i.defaultAgents.phase),
|
|
1974
|
+
planDraft: z(i.defaultAgents.planDraft),
|
|
1975
|
+
ideaExtend: z(i.defaultAgents.ideaExtend),
|
|
1976
|
+
commitSuggest: z(i.defaultAgents.commitSuggest)
|
|
1977
|
+
}), i;
|
|
1978
|
+
}
|
|
1979
|
+
},
|
|
1980
|
+
{
|
|
1981
|
+
path: "/api/docs",
|
|
1982
|
+
handler: async (t) => {
|
|
1983
|
+
const a = ["MAIN.md", "README.md", "CHANGELOG.md", "LICENSE"], i = [];
|
|
1984
|
+
for (const r of a) {
|
|
1985
|
+
const s = await j($(t, r));
|
|
1986
|
+
s && i.push({ name: r, content: s });
|
|
1987
|
+
}
|
|
1988
|
+
return { files: i };
|
|
1989
|
+
}
|
|
1990
|
+
},
|
|
1991
|
+
{
|
|
1992
|
+
path: "/api/configs",
|
|
1993
|
+
handler: async (t) => oe(t)
|
|
1994
|
+
}
|
|
1995
|
+
];
|
|
1996
|
+
function en(t) {
|
|
1997
|
+
return [
|
|
1998
|
+
...Ze(t),
|
|
1999
|
+
...Xe(t),
|
|
2000
|
+
...Qe(t),
|
|
2001
|
+
...ze(t),
|
|
2002
|
+
...Ve(t),
|
|
2003
|
+
...qe(t),
|
|
2004
|
+
...Je(t),
|
|
2005
|
+
...Ge(t),
|
|
2006
|
+
...Ke(t)
|
|
2007
|
+
];
|
|
2008
|
+
}
|
|
2009
|
+
const Qt = {
|
|
2010
|
+
lint: "npx biome lint .",
|
|
2011
|
+
format: "npx biome format .",
|
|
2012
|
+
test: "npx vitest run"
|
|
2013
|
+
};
|
|
2014
|
+
function nn(t) {
|
|
2015
|
+
const a = /* @__PURE__ */ new Set(), i = {
|
|
2016
|
+
lint: { status: "stale", lastRun: null, output: "" },
|
|
2017
|
+
format: { status: "stale", lastRun: null, output: "" },
|
|
2018
|
+
test: { status: "stale", lastRun: null, output: "" }
|
|
2019
|
+
}, r = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set();
|
|
2020
|
+
function e(h) {
|
|
2021
|
+
const y = `data: ${JSON.stringify(h)}
|
|
2022
|
+
|
|
2023
|
+
`;
|
|
2024
|
+
for (const g of a)
|
|
2025
|
+
try {
|
|
2026
|
+
g.write(y);
|
|
2027
|
+
} catch {
|
|
2028
|
+
a.delete(g);
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
function n(h, y, g) {
|
|
2032
|
+
i[h] = { status: y, lastRun: (/* @__PURE__ */ new Date()).toISOString(), output: g }, e({
|
|
2033
|
+
message: `${h}: ${y}`,
|
|
2034
|
+
timestamp: i[h].lastRun
|
|
2035
|
+
}), y !== "running" && s.has(h) && (s.delete(h), l(h));
|
|
2036
|
+
}
|
|
2037
|
+
function l(h) {
|
|
2038
|
+
var D, S;
|
|
2039
|
+
if (r.has(h)) {
|
|
2040
|
+
s.add(h);
|
|
2041
|
+
return;
|
|
1708
2042
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
2043
|
+
r.add(h), n(h, "running", "");
|
|
2044
|
+
const y = Qt[h], g = it(y, {
|
|
2045
|
+
cwd: t,
|
|
2046
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2047
|
+
shell: !0
|
|
2048
|
+
});
|
|
2049
|
+
let E = "", F = "";
|
|
2050
|
+
(D = g.stdout) == null || D.on("data", (m) => {
|
|
2051
|
+
E += m.toString();
|
|
2052
|
+
}), (S = g.stderr) == null || S.on("data", (m) => {
|
|
2053
|
+
F += m.toString();
|
|
2054
|
+
}), g.on("close", (m) => {
|
|
2055
|
+
r.delete(h);
|
|
2056
|
+
const w = E + F;
|
|
2057
|
+
m === 0 ? n(h, "pass", w) : n(h, "fail", w);
|
|
2058
|
+
}), g.on("error", (m) => {
|
|
2059
|
+
r.delete(h), n(h, "fail", `Failed to spawn process: ${m.message}`);
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
function c() {
|
|
2063
|
+
if (r.has("lint") || r.has("format")) return;
|
|
2064
|
+
n("lint", "running", "Applying automatic fixes…"), n("format", "running", "Applying automatic fixes…");
|
|
2065
|
+
const h = it("npx biome check . --write", {
|
|
2066
|
+
cwd: t,
|
|
2067
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2068
|
+
shell: !0
|
|
2069
|
+
});
|
|
2070
|
+
h.on("close", () => {
|
|
2071
|
+
l("lint"), l("format");
|
|
2072
|
+
}), h.on("error", (y) => {
|
|
2073
|
+
const g = `Failed to spawn fix process: ${y.message}`;
|
|
2074
|
+
n("lint", "fail", g), n("format", "fail", g);
|
|
2075
|
+
});
|
|
2076
|
+
}
|
|
2077
|
+
const d = $(t, "src");
|
|
2078
|
+
let u = null;
|
|
2079
|
+
try {
|
|
2080
|
+
Ot(d, { recursive: !0 }, () => {
|
|
2081
|
+
u && clearTimeout(u), u = setTimeout(() => {
|
|
2082
|
+
l("lint"), l("format");
|
|
2083
|
+
}, 1e3);
|
|
2084
|
+
});
|
|
2085
|
+
} catch {
|
|
2086
|
+
}
|
|
2087
|
+
function f() {
|
|
2088
|
+
return new Promise((h) => {
|
|
2089
|
+
const y = ["lint", "format", "test"], g = { lint: null, format: null, test: null };
|
|
2090
|
+
let E = y.length;
|
|
2091
|
+
function F(D, S) {
|
|
2092
|
+
g[D] = S, E--, E === 0 && h(y.every((m) => g[m] === !0));
|
|
2093
|
+
}
|
|
2094
|
+
for (const D of y) {
|
|
2095
|
+
const S = it(Qt[D], { cwd: t, stdio: "ignore", shell: !0 });
|
|
2096
|
+
S.on("close", (m) => F(D, m === 0)), S.on("error", () => F(D, !1));
|
|
2097
|
+
}
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
return {
|
|
2101
|
+
getStatus() {
|
|
2102
|
+
return {
|
|
2103
|
+
lint: { ...i.lint },
|
|
2104
|
+
format: { ...i.format },
|
|
2105
|
+
test: { ...i.test }
|
|
2106
|
+
};
|
|
2107
|
+
},
|
|
2108
|
+
runCheck: l,
|
|
2109
|
+
runChecksAndWait: f,
|
|
2110
|
+
runQualityFix: c,
|
|
2111
|
+
subscribe(h) {
|
|
2112
|
+
a.add(h);
|
|
2113
|
+
for (const y of ["lint", "format", "test"]) {
|
|
2114
|
+
const g = i[y];
|
|
2115
|
+
g.status !== "stale" && h.write(
|
|
2116
|
+
`data: ${JSON.stringify({ message: `${y}: ${g.status}`, timestamp: g.lastRun })}
|
|
2117
|
+
|
|
2118
|
+
`
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
h.on("close", () => a.delete(h));
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
function an(t) {
|
|
2126
|
+
const a = Re(t), i = _e(t), r = nn(t), s = Fe(t, i), e = Ce(
|
|
2127
|
+
t,
|
|
2128
|
+
(c) => i.ensureBranch(c),
|
|
2129
|
+
s.stampAuditDate,
|
|
2130
|
+
s.commitPhase,
|
|
2131
|
+
s.setRunReview
|
|
2132
|
+
), n = en({ root: t, activity: a, agent: e, git: i, status: r }), l = async (c, d, u) => {
|
|
2133
|
+
const f = (c.url ?? "").split("?")[0], h = n.find((g) => g.method === c.method && g.path === f);
|
|
2134
|
+
if (h) {
|
|
2135
|
+
try {
|
|
2136
|
+
await h.handle(c, d);
|
|
2137
|
+
} catch (g) {
|
|
2138
|
+
p(d, 500, { error: g.message });
|
|
2139
|
+
}
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
const y = tn.find((g) => g.path === f);
|
|
2143
|
+
if (!y) {
|
|
2144
|
+
u();
|
|
1712
2145
|
return;
|
|
1713
2146
|
}
|
|
1714
2147
|
try {
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
2148
|
+
p(d, 200, await y.handler(t));
|
|
2149
|
+
} catch (g) {
|
|
2150
|
+
p(d, 500, { error: g.message });
|
|
1719
2151
|
}
|
|
1720
2152
|
};
|
|
1721
|
-
return
|
|
2153
|
+
return l.agent = e, l;
|
|
1722
2154
|
}
|
|
1723
|
-
const
|
|
2155
|
+
const sn = {
|
|
1724
2156
|
".html": "text/html; charset=utf-8",
|
|
1725
2157
|
".js": "text/javascript; charset=utf-8",
|
|
1726
2158
|
".mjs": "text/javascript; charset=utf-8",
|
|
@@ -1733,301 +2165,304 @@ const xe = {
|
|
|
1733
2165
|
".woff": "font/woff",
|
|
1734
2166
|
".woff2": "font/woff2"
|
|
1735
2167
|
};
|
|
1736
|
-
function
|
|
1737
|
-
return
|
|
2168
|
+
function rn() {
|
|
2169
|
+
return $(de(Ie(import.meta.url)), "..", "app");
|
|
1738
2170
|
}
|
|
1739
|
-
async function
|
|
1740
|
-
const
|
|
1741
|
-
if (
|
|
2171
|
+
async function on({ root: t, port: a }) {
|
|
2172
|
+
const i = rn(), r = $(i, "index.html"), s = await at(r, "utf-8").catch(() => null);
|
|
2173
|
+
if (s === null)
|
|
1742
2174
|
throw new Error(
|
|
1743
|
-
`Dashboard assets not found at ${
|
|
2175
|
+
`Dashboard assets not found at ${i}. Run \`pnpm build\` (or reinstall the package) so dist/app exists.`
|
|
1744
2176
|
);
|
|
1745
|
-
const
|
|
1746
|
-
async function
|
|
1747
|
-
const
|
|
2177
|
+
const e = an(t);
|
|
2178
|
+
async function n(d, u) {
|
|
2179
|
+
const f = decodeURIComponent((d.url ?? "/").split("?")[0]), h = $(i, f === "/" ? "index.html" : f), y = h !== i && !h.startsWith(i + le);
|
|
1748
2180
|
try {
|
|
1749
|
-
|
|
1750
|
-
|
|
2181
|
+
const g = y ? null : await ht(h);
|
|
2182
|
+
if (g != null && g.isFile()) {
|
|
2183
|
+
u.statusCode = 200, u.setHeader("Content-Type", sn[ue(h)] ?? "application/octet-stream"), u.end(await at(h));
|
|
1751
2184
|
return;
|
|
1752
2185
|
}
|
|
1753
2186
|
} catch {
|
|
1754
2187
|
}
|
|
1755
|
-
|
|
2188
|
+
u.statusCode = 200, u.setHeader("Content-Type", "text/html; charset=utf-8"), u.end(s);
|
|
1756
2189
|
}
|
|
1757
|
-
const
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
2190
|
+
const l = xe((d, u) => {
|
|
2191
|
+
e(d, u, () => {
|
|
2192
|
+
n(d, u).catch((f) => {
|
|
2193
|
+
u.statusCode = 500, u.end(String(f));
|
|
1761
2194
|
});
|
|
1762
2195
|
});
|
|
1763
|
-
}),
|
|
1764
|
-
|
|
2196
|
+
}), c = () => {
|
|
2197
|
+
e.agent.killCurrent(), process.exit(0);
|
|
1765
2198
|
};
|
|
1766
|
-
process.on("SIGINT",
|
|
2199
|
+
process.on("SIGINT", c), process.on("SIGTERM", c), await new Promise((d, u) => {
|
|
2200
|
+
l.once("error", u), l.listen(a, d);
|
|
2201
|
+
});
|
|
1767
2202
|
}
|
|
1768
|
-
async function
|
|
2203
|
+
async function Bt(t) {
|
|
1769
2204
|
try {
|
|
1770
|
-
return await
|
|
2205
|
+
return await ht(t), !0;
|
|
1771
2206
|
} catch {
|
|
1772
2207
|
return !1;
|
|
1773
2208
|
}
|
|
1774
2209
|
}
|
|
1775
|
-
async function
|
|
1776
|
-
const
|
|
1777
|
-
if (await
|
|
1778
|
-
const
|
|
1779
|
-
return await
|
|
2210
|
+
async function cn(t, a) {
|
|
2211
|
+
const i = $(t, `${a}.md`);
|
|
2212
|
+
if (await Bt(i)) return i;
|
|
2213
|
+
const r = $(t, "archive", `${a}.md`);
|
|
2214
|
+
return await Bt(r) ? r : null;
|
|
1780
2215
|
}
|
|
1781
|
-
async function
|
|
2216
|
+
async function dn(t) {
|
|
1782
2217
|
try {
|
|
1783
|
-
return (await
|
|
2218
|
+
return (await ht(t)).mtime.toISOString().slice(0, 10);
|
|
1784
2219
|
} catch {
|
|
1785
2220
|
return null;
|
|
1786
2221
|
}
|
|
1787
2222
|
}
|
|
1788
|
-
async function
|
|
1789
|
-
const
|
|
1790
|
-
if (!
|
|
1791
|
-
throw new Error(`Could not parse plan file after audit: ${
|
|
1792
|
-
const
|
|
1793
|
-
id:
|
|
1794
|
-
title:
|
|
1795
|
-
kind:
|
|
1796
|
-
status:
|
|
1797
|
-
idea:
|
|
1798
|
-
agent:
|
|
1799
|
-
created:
|
|
1800
|
-
updated:
|
|
1801
|
-
audited:
|
|
1802
|
-
tags:
|
|
1803
|
-
body:
|
|
1804
|
-
phases:
|
|
1805
|
-
log:
|
|
1806
|
-
clarifications:
|
|
2223
|
+
async function ln(t, a) {
|
|
2224
|
+
const i = await at(t, "utf-8"), s = mt(i).entries[0];
|
|
2225
|
+
if (!s)
|
|
2226
|
+
throw new Error(`Could not parse plan file after audit: ${t}`);
|
|
2227
|
+
const e = {
|
|
2228
|
+
id: a,
|
|
2229
|
+
title: s.title,
|
|
2230
|
+
kind: s.kind ?? "feat",
|
|
2231
|
+
status: s.status,
|
|
2232
|
+
idea: s.idea,
|
|
2233
|
+
agent: s.agent,
|
|
2234
|
+
created: s.created,
|
|
2235
|
+
updated: s.updated,
|
|
2236
|
+
audited: ot(),
|
|
2237
|
+
tags: s.tags,
|
|
2238
|
+
body: s.body,
|
|
2239
|
+
phases: s.phases,
|
|
2240
|
+
log: s.log,
|
|
2241
|
+
clarifications: s.clarifications
|
|
1807
2242
|
};
|
|
1808
|
-
await
|
|
2243
|
+
await q(t, `${xt(e)}
|
|
1809
2244
|
`, "utf-8");
|
|
1810
2245
|
}
|
|
1811
|
-
async function
|
|
1812
|
-
var
|
|
1813
|
-
const
|
|
1814
|
-
cwd:
|
|
2246
|
+
async function un(t, a, i, r) {
|
|
2247
|
+
var n;
|
|
2248
|
+
const s = ie(a), e = it(i.command, i.buildArgs(s, r), {
|
|
2249
|
+
cwd: t,
|
|
1815
2250
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1816
2251
|
});
|
|
1817
|
-
return
|
|
1818
|
-
const
|
|
1819
|
-
|
|
2252
|
+
return e.stdout && Tt({ input: e.stdout }).on("line", (c) => {
|
|
2253
|
+
const d = i.parseLine(c);
|
|
2254
|
+
d != null && d.text && d.text !== "Agent is working…" && process.stdout.write(` ${d.text}
|
|
1820
2255
|
`);
|
|
1821
|
-
}), (
|
|
1822
|
-
|
|
2256
|
+
}), (n = e.stderr) == null || n.on("data", (l) => process.stderr.write(l)), new Promise((l) => {
|
|
2257
|
+
e.on("close", (c) => l(c === 0)), e.on("error", () => l(!1));
|
|
1823
2258
|
});
|
|
1824
2259
|
}
|
|
1825
|
-
const
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const
|
|
2260
|
+
const yt = new fe();
|
|
2261
|
+
yt.name("paper-camp").description("Local-first, AI-native project companion.").version(be);
|
|
2262
|
+
yt.command("init [project-name]").description("Initialize Paper Camp in the current directory").option("-i, --intent <text>", "one-line description of what you are building").action(async (t, a) => {
|
|
2263
|
+
const i = process.cwd(), r = t ?? pe(i);
|
|
1829
2264
|
try {
|
|
1830
|
-
await
|
|
1831
|
-
} catch (
|
|
1832
|
-
if (
|
|
1833
|
-
console.error(
|
|
2265
|
+
await Se(i, { projectName: r, intent: a.intent }), console.log(`Initialized Paper Camp in ${i}`), console.log(" papercamp/config.json"), console.log(" papercamp/plans/ (per-file plan entries)"), console.log(" papercamp/plans/index.md"), console.log(" papercamp/plans/archive/"), console.log(" papercamp/ideas/ (per-file idea entries)"), console.log(" papercamp/ideas/index.md"), console.log(" papercamp/progress.md, decisions.md, open-questions.md");
|
|
2266
|
+
} catch (s) {
|
|
2267
|
+
if (s instanceof ve) {
|
|
2268
|
+
console.error(s.message), process.exitCode = 1;
|
|
1834
2269
|
return;
|
|
1835
2270
|
}
|
|
1836
|
-
throw
|
|
2271
|
+
throw s;
|
|
1837
2272
|
}
|
|
1838
2273
|
});
|
|
1839
|
-
|
|
1840
|
-
const
|
|
2274
|
+
yt.command("dev").description("Start the local dashboard").option("-p, --port <number>", "port to listen on", "3333").action(async (t) => {
|
|
2275
|
+
const a = Number(t.port), i = process.cwd();
|
|
1841
2276
|
try {
|
|
1842
|
-
await
|
|
1843
|
-
} catch (
|
|
1844
|
-
console.error(
|
|
2277
|
+
await on({ root: i, port: a }), console.log(`Paper Camp dashboard running at http://localhost:${a}`);
|
|
2278
|
+
} catch (r) {
|
|
2279
|
+
console.error(r.message), process.exit(1);
|
|
1845
2280
|
}
|
|
1846
2281
|
});
|
|
1847
|
-
|
|
1848
|
-
if (
|
|
1849
|
-
console.error(`Unknown type "${
|
|
2282
|
+
yt.command("add <type> [name]").description("Add a new entry (currently supports: plan)").option("-k, --kind <kind>", `plan kind (${Et.join("|")})`, "feat").action(async (t, a, i) => {
|
|
2283
|
+
if (t !== "plan") {
|
|
2284
|
+
console.error(`Unknown type "${t}". Supported types: plan`), process.exitCode = 1;
|
|
1850
2285
|
return;
|
|
1851
2286
|
}
|
|
1852
|
-
if (!
|
|
2287
|
+
if (!a) {
|
|
1853
2288
|
console.error("Usage: paper-camp add plan <name> [--kind feat|fix|chore|docs|refactor]"), process.exitCode = 1;
|
|
1854
2289
|
return;
|
|
1855
2290
|
}
|
|
1856
|
-
if (!
|
|
1857
|
-
console.error(`Unknown kind "${
|
|
2291
|
+
if (!Et.includes(i.kind)) {
|
|
2292
|
+
console.error(`Unknown kind "${i.kind}". Supported kinds: ${Et.join(", ")}`), process.exitCode = 1;
|
|
1858
2293
|
return;
|
|
1859
2294
|
}
|
|
1860
|
-
const
|
|
1861
|
-
if (!
|
|
2295
|
+
const r = i.kind, s = process.cwd(), e = ft(s, "papercamp", "config.json"), n = await ae(e, r);
|
|
2296
|
+
if (!n) {
|
|
1862
2297
|
console.error("Could not assign plan ID — is the project initialized?"), process.exitCode = 1;
|
|
1863
2298
|
return;
|
|
1864
2299
|
}
|
|
1865
|
-
const
|
|
1866
|
-
await
|
|
1867
|
-
const
|
|
1868
|
-
id:
|
|
1869
|
-
title:
|
|
1870
|
-
kind:
|
|
2300
|
+
const l = ft(s, "papercamp", "plans");
|
|
2301
|
+
await ut(l, { recursive: !0 });
|
|
2302
|
+
const c = xt({
|
|
2303
|
+
id: n,
|
|
2304
|
+
title: a,
|
|
2305
|
+
kind: r,
|
|
1871
2306
|
status: "idea",
|
|
1872
|
-
created:
|
|
2307
|
+
created: ot()
|
|
1873
2308
|
});
|
|
1874
|
-
await
|
|
2309
|
+
await q($(l, `${n}.md`), `${c}
|
|
1875
2310
|
`, "utf-8");
|
|
1876
|
-
const { entries:
|
|
1877
|
-
await
|
|
2311
|
+
const { entries: d } = await lt(l);
|
|
2312
|
+
await q($(l, "index.md"), Lt(d), "utf-8"), console.log(`Added plan "${a}" (${n}) to papercamp/plans/${n}.md`);
|
|
1878
2313
|
});
|
|
1879
|
-
|
|
2314
|
+
yt.command("migrate").description(
|
|
1880
2315
|
"One-time migration: split monolithic plans.md/ideas.md into per-file YAML frontmatter entries"
|
|
1881
2316
|
).action(async () => {
|
|
1882
|
-
const
|
|
1883
|
-
await
|
|
1884
|
-
const
|
|
1885
|
-
let
|
|
1886
|
-
const
|
|
1887
|
-
if (
|
|
1888
|
-
const { entries:
|
|
1889
|
-
|
|
1890
|
-
for (const
|
|
1891
|
-
console.warn(` warning: ${
|
|
1892
|
-
for (const
|
|
1893
|
-
if (!
|
|
1894
|
-
console.warn(` skipping plan "${
|
|
2317
|
+
const t = process.cwd(), a = ft(t, "papercamp", "plans"), i = $(a, "archive"), r = ft(t, "papercamp", "ideas");
|
|
2318
|
+
await ut(i, { recursive: !0 }), await ut(r, { recursive: !0 });
|
|
2319
|
+
const s = ft(t, "papercamp", "plans.md"), e = ft(t, "papercamp", "ideas.md");
|
|
2320
|
+
let n = 0, l = 0, c = 0;
|
|
2321
|
+
const d = await at(s, "utf-8").catch(() => "");
|
|
2322
|
+
if (d.trim()) {
|
|
2323
|
+
const { entries: F, warnings: D } = Vt(d);
|
|
2324
|
+
c = D.length;
|
|
2325
|
+
for (const S of D)
|
|
2326
|
+
console.warn(` warning: ${S.title}: ${S.message}`);
|
|
2327
|
+
for (const S of F) {
|
|
2328
|
+
if (!S.id) {
|
|
2329
|
+
console.warn(` skipping plan "${S.title}" — no Id assigned, cannot migrate`), l++;
|
|
1895
2330
|
continue;
|
|
1896
2331
|
}
|
|
1897
|
-
const
|
|
1898
|
-
if (await
|
|
1899
|
-
|
|
2332
|
+
const m = S.status === "done" || S.status === "dropped" ? i : a, w = $(m, `${S.id}.md`);
|
|
2333
|
+
if (await Bt(w)) {
|
|
2334
|
+
l++;
|
|
1900
2335
|
continue;
|
|
1901
2336
|
}
|
|
1902
|
-
const
|
|
1903
|
-
id:
|
|
1904
|
-
title:
|
|
1905
|
-
kind:
|
|
1906
|
-
status:
|
|
1907
|
-
idea:
|
|
1908
|
-
agent:
|
|
1909
|
-
created:
|
|
1910
|
-
updated:
|
|
1911
|
-
tags:
|
|
1912
|
-
body:
|
|
1913
|
-
phases:
|
|
1914
|
-
log:
|
|
1915
|
-
clarifications:
|
|
2337
|
+
const I = xt({
|
|
2338
|
+
id: S.id,
|
|
2339
|
+
title: S.title,
|
|
2340
|
+
kind: S.kind ?? "feat",
|
|
2341
|
+
status: S.status,
|
|
2342
|
+
idea: S.idea,
|
|
2343
|
+
agent: S.agent,
|
|
2344
|
+
created: S.created,
|
|
2345
|
+
updated: S.updated,
|
|
2346
|
+
tags: S.tags,
|
|
2347
|
+
body: S.body,
|
|
2348
|
+
phases: S.phases,
|
|
2349
|
+
log: S.log,
|
|
2350
|
+
clarifications: S.clarifications
|
|
1916
2351
|
});
|
|
1917
|
-
await
|
|
1918
|
-
`, "utf-8"),
|
|
2352
|
+
await q(w, `${I}
|
|
2353
|
+
`, "utf-8"), n++;
|
|
1919
2354
|
}
|
|
1920
2355
|
}
|
|
1921
|
-
let
|
|
1922
|
-
const
|
|
1923
|
-
if (
|
|
1924
|
-
const
|
|
1925
|
-
for (const
|
|
1926
|
-
if (!
|
|
1927
|
-
console.warn(` skipping idea "${
|
|
2356
|
+
let u = 0, f = 0;
|
|
2357
|
+
const h = await at(e, "utf-8").catch(() => "");
|
|
2358
|
+
if (h.trim()) {
|
|
2359
|
+
const F = ee(h);
|
|
2360
|
+
for (const D of F) {
|
|
2361
|
+
if (!D.id) {
|
|
2362
|
+
console.warn(` skipping idea "${D.title}" — no Id assigned, cannot migrate`), f++;
|
|
1928
2363
|
continue;
|
|
1929
2364
|
}
|
|
1930
|
-
const
|
|
1931
|
-
if (await
|
|
1932
|
-
|
|
2365
|
+
const S = $(r, `${D.id}.md`);
|
|
2366
|
+
if (await Bt(S)) {
|
|
2367
|
+
f++;
|
|
1933
2368
|
continue;
|
|
1934
2369
|
}
|
|
1935
|
-
const
|
|
1936
|
-
await
|
|
1937
|
-
`, "utf-8"),
|
|
2370
|
+
const m = D.body.replace(/^#{1,3}\s+.+(?:\r?\n)?/, "").trim(), w = ne({ id: D.id, title: D.title, body: m });
|
|
2371
|
+
await q(S, `${w}
|
|
2372
|
+
`, "utf-8"), u++;
|
|
1938
2373
|
}
|
|
1939
2374
|
}
|
|
1940
|
-
const { entries:
|
|
1941
|
-
await
|
|
1942
|
-
const { entries:
|
|
1943
|
-
await
|
|
1944
|
-
`Migrated ${
|
|
2375
|
+
const { entries: y } = await lt(a);
|
|
2376
|
+
await q($(a, "index.md"), Lt(y), "utf-8");
|
|
2377
|
+
const { entries: g } = await te(r), E = se(g, y);
|
|
2378
|
+
await q($(r, "index.md"), Zt(E), "utf-8"), n > 0 && l === 0 && c === 0 && await q(s, "", "utf-8"), u > 0 && f === 0 && await q(e, "", "utf-8"), console.log(
|
|
2379
|
+
`Migrated ${n} plans (${l} skipped), ${u} ideas (${f} skipped).`
|
|
1945
2380
|
);
|
|
1946
2381
|
});
|
|
1947
|
-
|
|
1948
|
-
var
|
|
1949
|
-
const
|
|
1950
|
-
for (const
|
|
1951
|
-
console.warn(` warning: ${
|
|
1952
|
-
const
|
|
1953
|
-
if (
|
|
2382
|
+
yt.command("audit").description("Audit all review/done plans for missing phases").action(async () => {
|
|
2383
|
+
var S;
|
|
2384
|
+
const t = process.cwd(), a = ft(t, "papercamp", "plans"), { entries: i, warnings: r } = await lt(a);
|
|
2385
|
+
for (const m of r)
|
|
2386
|
+
console.warn(` warning: ${m.title}: ${m.message}`);
|
|
2387
|
+
const s = i.filter((m) => m.status === "review" || m.status === "done");
|
|
2388
|
+
if (s.length === 0) {
|
|
1954
2389
|
console.log('No plans with status "review" or "done" found.');
|
|
1955
2390
|
return;
|
|
1956
2391
|
}
|
|
1957
|
-
const
|
|
2392
|
+
const e = await at($(t, "papercamp", "config.json"), "utf-8").catch(
|
|
1958
2393
|
() => "{}"
|
|
1959
2394
|
);
|
|
1960
|
-
let
|
|
2395
|
+
let n;
|
|
1961
2396
|
try {
|
|
1962
|
-
|
|
2397
|
+
n = JSON.parse(e);
|
|
1963
2398
|
} catch {
|
|
1964
2399
|
console.error("Invalid papercamp/config.json"), process.exitCode = 1;
|
|
1965
2400
|
return;
|
|
1966
2401
|
}
|
|
1967
|
-
const
|
|
1968
|
-
phase:
|
|
1969
|
-
planDraft:
|
|
1970
|
-
ideaExtend:
|
|
1971
|
-
commitSuggest:
|
|
1972
|
-
} :
|
|
1973
|
-
console.log(`Auditing ${
|
|
2402
|
+
const l = n.defaultAgents, c = l ? {
|
|
2403
|
+
phase: z(l.phase),
|
|
2404
|
+
planDraft: z(l.planDraft),
|
|
2405
|
+
ideaExtend: z(l.ideaExtend),
|
|
2406
|
+
commitSuggest: z(l.commitSuggest)
|
|
2407
|
+
} : _t, { adapter: d, model: u, effort: f } = St({ defaultAgents: c, taskKind: "audit" });
|
|
2408
|
+
console.log(`Auditing ${s.length} plan(s):
|
|
1974
2409
|
`);
|
|
1975
|
-
const
|
|
1976
|
-
for (const
|
|
1977
|
-
const
|
|
1978
|
-
if (!
|
|
1979
|
-
console.log(` [skip] ${
|
|
2410
|
+
const h = [];
|
|
2411
|
+
for (const m of s) {
|
|
2412
|
+
const w = m.id ?? "(no id)", I = w.padEnd(14);
|
|
2413
|
+
if (!m.id) {
|
|
2414
|
+
console.log(` [skip] ${I} ${m.title} — no id`), h.push({ id: w, title: m.title, status: "skipped", skipReason: "no id" });
|
|
1980
2415
|
continue;
|
|
1981
2416
|
}
|
|
1982
|
-
const
|
|
1983
|
-
if (!
|
|
1984
|
-
console.log(` [skip] ${
|
|
2417
|
+
const T = await cn(a, m.id);
|
|
2418
|
+
if (!T) {
|
|
2419
|
+
console.log(` [skip] ${I} ${m.title} — file not found`), h.push({ id: w, title: m.title, status: "skipped", skipReason: "file not found" });
|
|
1985
2420
|
continue;
|
|
1986
2421
|
}
|
|
1987
|
-
if (
|
|
1988
|
-
const
|
|
1989
|
-
if (
|
|
2422
|
+
if (m.audited) {
|
|
2423
|
+
const B = await dn(T);
|
|
2424
|
+
if (B && m.audited >= B) {
|
|
1990
2425
|
console.log(
|
|
1991
|
-
` [skip] ${
|
|
1992
|
-
),
|
|
1993
|
-
id:
|
|
1994
|
-
title:
|
|
2426
|
+
` [skip] ${I} ${m.title} — audited ${m.audited}, unchanged since`
|
|
2427
|
+
), h.push({
|
|
2428
|
+
id: w,
|
|
2429
|
+
title: m.title,
|
|
1995
2430
|
status: "skipped",
|
|
1996
|
-
skipReason: `audited ${
|
|
2431
|
+
skipReason: `audited ${m.audited}, unchanged`
|
|
1997
2432
|
});
|
|
1998
2433
|
continue;
|
|
1999
2434
|
}
|
|
2000
2435
|
}
|
|
2001
|
-
const
|
|
2002
|
-
if (console.log(` [audit] ${
|
|
2436
|
+
const O = m.phases.length;
|
|
2437
|
+
if (console.log(` [audit] ${I} ${m.title}`), await un(t, m, d, { model: u, effort: f })) {
|
|
2003
2438
|
try {
|
|
2004
|
-
await
|
|
2005
|
-
} catch (
|
|
2006
|
-
console.log(` [fail] ${
|
|
2439
|
+
await ln(T, m.id);
|
|
2440
|
+
} catch (o) {
|
|
2441
|
+
console.log(` [fail] ${I} ${m.title} — ${o.message}`), process.exitCode = 1, h.push({ id: w, title: m.title, status: "failed" });
|
|
2007
2442
|
continue;
|
|
2008
2443
|
}
|
|
2009
|
-
const
|
|
2010
|
-
console.log(` [done] ${
|
|
2444
|
+
const B = await at(T, "utf-8").catch(() => ""), _ = ((S = mt(B).entries[0]) == null ? void 0 : S.phases.length) ?? O, R = Math.max(0, _ - O);
|
|
2445
|
+
console.log(` [done] ${I} ${m.title}`), h.push({ id: w, title: m.title, status: "audited", gapPhases: R });
|
|
2011
2446
|
} else
|
|
2012
|
-
console.log(` [fail] ${
|
|
2447
|
+
console.log(` [fail] ${I} ${m.title} — agent exited with error`), process.exitCode = 1, h.push({ id: w, title: m.title, status: "failed" });
|
|
2013
2448
|
}
|
|
2014
|
-
const
|
|
2449
|
+
const y = h.filter((m) => m.status === "audited"), g = h.filter((m) => m.status === "skipped"), E = h.filter((m) => m.status === "failed"), F = y.reduce((m, w) => m + (w.gapPhases ?? 0), 0), D = "─".repeat(43);
|
|
2015
2450
|
if (console.log(`
|
|
2016
|
-
${
|
|
2017
|
-
` Audited : ${
|
|
2018
|
-
),
|
|
2019
|
-
if (
|
|
2020
|
-
console.log(` Gap phases appended: ${
|
|
2021
|
-
for (const
|
|
2022
|
-
console.log(` ${
|
|
2451
|
+
${D}`), console.log("Audit summary"), console.log(
|
|
2452
|
+
` Audited : ${y.length} Skipped : ${g.length} Failed : ${E.length}`
|
|
2453
|
+
), y.length > 0)
|
|
2454
|
+
if (F > 0) {
|
|
2455
|
+
console.log(` Gap phases appended: ${F} total`);
|
|
2456
|
+
for (const m of y.filter((w) => (w.gapPhases ?? 0) > 0))
|
|
2457
|
+
console.log(` ${m.id.padEnd(14)} +${m.gapPhases} phase(s)`);
|
|
2023
2458
|
} else
|
|
2024
2459
|
console.log(" No gap phases appended — all audited plans are complete.");
|
|
2025
|
-
if (
|
|
2460
|
+
if (g.length > 0) {
|
|
2026
2461
|
console.log(" Skipped:");
|
|
2027
|
-
for (const
|
|
2028
|
-
console.log(` ${
|
|
2462
|
+
for (const m of g)
|
|
2463
|
+
console.log(` ${m.id.padEnd(14)} ${m.skipReason}`);
|
|
2029
2464
|
}
|
|
2030
|
-
console.log(
|
|
2465
|
+
console.log(D);
|
|
2031
2466
|
});
|
|
2032
|
-
|
|
2467
|
+
yt.parseAsync(process.argv);
|
|
2033
2468
|
//# sourceMappingURL=index.js.map
|