@dendelion/paper-camp 0.2.1 → 0.3.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/README.md +16 -7
- package/dist/app/assets/main-8b5RQGnS.js +208 -0
- package/dist/app/assets/main-8b5RQGnS.js.map +1 -0
- package/dist/app/index.html +1 -1
- package/dist/chunks/serializer.DL7H4ZTd.js +5249 -0
- package/dist/chunks/serializer.DL7H4ZTd.js.map +1 -0
- package/dist/cli/index.js +1497 -714
- package/dist/cli/index.js.map +1 -1
- package/dist/core/frontmatter-schemas.d.ts +11 -0
- package/dist/core/frontmatter-schemas.d.ts.map +1 -0
- package/dist/core/frontmatter.test.d.ts +2 -0
- package/dist/core/frontmatter.test.d.ts.map +1 -0
- package/dist/core/idea-status.d.ts +7 -0
- package/dist/core/idea-status.d.ts.map +1 -0
- package/dist/core/index.js +48 -29
- package/dist/core/parser.d.ts +49 -4
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/scaffold.d.ts +4 -2
- package/dist/core/scaffold.d.ts.map +1 -1
- package/dist/core/schemas.d.ts +75 -12
- package/dist/core/schemas.d.ts.map +1 -1
- package/dist/core/serializer.d.ts +52 -2
- package/dist/core/serializer.d.ts.map +1 -1
- package/dist/types/index.d.ts +22 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +32 -10
- package/dist/types/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/app/assets/main-tAEncq68.js +0 -272
- package/dist/app/assets/main-tAEncq68.js.map +0 -1
- package/dist/chunks/serializer.D1Ts_p8h.js +0 -462
- package/dist/chunks/serializer.D1Ts_p8h.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,150 +1,90 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
_(E(n, "progress.md"))
|
|
19
|
-
]);
|
|
20
|
-
return {
|
|
21
|
-
plans: k(c).entries,
|
|
22
|
-
decisions: Q(o).entries,
|
|
23
|
-
openQuestions: z(s).entries,
|
|
24
|
-
progress: nt(u)
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function Nt(n, c) {
|
|
28
|
-
const o = [], s = (/* @__PURE__ */ new Date()).toISOString(), u = new Map(n.plans.map((e) => [e.title, e])), y = new Map(c.plans.map((e) => [e.title, e]));
|
|
29
|
-
for (const [e, d] of y) {
|
|
30
|
-
const f = u.get(e);
|
|
31
|
-
if (!f)
|
|
32
|
-
o.push({ message: `New plan added: ${e}`, timestamp: s });
|
|
33
|
-
else if (f.status !== d.status)
|
|
34
|
-
o.push({ message: `Plan "${e}" marked ${d.status}`, timestamp: s });
|
|
35
|
-
else
|
|
36
|
-
for (let p = 0; p < d.phases.length; p++) {
|
|
37
|
-
const l = f.phases[p], m = d.phases[p];
|
|
38
|
-
l && l.done !== m.done && o.push({
|
|
39
|
-
message: m.done ? `Phase ${p + 1}/${d.phases.length} checked off in "${e}"` : `Phase ${p + 1}/${d.phases.length} unchecked in "${e}"`,
|
|
40
|
-
timestamp: s
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
for (const [e] of u)
|
|
45
|
-
y.has(e) || o.push({ message: `Plan removed: ${e}`, timestamp: s });
|
|
46
|
-
const r = new Map(n.decisions.map((e) => [e.title, e])), t = new Map(c.decisions.map((e) => [e.title, e]));
|
|
47
|
-
for (const [e] of t)
|
|
48
|
-
r.has(e) || o.push({ message: `New decision: ${e}`, timestamp: s });
|
|
49
|
-
const b = new Map(n.openQuestions.map((e) => [e.title, e])), C = new Map(c.openQuestions.map((e) => [e.title, e]));
|
|
50
|
-
for (const [e] of C)
|
|
51
|
-
b.has(e) || o.push({ message: `New open question: ${e}`, timestamp: s });
|
|
52
|
-
const S = new Map(n.progress.map((e) => [e.date, e])), i = new Map(c.progress.map((e) => [e.date, e]));
|
|
53
|
-
for (const [e] of i)
|
|
54
|
-
S.has(e) || o.push({ message: `Progress logged: ${e}`, timestamp: s });
|
|
55
|
-
return o;
|
|
56
|
-
}
|
|
57
|
-
function Ot(n) {
|
|
58
|
-
const c = /* @__PURE__ */ new Set();
|
|
59
|
-
let o = null, s = null;
|
|
60
|
-
const u = ["plans.md", "decisions.md", "open-questions.md", "progress.md"];
|
|
61
|
-
async function y() {
|
|
62
|
-
try {
|
|
63
|
-
const r = await q(n);
|
|
64
|
-
if (o) {
|
|
65
|
-
const t = Nt(o, r);
|
|
66
|
-
for (const b of t) {
|
|
67
|
-
const C = `data: ${JSON.stringify(b)}
|
|
2
|
+
import { spawn as V, spawnSync as ht } from "node:child_process";
|
|
3
|
+
import { stat as it, readFile as U, lstat as Ut, unlink as Wt, mkdir as q, writeFile as E } from "node:fs/promises";
|
|
4
|
+
import { join as N, dirname as zt, extname as Yt, basename as Qt, resolve as nt } from "node:path";
|
|
5
|
+
import { createInterface as jt } from "node:readline";
|
|
6
|
+
import { Command as Xt } from "commander";
|
|
7
|
+
import { AGENT_OPTIONS as Pt, coerceAgentConfig as B, DEFAULT_AGENTS as kt, PLAN_KINDS as wt, AGENT_IDS as Ot } from "../types/index.js";
|
|
8
|
+
import { K as Q, D as dt, L as Tt, M as ct, e as Bt, o as at, O as Z, d as Zt, j as Ft, E as vt, J as xt, B as Jt, x as Et, c as Vt, C as It, i as qt, b as te, m as ee, q as Ht, k as _t, F as ne, w as Dt, h as ae, P as ie, t as se, A as oe } from "../chunks/serializer.DL7H4ZTd.js";
|
|
9
|
+
import { createServer as re } from "node:http";
|
|
10
|
+
import { fileURLToPath as de } from "node:url";
|
|
11
|
+
import { watch as Ct, readFileSync as ce } from "node:fs";
|
|
12
|
+
function Mt(e) {
|
|
13
|
+
const i = e.phases.map((n, h) => `${h + 1}. [${n.done ? "x" : " "}] ${n.text}`).join(`
|
|
14
|
+
`), a = e.log && e.log.length > 0 ? e.log.map((n) => `- ${n.date}: ${n.text}`).join(`
|
|
15
|
+
`) : "(none)", p = e.clarifications && e.clarifications.length > 0 ? e.clarifications.map((n) => `- ${n.date}: ${n.text}`).join(`
|
|
16
|
+
`) : "(none)";
|
|
17
|
+
return `You're auditing the plan "${e.title}" (${e.id ?? "no id"}), stored as a single file at papercamp/plans/${e.id ?? "<ID>"}.md (or papercamp/plans/archive/${e.id ?? "<ID>"}.md if it's already done/dropped — check both). Edit only that file.
|
|
68
18
|
|
|
69
|
-
|
|
70
|
-
for (const S of c)
|
|
71
|
-
try {
|
|
72
|
-
S.write(C);
|
|
73
|
-
} catch {
|
|
74
|
-
c.delete(S);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
o = r;
|
|
79
|
-
} catch {
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
for (const r of u) {
|
|
83
|
-
const t = E(n, r);
|
|
84
|
-
try {
|
|
85
|
-
G(t, () => {
|
|
86
|
-
s && clearTimeout(s), s = setTimeout(y, 300);
|
|
87
|
-
});
|
|
88
|
-
} catch {
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return q(n).then((r) => {
|
|
92
|
-
o = r;
|
|
93
|
-
}), {
|
|
94
|
-
subscribe(r) {
|
|
95
|
-
c.add(r);
|
|
96
|
-
const t = JSON.stringify({
|
|
97
|
-
message: "Watching for changes…",
|
|
98
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
99
|
-
});
|
|
100
|
-
r.write(`data: ${t}
|
|
19
|
+
Plan body: ${e.body}
|
|
101
20
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
21
|
+
Current phases:
|
|
22
|
+
${i}
|
|
23
|
+
|
|
24
|
+
Log entries (issues, bugs, and review notes documented so far):
|
|
25
|
+
${a}
|
|
26
|
+
|
|
27
|
+
Clarifications (answered questions about scope and design):
|
|
28
|
+
${p}
|
|
29
|
+
|
|
30
|
+
Read this plan's phases, body, Log entries, and Clarifications, then inspect the current repo state. The Log entries often document bugs, UX issues, or missing functionality that should be turned into new phases. Append any phase that's clearly required but missing as a normal \`- [ ]\` line at the end of the \`### Phases\` list — optionally with the existing indented description format. Explicitly never touch existing lines: never reorder, check, uncheck, or rewrite anything already there, no matter how stale or redundant it looks.
|
|
31
|
+
|
|
32
|
+
If you append anything, finish with exactly one new \`### Log\` line (date: summary) describing what was found and appended.
|
|
33
|
+
|
|
34
|
+
If nothing is missing, write nothing at all — not even an empty heading or a Log line. The audit must be safe to re-run anytime without producing log spam.`;
|
|
105
35
|
}
|
|
106
|
-
function
|
|
107
|
-
|
|
36
|
+
function pe(e, i) {
|
|
37
|
+
const a = [
|
|
38
|
+
"-p",
|
|
39
|
+
e,
|
|
40
|
+
"--output-format",
|
|
41
|
+
"stream-json",
|
|
42
|
+
"--verbose",
|
|
43
|
+
"--permission-mode",
|
|
44
|
+
"auto"
|
|
45
|
+
];
|
|
46
|
+
return i != null && i.model && a.push("--model", i.model), i != null && i.effort && a.push("--effort", i.effort), a;
|
|
108
47
|
}
|
|
109
|
-
function
|
|
110
|
-
let
|
|
48
|
+
function ue(e) {
|
|
49
|
+
let i;
|
|
111
50
|
try {
|
|
112
|
-
|
|
51
|
+
i = JSON.parse(e);
|
|
113
52
|
} catch {
|
|
114
53
|
return null;
|
|
115
54
|
}
|
|
116
|
-
switch (
|
|
55
|
+
switch (i.type) {
|
|
117
56
|
case "system":
|
|
118
|
-
return
|
|
57
|
+
return i.subtype === "init" ? { text: "Agent session started" } : i.subtype === "post_turn_summary" && typeof i.status_detail == "string" ? { text: i.status_detail } : null;
|
|
119
58
|
case "rate_limit_event":
|
|
120
59
|
return null;
|
|
121
60
|
case "assistant": {
|
|
122
|
-
const
|
|
123
|
-
for (const
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
126
|
-
return { text: `Running ${
|
|
127
|
-
if (
|
|
128
|
-
return { text:
|
|
61
|
+
const a = i.message, p = (a == null ? void 0 : a.content) ?? [];
|
|
62
|
+
for (const n of p) {
|
|
63
|
+
const h = n;
|
|
64
|
+
if (h.type === "tool_use")
|
|
65
|
+
return { text: `Running ${h.name ?? "a tool"}…` };
|
|
66
|
+
if (h.type === "text" && typeof h.text == "string" && h.text.trim())
|
|
67
|
+
return { text: h.text.trim() };
|
|
129
68
|
}
|
|
130
69
|
return null;
|
|
131
70
|
}
|
|
132
71
|
case "user": {
|
|
133
|
-
const
|
|
134
|
-
return
|
|
72
|
+
const a = i.message, p = a == null ? void 0 : a.content, n = Array.isArray(p) ? p[0] : void 0;
|
|
73
|
+
return n != null && n.is_error ? { text: `Error: ${typeof n.content == "string" ? n.content : "Tool call failed"}`, error: !0 } : { text: "Tool finished" };
|
|
135
74
|
}
|
|
136
75
|
case "result": {
|
|
137
|
-
const
|
|
138
|
-
return { text: (typeof
|
|
76
|
+
const a = !!i.is_error;
|
|
77
|
+
return { text: (typeof i.result == "string" ? i.result.trim() : "") || (a ? "Agent run failed" : "Agent run finished"), done: !0, error: a };
|
|
139
78
|
}
|
|
140
79
|
default:
|
|
141
80
|
return { text: "Agent is working…" };
|
|
142
81
|
}
|
|
143
82
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
83
|
+
function le(e, i) {
|
|
84
|
+
const a = ["run", e, "--format", "json"];
|
|
85
|
+
return i != null && i.model && a.push("-m", i.model), i != null && i.effort && a.push("--variant", i.effort), a;
|
|
146
86
|
}
|
|
147
|
-
const
|
|
87
|
+
const fe = {
|
|
148
88
|
bash: "Running command",
|
|
149
89
|
read: "Reading file",
|
|
150
90
|
edit: "Editing file",
|
|
@@ -155,423 +95,789 @@ const vt = {
|
|
|
155
95
|
webfetch: "Fetching URL",
|
|
156
96
|
question: "Asking for input"
|
|
157
97
|
};
|
|
158
|
-
function
|
|
159
|
-
let
|
|
98
|
+
function ge(e) {
|
|
99
|
+
let i;
|
|
160
100
|
try {
|
|
161
|
-
|
|
101
|
+
i = JSON.parse(e);
|
|
162
102
|
} catch {
|
|
163
103
|
return null;
|
|
164
104
|
}
|
|
165
|
-
const
|
|
166
|
-
if (!
|
|
167
|
-
switch (
|
|
105
|
+
const a = i.type, p = i.part;
|
|
106
|
+
if (!a || !p) return null;
|
|
107
|
+
switch (a) {
|
|
168
108
|
case "step_start":
|
|
169
109
|
return null;
|
|
170
110
|
case "text": {
|
|
171
|
-
const
|
|
172
|
-
return
|
|
111
|
+
const n = p.text;
|
|
112
|
+
return n != null && n.trim() ? { text: n.trim() } : null;
|
|
173
113
|
}
|
|
174
114
|
case "tool_use": {
|
|
175
|
-
const
|
|
176
|
-
return
|
|
115
|
+
const n = p.tool, h = p.state ? p.state.input : void 0, m = h ? h.description : void 0, $ = n ? fe[n] : "Running tool", u = typeof m == "string" && m.trim() ? `: ${m.trim()}` : "";
|
|
116
|
+
return n ? { text: `${$}${u}…` } : null;
|
|
177
117
|
}
|
|
178
118
|
case "step_finish": {
|
|
179
|
-
const
|
|
180
|
-
return
|
|
119
|
+
const n = p.reason, h = n === "tool-calls" ? null : n === "stop" ? "Done" : "Step finished";
|
|
120
|
+
return h ? { text: h } : null;
|
|
181
121
|
}
|
|
182
122
|
default:
|
|
183
123
|
return null;
|
|
184
124
|
}
|
|
185
125
|
}
|
|
186
|
-
const
|
|
126
|
+
const Rt = "claude-code", lt = {
|
|
187
127
|
"claude-code": {
|
|
188
128
|
command: "claude",
|
|
189
|
-
buildArgs:
|
|
190
|
-
parseLine:
|
|
129
|
+
buildArgs: pe,
|
|
130
|
+
parseLine: ue,
|
|
131
|
+
options: Pt["claude-code"]
|
|
191
132
|
},
|
|
192
133
|
opencode: {
|
|
193
134
|
command: "opencode",
|
|
194
|
-
buildArgs:
|
|
195
|
-
parseLine:
|
|
135
|
+
buildArgs: le,
|
|
136
|
+
parseLine: ge,
|
|
137
|
+
options: Pt.opencode
|
|
196
138
|
}
|
|
197
|
-
},
|
|
139
|
+
}, me = {
|
|
198
140
|
phase: "phase",
|
|
199
141
|
audit: "phase",
|
|
142
|
+
"batch-audit": "phase",
|
|
200
143
|
draft: "planDraft",
|
|
201
|
-
extend: "ideaExtend"
|
|
144
|
+
extend: "ideaExtend",
|
|
145
|
+
"commit-suggest": "commitSuggest"
|
|
202
146
|
};
|
|
203
|
-
function
|
|
204
|
-
const { agentId:
|
|
205
|
-
if (
|
|
206
|
-
if (
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
147
|
+
function St(e) {
|
|
148
|
+
const { agentId: i, defaultAgents: a, taskKind: p } = e;
|
|
149
|
+
if (i && i in lt) return { id: i, adapter: lt[i] };
|
|
150
|
+
if (p && a) {
|
|
151
|
+
const n = me[p], h = a[n];
|
|
152
|
+
if (h) {
|
|
153
|
+
const m = h.agent;
|
|
154
|
+
if (m in lt)
|
|
155
|
+
return { id: m, adapter: lt[m], model: h.model, effort: h.effort };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { id: Rt, adapter: lt[Rt] };
|
|
159
|
+
}
|
|
160
|
+
function Kt(e, i) {
|
|
161
|
+
return e.map((a) => {
|
|
162
|
+
if (!a.id)
|
|
163
|
+
return { ...a, status: "planned" };
|
|
164
|
+
const p = i.filter((h) => h.idea === a.id);
|
|
165
|
+
if (p.length === 0)
|
|
166
|
+
return { ...a, status: "planned" };
|
|
167
|
+
const n = p.every((h) => h.status === "done" || h.status === "dropped");
|
|
168
|
+
return { ...a, status: n ? "done" : "planned" };
|
|
169
|
+
});
|
|
211
170
|
}
|
|
212
|
-
const
|
|
213
|
-
|
|
171
|
+
const he = (e, i) => {
|
|
172
|
+
if (e)
|
|
173
|
+
return e.find((a) => a.status === "in-progress" || a.status === "review");
|
|
174
|
+
};
|
|
175
|
+
function ye(e) {
|
|
176
|
+
const i = /* @__PURE__ */ new Set();
|
|
177
|
+
let a = null;
|
|
178
|
+
function p() {
|
|
179
|
+
const n = `data: ${JSON.stringify({
|
|
180
|
+
message: "changed",
|
|
181
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
182
|
+
})}
|
|
183
|
+
|
|
184
|
+
`;
|
|
185
|
+
for (const h of i)
|
|
186
|
+
try {
|
|
187
|
+
h.write(n);
|
|
188
|
+
} catch {
|
|
189
|
+
i.delete(h);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
214
192
|
try {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
planDraft: o.defaultAgent,
|
|
219
|
-
ideaExtend: o.defaultAgent
|
|
220
|
-
} : X;
|
|
193
|
+
Ct(N(e, "papercamp"), { recursive: !0 }, () => {
|
|
194
|
+
a && clearTimeout(a), a = setTimeout(p, 300);
|
|
195
|
+
});
|
|
221
196
|
} catch {
|
|
222
|
-
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
subscribe(n) {
|
|
200
|
+
i.add(n);
|
|
201
|
+
const h = JSON.stringify({
|
|
202
|
+
message: "Watching for changes…",
|
|
203
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
204
|
+
});
|
|
205
|
+
n.write(`data: ${h}
|
|
206
|
+
|
|
207
|
+
`), n.on("close", () => i.delete(n));
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const we = 50;
|
|
212
|
+
function Nt(e) {
|
|
213
|
+
try {
|
|
214
|
+
const i = ce(N(e, "papercamp", "config.json"), "utf-8"), a = JSON.parse(i), p = a.defaultAgents;
|
|
215
|
+
if (p)
|
|
216
|
+
return {
|
|
217
|
+
phase: B(p.phase),
|
|
218
|
+
planDraft: B(p.planDraft),
|
|
219
|
+
ideaExtend: B(p.ideaExtend),
|
|
220
|
+
commitSuggest: B(p.commitSuggest)
|
|
221
|
+
};
|
|
222
|
+
if (a.defaultAgent) {
|
|
223
|
+
const n = a.defaultAgent;
|
|
224
|
+
return {
|
|
225
|
+
phase: { agent: n },
|
|
226
|
+
planDraft: { agent: n },
|
|
227
|
+
ideaExtend: { agent: n },
|
|
228
|
+
commitSuggest: { agent: n }
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return kt;
|
|
232
|
+
} catch {
|
|
233
|
+
return kt;
|
|
223
234
|
}
|
|
224
235
|
}
|
|
225
|
-
function
|
|
226
|
-
return `You're working on phase ${
|
|
236
|
+
function Se(e, i, a) {
|
|
237
|
+
return `You're working on phase ${a + 1} ("${i.text}") of the plan "${e.title}" (${e.id ?? "no id"}), stored as a single file at papercamp/plans/${e.id ?? "<ID>"}.md.
|
|
227
238
|
|
|
228
|
-
${
|
|
239
|
+
${i.description ?? ""}
|
|
229
240
|
|
|
230
|
-
Plan context: ${
|
|
241
|
+
Plan context: ${e.body}
|
|
231
242
|
|
|
232
|
-
Do only this phase. When done, check it off in
|
|
243
|
+
Do only this phase. When done, check it off in that file's \`### Phases\` list (- [ ] -> - [x]) and append what you did to progress.md. If this was the last unchecked phase, set the plan's \`status:\` frontmatter field to \`review\`, not \`done\`, per this repo's AGENTS.md.`;
|
|
233
244
|
}
|
|
234
|
-
function
|
|
235
|
-
}) {
|
|
236
|
-
const
|
|
237
|
-
let
|
|
238
|
-
function
|
|
239
|
-
const
|
|
245
|
+
function Ce(e, i = () => {
|
|
246
|
+
}, a) {
|
|
247
|
+
const p = /* @__PURE__ */ new Set();
|
|
248
|
+
let n = null;
|
|
249
|
+
function h(s) {
|
|
250
|
+
const w = `data: ${JSON.stringify({ message: s, timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "agent" })}
|
|
240
251
|
|
|
241
252
|
`;
|
|
242
|
-
for (const
|
|
253
|
+
for (const C of p)
|
|
243
254
|
try {
|
|
244
|
-
|
|
255
|
+
C.write(w);
|
|
245
256
|
} catch {
|
|
246
|
-
|
|
257
|
+
p.delete(C);
|
|
247
258
|
}
|
|
248
259
|
}
|
|
249
|
-
function
|
|
250
|
-
|
|
260
|
+
function m(s, w) {
|
|
261
|
+
s.lines.push(w), s.lines.length > we && s.lines.shift(), h(w);
|
|
251
262
|
}
|
|
252
|
-
function
|
|
253
|
-
|
|
263
|
+
function $(s, w) {
|
|
264
|
+
s.status = w, h(`agent: ${w}`);
|
|
254
265
|
}
|
|
255
|
-
async function
|
|
256
|
-
var
|
|
266
|
+
async function u(s) {
|
|
267
|
+
var w, C;
|
|
257
268
|
try {
|
|
258
|
-
if (
|
|
259
|
-
const
|
|
260
|
-
return !
|
|
269
|
+
if (s.taskKind === "extend") {
|
|
270
|
+
const k = N(e, "papercamp", "ideas"), { entries: W } = await Tt(k, N(e, "papercamp", "ideas.md")), z = W.find((F) => F.id === s.ideaId);
|
|
271
|
+
return !z || s.ideaBodyBaseline === void 0 ? null : z.body !== s.ideaBodyBaseline;
|
|
261
272
|
}
|
|
262
|
-
const
|
|
263
|
-
if (
|
|
264
|
-
return
|
|
265
|
-
const
|
|
266
|
-
return
|
|
273
|
+
const O = N(e, "papercamp", "plans"), { entries: A } = await ct(O, N(e, "papercamp", "plans.md"));
|
|
274
|
+
if (s.ideaId !== void 0)
|
|
275
|
+
return A.some((k) => k.idea === s.ideaId);
|
|
276
|
+
const H = A.find((k) => k.id === s.planId) ?? A.find((k) => k.title === s.planTitle);
|
|
277
|
+
return H ? s.phaseIndex !== void 0 ? ((w = H.phases[s.phaseIndex]) == null ? void 0 : w.done) ?? null : s.planBaseline ? H.phases.length > s.planBaseline.phases || (((C = H.log) == null ? void 0 : C.length) ?? 0) > s.planBaseline.log : null : null;
|
|
267
278
|
} catch {
|
|
268
279
|
return null;
|
|
269
280
|
}
|
|
270
281
|
}
|
|
271
|
-
function
|
|
272
|
-
|
|
273
|
-
if (
|
|
274
|
-
const
|
|
275
|
-
|
|
282
|
+
function t(s, w) {
|
|
283
|
+
$(s, w ? "error" : "done"), !w && u(s).then((C) => {
|
|
284
|
+
if (n === s && C === !1) {
|
|
285
|
+
const O = s.taskKind === "extend" ? `Warning: agent finished but the idea body for ${s.ideaId} did not change — verify manually` : s.ideaId !== void 0 ? `Warning: agent finished but no plan linking idea: ${s.ideaId} appeared in papercamp/plans/ — verify manually` : s.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";
|
|
286
|
+
m(s, O);
|
|
276
287
|
}
|
|
288
|
+
n === s && s.taskKind === "audit" && s.planId && C === !0 && (a == null || a(s.planId, 0).catch(() => {
|
|
289
|
+
}));
|
|
277
290
|
});
|
|
278
291
|
}
|
|
279
|
-
function
|
|
280
|
-
if (!
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
}),
|
|
286
|
-
|
|
287
|
-
}),
|
|
288
|
-
|
|
292
|
+
function J(s) {
|
|
293
|
+
if (!s.proc.stdout) return;
|
|
294
|
+
jt({ input: s.proc.stdout }).on("line", (C) => {
|
|
295
|
+
if (n !== s || !C.trim()) return;
|
|
296
|
+
const O = s.adapter.parseLine(C);
|
|
297
|
+
O && (m(s, O.text), O.done && t(s, !!O.error));
|
|
298
|
+
}), s.proc.on("close", (C) => {
|
|
299
|
+
n === s && (s.status === "starting" || s.status === "running" ? t(s, C !== 0) : s.status === "stopping" && $(s, "done"));
|
|
300
|
+
}), s.proc.on("error", (C) => {
|
|
301
|
+
n === s && (m(s, `Failed to spawn agent: ${C.message}`), $(s, "error"));
|
|
289
302
|
});
|
|
290
303
|
}
|
|
291
|
-
function
|
|
292
|
-
return
|
|
293
|
-
cwd:
|
|
304
|
+
function g(s, w) {
|
|
305
|
+
return V(s.command, w, {
|
|
306
|
+
cwd: e,
|
|
294
307
|
stdio: ["ignore", "pipe", "pipe"]
|
|
295
308
|
});
|
|
296
309
|
}
|
|
297
|
-
function
|
|
298
|
-
return
|
|
310
|
+
function j() {
|
|
311
|
+
return n !== null && n.status !== "done" && n.status !== "error";
|
|
299
312
|
}
|
|
300
|
-
function
|
|
301
|
-
if (
|
|
313
|
+
function r(s, w, C) {
|
|
314
|
+
if (j())
|
|
302
315
|
return { ok: !1, error: "An agent task is already running" };
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
316
|
+
const O = Nt(e), {
|
|
317
|
+
id: A,
|
|
318
|
+
adapter: H,
|
|
319
|
+
model: k,
|
|
320
|
+
effort: W
|
|
321
|
+
} = St({
|
|
322
|
+
agentId: s.agentOverride,
|
|
323
|
+
defaultAgents: O,
|
|
324
|
+
taskKind: C.taskKind
|
|
325
|
+
}), z = g(H, H.buildArgs(w, { model: k, effort: W })), F = {
|
|
326
|
+
planTitle: s.planTitle,
|
|
327
|
+
planId: s.planId,
|
|
310
328
|
status: "starting",
|
|
311
|
-
agentId:
|
|
312
|
-
adapter:
|
|
313
|
-
proc:
|
|
329
|
+
agentId: A,
|
|
330
|
+
adapter: H,
|
|
331
|
+
proc: z,
|
|
314
332
|
lines: [],
|
|
315
|
-
...
|
|
333
|
+
...C
|
|
316
334
|
};
|
|
317
|
-
return
|
|
335
|
+
return n = F, J(F), $(F, "running"), { ok: !0 };
|
|
318
336
|
}
|
|
319
|
-
function d(
|
|
320
|
-
if (
|
|
337
|
+
function d(s, w) {
|
|
338
|
+
if (j())
|
|
321
339
|
return { ok: !1, error: "An agent task is already running" };
|
|
322
|
-
const
|
|
323
|
-
if (!
|
|
340
|
+
const C = s.phases[w];
|
|
341
|
+
if (!C)
|
|
324
342
|
return { ok: !1, error: "Phase not found" };
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
return
|
|
343
|
+
i(s);
|
|
344
|
+
const O = Se(s, C, w);
|
|
345
|
+
return r({ planTitle: s.title, planId: s.id, agentOverride: s.agent }, O, {
|
|
328
346
|
taskKind: "phase",
|
|
329
|
-
phaseIndex:
|
|
347
|
+
phaseIndex: w
|
|
330
348
|
});
|
|
331
349
|
}
|
|
332
|
-
function
|
|
333
|
-
var
|
|
334
|
-
return
|
|
350
|
+
function y(s, w) {
|
|
351
|
+
var C;
|
|
352
|
+
return r({ planTitle: s.title, planId: s.id, agentOverride: s.agent }, w, {
|
|
335
353
|
taskKind: "audit",
|
|
336
|
-
planBaseline: { phases:
|
|
354
|
+
planBaseline: { phases: s.phases.length, log: ((C = s.log) == null ? void 0 : C.length) ?? 0 }
|
|
337
355
|
});
|
|
338
356
|
}
|
|
339
|
-
function
|
|
340
|
-
return
|
|
357
|
+
function l(s, w) {
|
|
358
|
+
return s.id ? r({ planTitle: `Draft plan for ${s.id}` }, w, {
|
|
341
359
|
taskKind: "draft",
|
|
342
|
-
ideaId:
|
|
360
|
+
ideaId: s.id
|
|
343
361
|
}) : { ok: !1, error: "Idea has no id to link a drafted plan back to" };
|
|
344
362
|
}
|
|
345
|
-
function
|
|
346
|
-
return
|
|
363
|
+
function T(s, w) {
|
|
364
|
+
return s.id ? r({ planTitle: `Extend ${s.id}` }, w, {
|
|
347
365
|
taskKind: "extend",
|
|
348
|
-
ideaId:
|
|
349
|
-
ideaBodyBaseline:
|
|
366
|
+
ideaId: s.id,
|
|
367
|
+
ideaBodyBaseline: s.body
|
|
350
368
|
}) : { ok: !1, error: "Idea has no id to extend" };
|
|
351
369
|
}
|
|
352
|
-
function
|
|
353
|
-
|
|
370
|
+
async function c(s, w) {
|
|
371
|
+
const C = N(s, `${w}.md`);
|
|
372
|
+
try {
|
|
373
|
+
return await it(C), C;
|
|
374
|
+
} catch {
|
|
375
|
+
}
|
|
376
|
+
const O = N(s, "archive", `${w}.md`);
|
|
377
|
+
try {
|
|
378
|
+
return await it(O), O;
|
|
379
|
+
} catch {
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
function o() {
|
|
384
|
+
if (j())
|
|
385
|
+
return { ok: !1, error: "An agent task is already running" };
|
|
386
|
+
const s = Nt(e), {
|
|
387
|
+
id: w,
|
|
388
|
+
adapter: C,
|
|
389
|
+
model: O,
|
|
390
|
+
effort: A
|
|
391
|
+
} = St({ defaultAgents: s, taskKind: "audit" }), H = V("sh", ["-c", "exit 0"], {
|
|
392
|
+
cwd: e,
|
|
393
|
+
stdio: "ignore"
|
|
394
|
+
}), k = {
|
|
395
|
+
taskKind: "batch-audit",
|
|
396
|
+
planTitle: "Batch audit",
|
|
397
|
+
status: "starting",
|
|
398
|
+
agentId: w,
|
|
399
|
+
adapter: C,
|
|
400
|
+
proc: H,
|
|
401
|
+
lines: []
|
|
402
|
+
};
|
|
403
|
+
return n = k, $(k, "running"), (async () => {
|
|
404
|
+
var W, z;
|
|
405
|
+
try {
|
|
406
|
+
const F = N(e, "papercamp", "plans"), { entries: K } = await Q(F), R = K.filter((D) => D.status === "review" || D.status === "done");
|
|
407
|
+
if (R.length === 0) {
|
|
408
|
+
n === k && (m(k, 'No plans with status "review" or "done" to audit.'), $(k, "done"));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
m(k, `Auditing ${R.length} plan(s)…`);
|
|
412
|
+
let pt = 0, X = 0, ut = 0;
|
|
413
|
+
for (const D of R) {
|
|
414
|
+
if (n !== k || k.status === "stopping") break;
|
|
415
|
+
if (!D.id) {
|
|
416
|
+
X++;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const ot = await c(F, D.id);
|
|
420
|
+
if (!ot) {
|
|
421
|
+
X++;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (D.audited) {
|
|
425
|
+
const _ = await it(ot).catch(() => null), G = _ ? _.mtime.toISOString().slice(0, 10) : null;
|
|
426
|
+
if (G && D.audited >= G) {
|
|
427
|
+
m(k, `[skip] ${D.id} — up to date`), X++;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
m(k, `[audit] ${D.id} ${D.title}`);
|
|
432
|
+
const gt = Mt(D), Y = V(C.command, C.buildArgs(gt, { model: O, effort: A }), {
|
|
433
|
+
cwd: e,
|
|
434
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
435
|
+
});
|
|
436
|
+
k.proc = Y, Y.stdout && jt({ input: Y.stdout }).on("line", (G) => {
|
|
437
|
+
if (n !== k) return;
|
|
438
|
+
const L = C.parseLine(G);
|
|
439
|
+
L != null && L.text && L.text !== "Agent is working…" && m(k, ` ${L.text}`);
|
|
440
|
+
}), (W = Y.stderr) == null || W.on("data", () => {
|
|
441
|
+
});
|
|
442
|
+
const mt = await new Promise((_) => {
|
|
443
|
+
Y.on("close", (G) => _(G === 0)), Y.on("error", () => _(!1));
|
|
444
|
+
});
|
|
445
|
+
if (n !== k) return;
|
|
446
|
+
if (mt) {
|
|
447
|
+
let _ = 0;
|
|
448
|
+
try {
|
|
449
|
+
const G = await U(ot, "utf-8"), rt = ((z = dt(G).entries[0]) == null ? void 0 : z.phases.length) ?? D.phases.length;
|
|
450
|
+
_ = Math.max(0, rt - D.phases.length);
|
|
451
|
+
} catch {
|
|
452
|
+
}
|
|
453
|
+
await (a == null ? void 0 : a(D.id, _)), pt++, m(
|
|
454
|
+
k,
|
|
455
|
+
_ > 0 ? `[done] ${D.id} — ${_} gap phase${_ === 1 ? "" : "s"} added` : `[done] ${D.id}`
|
|
456
|
+
);
|
|
457
|
+
} else
|
|
458
|
+
ut++, m(k, `[fail] ${D.id} — agent error`);
|
|
459
|
+
}
|
|
460
|
+
if (n !== k) return;
|
|
461
|
+
if (k.status === "stopping") {
|
|
462
|
+
$(k, "done");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
m(k, `Audit complete — ${pt} audited, ${X} skipped, ${ut} failed`), $(k, ut > 0 ? "error" : "done");
|
|
466
|
+
} catch (F) {
|
|
467
|
+
n === k && (m(k, `Batch audit failed: ${F.message}`), $(k, "error"));
|
|
468
|
+
}
|
|
469
|
+
})(), { ok: !0 };
|
|
470
|
+
}
|
|
471
|
+
const f = 6e4, S = 10 * 1024 * 1024;
|
|
472
|
+
function b(s) {
|
|
473
|
+
if (j())
|
|
474
|
+
return Promise.reject(new Error("An agent task is already running"));
|
|
475
|
+
if (Buffer.byteLength(s, "utf-8") > S)
|
|
476
|
+
return Promise.reject(new Error("Commit suggestion prompt exceeds the 10MB stdin limit"));
|
|
477
|
+
const w = Nt(e), {
|
|
478
|
+
id: C,
|
|
479
|
+
adapter: O,
|
|
480
|
+
model: A,
|
|
481
|
+
effort: H
|
|
482
|
+
} = St({
|
|
483
|
+
defaultAgents: w,
|
|
484
|
+
taskKind: "commit-suggest"
|
|
485
|
+
}), k = C === "claude-code", W = k ? ["-p", "--output-format", "json"] : ["run", "--format", "json"];
|
|
486
|
+
return A && W.push(k ? "--model" : "-m", A), H && W.push(k ? "--effort" : "--variant", H), new Promise((z, F) => {
|
|
487
|
+
var gt, Y, mt, _, G;
|
|
488
|
+
const K = V(O.command, W, {
|
|
489
|
+
cwd: e,
|
|
490
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
491
|
+
}), R = {
|
|
492
|
+
taskKind: "commit-suggest",
|
|
493
|
+
planTitle: "Suggest commit message",
|
|
494
|
+
status: "starting",
|
|
495
|
+
agentId: C,
|
|
496
|
+
adapter: O,
|
|
497
|
+
proc: K,
|
|
498
|
+
lines: []
|
|
499
|
+
};
|
|
500
|
+
n = R, $(R, "running");
|
|
501
|
+
let pt = !1;
|
|
502
|
+
const X = (L) => {
|
|
503
|
+
pt || (pt = !0, clearTimeout(ut), L());
|
|
504
|
+
}, ut = setTimeout(() => {
|
|
505
|
+
X(() => {
|
|
506
|
+
n === R && (m(R, "Commit suggestion timed out"), $(R, "error"), K.killed || K.kill("SIGTERM")), F(new Error("Commit suggestion timed out"));
|
|
507
|
+
});
|
|
508
|
+
}, f);
|
|
509
|
+
(gt = K.stdin) == null || gt.on("error", () => {
|
|
510
|
+
}), (Y = K.stdin) == null || Y.write(s), (mt = K.stdin) == null || mt.end();
|
|
511
|
+
let D = "", ot = "";
|
|
512
|
+
(_ = K.stdout) == null || _.on("data", (L) => {
|
|
513
|
+
D += L.toString();
|
|
514
|
+
}), (G = K.stderr) == null || G.on("data", (L) => {
|
|
515
|
+
ot += L.toString();
|
|
516
|
+
}), K.on("close", (L) => {
|
|
517
|
+
n === R && X(() => {
|
|
518
|
+
if (R.status === "stopping") {
|
|
519
|
+
$(R, "done"), F(new Error("Stopped"));
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
if (L === 0) {
|
|
523
|
+
$(R, "done");
|
|
524
|
+
const rt = k ? D : D.split(`
|
|
525
|
+
`).map((Gt) => {
|
|
526
|
+
var At;
|
|
527
|
+
try {
|
|
528
|
+
const tt = JSON.parse(Gt);
|
|
529
|
+
if ((tt == null ? void 0 : tt.type) === "text" && ((At = tt == null ? void 0 : tt.part) != null && At.text)) return tt.part.text;
|
|
530
|
+
} catch {
|
|
531
|
+
}
|
|
532
|
+
return null;
|
|
533
|
+
}).filter(Boolean).join(`
|
|
534
|
+
`);
|
|
535
|
+
z(rt);
|
|
536
|
+
} else {
|
|
537
|
+
const rt = ot || `${O.command} exited with code ${L}`;
|
|
538
|
+
m(R, rt), $(R, "error"), F(new Error(rt));
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
}), K.on("error", (L) => {
|
|
542
|
+
n === R && X(() => {
|
|
543
|
+
m(R, `Failed to spawn agent: ${L.message}`), $(R, "error"), F(L);
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
function I() {
|
|
549
|
+
if (!n)
|
|
354
550
|
return { ok: !1, error: "No agent task running" };
|
|
355
|
-
const
|
|
356
|
-
return
|
|
357
|
-
|
|
551
|
+
const s = n;
|
|
552
|
+
return $(s, "stopping"), s.proc.killed || s.proc.kill("SIGTERM"), setTimeout(() => {
|
|
553
|
+
n === s && s.status === "stopping" && s.proc.kill("SIGKILL");
|
|
358
554
|
}, 5e3), { ok: !0 };
|
|
359
555
|
}
|
|
360
|
-
function
|
|
361
|
-
return
|
|
362
|
-
status:
|
|
363
|
-
taskKind:
|
|
364
|
-
planTitle:
|
|
365
|
-
planId:
|
|
366
|
-
phaseIndex:
|
|
367
|
-
ideaId:
|
|
368
|
-
agentId:
|
|
369
|
-
lines: [...
|
|
556
|
+
function v() {
|
|
557
|
+
return n ? {
|
|
558
|
+
status: n.status,
|
|
559
|
+
taskKind: n.taskKind,
|
|
560
|
+
planTitle: n.planTitle,
|
|
561
|
+
planId: n.planId,
|
|
562
|
+
phaseIndex: n.phaseIndex,
|
|
563
|
+
ideaId: n.ideaId,
|
|
564
|
+
agentId: n.agentId,
|
|
565
|
+
lines: [...n.lines]
|
|
370
566
|
} : null;
|
|
371
567
|
}
|
|
372
568
|
return {
|
|
373
569
|
start: d,
|
|
374
|
-
startForPlan:
|
|
375
|
-
startForIdea:
|
|
376
|
-
startForIdeaExtend:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
570
|
+
startForPlan: y,
|
|
571
|
+
startForIdea: l,
|
|
572
|
+
startForIdeaExtend: T,
|
|
573
|
+
startBatchAudit: o,
|
|
574
|
+
runCommitSuggest: b,
|
|
575
|
+
stop: I,
|
|
576
|
+
getStatus: v,
|
|
577
|
+
subscribe(s) {
|
|
578
|
+
p.add(s), s.on("close", () => p.delete(s));
|
|
381
579
|
},
|
|
382
580
|
killCurrent() {
|
|
383
|
-
|
|
581
|
+
n != null && n.proc && !n.proc.killed && n.proc.kill();
|
|
384
582
|
}
|
|
385
583
|
};
|
|
386
584
|
}
|
|
387
|
-
function
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
585
|
+
function $e(e, i) {
|
|
586
|
+
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.
|
|
587
|
+
|
|
588
|
+
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.${i ? `
|
|
589
|
+
This work belongs to plan ${i}. Do NOT put the plan id in the scope — instead end the message body with a \`Refs: ${i}\` line as its final line.` : ""}
|
|
590
|
+
|
|
591
|
+
Respond with ONLY a single JSON object, no prose, no code fences, no markdown — exactly this shape:
|
|
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}`;
|
|
596
|
+
}
|
|
597
|
+
async function Te(e, i, a) {
|
|
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;
|
|
602
|
+
try {
|
|
603
|
+
const t = JSON.parse(n);
|
|
604
|
+
typeof t.result == "string" && (h = t.result);
|
|
605
|
+
} catch {
|
|
606
|
+
}
|
|
607
|
+
const m = h.match(/\{[\s\S]*\}/);
|
|
608
|
+
if (!m) throw new Error("Agent did not return a parseable commit message");
|
|
609
|
+
const $ = JSON.parse(m[0]);
|
|
610
|
+
if (!$.title) throw new Error("Agent response missing a title");
|
|
611
|
+
let u = $.message ?? "";
|
|
612
|
+
return i && !/Refs:\s*\S/.test(u) && (u = u ? `${u}
|
|
613
|
+
|
|
614
|
+
Refs: ${i}` : `Refs: ${i}`), { title: $.title, message: u };
|
|
615
|
+
}
|
|
616
|
+
const Lt = [/(^|\/)\.env(\.|$)/i, /\.(pem|key|p12|crt)$/i], bt = (e) => `:(literal)${e}`;
|
|
617
|
+
function Oe(e) {
|
|
618
|
+
const i = /* @__PURE__ */ new Set();
|
|
619
|
+
function a(o) {
|
|
620
|
+
const f = `data: ${JSON.stringify(o)}
|
|
391
621
|
|
|
392
622
|
`;
|
|
393
|
-
for (const
|
|
623
|
+
for (const S of i)
|
|
394
624
|
try {
|
|
395
|
-
|
|
625
|
+
S.write(f);
|
|
396
626
|
} catch {
|
|
397
|
-
|
|
627
|
+
i.delete(S);
|
|
398
628
|
}
|
|
399
629
|
}
|
|
400
|
-
function
|
|
401
|
-
const
|
|
402
|
-
for (const
|
|
630
|
+
function p(o) {
|
|
631
|
+
const f = [];
|
|
632
|
+
for (const S of o.split(`
|
|
403
633
|
`)) {
|
|
404
|
-
if (!
|
|
405
|
-
const
|
|
406
|
-
|
|
634
|
+
if (!S.trim()) continue;
|
|
635
|
+
const b = S[0] ?? " ", I = S[1] ?? " ", v = S.slice(3), s = v.split(" -> "), w = s.pop() ?? v;
|
|
636
|
+
f.push({
|
|
407
637
|
path: w,
|
|
408
|
-
status: `${
|
|
409
|
-
staged:
|
|
638
|
+
status: `${b}${I}`,
|
|
639
|
+
staged: b !== " " && b !== "?",
|
|
640
|
+
renameSource: (b === "R" || b === "C") && s.length > 0 ? s[0] : void 0
|
|
410
641
|
});
|
|
411
642
|
}
|
|
412
|
-
return
|
|
643
|
+
return f;
|
|
413
644
|
}
|
|
414
|
-
function
|
|
415
|
-
return new Promise((
|
|
416
|
-
var
|
|
417
|
-
const
|
|
418
|
-
cwd:
|
|
645
|
+
function n(o) {
|
|
646
|
+
return new Promise((f, S) => {
|
|
647
|
+
var s, w;
|
|
648
|
+
const b = V("git", o, {
|
|
649
|
+
cwd: e,
|
|
419
650
|
stdio: ["ignore", "pipe", "pipe"]
|
|
420
651
|
});
|
|
421
|
-
let
|
|
422
|
-
(
|
|
423
|
-
|
|
424
|
-
}), (
|
|
425
|
-
|
|
426
|
-
}),
|
|
427
|
-
|
|
428
|
-
}),
|
|
652
|
+
let I = "", v = "";
|
|
653
|
+
(s = b.stdout) == null || s.on("data", (C) => {
|
|
654
|
+
I += C.toString();
|
|
655
|
+
}), (w = b.stderr) == null || w.on("data", (C) => {
|
|
656
|
+
v += C.toString();
|
|
657
|
+
}), b.on("close", (C) => {
|
|
658
|
+
C === 0 ? f(I) : S(new Error(v || `git ${o[0]} exited with code ${C}`));
|
|
659
|
+
}), b.on("error", S);
|
|
429
660
|
});
|
|
430
661
|
}
|
|
431
|
-
function
|
|
432
|
-
return
|
|
662
|
+
function h() {
|
|
663
|
+
return n(["status", "--porcelain=v1"]).then(p);
|
|
433
664
|
}
|
|
434
|
-
async function
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
665
|
+
async function m(o, f, S) {
|
|
666
|
+
if (o.length > 0) {
|
|
667
|
+
const I = await h(), v = new Map(I.map((w) => [w.path, w.status])), s = o.filter((w) => {
|
|
668
|
+
const C = v.get(w);
|
|
669
|
+
return C === void 0 || C[1] !== " ";
|
|
670
|
+
});
|
|
671
|
+
s.length > 0 && await n(["add", "--", ...s.map(bt)]);
|
|
672
|
+
}
|
|
673
|
+
const b = ["commit", "-m", f];
|
|
674
|
+
S && b.push("-m", S), o.length > 0 && b.push("--", ...o.map(bt)), await n(b);
|
|
438
675
|
}
|
|
439
|
-
function
|
|
440
|
-
if (!
|
|
441
|
-
const
|
|
442
|
-
if (
|
|
676
|
+
function $(o) {
|
|
677
|
+
if (!o.kind || !o.id) return;
|
|
678
|
+
const f = o.kind.toLowerCase(), S = o.id.toLowerCase(), b = o.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), I = `${f}/${S}-${b}`, v = ht("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: e });
|
|
679
|
+
if (v.status !== 0)
|
|
443
680
|
throw new Error(
|
|
444
|
-
|
|
681
|
+
v.stderr.toString().trim() || "Unable to read current git branch"
|
|
445
682
|
);
|
|
446
|
-
if (
|
|
447
|
-
const
|
|
448
|
-
if (
|
|
449
|
-
const
|
|
450
|
-
if (
|
|
683
|
+
if (v.stdout.toString().trim() === I) return;
|
|
684
|
+
const w = ht("git", ["checkout", "-b", I, "main"], { cwd: e });
|
|
685
|
+
if (w.status !== 0) {
|
|
686
|
+
const C = ht("git", ["checkout", I], { cwd: e });
|
|
687
|
+
if (C.status !== 0)
|
|
451
688
|
throw new Error(
|
|
452
|
-
|
|
689
|
+
C.stderr.toString().trim() || w.stderr.toString().trim() || `Unable to check out ${I}`
|
|
453
690
|
);
|
|
454
691
|
}
|
|
455
692
|
}
|
|
456
|
-
async function
|
|
693
|
+
async function u() {
|
|
457
694
|
try {
|
|
458
|
-
await
|
|
695
|
+
await h(), a({
|
|
459
696
|
message: "Working tree status updated",
|
|
460
697
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
461
698
|
});
|
|
462
699
|
} catch {
|
|
463
700
|
}
|
|
464
701
|
}
|
|
465
|
-
const
|
|
466
|
-
let
|
|
702
|
+
const t = N(e, ".git");
|
|
703
|
+
let J = null;
|
|
467
704
|
try {
|
|
468
|
-
|
|
469
|
-
|
|
705
|
+
Ct(t, { recursive: !0 }, (o, f) => {
|
|
706
|
+
f === "index" && (J && clearTimeout(J), J = setTimeout(u, 500));
|
|
470
707
|
});
|
|
471
708
|
} catch {
|
|
472
709
|
}
|
|
473
|
-
const
|
|
474
|
-
let
|
|
710
|
+
const g = N(e, "src");
|
|
711
|
+
let j = null;
|
|
475
712
|
try {
|
|
476
|
-
|
|
477
|
-
|
|
713
|
+
Ct(g, { recursive: !0 }, () => {
|
|
714
|
+
j && clearTimeout(j), j = setTimeout(u, 500);
|
|
478
715
|
});
|
|
479
716
|
} catch {
|
|
480
717
|
}
|
|
481
|
-
function
|
|
482
|
-
|
|
718
|
+
async function r() {
|
|
719
|
+
try {
|
|
720
|
+
return await n(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]), !0;
|
|
721
|
+
} catch {
|
|
722
|
+
return !1;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
async function d() {
|
|
726
|
+
try {
|
|
727
|
+
const o = await r() ? ["rev-list", "--count", "@{u}..HEAD"] : ["rev-list", "--count", "HEAD", "--not", "--remotes"], f = await n(o);
|
|
728
|
+
return Number.parseInt(f.trim(), 10) || 0;
|
|
729
|
+
} catch {
|
|
730
|
+
return 0;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async function y() {
|
|
734
|
+
await r() ? await n(["push"]) : await n(["push", "--set-upstream", "origin", l()]);
|
|
735
|
+
}
|
|
736
|
+
function l() {
|
|
737
|
+
return ht("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: e }).stdout.toString().trim();
|
|
483
738
|
}
|
|
484
|
-
function
|
|
485
|
-
const
|
|
486
|
-
return
|
|
739
|
+
function T() {
|
|
740
|
+
const f = l().match(/^[a-z]+\/([a-z]+-\d+)-/);
|
|
741
|
+
return f ? f[1].toUpperCase() : null;
|
|
742
|
+
}
|
|
743
|
+
async function c(o, f = 12e3) {
|
|
744
|
+
if (o.length === 0) return "";
|
|
745
|
+
const S = o.find((O) => Lt.some((A) => A.test(O)));
|
|
746
|
+
if (S)
|
|
747
|
+
throw new Error(`Refusing to send sensitive file "${S}" to commit suggestion`);
|
|
748
|
+
const b = await h(), I = new Set(b.filter((O) => O.status === "??").map((O) => O.path)), v = new Map(
|
|
749
|
+
b.filter((O) => !!O.renameSource).map((O) => [O.path, O.renameSource])
|
|
750
|
+
), s = o.filter((O) => {
|
|
751
|
+
if (I.has(O)) return !1;
|
|
752
|
+
const A = v.get(O);
|
|
753
|
+
return !(A && Lt.some((H) => H.test(A)));
|
|
754
|
+
}), w = [];
|
|
755
|
+
if (s.length > 0) {
|
|
756
|
+
const O = await n([
|
|
757
|
+
"diff",
|
|
758
|
+
"HEAD",
|
|
759
|
+
"--",
|
|
760
|
+
...s.map(bt)
|
|
761
|
+
]).catch(() => "");
|
|
762
|
+
O && w.push(O);
|
|
763
|
+
}
|
|
764
|
+
for (const O of o.filter((A) => I.has(A))) {
|
|
765
|
+
const A = N(e, O), H = await Ut(A).catch(() => null);
|
|
766
|
+
if (!H) continue;
|
|
767
|
+
if (H.isSymbolicLink()) {
|
|
768
|
+
w.push(`--- /dev/null
|
|
769
|
+
+++ b/${O}
|
|
770
|
+
(new file omitted: symlink)`);
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
if (H.size > f) {
|
|
774
|
+
w.push(`--- /dev/null
|
|
775
|
+
+++ b/${O}
|
|
776
|
+
(new file omitted: exceeds diff size cap)`);
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
const k = await U(A, "utf-8").catch(() => "");
|
|
780
|
+
k && w.push(`--- /dev/null
|
|
781
|
+
+++ b/${O}
|
|
782
|
+
(new file)
|
|
783
|
+
${k}`);
|
|
784
|
+
}
|
|
785
|
+
const C = w.join(`
|
|
786
|
+
|
|
787
|
+
`);
|
|
788
|
+
return C.length > f ? `${C.slice(0, f)}
|
|
789
|
+
... (truncated)` : C;
|
|
487
790
|
}
|
|
488
791
|
return {
|
|
489
792
|
async getStatus() {
|
|
490
|
-
return
|
|
793
|
+
return h();
|
|
491
794
|
},
|
|
492
|
-
getCurrentBranch:
|
|
493
|
-
commit:
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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));
|
|
498
804
|
}
|
|
499
805
|
};
|
|
500
806
|
}
|
|
501
|
-
const
|
|
807
|
+
const Ne = {
|
|
502
808
|
lint: "npx biome lint .",
|
|
503
809
|
format: "npx biome format .",
|
|
504
810
|
test: "npx vitest run"
|
|
505
811
|
};
|
|
506
|
-
function
|
|
507
|
-
const
|
|
812
|
+
function be(e) {
|
|
813
|
+
const i = /* @__PURE__ */ new Set(), a = {
|
|
508
814
|
lint: { status: "stale", lastRun: null, output: "" },
|
|
509
815
|
format: { status: "stale", lastRun: null, output: "" },
|
|
510
816
|
test: { status: "stale", lastRun: null, output: "" }
|
|
511
|
-
},
|
|
512
|
-
function
|
|
513
|
-
const
|
|
817
|
+
}, p = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set();
|
|
818
|
+
function h(g) {
|
|
819
|
+
const j = `data: ${JSON.stringify(g)}
|
|
514
820
|
|
|
515
821
|
`;
|
|
516
|
-
for (const
|
|
822
|
+
for (const r of i)
|
|
517
823
|
try {
|
|
518
|
-
|
|
824
|
+
r.write(j);
|
|
519
825
|
} catch {
|
|
520
|
-
|
|
826
|
+
i.delete(r);
|
|
521
827
|
}
|
|
522
828
|
}
|
|
523
|
-
function
|
|
524
|
-
|
|
525
|
-
message: `${
|
|
526
|
-
timestamp:
|
|
527
|
-
}),
|
|
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));
|
|
528
834
|
}
|
|
529
|
-
function
|
|
530
|
-
var l,
|
|
531
|
-
if (
|
|
532
|
-
|
|
835
|
+
function $(g) {
|
|
836
|
+
var l, T;
|
|
837
|
+
if (p.has(g)) {
|
|
838
|
+
n.add(g);
|
|
533
839
|
return;
|
|
534
840
|
}
|
|
535
|
-
|
|
536
|
-
const
|
|
537
|
-
cwd:
|
|
841
|
+
p.add(g), m(g, "running", "");
|
|
842
|
+
const j = Ne[g], r = V(j, {
|
|
843
|
+
cwd: e,
|
|
538
844
|
stdio: ["ignore", "pipe", "pipe"],
|
|
539
845
|
shell: !0
|
|
540
846
|
});
|
|
541
|
-
let
|
|
542
|
-
(l =
|
|
543
|
-
|
|
544
|
-
}), (
|
|
545
|
-
|
|
546
|
-
}),
|
|
547
|
-
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
}),
|
|
551
|
-
|
|
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}`);
|
|
552
858
|
});
|
|
553
859
|
}
|
|
554
|
-
function
|
|
555
|
-
if (
|
|
556
|
-
|
|
557
|
-
const
|
|
558
|
-
cwd:
|
|
860
|
+
function u() {
|
|
861
|
+
if (p.has("lint") || p.has("format")) return;
|
|
862
|
+
m("lint", "running", "Applying automatic fixes…"), m("format", "running", "Applying automatic fixes…");
|
|
863
|
+
const g = V("npx biome check . --write", {
|
|
864
|
+
cwd: e,
|
|
559
865
|
stdio: ["ignore", "pipe", "pipe"],
|
|
560
866
|
shell: !0
|
|
561
867
|
});
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}),
|
|
565
|
-
const
|
|
566
|
-
|
|
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);
|
|
567
873
|
});
|
|
568
874
|
}
|
|
569
|
-
const
|
|
570
|
-
let
|
|
875
|
+
const t = N(e, "src");
|
|
876
|
+
let J = null;
|
|
571
877
|
try {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
878
|
+
Ct(t, { recursive: !0 }, () => {
|
|
879
|
+
J && clearTimeout(J), J = setTimeout(() => {
|
|
880
|
+
$("lint"), $("format");
|
|
575
881
|
}, 1e3);
|
|
576
882
|
});
|
|
577
883
|
} catch {
|
|
@@ -579,51 +885,51 @@ function Rt(n) {
|
|
|
579
885
|
return {
|
|
580
886
|
getStatus() {
|
|
581
887
|
return {
|
|
582
|
-
lint: { ...
|
|
583
|
-
format: { ...
|
|
584
|
-
test: { ...
|
|
888
|
+
lint: { ...a.lint },
|
|
889
|
+
format: { ...a.format },
|
|
890
|
+
test: { ...a.test }
|
|
585
891
|
};
|
|
586
892
|
},
|
|
587
|
-
runCheck:
|
|
588
|
-
runQualityFix:
|
|
589
|
-
subscribe(
|
|
590
|
-
|
|
591
|
-
for (const
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
`data: ${JSON.stringify({ message: `${
|
|
893
|
+
runCheck: $,
|
|
894
|
+
runQualityFix: u,
|
|
895
|
+
subscribe(g) {
|
|
896
|
+
i.add(g);
|
|
897
|
+
for (const j of ["lint", "format", "test"]) {
|
|
898
|
+
const r = a[j];
|
|
899
|
+
r.status !== "stale" && g.write(
|
|
900
|
+
`data: ${JSON.stringify({ message: `${j}: ${r.status}`, timestamp: r.lastRun })}
|
|
595
901
|
|
|
596
902
|
`
|
|
597
903
|
);
|
|
598
904
|
}
|
|
599
|
-
|
|
905
|
+
g.on("close", () => i.delete(g));
|
|
600
906
|
}
|
|
601
907
|
};
|
|
602
908
|
}
|
|
603
|
-
async function
|
|
909
|
+
async function P(e) {
|
|
604
910
|
try {
|
|
605
|
-
return await
|
|
606
|
-
} catch (
|
|
607
|
-
if (
|
|
608
|
-
throw
|
|
911
|
+
return await U(e, "utf-8");
|
|
912
|
+
} catch (i) {
|
|
913
|
+
if (i.code === "ENOENT") return "";
|
|
914
|
+
throw i;
|
|
609
915
|
}
|
|
610
916
|
}
|
|
611
|
-
async function
|
|
917
|
+
async function ft(e) {
|
|
612
918
|
try {
|
|
613
|
-
return await
|
|
919
|
+
return await it(e), !0;
|
|
614
920
|
} catch {
|
|
615
921
|
return !1;
|
|
616
922
|
}
|
|
617
923
|
}
|
|
618
|
-
async function
|
|
619
|
-
return new Promise((
|
|
620
|
-
let
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}),
|
|
924
|
+
async function M(e) {
|
|
925
|
+
return new Promise((i, a) => {
|
|
926
|
+
let p = "";
|
|
927
|
+
e.on("data", (n) => {
|
|
928
|
+
p += n;
|
|
929
|
+
}), e.on("end", () => i(p)), e.on("error", a);
|
|
624
930
|
});
|
|
625
931
|
}
|
|
626
|
-
const
|
|
932
|
+
const x = (e, i) => N(e, "papercamp", i), je = [
|
|
627
933
|
"biome.json",
|
|
628
934
|
"tsconfig.json",
|
|
629
935
|
"tailwind.config.ts",
|
|
@@ -632,22 +938,30 @@ const J = (n, c) => O(n, "papercamp", c), Lt = [
|
|
|
632
938
|
"postcss.config.js",
|
|
633
939
|
"package.json"
|
|
634
940
|
];
|
|
635
|
-
async function
|
|
636
|
-
const
|
|
637
|
-
if (!
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
const { entries: y } = k(u), r = y.find((t) => t.id === s);
|
|
641
|
-
return !r || r.status === "done" || r.status === "dropped" ? null : `Finish \`${s}\` — ${r.title} — before starting another plan`;
|
|
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`;
|
|
642
946
|
}
|
|
643
|
-
|
|
947
|
+
async function yt(e) {
|
|
948
|
+
const i = x(e, "plans"), a = x(e, "ideas"), [p, n] = await Promise.all([
|
|
949
|
+
ct(i, x(e, "plans.md")),
|
|
950
|
+
Tt(a, x(e, "ideas.md"))
|
|
951
|
+
]), h = Kt(n.entries, p.entries);
|
|
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
|
+
]);
|
|
956
|
+
}
|
|
957
|
+
const ke = [
|
|
644
958
|
{
|
|
645
959
|
path: "/api/package-name",
|
|
646
|
-
handler: async (
|
|
647
|
-
const
|
|
648
|
-
if (!
|
|
960
|
+
handler: async (e) => {
|
|
961
|
+
const i = await P(N(e, "package.json"));
|
|
962
|
+
if (!i) return null;
|
|
649
963
|
try {
|
|
650
|
-
return JSON.parse(
|
|
964
|
+
return JSON.parse(i).name ?? null;
|
|
651
965
|
} catch {
|
|
652
966
|
return null;
|
|
653
967
|
}
|
|
@@ -655,61 +969,66 @@ const _t = [
|
|
|
655
969
|
},
|
|
656
970
|
{
|
|
657
971
|
path: "/api/plans",
|
|
658
|
-
handler: async (
|
|
972
|
+
handler: async (e) => ct(x(e, "plans"), x(e, "plans.md"))
|
|
659
973
|
},
|
|
660
974
|
{
|
|
661
975
|
path: "/api/progress",
|
|
662
|
-
handler: async (
|
|
663
|
-
entries:
|
|
976
|
+
handler: async (e) => ({
|
|
977
|
+
entries: ne(await P(x(e, "progress.md")))
|
|
664
978
|
})
|
|
665
979
|
},
|
|
666
980
|
{
|
|
667
981
|
path: "/api/decisions",
|
|
668
|
-
handler: async (
|
|
982
|
+
handler: async (e) => Dt(await P(x(e, "decisions.md")))
|
|
669
983
|
},
|
|
670
984
|
{
|
|
671
985
|
path: "/api/open-questions",
|
|
672
|
-
handler: async (
|
|
986
|
+
handler: async (e) => It(await P(x(e, "open-questions.md")))
|
|
673
987
|
},
|
|
674
988
|
{
|
|
675
989
|
path: "/api/ideas",
|
|
676
|
-
handler: async (
|
|
677
|
-
content: await N(J(n, "ideas.md"))
|
|
678
|
-
})
|
|
990
|
+
handler: async (e) => Tt(x(e, "ideas"), x(e, "ideas.md"))
|
|
679
991
|
},
|
|
680
992
|
{
|
|
681
993
|
path: "/api/consistency",
|
|
682
|
-
handler: async (
|
|
683
|
-
const [
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
]),
|
|
688
|
-
return
|
|
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);
|
|
689
1001
|
}
|
|
690
1002
|
},
|
|
691
1003
|
{
|
|
692
1004
|
path: "/api/config",
|
|
693
|
-
handler: async (
|
|
694
|
-
const
|
|
695
|
-
|
|
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;
|
|
696
1015
|
}
|
|
697
1016
|
},
|
|
698
1017
|
{
|
|
699
1018
|
path: "/api/docs",
|
|
700
|
-
handler: async (
|
|
701
|
-
const
|
|
702
|
-
for (const
|
|
703
|
-
const
|
|
704
|
-
|
|
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 });
|
|
705
1024
|
}
|
|
706
|
-
return { files:
|
|
1025
|
+
return { files: a };
|
|
707
1026
|
}
|
|
708
1027
|
},
|
|
709
1028
|
{
|
|
710
1029
|
path: "/api/configs",
|
|
711
|
-
handler: async (
|
|
712
|
-
const
|
|
1030
|
+
handler: async (e) => {
|
|
1031
|
+
const i = [
|
|
713
1032
|
"biome.json",
|
|
714
1033
|
"tsconfig.json",
|
|
715
1034
|
"tailwind.config.ts",
|
|
@@ -717,131 +1036,250 @@ const _t = [
|
|
|
717
1036
|
"vite.app.config.ts",
|
|
718
1037
|
"postcss.config.js",
|
|
719
1038
|
"package.json"
|
|
720
|
-
],
|
|
721
|
-
for (const
|
|
722
|
-
await N(
|
|
723
|
-
return { files:
|
|
1039
|
+
], a = [];
|
|
1040
|
+
for (const p of i)
|
|
1041
|
+
await P(N(e, p)) && a.push(p);
|
|
1042
|
+
return { files: a };
|
|
724
1043
|
}
|
|
725
1044
|
}
|
|
726
1045
|
];
|
|
727
|
-
function
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
1046
|
+
function ve(e) {
|
|
1047
|
+
const i = ye(e), a = Oe(e), p = be(e);
|
|
1048
|
+
async function n(u) {
|
|
1049
|
+
const t = x(e, "progress.md"), g = `## ${Z()}`, j = await P(t);
|
|
1050
|
+
if (j.startsWith(`${g}
|
|
1051
|
+
`))
|
|
1052
|
+
await E(
|
|
1053
|
+
t,
|
|
1054
|
+
`${g}
|
|
1055
|
+
- ${u}
|
|
1056
|
+
${j.slice(g.length + 1)}`,
|
|
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");
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
async function h(u, t) {
|
|
1071
|
+
const J = x(e, "plans"), g = N(J, `${u}.md`), j = N(J, "archive", `${u}.md`), r = await ft(g) ? g : await ft(j) ? j : null;
|
|
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
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
const m = Ce(e, (u) => a.ensureBranch(u), h), $ = async (u, t, J) => {
|
|
1102
|
+
const g = (u.url ?? "").split("?")[0];
|
|
1103
|
+
if (u.method === "DELETE" && g === "/api/plans") {
|
|
731
1104
|
try {
|
|
732
|
-
const
|
|
733
|
-
if (!(
|
|
1105
|
+
const d = new URL(u.url ?? "", `http://${u.headers.host ?? "localhost"}`).searchParams.get("title");
|
|
1106
|
+
if (!(d != null && d.trim())) {
|
|
734
1107
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "title is required" }));
|
|
735
1108
|
return;
|
|
736
1109
|
}
|
|
737
|
-
const
|
|
738
|
-
if (
|
|
739
|
-
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "plan not found" }));
|
|
1110
|
+
const y = x(e, "plans"), l = d.trim(), { entries: T } = await Q(y), c = T.find((f) => f.title === l || f.id === l);
|
|
1111
|
+
if (!(c != null && c.id)) {
|
|
1112
|
+
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "plan not found in per-file storage" }));
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const o = N(y, `${c.id}.md`);
|
|
1116
|
+
if (!await ft(o)) {
|
|
1117
|
+
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "plan file not found" }));
|
|
740
1118
|
return;
|
|
741
1119
|
}
|
|
742
|
-
await
|
|
743
|
-
} catch (
|
|
744
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1120
|
+
await Wt(o), await yt(e), t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1121
|
+
} catch (r) {
|
|
1122
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
745
1123
|
}
|
|
746
1124
|
return;
|
|
747
1125
|
}
|
|
748
|
-
if (
|
|
1126
|
+
if (u.method === "POST" && g === "/api/plans") {
|
|
749
1127
|
try {
|
|
750
|
-
const
|
|
751
|
-
if (!(
|
|
1128
|
+
const r = await M(u), { title: d, content: y, kind: l } = JSON.parse(r);
|
|
1129
|
+
if (!(d != null && d.trim())) {
|
|
752
1130
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "title is required" }));
|
|
753
1131
|
return;
|
|
754
1132
|
}
|
|
755
|
-
const
|
|
756
|
-
|
|
1133
|
+
const T = l && wt.includes(l) ? l : "feat", c = N(e, "papercamp", "config.json"), o = await Bt(c, T);
|
|
1134
|
+
if (!o) {
|
|
1135
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "could not assign plan ID" }));
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
const f = x(e, "plans");
|
|
1139
|
+
await q(f, { recursive: !0 });
|
|
1140
|
+
const S = at({
|
|
1141
|
+
id: o,
|
|
1142
|
+
title: d.trim(),
|
|
1143
|
+
kind: T,
|
|
757
1144
|
status: "idea",
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
created: B(),
|
|
761
|
-
body: d == null ? void 0 : d.trim()
|
|
1145
|
+
created: Z(),
|
|
1146
|
+
body: y == null ? void 0 : y.trim()
|
|
762
1147
|
});
|
|
763
|
-
await
|
|
764
|
-
|
|
765
|
-
|
|
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 }));
|
|
766
1152
|
}
|
|
767
1153
|
return;
|
|
768
1154
|
}
|
|
769
|
-
if (
|
|
1155
|
+
if (u.method === "PATCH" && g === "/api/plans") {
|
|
770
1156
|
try {
|
|
771
|
-
const
|
|
772
|
-
if (!(
|
|
1157
|
+
const d = new URL(u.url ?? "", `http://${u.headers.host ?? "localhost"}`).searchParams.get("title");
|
|
1158
|
+
if (!(d != null && d.trim())) {
|
|
773
1159
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "title is required" }));
|
|
774
1160
|
return;
|
|
775
1161
|
}
|
|
776
|
-
const
|
|
777
|
-
if (
|
|
1162
|
+
const y = await M(u), l = JSON.parse(y);
|
|
1163
|
+
if (l.agent && !Ot.includes(l.agent)) {
|
|
778
1164
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "agent must be a known agent id" }));
|
|
779
1165
|
return;
|
|
780
1166
|
}
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
const a = l.entries.map((g) => g.title === m ? (h = !0, {
|
|
784
|
-
...g,
|
|
785
|
-
...f.status !== void 0 && { status: f.status },
|
|
786
|
-
...f.phases !== void 0 && { phases: f.phases },
|
|
787
|
-
...f.log !== void 0 && { log: f.log },
|
|
788
|
-
...f.agent !== void 0 && { agent: f.agent ?? void 0 },
|
|
789
|
-
updated: B()
|
|
790
|
-
}) : f.status === "in-progress" && g.status === "in-progress" ? { ...g, status: "planned", updated: B() } : g);
|
|
791
|
-
if (!h) {
|
|
1167
|
+
const T = x(e, "plans"), { entries: c } = await Q(T), o = d.trim(), f = c.find((w) => w.title === o || w.id === o);
|
|
1168
|
+
if (!(f != null && f.id)) {
|
|
792
1169
|
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "plan not found" }));
|
|
793
1170
|
return;
|
|
794
1171
|
}
|
|
795
|
-
|
|
796
|
-
|
|
1172
|
+
const S = N(T, `${f.id}.md`), b = await P(S);
|
|
1173
|
+
if (!b) {
|
|
1174
|
+
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "plan file not found" }));
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const I = dt(b);
|
|
1178
|
+
if (I.entries.length === 0) {
|
|
1179
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "failed to parse plan file" }));
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
const v = {
|
|
1183
|
+
...I.entries[0],
|
|
1184
|
+
...l.status !== void 0 && { status: l.status },
|
|
1185
|
+
...l.phases !== void 0 && { phases: l.phases },
|
|
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);
|
|
797
1192
|
if (w) {
|
|
798
1193
|
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: w }));
|
|
799
1194
|
return;
|
|
800
1195
|
}
|
|
801
1196
|
}
|
|
802
|
-
if (
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1197
|
+
if (l.status === "in-progress") {
|
|
1198
|
+
const w = await Q(T);
|
|
1199
|
+
for (const C of w.entries)
|
|
1200
|
+
if (C.id !== f.id && C.status === "in-progress") {
|
|
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
|
+
}
|
|
808
1222
|
}
|
|
809
1223
|
}
|
|
1224
|
+
const s = {
|
|
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
|
+
}
|
|
810
1248
|
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
811
|
-
} catch (
|
|
812
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1249
|
+
} catch (r) {
|
|
1250
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
813
1251
|
}
|
|
814
1252
|
return;
|
|
815
1253
|
}
|
|
816
|
-
if (
|
|
1254
|
+
if (u.method === "POST" && g === "/api/ideas") {
|
|
817
1255
|
try {
|
|
818
|
-
const
|
|
819
|
-
if (!(
|
|
1256
|
+
const r = await M(u), { title: d, content: y } = JSON.parse(r);
|
|
1257
|
+
if (!(d != null && d.trim())) {
|
|
820
1258
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "title is required" }));
|
|
821
1259
|
return;
|
|
822
1260
|
}
|
|
823
|
-
const
|
|
824
|
-
if (
|
|
825
|
-
const
|
|
826
|
-
return Number.isNaN(
|
|
1261
|
+
const l = x(e, "ideas"), o = `IDEA-${(await Tt(l, x(e, "ideas.md"))).entries.reduce((S, b) => {
|
|
1262
|
+
if (b.id) {
|
|
1263
|
+
const I = Number.parseInt(b.id.replace("IDEA-", ""), 10);
|
|
1264
|
+
return Number.isNaN(I) ? S : Math.max(S, I);
|
|
827
1265
|
}
|
|
828
|
-
return
|
|
829
|
-
}, 0) + 1}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
await
|
|
837
|
-
|
|
838
|
-
} catch (
|
|
839
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1266
|
+
return S;
|
|
1267
|
+
}, 0) + 1}`;
|
|
1268
|
+
await q(l, { recursive: !0 });
|
|
1269
|
+
const f = Ft({
|
|
1270
|
+
id: o,
|
|
1271
|
+
title: d.trim(),
|
|
1272
|
+
body: y == null ? void 0 : y.trim()
|
|
1273
|
+
});
|
|
1274
|
+
await E(N(l, `${o}.md`), `${f}
|
|
1275
|
+
`, "utf-8"), await yt(e), t.statusCode = 201, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0, id: o }));
|
|
1276
|
+
} catch (r) {
|
|
1277
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
840
1278
|
}
|
|
841
1279
|
return;
|
|
842
1280
|
}
|
|
843
|
-
if (
|
|
844
|
-
const
|
|
1281
|
+
if (u.method === "GET" && g === "/api/icon") {
|
|
1282
|
+
const r = N(e, "papercamp", "assets"), d = {
|
|
845
1283
|
svg: "image/svg+xml",
|
|
846
1284
|
png: "image/png",
|
|
847
1285
|
jpg: "image/jpeg",
|
|
@@ -849,315 +1287,440 @@ ${(d == null ? void 0 : d.trim()) ?? ""}`, g = p.trimEnd(), w = g.length === 0 ?
|
|
|
849
1287
|
gif: "image/gif",
|
|
850
1288
|
webp: "image/webp"
|
|
851
1289
|
};
|
|
852
|
-
for (const [
|
|
1290
|
+
for (const [y, l] of Object.entries(d))
|
|
853
1291
|
try {
|
|
854
|
-
const
|
|
855
|
-
t.statusCode = 200, t.setHeader("Content-Type",
|
|
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);
|
|
856
1294
|
return;
|
|
857
1295
|
} catch {
|
|
858
1296
|
}
|
|
859
1297
|
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "no icon uploaded" }));
|
|
860
1298
|
return;
|
|
861
1299
|
}
|
|
862
|
-
if (
|
|
1300
|
+
if (u.method === "POST" && g === "/api/icon") {
|
|
863
1301
|
try {
|
|
864
|
-
const
|
|
865
|
-
if (!
|
|
1302
|
+
const r = await M(u), { dataUri: d } = JSON.parse(r);
|
|
1303
|
+
if (!d) {
|
|
866
1304
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "dataUri is required" }));
|
|
867
1305
|
return;
|
|
868
1306
|
}
|
|
869
|
-
const
|
|
870
|
-
if (!
|
|
1307
|
+
const y = d.match(/^data:(image\/[a-z0-9+.-]+);base64,(.+)$/);
|
|
1308
|
+
if (!y) {
|
|
871
1309
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "invalid data URI" }));
|
|
872
1310
|
return;
|
|
873
1311
|
}
|
|
874
|
-
const
|
|
875
|
-
await
|
|
876
|
-
} catch (
|
|
877
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1312
|
+
const l = y[1], T = l === "image/svg+xml" ? "svg" : l.split("/")[1], c = Buffer.from(y[2], "base64"), o = N(e, "papercamp", "assets");
|
|
1313
|
+
await q(o, { recursive: !0 }), await E(N(o, `icon.${T}`), c), t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1314
|
+
} catch (r) {
|
|
1315
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
878
1316
|
}
|
|
879
1317
|
return;
|
|
880
1318
|
}
|
|
881
|
-
if (
|
|
1319
|
+
if (u.method === "GET" && g === "/api/git/status") {
|
|
882
1320
|
try {
|
|
883
|
-
const
|
|
884
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ branch:
|
|
885
|
-
} catch (
|
|
886
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
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 }));
|
|
887
1325
|
}
|
|
888
1326
|
return;
|
|
889
1327
|
}
|
|
890
|
-
if (
|
|
1328
|
+
if (u.method === "POST" && g === "/api/git/push") {
|
|
891
1329
|
try {
|
|
892
|
-
|
|
893
|
-
|
|
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())) {
|
|
894
1340
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "title is required" }));
|
|
895
1341
|
return;
|
|
896
1342
|
}
|
|
897
|
-
await
|
|
898
|
-
} catch (
|
|
899
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1343
|
+
await a.commit(d ?? [], y.trim(), l == null ? void 0 : l.trim()), t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1344
|
+
} catch (r) {
|
|
1345
|
+
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1346
|
+
}
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
if (u.method === "POST" && g === "/api/git/suggest-commit-message") {
|
|
1350
|
+
try {
|
|
1351
|
+
const r = await M(u), { files: d } = JSON.parse(r);
|
|
1352
|
+
if (!(d != null && d.length)) {
|
|
1353
|
+
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "files is required" }));
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
const y = await a.diff(d), { entries: l } = await ct(
|
|
1357
|
+
x(e, "plans"),
|
|
1358
|
+
x(e, "plans.md")
|
|
1359
|
+
), T = he(l), c = await Te(
|
|
1360
|
+
y,
|
|
1361
|
+
T == null ? void 0 : T.id,
|
|
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 }));
|
|
900
1367
|
}
|
|
901
1368
|
return;
|
|
902
1369
|
}
|
|
903
|
-
if (
|
|
904
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(
|
|
1370
|
+
if (u.method === "GET" && g === "/api/status") {
|
|
1371
|
+
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(p.getStatus()));
|
|
905
1372
|
return;
|
|
906
1373
|
}
|
|
907
|
-
if (
|
|
908
|
-
const
|
|
909
|
-
if (
|
|
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") {
|
|
910
1377
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "name must be lint, format, or test" }));
|
|
911
1378
|
return;
|
|
912
1379
|
}
|
|
913
|
-
|
|
1380
|
+
p.runCheck(d), t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
914
1381
|
return;
|
|
915
1382
|
}
|
|
916
|
-
if (
|
|
917
|
-
|
|
1383
|
+
if (u.method === "POST" && g === "/api/status/fix") {
|
|
1384
|
+
p.runQualityFix(), t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
918
1385
|
return;
|
|
919
1386
|
}
|
|
920
|
-
if (
|
|
921
|
-
t.statusCode = 200, t.setHeader("Content-Type", "text/event-stream"), t.setHeader("Cache-Control", "no-cache"), t.setHeader("Connection", "keep-alive"), t.flushHeaders(),
|
|
1387
|
+
if (u.method === "GET" && g === "/api/activity/stream") {
|
|
1388
|
+
t.statusCode = 200, t.setHeader("Content-Type", "text/event-stream"), t.setHeader("Cache-Control", "no-cache"), t.setHeader("Connection", "keep-alive"), t.flushHeaders(), i.subscribe(t), a.subscribe(t), p.subscribe(t), m.subscribe(t);
|
|
922
1389
|
return;
|
|
923
1390
|
}
|
|
924
|
-
if (
|
|
925
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(
|
|
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()));
|
|
926
1393
|
return;
|
|
927
1394
|
}
|
|
928
|
-
if (
|
|
1395
|
+
if (u.method === "POST" && g === "/api/agent/launch") {
|
|
929
1396
|
try {
|
|
930
|
-
const
|
|
931
|
-
if (!
|
|
1397
|
+
const r = await M(u), { planId: d, phaseIndex: y } = JSON.parse(r);
|
|
1398
|
+
if (!d || typeof y != "number") {
|
|
932
1399
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "planId and phaseIndex are required" }));
|
|
933
1400
|
return;
|
|
934
1401
|
}
|
|
935
|
-
const
|
|
936
|
-
if (!
|
|
937
|
-
|
|
1402
|
+
const l = x(e, "plans"), c = (await Q(l)).entries.find((S) => S.id === d);
|
|
1403
|
+
if (!c) {
|
|
1404
|
+
const b = vt(await P(x(e, "plans.md"))).entries.find((s) => s.id === d);
|
|
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 }));
|
|
938
1420
|
return;
|
|
939
1421
|
}
|
|
940
|
-
const
|
|
941
|
-
if (
|
|
942
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1422
|
+
const o = await et(e, a, c.id);
|
|
1423
|
+
if (o) {
|
|
1424
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: o }));
|
|
943
1425
|
return;
|
|
944
1426
|
}
|
|
945
|
-
const
|
|
946
|
-
if (!
|
|
947
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1427
|
+
const f = await m.start(c, y);
|
|
1428
|
+
if (!f.ok) {
|
|
1429
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: f.error }));
|
|
948
1430
|
return;
|
|
949
1431
|
}
|
|
950
1432
|
t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
951
|
-
} catch (
|
|
952
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1433
|
+
} catch (r) {
|
|
1434
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
953
1435
|
}
|
|
954
1436
|
return;
|
|
955
1437
|
}
|
|
956
|
-
if (
|
|
1438
|
+
if (u.method === "POST" && g === "/api/agent/launch-audit") {
|
|
957
1439
|
try {
|
|
958
|
-
const
|
|
959
|
-
if (!
|
|
1440
|
+
const r = await M(u), { planId: d, prompt: y } = JSON.parse(r);
|
|
1441
|
+
if (!d || !y) {
|
|
960
1442
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "planId and prompt are required" }));
|
|
961
1443
|
return;
|
|
962
1444
|
}
|
|
963
|
-
const
|
|
964
|
-
|
|
1445
|
+
const l = x(e, "plans");
|
|
1446
|
+
let c = (await Q(l)).entries.find((S) => S.id === d);
|
|
1447
|
+
if (c || (c = vt(await P(x(e, "plans.md"))).entries.find((b) => b.id === d)), !c) {
|
|
965
1448
|
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "plan not found" }));
|
|
966
1449
|
return;
|
|
967
1450
|
}
|
|
968
|
-
const
|
|
969
|
-
if (
|
|
970
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1451
|
+
const o = await et(e, a, c.id);
|
|
1452
|
+
if (o) {
|
|
1453
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: o }));
|
|
971
1454
|
return;
|
|
972
1455
|
}
|
|
973
|
-
const
|
|
974
|
-
if (!
|
|
975
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1456
|
+
const f = m.startForPlan(c, y);
|
|
1457
|
+
if (!f.ok) {
|
|
1458
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: f.error }));
|
|
976
1459
|
return;
|
|
977
1460
|
}
|
|
978
1461
|
t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
979
|
-
} catch (
|
|
980
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1462
|
+
} catch (r) {
|
|
1463
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
981
1464
|
}
|
|
982
1465
|
return;
|
|
983
1466
|
}
|
|
984
|
-
if (
|
|
1467
|
+
if (u.method === "POST" && g === "/api/agent/launch-draft") {
|
|
985
1468
|
try {
|
|
986
|
-
const
|
|
987
|
-
if (!
|
|
1469
|
+
const r = await M(u), { ideaId: d, prompt: y } = JSON.parse(r);
|
|
1470
|
+
if (!d || !y) {
|
|
988
1471
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "ideaId and prompt are required" }));
|
|
989
1472
|
return;
|
|
990
1473
|
}
|
|
991
|
-
const
|
|
992
|
-
|
|
1474
|
+
const l = x(e, "ideas");
|
|
1475
|
+
let c = (await xt(l)).entries.find((S) => S.id === d);
|
|
1476
|
+
if (c || (c = Jt(await P(x(e, "ideas.md"))).find((b) => b.id === d)), !c) {
|
|
993
1477
|
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "idea not found" }));
|
|
994
1478
|
return;
|
|
995
1479
|
}
|
|
996
|
-
const
|
|
997
|
-
if (
|
|
998
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1480
|
+
const o = await et(e, a);
|
|
1481
|
+
if (o) {
|
|
1482
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: o }));
|
|
999
1483
|
return;
|
|
1000
1484
|
}
|
|
1001
|
-
const
|
|
1002
|
-
if (!
|
|
1003
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1485
|
+
const f = m.startForIdea(c, y);
|
|
1486
|
+
if (!f.ok) {
|
|
1487
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: f.error }));
|
|
1004
1488
|
return;
|
|
1005
1489
|
}
|
|
1006
1490
|
t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1007
|
-
} catch (
|
|
1008
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1491
|
+
} catch (r) {
|
|
1492
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1009
1493
|
}
|
|
1010
1494
|
return;
|
|
1011
1495
|
}
|
|
1012
|
-
if (
|
|
1496
|
+
if (u.method === "POST" && g === "/api/agent/launch-extend") {
|
|
1013
1497
|
try {
|
|
1014
|
-
const
|
|
1015
|
-
if (!
|
|
1498
|
+
const r = await M(u), { ideaId: d, prompt: y } = JSON.parse(r);
|
|
1499
|
+
if (!d || !y) {
|
|
1016
1500
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "ideaId and prompt are required" }));
|
|
1017
1501
|
return;
|
|
1018
1502
|
}
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1503
|
+
const l = x(e, "ideas");
|
|
1504
|
+
let c = (await xt(l)).entries.find((S) => S.id === d);
|
|
1505
|
+
if (c || (c = Jt(await P(x(e, "ideas.md"))).find((b) => b.id === d)), !c) {
|
|
1021
1506
|
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "idea not found" }));
|
|
1022
1507
|
return;
|
|
1023
1508
|
}
|
|
1024
|
-
const
|
|
1025
|
-
if (
|
|
1026
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1509
|
+
const o = await et(e, a);
|
|
1510
|
+
if (o) {
|
|
1511
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: o }));
|
|
1027
1512
|
return;
|
|
1028
1513
|
}
|
|
1029
|
-
const
|
|
1030
|
-
if (!
|
|
1031
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1514
|
+
const f = m.startForIdeaExtend(c, y);
|
|
1515
|
+
if (!f.ok) {
|
|
1516
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: f.error }));
|
|
1032
1517
|
return;
|
|
1033
1518
|
}
|
|
1034
1519
|
t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1035
|
-
} catch (
|
|
1036
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1520
|
+
} catch (r) {
|
|
1521
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1037
1522
|
}
|
|
1038
1523
|
return;
|
|
1039
1524
|
}
|
|
1040
|
-
if (
|
|
1041
|
-
const
|
|
1042
|
-
if (
|
|
1043
|
-
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1525
|
+
if (u.method === "POST" && g === "/api/agent/launch-audit-all") {
|
|
1526
|
+
const r = await et(e, a);
|
|
1527
|
+
if (r) {
|
|
1528
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r }));
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
const d = m.startBatchAudit();
|
|
1532
|
+
if (!d.ok) {
|
|
1533
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: d.error }));
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
if (u.method === "POST" && g === "/api/agent/stop") {
|
|
1540
|
+
const r = m.stop();
|
|
1541
|
+
if (!r.ok) {
|
|
1542
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.error }));
|
|
1044
1543
|
return;
|
|
1045
1544
|
}
|
|
1046
1545
|
t.statusCode = 202, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1047
1546
|
return;
|
|
1048
1547
|
}
|
|
1049
|
-
if (
|
|
1548
|
+
if (u.method === "POST" && g === "/api/config") {
|
|
1050
1549
|
try {
|
|
1051
|
-
const
|
|
1052
|
-
if (!
|
|
1550
|
+
const r = N(e, "papercamp", "config.json"), d = await P(r);
|
|
1551
|
+
if (!d) {
|
|
1053
1552
|
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "config not found" }));
|
|
1054
1553
|
return;
|
|
1055
1554
|
}
|
|
1056
|
-
const
|
|
1057
|
-
if (
|
|
1555
|
+
const y = await M(u), l = JSON.parse(y), { port: T, projectName: c, defaultAgent: o } = l, f = l.defaultAgents;
|
|
1556
|
+
if (T !== void 0 && (!Number.isInteger(T) || T <= 0)) {
|
|
1058
1557
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "port must be a positive integer" }));
|
|
1059
1558
|
return;
|
|
1060
1559
|
}
|
|
1061
|
-
if (
|
|
1560
|
+
if (c !== void 0 && c.trim().length === 0) {
|
|
1062
1561
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "projectName must not be empty" }));
|
|
1063
1562
|
return;
|
|
1064
1563
|
}
|
|
1065
|
-
if (
|
|
1564
|
+
if (o !== void 0 && !Ot.includes(o)) {
|
|
1066
1565
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "defaultAgent must be a known agent id" }));
|
|
1067
1566
|
return;
|
|
1068
1567
|
}
|
|
1069
|
-
if (
|
|
1070
|
-
for (const
|
|
1071
|
-
|
|
1072
|
-
|
|
1568
|
+
if (f !== void 0)
|
|
1569
|
+
for (const O of ["phase", "planDraft", "ideaExtend", "commitSuggest"]) {
|
|
1570
|
+
const A = f[O], H = typeof A == "string" ? A : A == null ? void 0 : A.agent;
|
|
1571
|
+
if (!H || !Ot.includes(H)) {
|
|
1572
|
+
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: `defaultAgents.${O} must be a known agent id` }));
|
|
1073
1573
|
return;
|
|
1074
1574
|
}
|
|
1075
|
-
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1575
|
+
}
|
|
1576
|
+
const S = JSON.parse(d), I = (f ? {
|
|
1577
|
+
phase: B(f.phase),
|
|
1578
|
+
planDraft: B(f.planDraft),
|
|
1579
|
+
ideaExtend: B(f.ideaExtend),
|
|
1580
|
+
commitSuggest: B(f.commitSuggest)
|
|
1581
|
+
} : void 0) ?? (o !== void 0 ? {
|
|
1582
|
+
phase: { agent: o },
|
|
1583
|
+
planDraft: { agent: o },
|
|
1584
|
+
ideaExtend: { agent: o },
|
|
1585
|
+
commitSuggest: { agent: o }
|
|
1586
|
+
} : void 0), v = S, { defaultAgent: s, ...w } = v, C = {
|
|
1587
|
+
...w,
|
|
1588
|
+
...T !== void 0 && { port: T },
|
|
1589
|
+
...c !== void 0 && { projectName: c.trim() },
|
|
1590
|
+
...I && { defaultAgents: I }
|
|
1081
1591
|
};
|
|
1082
|
-
await
|
|
1083
|
-
} catch (
|
|
1084
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
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 }));
|
|
1085
1595
|
}
|
|
1086
1596
|
return;
|
|
1087
1597
|
}
|
|
1088
|
-
if (
|
|
1598
|
+
if (u.method === "GET" && g === "/api/env") {
|
|
1089
1599
|
try {
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
]),
|
|
1094
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ exists:
|
|
1095
|
-
} catch (
|
|
1096
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1600
|
+
const r = N(e, ".env"), d = N(e, ".env.example"), [y, l] = await Promise.all([
|
|
1601
|
+
ft(r),
|
|
1602
|
+
ft(d)
|
|
1603
|
+
]), T = y ? Et(await P(r)) : [], c = l ? Et(await P(d)).map((S) => S.key) : [], o = new Set(T.map((S) => S.key)), f = c.filter((S) => !o.has(S));
|
|
1604
|
+
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ exists: y, exampleExists: l, entries: T, missingKeys: f }));
|
|
1605
|
+
} catch (r) {
|
|
1606
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1097
1607
|
}
|
|
1098
1608
|
return;
|
|
1099
1609
|
}
|
|
1100
|
-
if (
|
|
1610
|
+
if (u.method === "POST" && g === "/api/env") {
|
|
1101
1611
|
try {
|
|
1102
|
-
const
|
|
1103
|
-
if (!Array.isArray(
|
|
1612
|
+
const r = await M(u), { entries: d } = JSON.parse(r);
|
|
1613
|
+
if (!Array.isArray(d)) {
|
|
1104
1614
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "entries is required" }));
|
|
1105
1615
|
return;
|
|
1106
1616
|
}
|
|
1107
|
-
const
|
|
1108
|
-
for (const
|
|
1109
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(
|
|
1110
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: `invalid key: ${
|
|
1617
|
+
const y = /* @__PURE__ */ new Set();
|
|
1618
|
+
for (const c of d) {
|
|
1619
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(c.key)) {
|
|
1620
|
+
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: `invalid key: ${c.key}` }));
|
|
1111
1621
|
return;
|
|
1112
1622
|
}
|
|
1113
|
-
if (
|
|
1114
|
-
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: `duplicate key: ${
|
|
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}` }));
|
|
1115
1625
|
return;
|
|
1116
1626
|
}
|
|
1117
|
-
|
|
1627
|
+
y.add(c.key);
|
|
1118
1628
|
}
|
|
1119
|
-
const
|
|
1120
|
-
await
|
|
1121
|
-
} catch (
|
|
1122
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1629
|
+
const l = N(e, ".env"), T = await P(l);
|
|
1630
|
+
await E(l, Vt(T, d)), t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ ok: !0 }));
|
|
1631
|
+
} catch (r) {
|
|
1632
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1123
1633
|
}
|
|
1124
1634
|
return;
|
|
1125
1635
|
}
|
|
1126
|
-
if (
|
|
1127
|
-
|
|
1128
|
-
|
|
1636
|
+
if (u.method === "POST" && g === "/api/open-questions/resolve") {
|
|
1637
|
+
try {
|
|
1638
|
+
const d = new URL(u.url ?? "", `http://${u.headers.host ?? "localhost"}`).searchParams.get("title");
|
|
1639
|
+
if (!(d != null && d.trim())) {
|
|
1640
|
+
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "title is required" }));
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
const y = await M(u), { decision: l, rationale: T } = JSON.parse(y);
|
|
1644
|
+
if (!(l != null && l.trim())) {
|
|
1645
|
+
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "decision is required" }));
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1648
|
+
const c = x(e, "open-questions.md"), o = await P(c);
|
|
1649
|
+
if (!o) {
|
|
1650
|
+
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "open-questions.md not found" }));
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
const f = It(o);
|
|
1654
|
+
if (f.warnings.length > 0) {
|
|
1655
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(
|
|
1656
|
+
JSON.stringify({
|
|
1657
|
+
error: "open-questions.md has parse warnings — resolve them before updating to avoid data loss",
|
|
1658
|
+
warnings: f.warnings.map((s) => `${s.title}: ${s.message}`)
|
|
1659
|
+
})
|
|
1660
|
+
);
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
const S = d.trim(), b = f.entries.find((s) => s.title === S);
|
|
1664
|
+
if (!b) {
|
|
1665
|
+
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: `open question "${S}" not found` }));
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
if (b.status !== "open") {
|
|
1669
|
+
t.statusCode = 409, t.setHeader("Content-Type", "application/json"), t.end(
|
|
1670
|
+
JSON.stringify({ error: `open question "${S}" is already ${b.status}` })
|
|
1671
|
+
);
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
const I = qt({
|
|
1675
|
+
title: l.trim(),
|
|
1676
|
+
date: Z(),
|
|
1677
|
+
status: "decided",
|
|
1678
|
+
body: T == null ? void 0 : T.trim()
|
|
1679
|
+
});
|
|
1680
|
+
await te(x(e, "decisions.md"), I), b.status = "resolved", b.resolvedBy = l.trim();
|
|
1681
|
+
const v = ee(f.entries);
|
|
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 }));
|
|
1686
|
+
}
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
if (u.method === "GET" && g === "/api/configs") {
|
|
1690
|
+
const d = new URL(u.url ?? "", `http://${u.headers.host ?? "localhost"}`).searchParams.get("name");
|
|
1691
|
+
if (d)
|
|
1129
1692
|
try {
|
|
1130
|
-
if (!
|
|
1693
|
+
if (!je.includes(d)) {
|
|
1131
1694
|
t.statusCode = 400, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "invalid config file name" }));
|
|
1132
1695
|
return;
|
|
1133
1696
|
}
|
|
1134
|
-
const
|
|
1135
|
-
if (!
|
|
1697
|
+
const y = await P(N(e, d));
|
|
1698
|
+
if (!y) {
|
|
1136
1699
|
t.statusCode = 404, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: "config file not found" }));
|
|
1137
1700
|
return;
|
|
1138
1701
|
}
|
|
1139
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ name:
|
|
1702
|
+
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ name: d, content: y }));
|
|
1140
1703
|
return;
|
|
1141
|
-
} catch (
|
|
1142
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1704
|
+
} catch (y) {
|
|
1705
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: y.message }));
|
|
1143
1706
|
return;
|
|
1144
1707
|
}
|
|
1145
1708
|
}
|
|
1146
|
-
const
|
|
1147
|
-
if (!
|
|
1148
|
-
|
|
1709
|
+
const j = ke.find((r) => r.path === g);
|
|
1710
|
+
if (!j) {
|
|
1711
|
+
J();
|
|
1149
1712
|
return;
|
|
1150
1713
|
}
|
|
1151
1714
|
try {
|
|
1152
|
-
const
|
|
1153
|
-
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(
|
|
1154
|
-
} catch (
|
|
1155
|
-
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error:
|
|
1715
|
+
const r = await j.handler(e);
|
|
1716
|
+
t.statusCode = 200, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(r));
|
|
1717
|
+
} catch (r) {
|
|
1718
|
+
t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: r.message }));
|
|
1156
1719
|
}
|
|
1157
1720
|
};
|
|
1158
|
-
return
|
|
1721
|
+
return $.agent = m, $;
|
|
1159
1722
|
}
|
|
1160
|
-
const
|
|
1723
|
+
const xe = {
|
|
1161
1724
|
".html": "text/html; charset=utf-8",
|
|
1162
1725
|
".js": "text/javascript; charset=utf-8",
|
|
1163
1726
|
".mjs": "text/javascript; charset=utf-8",
|
|
@@ -1170,81 +1733,301 @@ const Bt = {
|
|
|
1170
1733
|
".woff": "font/woff",
|
|
1171
1734
|
".woff2": "font/woff2"
|
|
1172
1735
|
};
|
|
1173
|
-
function
|
|
1174
|
-
return
|
|
1736
|
+
function Je() {
|
|
1737
|
+
return N(zt(de(import.meta.url)), "..", "app");
|
|
1175
1738
|
}
|
|
1176
|
-
async function
|
|
1177
|
-
const
|
|
1178
|
-
if (
|
|
1739
|
+
async function Ie({ root: e, port: i }) {
|
|
1740
|
+
const a = Je(), p = N(a, "index.html"), n = await U(p, "utf-8").catch(() => null);
|
|
1741
|
+
if (n === null)
|
|
1179
1742
|
throw new Error(
|
|
1180
|
-
`Dashboard assets not found at ${
|
|
1743
|
+
`Dashboard assets not found at ${a}. Run \`pnpm build\` (or reinstall the package) so dist/app exists.`
|
|
1181
1744
|
);
|
|
1182
|
-
const
|
|
1183
|
-
async function
|
|
1184
|
-
const
|
|
1745
|
+
const h = ve(e);
|
|
1746
|
+
async function m(t, J) {
|
|
1747
|
+
const g = decodeURIComponent((t.url ?? "/").split("?")[0]), j = N(a, g === "/" ? "index.html" : g);
|
|
1185
1748
|
try {
|
|
1186
|
-
if ((await
|
|
1187
|
-
|
|
1749
|
+
if ((await it(j)).isFile()) {
|
|
1750
|
+
J.statusCode = 200, J.setHeader("Content-Type", xe[Yt(j)] ?? "application/octet-stream"), J.end(await U(j));
|
|
1188
1751
|
return;
|
|
1189
1752
|
}
|
|
1190
1753
|
} catch {
|
|
1191
1754
|
}
|
|
1192
|
-
|
|
1755
|
+
J.statusCode = 200, J.setHeader("Content-Type", "text/html; charset=utf-8"), J.end(n);
|
|
1193
1756
|
}
|
|
1194
|
-
const
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1757
|
+
const $ = re((t, J) => {
|
|
1758
|
+
h(t, J, () => {
|
|
1759
|
+
m(t, J).catch((g) => {
|
|
1760
|
+
J.statusCode = 500, J.end(String(g));
|
|
1198
1761
|
});
|
|
1199
1762
|
});
|
|
1200
|
-
}),
|
|
1201
|
-
|
|
1763
|
+
}), u = () => {
|
|
1764
|
+
h.agent.killCurrent(), process.exit(0);
|
|
1202
1765
|
};
|
|
1203
|
-
process.on("SIGINT",
|
|
1766
|
+
process.on("SIGINT", u), process.on("SIGTERM", u), await new Promise((t) => $.listen(i, t));
|
|
1767
|
+
}
|
|
1768
|
+
async function $t(e) {
|
|
1769
|
+
try {
|
|
1770
|
+
return await it(e), !0;
|
|
1771
|
+
} catch {
|
|
1772
|
+
return !1;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
async function He(e, i) {
|
|
1776
|
+
const a = N(e, `${i}.md`);
|
|
1777
|
+
if (await $t(a)) return a;
|
|
1778
|
+
const p = N(e, "archive", `${i}.md`);
|
|
1779
|
+
return await $t(p) ? p : null;
|
|
1204
1780
|
}
|
|
1205
|
-
|
|
1206
|
-
D.name("paper-camp").description("Local-first, AI-native project companion.").version(gt);
|
|
1207
|
-
D.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 (n, c) => {
|
|
1208
|
-
const o = process.cwd(), s = n ?? pt(o);
|
|
1781
|
+
async function Ae(e) {
|
|
1209
1782
|
try {
|
|
1210
|
-
|
|
1211
|
-
} catch
|
|
1212
|
-
|
|
1213
|
-
|
|
1783
|
+
return (await it(e)).mtime.toISOString().slice(0, 10);
|
|
1784
|
+
} catch {
|
|
1785
|
+
return null;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
async function Pe(e, i) {
|
|
1789
|
+
const a = await U(e, "utf-8"), n = dt(a).entries[0];
|
|
1790
|
+
if (!n)
|
|
1791
|
+
throw new Error(`Could not parse plan file after audit: ${e}`);
|
|
1792
|
+
const h = {
|
|
1793
|
+
id: i,
|
|
1794
|
+
title: n.title,
|
|
1795
|
+
kind: n.kind ?? "feat",
|
|
1796
|
+
status: n.status,
|
|
1797
|
+
idea: n.idea,
|
|
1798
|
+
agent: n.agent,
|
|
1799
|
+
created: n.created,
|
|
1800
|
+
updated: n.updated,
|
|
1801
|
+
audited: Z(),
|
|
1802
|
+
tags: n.tags,
|
|
1803
|
+
body: n.body,
|
|
1804
|
+
phases: n.phases,
|
|
1805
|
+
log: n.log,
|
|
1806
|
+
clarifications: n.clarifications
|
|
1807
|
+
};
|
|
1808
|
+
await E(e, `${at(h)}
|
|
1809
|
+
`, "utf-8");
|
|
1810
|
+
}
|
|
1811
|
+
async function Ee(e, i, a, p) {
|
|
1812
|
+
var m;
|
|
1813
|
+
const n = Mt(i), h = V(a.command, a.buildArgs(n, p), {
|
|
1814
|
+
cwd: e,
|
|
1815
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1816
|
+
});
|
|
1817
|
+
return h.stdout && jt({ input: h.stdout }).on("line", (u) => {
|
|
1818
|
+
const t = a.parseLine(u);
|
|
1819
|
+
t != null && t.text && t.text !== "Agent is working…" && process.stdout.write(` ${t.text}
|
|
1820
|
+
`);
|
|
1821
|
+
}), (m = h.stderr) == null || m.on("data", ($) => process.stderr.write($)), new Promise(($) => {
|
|
1822
|
+
h.on("close", (u) => $(u === 0)), h.on("error", () => $(!1));
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
const st = new Xt();
|
|
1826
|
+
st.name("paper-camp").description("Local-first, AI-native project companion.").version(ie);
|
|
1827
|
+
st.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 (e, i) => {
|
|
1828
|
+
const a = process.cwd(), p = e ?? Qt(a);
|
|
1829
|
+
try {
|
|
1830
|
+
await se(a, { projectName: p, intent: i.intent }), console.log(`Initialized Paper Camp in ${a}`), 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");
|
|
1831
|
+
} catch (n) {
|
|
1832
|
+
if (n instanceof oe) {
|
|
1833
|
+
console.error(n.message), process.exitCode = 1;
|
|
1214
1834
|
return;
|
|
1215
1835
|
}
|
|
1216
|
-
throw
|
|
1836
|
+
throw n;
|
|
1217
1837
|
}
|
|
1218
1838
|
});
|
|
1219
|
-
|
|
1220
|
-
const
|
|
1839
|
+
st.command("dev").description("Start the local dashboard").option("-p, --port <number>", "port to listen on", "3333").action(async (e) => {
|
|
1840
|
+
const i = Number(e.port), a = process.cwd();
|
|
1221
1841
|
try {
|
|
1222
|
-
await
|
|
1223
|
-
} catch (
|
|
1224
|
-
console.error(
|
|
1842
|
+
await Ie({ root: a, port: i }), console.log(`Paper Camp dashboard running at http://localhost:${i}`);
|
|
1843
|
+
} catch (p) {
|
|
1844
|
+
console.error(p.message), process.exitCode = 1;
|
|
1225
1845
|
}
|
|
1226
1846
|
});
|
|
1227
|
-
|
|
1228
|
-
if (
|
|
1229
|
-
console.error(`Unknown type "${
|
|
1847
|
+
st.command("add <type> [name]").description("Add a new entry (currently supports: plan)").option("-k, --kind <kind>", `plan kind (${wt.join("|")})`, "feat").action(async (e, i, a) => {
|
|
1848
|
+
if (e !== "plan") {
|
|
1849
|
+
console.error(`Unknown type "${e}". Supported types: plan`), process.exitCode = 1;
|
|
1230
1850
|
return;
|
|
1231
1851
|
}
|
|
1232
|
-
if (!
|
|
1852
|
+
if (!i) {
|
|
1233
1853
|
console.error("Usage: paper-camp add plan <name> [--kind feat|fix|chore|docs|refactor]"), process.exitCode = 1;
|
|
1234
1854
|
return;
|
|
1235
1855
|
}
|
|
1236
|
-
if (!
|
|
1237
|
-
console.error(`Unknown kind "${
|
|
1856
|
+
if (!wt.includes(a.kind)) {
|
|
1857
|
+
console.error(`Unknown kind "${a.kind}". Supported kinds: ${wt.join(", ")}`), process.exitCode = 1;
|
|
1238
1858
|
return;
|
|
1239
1859
|
}
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1860
|
+
const p = a.kind, n = process.cwd(), h = nt(n, "papercamp", "config.json"), m = await Bt(h, p);
|
|
1861
|
+
if (!m) {
|
|
1862
|
+
console.error("Could not assign plan ID — is the project initialized?"), process.exitCode = 1;
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
const $ = nt(n, "papercamp", "plans");
|
|
1866
|
+
await q($, { recursive: !0 });
|
|
1867
|
+
const u = at({
|
|
1868
|
+
id: m,
|
|
1869
|
+
title: i,
|
|
1870
|
+
kind: p,
|
|
1242
1871
|
status: "idea",
|
|
1243
|
-
|
|
1244
|
-
id: y,
|
|
1245
|
-
created: B()
|
|
1872
|
+
created: Z()
|
|
1246
1873
|
});
|
|
1247
|
-
await
|
|
1874
|
+
await E(N($, `${m}.md`), `${u}
|
|
1875
|
+
`, "utf-8");
|
|
1876
|
+
const { entries: t } = await Q($);
|
|
1877
|
+
await E(N($, "index.md"), Ht(t), "utf-8"), console.log(`Added plan "${i}" (${m}) to papercamp/plans/${m}.md`);
|
|
1878
|
+
});
|
|
1879
|
+
st.command("migrate").description(
|
|
1880
|
+
"One-time migration: split monolithic plans.md/ideas.md into per-file YAML frontmatter entries"
|
|
1881
|
+
).action(async () => {
|
|
1882
|
+
const e = process.cwd(), i = nt(e, "papercamp", "plans"), a = N(i, "archive"), p = nt(e, "papercamp", "ideas");
|
|
1883
|
+
await q(a, { recursive: !0 }), await q(p, { recursive: !0 });
|
|
1884
|
+
const n = nt(e, "papercamp", "plans.md"), h = nt(e, "papercamp", "ideas.md");
|
|
1885
|
+
let m = 0, $ = 0, u = 0;
|
|
1886
|
+
const t = await U(n, "utf-8").catch(() => "");
|
|
1887
|
+
if (t.trim()) {
|
|
1888
|
+
const { entries: l, warnings: T } = vt(t);
|
|
1889
|
+
u = T.length;
|
|
1890
|
+
for (const c of T)
|
|
1891
|
+
console.warn(` warning: ${c.title}: ${c.message}`);
|
|
1892
|
+
for (const c of l) {
|
|
1893
|
+
if (!c.id) {
|
|
1894
|
+
console.warn(` skipping plan "${c.title}" — no Id assigned, cannot migrate`), $++;
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
const o = c.status === "done" || c.status === "dropped" ? a : i, f = N(o, `${c.id}.md`);
|
|
1898
|
+
if (await $t(f)) {
|
|
1899
|
+
$++;
|
|
1900
|
+
continue;
|
|
1901
|
+
}
|
|
1902
|
+
const S = at({
|
|
1903
|
+
id: c.id,
|
|
1904
|
+
title: c.title,
|
|
1905
|
+
kind: c.kind ?? "feat",
|
|
1906
|
+
status: c.status,
|
|
1907
|
+
idea: c.idea,
|
|
1908
|
+
agent: c.agent,
|
|
1909
|
+
created: c.created,
|
|
1910
|
+
updated: c.updated,
|
|
1911
|
+
tags: c.tags,
|
|
1912
|
+
body: c.body,
|
|
1913
|
+
phases: c.phases,
|
|
1914
|
+
log: c.log,
|
|
1915
|
+
clarifications: c.clarifications
|
|
1916
|
+
});
|
|
1917
|
+
await E(f, `${S}
|
|
1918
|
+
`, "utf-8"), m++;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
let J = 0, g = 0;
|
|
1922
|
+
const j = await U(h, "utf-8").catch(() => "");
|
|
1923
|
+
if (j.trim()) {
|
|
1924
|
+
const l = Jt(j);
|
|
1925
|
+
for (const T of l) {
|
|
1926
|
+
if (!T.id) {
|
|
1927
|
+
console.warn(` skipping idea "${T.title}" — no Id assigned, cannot migrate`), g++;
|
|
1928
|
+
continue;
|
|
1929
|
+
}
|
|
1930
|
+
const c = N(p, `${T.id}.md`);
|
|
1931
|
+
if (await $t(c)) {
|
|
1932
|
+
g++;
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
const o = T.body.replace(/^#{1,3}\s+.+(?:\r?\n)?/, "").trim(), f = Ft({ id: T.id, title: T.title, body: o });
|
|
1936
|
+
await E(c, `${f}
|
|
1937
|
+
`, "utf-8"), J++;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
const { entries: r } = await Q(i);
|
|
1941
|
+
await E(N(i, "index.md"), Ht(r), "utf-8");
|
|
1942
|
+
const { entries: d } = await xt(p), y = Kt(d, r);
|
|
1943
|
+
await E(N(p, "index.md"), _t(y), "utf-8"), m > 0 && $ === 0 && u === 0 && await E(n, "", "utf-8"), J > 0 && g === 0 && await E(h, "", "utf-8"), console.log(
|
|
1944
|
+
`Migrated ${m} plans (${$} skipped), ${J} ideas (${g} skipped).`
|
|
1945
|
+
);
|
|
1946
|
+
});
|
|
1947
|
+
st.command("audit").description("Audit all review/done plans for missing phases").action(async () => {
|
|
1948
|
+
var c;
|
|
1949
|
+
const e = process.cwd(), i = nt(e, "papercamp", "plans"), { entries: a, warnings: p } = await Q(i);
|
|
1950
|
+
for (const o of p)
|
|
1951
|
+
console.warn(` warning: ${o.title}: ${o.message}`);
|
|
1952
|
+
const n = a.filter((o) => o.status === "review" || o.status === "done");
|
|
1953
|
+
if (n.length === 0) {
|
|
1954
|
+
console.log('No plans with status "review" or "done" found.');
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
const h = await U(N(e, "papercamp", "config.json"), "utf-8").catch(
|
|
1958
|
+
() => "{}"
|
|
1959
|
+
);
|
|
1960
|
+
let m;
|
|
1961
|
+
try {
|
|
1962
|
+
m = JSON.parse(h);
|
|
1963
|
+
} catch {
|
|
1964
|
+
console.error("Invalid papercamp/config.json"), process.exitCode = 1;
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
const $ = m.defaultAgents, u = $ ? {
|
|
1968
|
+
phase: B($.phase),
|
|
1969
|
+
planDraft: B($.planDraft),
|
|
1970
|
+
ideaExtend: B($.ideaExtend),
|
|
1971
|
+
commitSuggest: B($.commitSuggest)
|
|
1972
|
+
} : kt, { adapter: t, model: J, effort: g } = St({ defaultAgents: u, taskKind: "audit" });
|
|
1973
|
+
console.log(`Auditing ${n.length} plan(s):
|
|
1974
|
+
`);
|
|
1975
|
+
const j = [];
|
|
1976
|
+
for (const o of n) {
|
|
1977
|
+
const f = o.id ?? "(no id)", S = f.padEnd(14);
|
|
1978
|
+
if (!o.id) {
|
|
1979
|
+
console.log(` [skip] ${S} ${o.title} — no id`), j.push({ id: f, title: o.title, status: "skipped", skipReason: "no id" });
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
const b = await He(i, o.id);
|
|
1983
|
+
if (!b) {
|
|
1984
|
+
console.log(` [skip] ${S} ${o.title} — file not found`), j.push({ id: f, title: o.title, status: "skipped", skipReason: "file not found" });
|
|
1985
|
+
continue;
|
|
1986
|
+
}
|
|
1987
|
+
if (o.audited) {
|
|
1988
|
+
const s = await Ae(b);
|
|
1989
|
+
if (s && o.audited >= s) {
|
|
1990
|
+
console.log(
|
|
1991
|
+
` [skip] ${S} ${o.title} — audited ${o.audited}, unchanged since`
|
|
1992
|
+
), j.push({
|
|
1993
|
+
id: f,
|
|
1994
|
+
title: o.title,
|
|
1995
|
+
status: "skipped",
|
|
1996
|
+
skipReason: `audited ${o.audited}, unchanged`
|
|
1997
|
+
});
|
|
1998
|
+
continue;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
const I = o.phases.length;
|
|
2002
|
+
if (console.log(` [audit] ${S} ${o.title}`), await Ee(e, o, t, { model: J, effort: g })) {
|
|
2003
|
+
try {
|
|
2004
|
+
await Pe(b, o.id);
|
|
2005
|
+
} catch (A) {
|
|
2006
|
+
console.log(` [fail] ${S} ${o.title} — ${A.message}`), process.exitCode = 1, j.push({ id: f, title: o.title, status: "failed" });
|
|
2007
|
+
continue;
|
|
2008
|
+
}
|
|
2009
|
+
const s = await U(b, "utf-8").catch(() => ""), C = ((c = dt(s).entries[0]) == null ? void 0 : c.phases.length) ?? I, O = Math.max(0, C - I);
|
|
2010
|
+
console.log(` [done] ${S} ${o.title}`), j.push({ id: f, title: o.title, status: "audited", gapPhases: O });
|
|
2011
|
+
} else
|
|
2012
|
+
console.log(` [fail] ${S} ${o.title} — agent exited with error`), process.exitCode = 1, j.push({ id: f, title: o.title, status: "failed" });
|
|
2013
|
+
}
|
|
2014
|
+
const r = j.filter((o) => o.status === "audited"), d = j.filter((o) => o.status === "skipped"), y = j.filter((o) => o.status === "failed"), l = r.reduce((o, f) => o + (f.gapPhases ?? 0), 0), T = "─".repeat(43);
|
|
2015
|
+
if (console.log(`
|
|
2016
|
+
${T}`), console.log("Audit summary"), console.log(
|
|
2017
|
+
` Audited : ${r.length} Skipped : ${d.length} Failed : ${y.length}`
|
|
2018
|
+
), r.length > 0)
|
|
2019
|
+
if (l > 0) {
|
|
2020
|
+
console.log(` Gap phases appended: ${l} total`);
|
|
2021
|
+
for (const o of r.filter((f) => (f.gapPhases ?? 0) > 0))
|
|
2022
|
+
console.log(` ${o.id.padEnd(14)} +${o.gapPhases} phase(s)`);
|
|
2023
|
+
} else
|
|
2024
|
+
console.log(" No gap phases appended — all audited plans are complete.");
|
|
2025
|
+
if (d.length > 0) {
|
|
2026
|
+
console.log(" Skipped:");
|
|
2027
|
+
for (const o of d)
|
|
2028
|
+
console.log(` ${o.id.padEnd(14)} ${o.skipReason}`);
|
|
2029
|
+
}
|
|
2030
|
+
console.log(T);
|
|
1248
2031
|
});
|
|
1249
|
-
|
|
2032
|
+
st.parseAsync(process.argv);
|
|
1250
2033
|
//# sourceMappingURL=index.js.map
|