@dendelion/paper-camp 0.39.0 → 0.41.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.
Files changed (66) hide show
  1. package/dist/chunks/{daemon-state.BeDnhzBY.js → daemon-state.Evil_jr0.js} +93 -104
  2. package/dist/chunks/daemon-state.Evil_jr0.js.map +1 -0
  3. package/dist/chunks/needs.CUNwDixH.js +3516 -0
  4. package/dist/chunks/needs.CUNwDixH.js.map +1 -0
  5. package/dist/cli/index.js +4389 -4134
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/core/chat-file.d.ts +1 -0
  8. package/dist/core/chat-file.d.ts.map +1 -1
  9. package/dist/core/ci/ci-release.d.ts +5 -0
  10. package/dist/core/ci/ci-release.d.ts.map +1 -1
  11. package/dist/core/daemon-state/daemon-state.d.ts +3 -11
  12. package/dist/core/daemon-state/daemon-state.d.ts.map +1 -1
  13. package/dist/core/decision-text.d.ts +9 -0
  14. package/dist/core/decision-text.d.ts.map +1 -0
  15. package/dist/core/decision-text.test.d.ts +2 -0
  16. package/dist/core/decision-text.test.d.ts.map +1 -0
  17. package/dist/core/desk-discovery/evidence.d.ts +1 -0
  18. package/dist/core/desk-discovery/evidence.d.ts.map +1 -1
  19. package/dist/core/doctor/checks/tooling.d.ts.map +1 -1
  20. package/dist/core/doctor/doctor.d.ts +2 -0
  21. package/dist/core/doctor/doctor.d.ts.map +1 -1
  22. package/dist/core/doctor/fix.d.ts +1 -1
  23. package/dist/core/doctor/fix.d.ts.map +1 -1
  24. package/dist/core/doctor/rules.d.ts +10 -0
  25. package/dist/core/doctor/rules.d.ts.map +1 -1
  26. package/dist/core/entity-view.d.ts.map +1 -1
  27. package/dist/core/index.d.ts +1 -0
  28. package/dist/core/index.d.ts.map +1 -1
  29. package/dist/core/index.js +192 -178
  30. package/dist/core/needs-status.d.ts +4 -0
  31. package/dist/core/needs-status.d.ts.map +1 -0
  32. package/dist/core/needs.d.ts +32 -0
  33. package/dist/core/needs.d.ts.map +1 -0
  34. package/dist/core/needs.test.d.ts +2 -0
  35. package/dist/core/needs.test.d.ts.map +1 -0
  36. package/dist/core/parked-questions.d.ts +0 -1
  37. package/dist/core/parked-questions.d.ts.map +1 -1
  38. package/dist/core/parse/parser.d.ts.map +1 -1
  39. package/dist/core/parse/schemas.d.ts +4 -0
  40. package/dist/core/parse/schemas.d.ts.map +1 -1
  41. package/dist/core/roadmap.d.ts.map +1 -1
  42. package/dist/core/run-filters.d.ts +2 -0
  43. package/dist/core/run-filters.d.ts.map +1 -1
  44. package/dist/core/run-order.d.ts +5 -1
  45. package/dist/core/run-order.d.ts.map +1 -1
  46. package/dist/core/run-rows.d.ts.map +1 -1
  47. package/dist/core/scaffold/scaffold.d.ts +1 -0
  48. package/dist/core/scaffold/scaffold.d.ts.map +1 -1
  49. package/dist/core/scaffold/templates.d.ts +6 -0
  50. package/dist/core/scaffold/templates.d.ts.map +1 -1
  51. package/dist/core/sections.d.ts.map +1 -1
  52. package/dist/core/sections.test.d.ts +2 -0
  53. package/dist/core/sections.test.d.ts.map +1 -0
  54. package/dist/core/serialize/entity-file.d.ts +6 -2
  55. package/dist/core/serialize/entity-file.d.ts.map +1 -1
  56. package/dist/toolbar/toolbar.js +3192 -3187
  57. package/dist/toolbar/toolbar.js.map +1 -1
  58. package/dist/types/index.d.ts +59 -0
  59. package/dist/types/index.d.ts.map +1 -1
  60. package/dist/types/index.js +16 -15
  61. package/dist/types/index.js.map +1 -1
  62. package/dist/vite/index.js +1 -1
  63. package/package.json +6 -2
  64. package/dist/chunks/daemon-state.BeDnhzBY.js.map +0 -1
  65. package/dist/chunks/phase-progress.2akB88O9.js +0 -3178
  66. package/dist/chunks/phase-progress.2akB88O9.js.map +0 -1
@@ -1,3178 +0,0 @@
1
- import { readFile as v, mkdir as P, writeFile as E, readdir as z, stat as Ht, rename as He, access as Gt } from "node:fs/promises";
2
- import { dirname as M, join as p, basename as Bt } from "node:path";
3
- import { parse as V, stringify as Yt } from "yaml";
4
- import { spawn as b } from "node:child_process";
5
- import { AGENT_IDS as L, NIGHT_CHECK_IDS as qt, NOTIFICATION_SETTING_KINDS as Ut, PLAN_KINDS as Kt } from "../types/index.js";
6
- import { createHash as Wt } from "node:crypto";
7
- import { existsSync as ie, readFileSync as Ge } from "node:fs";
8
- import { fileURLToPath as Jt } from "node:url";
9
- import { z as zt, A as Vt, e as Xt, n as Zt, a as Qt, u as en } from "./daemon-state.BeDnhzBY.js";
10
- import { z as d } from "zod";
11
- const tn = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/;
12
- function nn(e) {
13
- return e.length >= 2 && (e.startsWith('"') && e.endsWith('"') || e.startsWith("'") && e.endsWith("'")) ? e.slice(1, -1) : e;
14
- }
15
- function Ie(e) {
16
- return e === "" || /[\s#"]/.test(e) ? `"${e.replace(/"/g, '\\"')}"` : e;
17
- }
18
- function Be(e) {
19
- const t = e.trim();
20
- if (!t || t.startsWith("#")) return null;
21
- const n = t.match(tn);
22
- return n ? { key: n[1], value: nn(n[2]) } : null;
23
- }
24
- function Er(e) {
25
- const t = [];
26
- for (const n of e.split(`
27
- `)) {
28
- const s = Be(n);
29
- s && t.push(s);
30
- }
31
- return t;
32
- }
33
- function kr(e, t) {
34
- const n = new Map(t.map((r) => [r.key, r.value])), s = e.length > 0 ? e.split(`
35
- `) : [], a = [];
36
- for (const r of s) {
37
- const i = Be(r);
38
- if (!i) {
39
- a.push(r);
40
- continue;
41
- }
42
- n.has(i.key) && (a.push(`${i.key}=${Ie(n.get(i.key) ?? "")}`), n.delete(i.key));
43
- }
44
- for (const [r, i] of n)
45
- a.push(`${r}=${Ie(i)}`);
46
- for (; a.length > 0 && a[a.length - 1] === ""; ) a.pop();
47
- return a.length > 0 ? `${a.join(`
48
- `)}
49
- ` : "";
50
- }
51
- function Sr(e) {
52
- return e.archived === !0 || e.status === "done";
53
- }
54
- function Ae(e) {
55
- return e.filter((t) => t.source !== "manual").every((t) => t.done);
56
- }
57
- function te(e) {
58
- const t = e.phases.length > 0 && Ae(e.phases), n = Ae(e.fixes ?? []);
59
- return t && n;
60
- }
61
- function X(e, t, n, s = !1) {
62
- return e.kind === "note" || e.status === "dropped" ? e.status : e.archived ? "done" : t ? t.state === "merged" ? te(e) ? "done" : "planned" : t.state === "closed" ? "dropped" : te(e) ? "review" : "in-progress" : n ? e.status === "review" || e.status === "done" ? e.status : s && e.phases.length > 0 ? te(e) ? "review" : "in-progress" : e.phases.length > 0 ? "planned" : "idea" : e.status ?? (e.phases.length > 0 ? "planned" : "idea");
63
- }
64
- function sn(e, t, n) {
65
- return e.kind === "note" || e.kind === "board" || e.status === "dropped" || e.archived || t ? !1 : !n;
66
- }
67
- function an(e, t) {
68
- if (e.kind === "note" || e.archived || (t == null ? void 0 : t.state) !== "merged") return !1;
69
- const n = X(e, t, !0);
70
- return n === "review" || n === "done";
71
- }
72
- function Ye(e) {
73
- return e.length === 0 ? "idea" : e.every((t) => t === "done" || t === "dropped") ? "review" : e.some((t) => t !== "idea" && t !== "planned") ? "in-progress" : "planned";
74
- }
75
- function rn(e = [], t = [], n = [], s = []) {
76
- const a = [
77
- ...e.map((i) => ({ kind: "log", date: i.date, text: i.text })),
78
- ...t.map((i) => ({ kind: "clarification", date: i.date, text: i.text })),
79
- ...s.map((i) => ({ kind: "review", date: i.date, text: i.text }))
80
- ].sort((i, o) => i.date.localeCompare(o.date)), r = n.map((i) => ({
81
- kind: i.kind ?? "note",
82
- text: i.anchor.kind === "phase" ? `[phase ${i.anchor.index}] ${i.prose}` : i.prose,
83
- state: i.state
84
- }));
85
- return [...a, ...r];
86
- }
87
- function oe(e, t) {
88
- return e.filter((n) => n.kind === t).map((n) => ({ date: n.date ?? "", text: n.text }));
89
- }
90
- function qe(e = []) {
91
- return oe(e, "log");
92
- }
93
- function on(e = []) {
94
- return oe(e, "clarification");
95
- }
96
- function cn(e = []) {
97
- return oe(e, "review");
98
- }
99
- function dn(e = []) {
100
- return e.filter(
101
- (t) => t.kind === "note" || t.kind === "decision" || t.kind === "question"
102
- ).map((t) => ({
103
- anchor: { kind: "body" },
104
- prose: t.text,
105
- state: t.state ?? "open",
106
- kind: t.kind
107
- }));
108
- }
109
- function $r(e, t, n) {
110
- return [...(e ?? []).filter((s) => !t.includes(s.kind)), ...n];
111
- }
112
- function Ir(e, t, n, s) {
113
- return e.map((a, r) => {
114
- if (r !== t) return a;
115
- const i = s ? `${a.text}
116
-
117
- ${s}` : a.text;
118
- if (n === "decision") return { ...a, kind: n, state: "open", text: i };
119
- const { state: o, ...c } = a;
120
- return { ...c, kind: n, text: i };
121
- });
122
- }
123
- function Ar(e = []) {
124
- const t = e.reduce((n, s, a) => s.kind === "log" ? a : n, -1);
125
- return e.slice(t + 1).filter((n) => n.kind === "chat");
126
- }
127
- function Ue(e, t, n = !1, s = !1) {
128
- return {
129
- title: e.title,
130
- // Non-note entities can't carry the note-only 'open' (schema-enforced).
131
- status: X(e, t, n, s),
132
- statusFallback: sn(e, t, n),
133
- kind: e.type,
134
- entityKind: e.kind === "note" ? void 0 : e.kind,
135
- id: e.id,
136
- idea: e.idea,
137
- agent: e.agent,
138
- created: e.created,
139
- updated: e.updated,
140
- audited: e.audited,
141
- auditedHash: e.auditedHash,
142
- released: e.released,
143
- tags: e.tags,
144
- subject: e.subject,
145
- order: e.order,
146
- issueSource: e.issueSource,
147
- body: e.body,
148
- phases: e.phases,
149
- fixes: e.fixes,
150
- log: qe(e.thread),
151
- clarifications: on(e.thread),
152
- notes: dn(e.thread),
153
- review: cn(e.thread),
154
- thread: e.thread,
155
- archived: e.archived,
156
- pr: t
157
- };
158
- }
159
- function un(e) {
160
- return {
161
- id: e.id,
162
- title: e.title,
163
- body: e.body,
164
- kind: "note",
165
- status: e.status,
166
- subject: e.subject,
167
- order: e.order,
168
- created: e.created,
169
- log: qe(e.thread)
170
- };
171
- }
172
- const ln = /^[0-9a-f]{4,40}$/i, fn = /^Merge pull request #\d+ from \S+\/([a-z]+-\d+)-/i;
173
- function S(e, t) {
174
- return new Promise((n) => {
175
- var r;
176
- const s = b("git", t, { cwd: e, stdio: ["ignore", "pipe", "ignore"] });
177
- let a = "";
178
- (r = s.stdout) == null || r.on("data", (i) => {
179
- a += i.toString();
180
- }), s.on("close", () => n(a)), s.on("error", () => n(""));
181
- });
182
- }
183
- async function Ke(e) {
184
- var n;
185
- return ((n = (await S(e, ["symbolic-ref", "refs/remotes/origin/HEAD"])).trim().match(/refs\/remotes\/origin\/(.+)/)) == null ? void 0 : n[1]) ?? "main";
186
- }
187
- function ne(e) {
188
- const t = e.match(/^Refs:\s*([A-Za-z]+-\d+)\s*$/m);
189
- if (t) return t[1].toUpperCase();
190
- const n = e.split(`
191
- `, 1)[0], s = n.match(/\(([A-Za-z]+-\d+)\)/);
192
- if (s) return s[1].toUpperCase();
193
- const a = n.match(fn);
194
- return a ? a[1].toUpperCase() : null;
195
- }
196
- function pn(e) {
197
- return e.length > 0 && e.every((t) => t.startsWith("papercamp/"));
198
- }
199
- async function We(e) {
200
- const t = await Ke(e), n = await S(e, ["log", "--format=%x02%B%x00", "--name-only", t]), s = /* @__PURE__ */ new Set();
201
- for (const a of n.split("")) {
202
- if (!a) continue;
203
- const [r, i = ""] = a.split("\0"), o = i.split(`
204
- `).map((u) => u.trim()).filter(Boolean);
205
- if (pn(o)) continue;
206
- const c = ne(r.trim());
207
- c && s.add(c);
208
- }
209
- return s;
210
- }
211
- const Je = "", j = "", hn = `${Je}%H${j}%s${j}%cI${j}%D${j}%(trailers:key=Refs,valueonly)`, mn = /^([a-z]+(?:\([^)]*\))?):\s*(.+)$/i;
212
- function gn(e) {
213
- const t = e.match(mn);
214
- return t ? { prefix: t[1], subject: t[2] } : { prefix: "", subject: e };
215
- }
216
- function yn(e, t) {
217
- const n = [];
218
- let s = !1;
219
- for (const a of e.split(",")) {
220
- const r = a.trim().replace(/^HEAD -> /, "");
221
- r && (r.startsWith("tag: ") ? n.push(r.slice(5)) : t && r === t && (s = !0));
222
- }
223
- return { tags: n, isUpstreamHead: s };
224
- }
225
- function wn(e) {
226
- const t = e.trim();
227
- return /^[A-Za-z]+-\d+$/.test(t) ? t.toUpperCase() : null;
228
- }
229
- function bn(e, t, n) {
230
- return e.split(Je).map((s) => s.trim()).filter(Boolean).map((s) => {
231
- const [a, r = "", i = "", o = "", c = ""] = s.split(j), { prefix: u, subject: l } = gn(r), { tags: f, isUpstreamHead: h } = yn(o, t);
232
- return {
233
- hash: a,
234
- subject: l,
235
- prefix: u,
236
- date: i,
237
- tags: f,
238
- isUpstreamHead: h,
239
- pushed: t !== null && !n.has(a),
240
- ideaId: wn(c)
241
- };
242
- });
243
- }
244
- async function vn(e) {
245
- return (await S(e, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"])).trim() || null;
246
- }
247
- async function Rr(e, t, n = 30) {
248
- const s = await vn(e), a = await S(e, [
249
- "log",
250
- "--first-parent",
251
- `--max-count=${n + 1}`,
252
- `--skip=${t}`,
253
- `--format=${hn}`
254
- ]), r = /* @__PURE__ */ new Set();
255
- if (s) {
256
- const c = await S(e, ["rev-list", `${s}..HEAD`]);
257
- for (const u of c.split(`
258
- `)) {
259
- const l = u.trim();
260
- l && r.add(l);
261
- }
262
- }
263
- const i = bn(a, s, r), o = i.length > n;
264
- return { commits: i.slice(0, n), upstream: s, hasMore: o };
265
- }
266
- const k = /* @__PURE__ */ new Map(), En = 5 * 60 * 1e3, ce = (e) => p(e, "papercamp", "pr-map.json");
267
- async function kn(e, t, n) {
268
- const s = ce(e);
269
- try {
270
- const a = { fetchedAt: n, prs: Object.fromEntries(t) };
271
- await P(M(s), { recursive: !0 }), await E(s, `${JSON.stringify(a, null, 2)}
272
- `, "utf-8");
273
- } catch (a) {
274
- console.error(`papercamp: could not persist PR map for ${e}:`, a);
275
- }
276
- }
277
- async function ze(e) {
278
- try {
279
- const t = await v(ce(e), "utf-8"), n = JSON.parse(t);
280
- return { prs: new Map(Object.entries(n.prs)), fetchedAt: n.fetchedAt };
281
- } catch {
282
- return;
283
- }
284
- }
285
- const Sn = {
286
- APPROVED: "approved",
287
- CHANGES_REQUESTED: "changes-requested",
288
- REVIEW_REQUIRED: "review-required"
289
- };
290
- function $n(e) {
291
- const t = e.state === "MERGED" ? "merged" : e.state === "CLOSED" ? "closed" : e.isDraft ? "draft" : "open", n = Sn[e.reviewDecision];
292
- return {
293
- number: e.number,
294
- url: e.url,
295
- state: t,
296
- ...n && { reviewDecision: n },
297
- ...e.headRefOid && { headSha: e.headRefOid },
298
- ...e.headRefName && { headBranch: e.headRefName }
299
- };
300
- }
301
- const Re = { merged: 4, open: 3, draft: 2, closed: 1 };
302
- function Ve(e, t) {
303
- const n = e == null ? void 0 : e.match(/\*\*Plan:\*\*\s*`?([A-Za-z]+-\d+)`?/);
304
- if (n) return n[1].toUpperCase();
305
- const s = t == null ? void 0 : t.match(/^[a-z]+\/([a-z]+-\d+)-/);
306
- return s ? s[1].toUpperCase() : null;
307
- }
308
- const In = `
309
- query($owner: String!, $repo: String!, $number: Int!) {
310
- repository(owner: $owner, name: $repo) {
311
- pullRequest(number: $number) {
312
- reviewThreads(first: 100) { nodes { isResolved } }
313
- commits(last: 1) { nodes { commit { committedDate } } }
314
- comments(last: 1) { nodes { createdAt } }
315
- reviews(last: 20) { nodes { createdAt body } }
316
- }
317
- }
318
- }`;
319
- function An(e, t) {
320
- return `Paper Scout · ${e} · ${t.slice(0, 7)}`;
321
- }
322
- function F(e) {
323
- const t = e.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
324
- if (!t) return null;
325
- const [, n, s, a] = t;
326
- return { owner: n, repo: s, number: a };
327
- }
328
- const Rn = 15e3;
329
- function Xe(e, t) {
330
- return new Promise((n) => {
331
- var c, u;
332
- const s = b("gh", t, { cwd: e, stdio: ["ignore", "pipe", "pipe"] });
333
- let a = !1;
334
- const r = (l) => {
335
- a || (a = !0, clearTimeout(i), l());
336
- }, i = setTimeout(() => {
337
- s.kill(), r(() => n(void 0));
338
- }, Rn);
339
- let o = "";
340
- (c = s.stdout) == null || c.on("data", (l) => {
341
- o += l.toString();
342
- }), (u = s.stderr) == null || u.on("data", () => {
343
- }), s.on("close", (l) => {
344
- r(() => {
345
- if (l !== 0) {
346
- n(void 0);
347
- return;
348
- }
349
- try {
350
- n(JSON.parse(o));
351
- } catch {
352
- n(void 0);
353
- }
354
- });
355
- }), s.on("error", () => r(() => n(void 0)));
356
- });
357
- }
358
- function Ze(e, t, n, s, a) {
359
- return et(e, [
360
- "-f",
361
- `query=${t}`,
362
- "-f",
363
- `owner=${n}`,
364
- "-f",
365
- `repo=${s}`,
366
- "-F",
367
- `number=${a}`
368
- ]);
369
- }
370
- function Qe(e, t, n) {
371
- const s = ["-f", `query=${t}`];
372
- for (const [a, r] of Object.entries(n))
373
- s.push("-f", `${a}=${r}`);
374
- return et(e, s);
375
- }
376
- function et(e, t) {
377
- return Xe(e, ["api", "graphql", ...t]);
378
- }
379
- async function Pn(e, t) {
380
- var l, f, h, g, w;
381
- const n = F(t);
382
- if (!n) return;
383
- const s = await Ze(e, In, n.owner, n.repo, n.number), a = (f = (l = s == null ? void 0 : s.data) == null ? void 0 : l.repository) == null ? void 0 : f.pullRequest;
384
- if (!a) return;
385
- const r = a.reviewThreads.nodes.filter((y) => !y.isResolved).length, i = (h = a.commits.nodes[0]) == null ? void 0 : h.commit.committedDate, o = [(g = a.comments.nodes[0]) == null ? void 0 : g.createdAt, (w = a.reviews.nodes.at(-1)) == null ? void 0 : w.createdAt].filter((y) => !!y).sort().at(-1), c = !!(i && o && o > i), u = a.reviews.nodes.map((y) => y.body);
386
- return { unresolvedThreadCount: r, hasNewCommentsSincePush: c, reviewBodies: u };
387
- }
388
- const Cn = `
389
- query($owner: String!, $repo: String!, $number: Int!) {
390
- repository(owner: $owner, name: $repo) {
391
- pullRequest(number: $number) {
392
- reviewThreads(first: 100) {
393
- nodes {
394
- id
395
- isResolved
396
- comments(first: 1) { nodes { path line body author { login } } }
397
- }
398
- }
399
- }
400
- }
401
- }`;
402
- async function Pr(e, t) {
403
- var r, i, o;
404
- const n = F(t);
405
- if (!n) return [];
406
- const s = await Ze(e, Cn, n.owner, n.repo, n.number);
407
- return (((o = (i = (r = s == null ? void 0 : s.data) == null ? void 0 : r.repository) == null ? void 0 : i.pullRequest) == null ? void 0 : o.reviewThreads.nodes) ?? []).filter((c) => !c.isResolved).map((c) => ({ id: c.id, comment: c.comments.nodes[0] })).filter((c) => !!c.comment).map(({ id: c, comment: u }) => {
408
- var l;
409
- return {
410
- id: c,
411
- ...u.path ? { path: u.path } : {},
412
- ...u.line != null ? { line: u.line } : {},
413
- ...(l = u.author) != null && l.login ? { author: u.author.login } : {},
414
- body: u.body
415
- };
416
- });
417
- }
418
- const _n = `
419
- mutation($threadId: ID!) {
420
- resolveReviewThread(input: { threadId: $threadId }) {
421
- thread { id isResolved }
422
- }
423
- }`, Nn = `
424
- mutation($threadId: ID!, $body: String!) {
425
- addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) {
426
- comment { id }
427
- }
428
- }`;
429
- async function Cr(e, t) {
430
- var s, a, r;
431
- const n = await Qe(e, _n, { threadId: t });
432
- return !!((r = (a = (s = n == null ? void 0 : n.data) == null ? void 0 : s.resolveReviewThread) == null ? void 0 : a.thread) != null && r.isResolved);
433
- }
434
- async function _r(e, t, n) {
435
- var a, r, i;
436
- const s = await Qe(e, Nn, { threadId: t, body: n });
437
- return !!((i = (r = (a = s == null ? void 0 : s.data) == null ? void 0 : a.addPullRequestReviewThreadReply) == null ? void 0 : r.comment) != null && i.id);
438
- }
439
- function tt(e, t, n) {
440
- return new Promise((s) => {
441
- var i, o, c;
442
- const a = b("gh", ["api", t, "-X", "POST", "--input", "-"], {
443
- cwd: e,
444
- stdio: ["pipe", "ignore", "pipe"]
445
- });
446
- let r = "";
447
- (i = a.stderr) == null || i.on("data", (u) => {
448
- r += u;
449
- }), a.on(
450
- "close",
451
- (u) => s(u === 0 ? { delivered: !0 } : { delivered: !1, body: r.trim() })
452
- ), a.on("error", (u) => s({ delivered: !1, body: u.message })), (o = a.stdin) == null || o.on("error", () => {
453
- }), (c = a.stdin) == null || c.end(JSON.stringify(n));
454
- });
455
- }
456
- async function Nr(e, t, n) {
457
- const s = F(t);
458
- return s ? tt(
459
- e,
460
- `repos/${s.owner}/${s.repo}/pulls/${s.number}/reviews`,
461
- n
462
- ) : { delivered: !1 };
463
- }
464
- async function Tr(e, t, n) {
465
- const s = F(t);
466
- return s ? tt(e, `repos/${s.owner}/${s.repo}/dispatches`, {
467
- event_type: "paper-camp-review",
468
- client_payload: { review: { number: Number(s.number), ...n } }
469
- }) : { delivered: !1 };
470
- }
471
- async function Tn(e, t) {
472
- const n = [...t.entries()].filter(
473
- ([, a]) => a.state === "open" || a.state === "draft"
474
- ), s = await Promise.all(n.map(([, a]) => Pn(e, a.url)));
475
- n.forEach(([a, r], i) => {
476
- const o = s[i];
477
- if (!o) return;
478
- const { reviewBodies: c, ...u } = o, l = !!r.headSha && c.some((f) => f.includes(An(a, r.headSha)));
479
- t.set(a, { ...r, ...u, ...l && { scoutReviewObserved: l } });
480
- });
481
- }
482
- async function xn(e) {
483
- const t = await Xe(e, [
484
- "pr",
485
- "list",
486
- "--state",
487
- "all",
488
- "--limit",
489
- "2000",
490
- "--json",
491
- "number,url,state,isDraft,headRefName,headRefOid,body,reviewDecision"
492
- ]);
493
- if (!t) return;
494
- const n = /* @__PURE__ */ new Map();
495
- for (const s of t) {
496
- const a = Ve(s.body, s.headRefName);
497
- if (!a) continue;
498
- const r = $n(s), i = n.get(a);
499
- (!i || Re[r.state] > Re[i.state]) && n.set(a, r);
500
- }
501
- return await Tn(e, n), n;
502
- }
503
- async function nt(e, t = En) {
504
- let n = k.get(e);
505
- if (!n) {
506
- const a = await ze(e);
507
- a && (k.set(e, a), n = a);
508
- }
509
- if (n && Date.now() - n.fetchedAt < t) return n.prs;
510
- if (!Number.isFinite(t)) return n == null ? void 0 : n.prs;
511
- const s = await xn(e);
512
- if (s !== void 0) {
513
- const a = Date.now();
514
- return k.set(e, { prs: s, fetchedAt: a }), await kn(e, s, a), s;
515
- }
516
- return n == null ? void 0 : n.prs;
517
- }
518
- function xr(e) {
519
- var t;
520
- return (t = k.get(e)) == null ? void 0 : t.prs;
521
- }
522
- function Or(e, t) {
523
- const n = (a) => `${a.number}:${a.headSha ?? "unknown"}`, s = [];
524
- for (const [a, r] of t) {
525
- if (r.reviewDecision !== "changes-requested") continue;
526
- const i = e == null ? void 0 : e.get(a);
527
- ((i == null ? void 0 : i.reviewDecision) === "changes-requested" ? n(i) : void 0) !== n(r) && s.push({ entityId: a, pr: r });
528
- }
529
- return s;
530
- }
531
- async function Dr(e) {
532
- let t = k.get(e);
533
- if (!t) {
534
- const n = await ze(e);
535
- n && (k.set(e, n), t = n);
536
- }
537
- return (t == null ? void 0 : t.fetchedAt) ?? null;
538
- }
539
- async function jr(e) {
540
- if (e === void 0) {
541
- k.clear();
542
- return;
543
- }
544
- const t = k.get(e);
545
- t && k.set(e, { ...t, fetchedAt: 0 });
546
- const n = ce(e);
547
- try {
548
- const s = await v(n, "utf-8"), a = JSON.parse(s);
549
- a.fetchedAt = 0, await E(n, `${JSON.stringify(a, null, 2)}
550
- `, "utf-8");
551
- } catch {
552
- }
553
- }
554
- const st = /^#{2,3}\s+/, Y = /^[-*]\s+\[([ xX])\]\s+(.*)$/, On = /^\[(review|manual|issue)\]\s+(.*)$/, Dn = /^run:\s*(.+)$/, Pe = /^-\s+(\d{4}-\d{2}-\d{2}):\s*(.*)$/, jn = /^\[(?:phase:(\d+)|body)\]\s+(?:\[(decision|question)\]\s+)?(.*)$/, Ce = /^-\s+\[([ xX])\]\s+(?:(\d{4}-\d{2}-\d{2})\s+)?\[(log|clarification|review|note|decision|question|chat)\]\s+(\[agent\]\s+)?(?:\[\[([A-Za-z]+-\d+)\]\]\s+)?(.*)$/, Mn = ["note", "decision", "question"];
555
- function de(e, t, n, s) {
556
- const a = [];
557
- let r = t;
558
- for (; r < n; ) {
559
- const i = e[r];
560
- if (i.trim() === "" || !/^\s/.test(i)) break;
561
- const o = i.trimStart();
562
- if (s.test(o) || st.test(o)) break;
563
- a.push(o), r++;
564
- }
565
- return { text: a.join(" "), next: r };
566
- }
567
- function ue(e, t) {
568
- return t ? `${e.trim()} ${t}` : e.trim();
569
- }
570
- function at(e, t, n) {
571
- const s = [];
572
- let a = t;
573
- for (; a < n; ) {
574
- const r = e[a].match(Y);
575
- if (r) {
576
- const i = r[1].toLowerCase() === "x", o = r[2].trim(), c = o.match(On), u = c ? c[2].trim() : o, l = c ? c[1] : void 0, f = [];
577
- let h;
578
- for (a++; a < n; ) {
579
- const g = e[a];
580
- if (g.trim() === "" || Y.test(g) || st.test(g)) break;
581
- if (/^\s/.test(g)) {
582
- const w = g.trimStart(), y = w.match(Dn), m = y ? zt(y[1]) : void 0;
583
- m ? h = m : f.push(w), a++;
584
- } else
585
- break;
586
- }
587
- s.push({
588
- done: i,
589
- text: u,
590
- description: f.length > 0 ? f.join(`
591
- `) : void 0,
592
- source: l,
593
- run: h
594
- });
595
- } else
596
- a++;
597
- }
598
- return s;
599
- }
600
- function le(e, t, n) {
601
- const s = [];
602
- let a = t;
603
- for (; a < n; ) {
604
- const r = e[a].match(Pe);
605
- if (!r) {
606
- a++;
607
- continue;
608
- }
609
- const i = de(e, a + 1, n, Pe);
610
- s.push({ date: r[1], text: ue(r[2], i.text) }), a = i.next;
611
- }
612
- return s;
613
- }
614
- function rt(e, t) {
615
- const n = [e];
616
- for (const s of t) {
617
- const a = s.source ? `[${s.source}] ${s.text}` : s.text;
618
- if (n.push(`- [${s.done ? "x" : " "}] ${a}`), s.description)
619
- for (const r of s.description.split(`
620
- `)) n.push(` ${r}`);
621
- s.run && n.push(` run: ${Vt(s.run)}`);
622
- }
623
- return n;
624
- }
625
- function fe(e, t) {
626
- return [e, ...t.map((n) => `- ${n.date}: ${n.text}`)];
627
- }
628
- function Ln(e) {
629
- return e.kind === "phase" ? `[phase:${e.index}]` : "[body]";
630
- }
631
- function Fn(e, t, n) {
632
- const s = [];
633
- let a = t;
634
- for (; a < n; ) {
635
- const r = e[a].match(Y), i = r ? r[2].trim().match(jn) : null;
636
- if (!r || !i) {
637
- a++;
638
- continue;
639
- }
640
- const o = i[1] ? { kind: "phase", index: Number(i[1]) } : { kind: "body" }, c = i[2], u = de(e, a + 1, n, Y);
641
- s.push({
642
- anchor: o,
643
- prose: ue(i[3], u.text),
644
- state: r[1].toLowerCase() === "x" ? "resolved" : "open",
645
- ...c ? { kind: c } : {}
646
- }), a = u.next;
647
- }
648
- return s;
649
- }
650
- function Hn(e) {
651
- const t = ["### Notes"];
652
- for (const n of e) {
653
- const s = n.kind && n.kind !== "note" ? `[${n.kind}] ` : "";
654
- t.push(
655
- `- [${n.state === "resolved" ? "x" : " "}] ${Ln(n.anchor)} ${s}${n.prose}`
656
- );
657
- }
658
- return t;
659
- }
660
- const Z = {
661
- headingRe: /^#{2,3}\s+Phases\s*$/i,
662
- parseEntries: at,
663
- formatLines: (e) => rt("### Phases", e)
664
- }, it = {
665
- headingRe: /^#{2,3}\s+Fixes\s*$/i,
666
- parseEntries: at,
667
- formatLines: (e) => rt("### Fixes", e)
668
- }, H = {
669
- headingRe: /^#{2,3}\s+Log\s*$/i,
670
- parseEntries: le,
671
- formatLines: (e) => fe("### Log", e)
672
- }, pe = {
673
- headingRe: /^#{2,3}\s+Clarifications\s*$/i,
674
- parseEntries: le,
675
- formatLines: (e) => fe("### Clarifications", e)
676
- }, he = {
677
- headingRe: /^#{2,3}\s+Notes\s*$/i,
678
- parseEntries: Fn,
679
- formatLines: Hn
680
- }, Gn = {
681
- headingRe: /^#{2,3}\s+Review\s*$/i,
682
- parseEntries: le,
683
- formatLines: (e) => fe("### Review", e)
684
- };
685
- function Bn(e, t, n) {
686
- const s = [];
687
- let a = t;
688
- for (; a < n; ) {
689
- const r = e[a].match(Ce);
690
- if (!r) {
691
- a++;
692
- continue;
693
- }
694
- const i = r[3], o = de(e, a + 1, n, Ce), c = { kind: i, text: ue(r[6], o.text) };
695
- r[2] && (c.date = r[2]), r[4] && (c.from = "agent"), r[5] && (c.entityId = r[5]), Mn.includes(i) && (c.state = r[1].toLowerCase() === "x" ? "resolved" : "open"), s.push(c), a = o.next;
696
- }
697
- return s;
698
- }
699
- function Yn(e) {
700
- const t = ["### Thread"];
701
- for (const n of e) {
702
- const s = n.state ? n.state === "resolved" : !0, a = n.date ? `${n.date} ` : "", r = n.from === "agent" ? "[agent] " : "", i = n.entityId ? `[[${n.entityId}]] ` : "";
703
- t.push(`- [${s ? "x" : " "}] ${a}[${n.kind}] ${r}${i}${n.text}`);
704
- }
705
- return t;
706
- }
707
- const ot = {
708
- headingRe: /^#{2,3}\s+Thread\s*$/i,
709
- parseEntries: Bn,
710
- formatLines: Yn
711
- }, $ = d.preprocess(
712
- (e) => typeof e == "string" ? { agent: e } : e,
713
- d.object({
714
- agent: d.enum(L),
715
- model: d.string().optional(),
716
- effort: d.string().optional()
717
- })
718
- ), I = d.string().regex(/^\d{4}-\d{2}-\d{2}$/, "expected YYYY-MM-DD"), qn = d.object({
719
- status: d.enum(["idea", "planned", "in-progress", "review", "done", "dropped"]),
720
- kind: d.enum(["feat", "fix", "chore", "docs", "refactor"]).optional(),
721
- id: d.string().optional(),
722
- idea: d.string().optional(),
723
- agent: d.enum(L).optional(),
724
- created: I,
725
- updated: I.optional(),
726
- tags: d.string().optional()
727
- }), Un = d.object({
728
- id: d.string().describe("Permanent plan ID, e.g. FEAT-24"),
729
- title: d.string().describe('Human-readable plan name, e.g. "Plan storage architecture"'),
730
- kind: d.enum(["feat", "fix", "chore", "docs", "refactor"]).describe("Plan kind matching Conventional Commits types"),
731
- status: d.enum(["idea", "planned", "in-progress", "review", "done", "dropped"]).describe("Current lifecycle status"),
732
- idea: d.string().optional().describe("IDEA-N backlink if this plan grew out of an idea"),
733
- agent: d.enum(L).optional().describe("Per-plan agent override"),
734
- created: I.describe("Creation date (YYYY-MM-DD)"),
735
- updated: I.optional().describe("Last significant update date (YYYY-MM-DD)"),
736
- audited: I.optional().describe("Date of last successful convergence audit (YYYY-MM-DD)"),
737
- "audited-hash": d.string().optional().describe("Content hash of the plan at last audit, used to detect edits regardless of mtime"),
738
- tags: d.array(d.string()).optional().describe("Tagging categories")
739
- }).passthrough(), Kn = d.object({
740
- id: d.string().describe("Permanent idea ID, e.g. IDEA-20"),
741
- title: d.string().describe("Short idea headline (3-6 words)"),
742
- kind: d.enum(["idea", "note"]).optional().describe('"note" for ideas that never need a plan; omitted means a plan-bearing idea'),
743
- status: d.enum(["open", "done", "dropped"]).optional().describe("Manual lifecycle, valid only on notes — plan-bearing ideas carry no status")
744
- }).passthrough().refine((e) => e.status === void 0 || e.kind === "note", {
745
- message: "status is only valid on ideas with kind: note",
746
- path: ["status"]
747
- }), ct = d.object({
748
- id: d.string().describe("Permanent lifetime entity ID, e.g. IDEA-45 — never changes"),
749
- title: d.string().describe("Human-readable entity name"),
750
- type: d.enum(["feat", "fix", "chore", "docs", "refactor"]).optional().describe(
751
- "Work classification (Conventional Commits values) driving commit types and branch prefixes; usually set once a plan is drafted"
752
- ),
753
- kind: d.enum(["note", "fix", "board", "ticket"]).optional().describe(
754
- '"note" marks an entity that never grows phases; "fix" is a follow-up entity linked to a done/archived parent via `idea`; "board" decomposes into "ticket" children instead of phases; "ticket" is a full entity linked back to its board via `idea`; omitted for normal ideas'
755
- ),
756
- status: d.enum(["idea", "planned", "in-progress", "review", "done", "dropped", "open"]).optional().describe(
757
- "Stored override, no longer the source of truth: most lifecycle states derive from phases/branch/PR. Only dropped, a planless idea/note being closed, or the offline fallback need this set."
758
- ),
759
- agent: d.enum(L).optional().describe("Per-entity agent override"),
760
- created: I.describe("Creation date (YYYY-MM-DD)"),
761
- updated: I.optional().describe("Last significant update date (YYYY-MM-DD)"),
762
- audited: I.optional().describe("Date of last successful convergence audit (YYYY-MM-DD)"),
763
- "audited-hash": d.string().optional().describe(
764
- "Content hash of the entity at last audit, used to detect edits regardless of mtime"
765
- ),
766
- released: d.string().optional().describe(
767
- "Version tag (e.g. v0.13.1) that first shipped this idea, stamped from the release train"
768
- ),
769
- tags: d.array(d.string()).optional().describe("Tagging categories"),
770
- idea: d.string().optional().describe("IDEA-N backlink to the parent this fix addresses; required when kind: fix"),
771
- subject: d.string().optional().describe('Subject group name; absent renders as the virtual "No subject" group'),
772
- order: d.number().int().optional().describe("Run order; absent means unordered, sorting after ordered entries"),
773
- issueSource: d.string().optional().describe(
774
- "sourceKind:sourceKey of the Issue (IDEA-192) this entity was promoted from, if any"
775
- )
776
- }).passthrough(), Wn = new Set(
777
- Object.keys(ct.shape)
778
- ), dt = ct.refine(
779
- (e) => e.kind !== "note" || e.status === void 0 || ["open", "done", "dropped"].includes(e.status),
780
- {
781
- message: "a note entity must use status open, done, or dropped",
782
- path: ["status"]
783
- }
784
- ).refine((e) => e.kind === "note" || e.status !== "open", {
785
- message: "status open is only valid on entities with kind: note",
786
- path: ["status"]
787
- }).refine((e) => e.kind !== "fix" || e.idea !== void 0, {
788
- message: "a kind: fix entity requires an idea: link to its parent",
789
- path: ["idea"]
790
- }).refine((e) => e.kind !== "ticket" || e.idea !== void 0, {
791
- message: "a kind: ticket entity requires an idea: link to its board",
792
- path: ["idea"]
793
- }), Jn = d.object({
794
- name: d.string(),
795
- cmd: d.string(),
796
- port: d.number().int().positive().optional(),
797
- healthcheck: d.string().optional()
798
- }), zn = d.object({
799
- name: d.string(),
800
- cmd: d.string(),
801
- fixCmd: d.string().optional()
802
- }), Vn = d.object({
803
- repo: d.string(),
804
- branch: d.string().optional(),
805
- releasePlease: d.boolean().optional()
806
- }), Xn = d.object({
807
- services: d.array(Jn).optional(),
808
- checks: d.array(zn).optional(),
809
- ci: Vn.optional()
810
- }), Zn = d.object({
811
- from: d.string(),
812
- to: d.string()
813
- }), Qn = d.object({
814
- name: d.string(),
815
- prompt: d.string()
816
- }), es = d.object({
817
- ceiling: d.number().optional(),
818
- floor: d.number().optional(),
819
- window: Zn.optional(),
820
- roots: d.array(d.string()).optional(),
821
- maxChunks: d.number().int().positive().optional(),
822
- threshold: d.number().min(0).max(100).optional(),
823
- checks: d.record(d.enum(qt), d.boolean()).optional(),
824
- customChecks: d.array(Qn).optional(),
825
- maxTurns: d.number().int().positive().optional(),
826
- maxCostUsd: d.number().positive().optional()
827
- }), ts = d.object({
828
- kinds: d.record(d.enum(Ut), d.boolean()).optional()
829
- }), ns = d.object({
830
- endpoint: d.string(),
831
- keys: d.object({ p256dh: d.string(), auth: d.string() })
832
- }), Mr = d.discriminatedUnion("transport", [
833
- d.object({
834
- transport: d.literal("webpush"),
835
- name: d.string(),
836
- subscription: ns
837
- }),
838
- d.object({ transport: d.literal("expo"), name: d.string(), token: d.string() })
839
- ]), Lr = d.discriminatedUnion("transport", [
840
- d.object({ transport: d.literal("webpush"), endpoint: d.string() }),
841
- d.object({ transport: d.literal("expo"), token: d.string() })
842
- ]), ss = d.object({
843
- version: d.number().int().describe("Corpus format version (see CORPUS_FORMAT_VERSION), not the package version"),
844
- projectName: d.string(),
845
- initializedAt: d.string(),
846
- nextId: d.object({
847
- // idea: unified-entity counter minting lifetime IDEA-N ids. ticket: board/ticket
848
- // counter (IDEA-201) minting TICKET-N ids. The rest are legacy per-kind counters.
849
- idea: d.number().optional(),
850
- ticket: d.number().optional(),
851
- feat: d.number().optional(),
852
- fix: d.number().optional(),
853
- chore: d.number().optional(),
854
- docs: d.number().optional(),
855
- refactor: d.number().optional()
856
- }).optional(),
857
- defaultAgent: d.enum(L).optional(),
858
- defaultAgents: d.object({
859
- phase: $,
860
- planDraft: $,
861
- ideaExtend: $,
862
- commitSuggest: $,
863
- feedback: $,
864
- codeReview: $,
865
- deskDiscovery: $,
866
- nightShift: $
867
- }).optional(),
868
- desk: Xn.optional(),
869
- night: es.optional(),
870
- notifications: ts.optional()
871
- }), as = d.object({
872
- id: d.string(),
873
- kind: d.enum([
874
- "completed",
875
- "reply",
876
- "check-failed",
877
- "pr-review-changes-requested",
878
- "night-review-findings",
879
- "service-stopped"
880
- ]),
881
- entityId: d.string(),
882
- entityTitle: d.string(),
883
- text: d.string(),
884
- date: d.string(),
885
- read: d.boolean(),
886
- outcome: d.enum(["done", "error"]).optional(),
887
- push: d.boolean().optional().default(!0)
888
- }), _e = /^##\s+(.+?)\s*$/, rs = /^\*\*([A-Za-z][A-Za-z-]*):\*\*\s*(.*)$/, is = /^#{2,3}\s+/;
889
- function q(e, t) {
890
- const n = e.split(`
891
- `), s = n.findIndex((o) => t.headingRe.test(o));
892
- if (s === -1) return { body: e, entries: [] };
893
- let a = n.length;
894
- for (let o = s + 1; o < n.length; o++)
895
- if (is.test(n[o])) {
896
- a = o;
897
- break;
898
- }
899
- const r = t.parseEntries(n, s + 1, a);
900
- return { body: [...n.slice(0, s), ...n.slice(a)].join(`
901
- `).trim(), entries: r };
902
- }
903
- const Ne = {
904
- phases: Z,
905
- log: H,
906
- clarifications: pe,
907
- notes: he,
908
- review: Gn
909
- };
910
- function me(e) {
911
- const t = {
912
- phases: [],
913
- log: [],
914
- clarifications: [],
915
- notes: [],
916
- review: []
917
- };
918
- let n = e;
919
- for (const s of Object.keys(Ne)) {
920
- const a = q(n, Ne[s]);
921
- n = a.body, t[s] = a.entries;
922
- }
923
- return { body: n, ...t };
924
- }
925
- function os(e) {
926
- const t = e.split(`
927
- `), n = [];
928
- for (let a = 0; a < t.length; a++)
929
- _e.test(t[a]) && n.push(a);
930
- const s = [];
931
- for (let a = 0; a < n.length; a++) {
932
- const r = n[a], i = a + 1 < n.length ? n[a + 1] : t.length, o = t[r].match(_e)[1], c = t.slice(r + 1, i);
933
- let u = 0;
934
- for (; u < c.length && c[u].trim() === ""; ) u++;
935
- const l = {};
936
- for (; u < c.length; ) {
937
- const G = c[u].match(rs);
938
- if (!G) break;
939
- l[G[1].toLowerCase()] = G[2].trim(), u++;
940
- }
941
- for (; u < c.length && c[u].trim() === ""; ) u++;
942
- const f = c.slice(u).join(`
943
- `).trim(), { body: h, phases: g, log: w, clarifications: y, notes: m, review: D } = me(f);
944
- s.push({ title: o, fields: l, body: h, phases: g, log: w, clarifications: y, notes: m, review: D });
945
- }
946
- return s;
947
- }
948
- function Fr(e) {
949
- const t = [], n = [];
950
- for (const s of os(e)) {
951
- const a = qn.safeParse(s.fields);
952
- if (!a.success) {
953
- n.push({
954
- title: s.title,
955
- message: a.error.issues.map((i) => i.message).join("; ")
956
- });
957
- continue;
958
- }
959
- const r = a.data;
960
- t.push({
961
- title: s.title,
962
- status: r.status,
963
- kind: r.kind,
964
- id: r.id,
965
- idea: r.idea,
966
- agent: r.agent,
967
- created: r.created,
968
- updated: r.updated,
969
- tags: r.tags ? r.tags.split(",").map((i) => i.trim()).filter(Boolean) : [],
970
- body: s.body,
971
- phases: s.phases,
972
- log: s.log,
973
- clarifications: s.clarifications,
974
- notes: s.notes,
975
- review: s.review
976
- });
977
- }
978
- return { entries: t, warnings: n };
979
- }
980
- const cs = /^---\s*\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n)?/;
981
- function ge(e, t) {
982
- const n = [], s = e.match(cs);
983
- if (!s)
984
- return { data: null, body: e.trim(), warnings: n };
985
- const a = s[1], r = e.slice(s[0].length).trim();
986
- let i;
987
- try {
988
- i = V(a);
989
- } catch (c) {
990
- return n.push({
991
- title: "(frontmatter)",
992
- message: `Invalid YAML frontmatter: ${c.message}`
993
- }), { data: null, body: r, warnings: n };
994
- }
995
- if (typeof i != "object" || i === null || Array.isArray(i))
996
- return n.push({
997
- title: "(frontmatter)",
998
- message: "YAML frontmatter did not produce an object"
999
- }), { data: null, body: r, warnings: n };
1000
- const o = t.safeParse(i);
1001
- if (!o.success) {
1002
- const c = i.id;
1003
- return n.push({
1004
- title: c ?? "(frontmatter)",
1005
- message: o.error.issues.map((u) => u.message).join("; ")
1006
- }), { data: null, body: r, warnings: n };
1007
- }
1008
- return { data: o.data, body: r, warnings: n };
1009
- }
1010
- function ds(e, t) {
1011
- const n = Object.fromEntries(
1012
- Object.entries(e).filter(([s]) => !t.has(s))
1013
- );
1014
- return Object.keys(n).length > 0 ? n : void 0;
1015
- }
1016
- function ut(e) {
1017
- const t = [], {
1018
- data: n,
1019
- body: s,
1020
- warnings: a
1021
- } = ge(e, dt);
1022
- if (t.push(...a), !n)
1023
- return { entries: [], warnings: t };
1024
- const {
1025
- body: r,
1026
- phases: i,
1027
- log: o,
1028
- clarifications: c,
1029
- notes: u,
1030
- review: l
1031
- } = me(s), { body: f, entries: h } = q(r, it), { body: g, entries: w } = q(f, ot), y = [...w, ...rn(o, c, u, l)];
1032
- n.kind === "note" && i.length > 0 && t.push({
1033
- title: n.title,
1034
- message: "note entities must not carry a Phases section — kept, but fix the file"
1035
- });
1036
- const m = ds(n, Wn);
1037
- return { entries: [{
1038
- id: n.id,
1039
- title: n.title,
1040
- type: n.type,
1041
- kind: n.kind,
1042
- status: n.status,
1043
- idea: n.idea,
1044
- agent: n.agent,
1045
- created: n.created,
1046
- updated: n.updated,
1047
- audited: n.audited,
1048
- auditedHash: n["audited-hash"],
1049
- released: n.released,
1050
- tags: n.tags ?? [],
1051
- subject: n.subject,
1052
- order: n.order,
1053
- issueSource: n.issueSource,
1054
- body: g,
1055
- phases: i,
1056
- ...h.length > 0 && { fixes: h },
1057
- ...y.length > 0 && { thread: y },
1058
- ...m && { unknownFrontmatter: m }
1059
- }], warnings: t };
1060
- }
1061
- function Hr(e) {
1062
- const t = [], {
1063
- data: n,
1064
- body: s,
1065
- warnings: a
1066
- } = ge(e, Un);
1067
- if (t.push(...a), !n)
1068
- return { entries: [], warnings: t };
1069
- const { body: r, phases: i, log: o, clarifications: c, notes: u, review: l } = me(s);
1070
- return { entries: [{
1071
- title: n.title,
1072
- status: n.status,
1073
- kind: n.kind,
1074
- id: n.id,
1075
- idea: n.idea,
1076
- agent: n.agent,
1077
- created: n.created,
1078
- updated: n.updated,
1079
- audited: n.audited,
1080
- auditedHash: n["audited-hash"],
1081
- tags: n.tags ?? [],
1082
- body: r,
1083
- phases: i,
1084
- log: o,
1085
- clarifications: c,
1086
- notes: u,
1087
- review: l
1088
- }], warnings: t };
1089
- }
1090
- function Gr(e) {
1091
- const {
1092
- data: t,
1093
- body: n,
1094
- warnings: s
1095
- } = ge(e, Kn);
1096
- if (!t)
1097
- return { entries: [], warnings: s };
1098
- const { body: a, entries: r } = q(n, H);
1099
- return { entries: [{
1100
- id: t.id,
1101
- title: t.title,
1102
- body: a || "",
1103
- ...t.kind && { kind: t.kind },
1104
- ...t.status && { status: t.status },
1105
- ...r.length > 0 && { log: r }
1106
- }], warnings: s };
1107
- }
1108
- const us = /^(IDEA-\d+):\s*/, ls = /\n---+\n/;
1109
- function Br(e) {
1110
- return e.split(ls).filter(Boolean).map((n) => {
1111
- var c;
1112
- const s = n.match(/^#{1,3}\s+(.+)/m), a = s ? s[1].trim() : ((c = n.trim().split(`
1113
- `)[0]) == null ? void 0 : c.trim()) ?? "Untitled", r = a.match(us), i = (r == null ? void 0 : r[1]) ?? null, o = i ? a.slice(r[0].length) : a;
1114
- return { id: i, title: o, body: n.trim() };
1115
- });
1116
- }
1117
- const Te = 40;
1118
- function fs(e, t = []) {
1119
- const n = [];
1120
- for (const s of e)
1121
- if (!s.archived && (t.length > 0 && s.subject && !t.includes(s.subject) && n.push({
1122
- kind: "orphan-subject",
1123
- section: "plans",
1124
- title: s.title,
1125
- planId: s.id,
1126
- message: `Subject "${s.subject}" isn't in the roadmap vocabulary — "${s.title}"`
1127
- }), s.status !== "done" && s.status !== "dropped")) {
1128
- const a = [];
1129
- s.title.length > Te && a.push(`${s.title.length} chars over the ${Te}-char limit`), s.title.includes("—") && a.push("em-dash subtitle"), a.length > 0 && n.push({
1130
- kind: "title-style",
1131
- section: "plans",
1132
- title: s.title,
1133
- planId: s.id,
1134
- message: `Title style — ${a.join(", ")}; the detail belongs in the body: "${s.title}"`
1135
- });
1136
- }
1137
- return n;
1138
- }
1139
- function ps(e) {
1140
- const t = /* @__PURE__ */ new Map(), n = [];
1141
- for (const s of e.split(`
1142
- `)) {
1143
- const a = s.trim();
1144
- if (!a) continue;
1145
- let r;
1146
- try {
1147
- r = JSON.parse(a);
1148
- } catch {
1149
- continue;
1150
- }
1151
- const i = t.get(r.id);
1152
- i ? t.set(r.id, { ...i, ...r }) : (t.set(r.id, r), n.push(r.id));
1153
- }
1154
- return n.map((s) => t.get(s));
1155
- }
1156
- function Yr(e) {
1157
- const t = [];
1158
- for (const n of e.split(`
1159
- `)) {
1160
- const s = n.trim();
1161
- if (s)
1162
- try {
1163
- const a = as.safeParse(JSON.parse(s));
1164
- a.success && t.push(a.data);
1165
- } catch {
1166
- }
1167
- }
1168
- return t;
1169
- }
1170
- const lt = /^-\s+(\d{4}-\d{2}-\d{2}):\s+(.+?)\s+—\s+(.*)$/;
1171
- function qr(e) {
1172
- const t = [];
1173
- for (const n of e.split(`
1174
- `)) {
1175
- const s = n.match(lt);
1176
- s && t.push({ date: s[1], title: s[2].trim(), description: s[3].trim() });
1177
- }
1178
- return t;
1179
- }
1180
- const se = " — ";
1181
- function hs(e) {
1182
- return e.split(`
1183
- `).map((t) => t.trim()).filter((t) => t.length > 0).map((t) => {
1184
- const n = t.indexOf(se);
1185
- return n === -1 ? { id: t, title: "" } : {
1186
- id: t.slice(0, n).trim(),
1187
- title: t.slice(n + se.length).trim()
1188
- };
1189
- });
1190
- }
1191
- function Ur(e) {
1192
- return e.length === 0 ? "" : `${e.map((t) => `${t.id}${se}${t.title}`).join(`
1193
- `)}
1194
- `;
1195
- }
1196
- async function ms(e) {
1197
- try {
1198
- return await z(e);
1199
- } catch {
1200
- return [];
1201
- }
1202
- }
1203
- async function ft(e) {
1204
- try {
1205
- return await v(e, "utf-8");
1206
- } catch {
1207
- return "";
1208
- }
1209
- }
1210
- async function gs(e) {
1211
- const t = await ft(p(e, "..", "run-order.md"));
1212
- return new Map(hs(t).map((n, s) => [n.id, s + 1]));
1213
- }
1214
- async function x(e) {
1215
- const t = [], n = [];
1216
- let s = 0;
1217
- const a = await gs(e);
1218
- for (const r of [e, p(e, "archive")]) {
1219
- const i = r !== e, o = (await ms(r)).filter((u) => u.endsWith(".md") && u !== "index.md");
1220
- s += o.length;
1221
- const c = await Promise.all(
1222
- o.map(async (u) => {
1223
- const l = await ft(p(r, u));
1224
- return l ? ut(l) : { warnings: [{ title: u, message: "Could not read entity file" }] };
1225
- })
1226
- );
1227
- for (const u of c)
1228
- "entries" in u && t.push(
1229
- ...u.entries.map((l) => ({
1230
- ...l,
1231
- archived: i,
1232
- storedOrder: l.order,
1233
- order: a.get(l.id) ?? l.order
1234
- }))
1235
- ), n.push(...u.warnings);
1236
- }
1237
- return { entries: t, warnings: n, fileCount: s };
1238
- }
1239
- async function ye(e, t) {
1240
- const { entries: n, warnings: s } = await x(e), a = p(e, "..", ".."), [r, i] = await Promise.all([
1241
- nt(a, t),
1242
- We(a)
1243
- ]);
1244
- return { entries: n, warnings: s, prs: r, resolved: r !== void 0, mainActivityIds: i };
1245
- }
1246
- async function Kr(e) {
1247
- const { entries: t, warnings: n, prs: s, resolved: a, mainActivityIds: r } = await ye(e), i = t.map((c) => ({
1248
- ...c,
1249
- status: X(c, s == null ? void 0 : s.get(c.id), a, r.has(c.id))
1250
- }));
1251
- return { entries: i.map((c) => {
1252
- if (c.kind !== "board" || c.archived || c.status === "done" || c.status === "dropped") return c;
1253
- const u = i.filter((l) => l.kind === "ticket" && l.idea === c.id).map((l) => l.status ?? "idea");
1254
- return { ...c, status: Ye(u) };
1255
- }), warnings: n };
1256
- }
1257
- async function Wr(e, t) {
1258
- const { entries: n, warnings: s, prs: a, resolved: r, mainActivityIds: i } = await ye(
1259
- e,
1260
- t
1261
- ), o = n.filter((u) => u.kind !== "note").map((u) => Ue(u, a == null ? void 0 : a.get(u.id), r, i.has(u.id)));
1262
- return { entries: o.map((u) => {
1263
- if (u.entityKind !== "board" || u.archived || u.status === "done" || u.status === "dropped")
1264
- return u;
1265
- const l = o.filter((f) => f.entityKind === "ticket" && f.idea === u.id).map((f) => f.status);
1266
- return { ...u, status: Ye(l) };
1267
- }), warnings: s, resolved: r };
1268
- }
1269
- async function Jr(e) {
1270
- const { entries: t, prs: n, resolved: s } = await ye(e);
1271
- return {
1272
- entries: t.flatMap((a) => {
1273
- const r = n == null ? void 0 : n.get(a.id);
1274
- return r && an(a, r) ? [{ id: a.id, title: a.title, pr: r }] : [];
1275
- }),
1276
- resolved: s
1277
- };
1278
- }
1279
- async function zr(e) {
1280
- const { entries: t, warnings: n } = await x(e);
1281
- return {
1282
- entries: t.filter((s) => s.kind === "note").map(un),
1283
- warnings: n
1284
- };
1285
- }
1286
- const ys = [
1287
- {
1288
- id: "frontmatter-schema",
1289
- category: "metadata",
1290
- severity: "error",
1291
- summary: "Frontmatter fails entityFrontmatterSchema — missing required field, bad enum, malformed date, or a violated note/status refinement."
1292
- },
1293
- {
1294
- id: "filename-id-mismatch",
1295
- category: "metadata",
1296
- severity: "error",
1297
- summary: "Frontmatter id does not match the file name (IDEA-N.md); references break silently."
1298
- },
1299
- {
1300
- id: "duplicate-id",
1301
- category: "metadata",
1302
- severity: "error",
1303
- summary: "Two entity files share one id; whichever loads last wins and the other disappears."
1304
- },
1305
- {
1306
- id: "id-counter-stale",
1307
- category: "metadata",
1308
- severity: "error",
1309
- summary: "An entity id is greater than or equal to config.nextId.idea, so the next mint collides with an existing file."
1310
- },
1311
- {
1312
- id: "corpus-format-too-new",
1313
- category: "metadata",
1314
- severity: "error",
1315
- summary: "config.version is newer than the format this paper-camp understands; reads stay tolerant but writes are refused until it upgrades."
1316
- },
1317
- {
1318
- id: "phases-list-split",
1319
- category: "structural",
1320
- severity: "error",
1321
- summary: "A checkbox line lives outside the Phases section — a mid-file heading split the list and orphaned phases, so the entity parses as fewer phases than it has."
1322
- },
1323
- {
1324
- id: "note-has-phases",
1325
- category: "structural",
1326
- severity: "error",
1327
- summary: "A kind: note entity carries a Phases section; notes never grow phases."
1328
- },
1329
- {
1330
- id: "duplicate-phases-section",
1331
- category: "structural",
1332
- severity: "error",
1333
- summary: "A file carries more than one Phases heading — a redraft appended instead of replacing the existing list in place."
1334
- },
1335
- {
1336
- id: "no-agent-phase",
1337
- category: "structural",
1338
- severity: "warning",
1339
- summary: "A planned/in-progress/review entity has no phase an agent can run — every phase is `[manual]` or the Phases section is empty, so the idea can never move on its own."
1340
- },
1341
- {
1342
- id: "archive-placement",
1343
- category: "structural",
1344
- severity: "warning",
1345
- summary: "File location contradicts status — a done/dropped entity still under ideas/, or an active entity already under ideas/archive/."
1346
- },
1347
- {
1348
- id: "dangling-link",
1349
- category: "structural",
1350
- severity: "warning",
1351
- summary: "A [[IDEA-N]] wikilink targets an id that no entity in the corpus defines."
1352
- },
1353
- {
1354
- id: "missing-permissions-allow",
1355
- category: "tooling",
1356
- severity: "warning",
1357
- summary: ".claude/settings.json has no permissions.allow list, so a headless run denies every edit and shell command outside the defaults — run `paper-camp init --settings` to add it."
1358
- },
1359
- {
1360
- id: "no-default-agents",
1361
- category: "tooling",
1362
- severity: "warning",
1363
- summary: "config.json has no defaultAgents, so every run uses the built-in agent and model — choose them in Settings → General so a project never runs on a fallback it did not pick."
1364
- }
1365
- ], ws = Object.fromEntries(
1366
- ys.map((e) => [e.id, e])
1367
- );
1368
- function U(e) {
1369
- return ws[e.rule].severity;
1370
- }
1371
- const xe = { error: 0, warning: 1 };
1372
- function pt(e) {
1373
- return [...e].sort(
1374
- (t, n) => t.file.localeCompare(n.file) || t.line - n.line || xe[U(t)] - xe[U(n)] || t.rule.localeCompare(n.rule)
1375
- );
1376
- }
1377
- function Vr(e) {
1378
- const t = pt(e).map((n) => ({
1379
- ...n,
1380
- severity: U(n)
1381
- }));
1382
- return {
1383
- findings: t,
1384
- errorCount: t.filter((n) => n.severity === "error").length,
1385
- warningCount: t.filter((n) => n.severity === "warning").length
1386
- };
1387
- }
1388
- function Xr(e) {
1389
- const t = pt(e);
1390
- let n = 0, s = 0;
1391
- const a = t.map((i) => {
1392
- const o = U(i);
1393
- return o === "error" ? n++ : s++, `${i.file}:${i.line} ${o.padEnd(7)} ${i.rule} ${i.message}`;
1394
- }), r = t.length === 0 ? "paper-camp doctor: no issues found." : `paper-camp doctor: ${n} error(s), ${s} warning(s).`;
1395
- return {
1396
- text: [...a, ...a.length > 0 ? [""] : [], r].join(`
1397
- `),
1398
- errorCount: n,
1399
- warningCount: s
1400
- };
1401
- }
1402
- const A = 1;
1403
- class bs extends Error {
1404
- constructor(t) {
1405
- super(
1406
- `This corpus is in format version ${t}, newer than the format version ${A} this paper-camp understands. Writes are refused so a field this version doesn’t recognise is never dropped — upgrade paper-camp to edit this corpus.`
1407
- ), this.name = "CorpusTooNewError";
1408
- }
1409
- }
1410
- async function ht(e) {
1411
- let t;
1412
- try {
1413
- t = await v(e, "utf-8");
1414
- } catch {
1415
- return null;
1416
- }
1417
- try {
1418
- const n = JSON.parse(t);
1419
- return n && typeof n == "object" && !Array.isArray(n) ? n : null;
1420
- } catch {
1421
- return null;
1422
- }
1423
- }
1424
- function mt(e) {
1425
- const t = e == null ? void 0 : e.version;
1426
- return typeof t == "number" ? t : null;
1427
- }
1428
- async function Zr(e) {
1429
- const t = mt(await ht(e));
1430
- if (t !== null && t > A)
1431
- throw new bs(t);
1432
- }
1433
- async function Qr(e) {
1434
- const t = await ht(e);
1435
- if (!t) return null;
1436
- const n = mt(t);
1437
- return n !== null && n >= A ? null : (t.version = A, await E(e, `${JSON.stringify(t, null, 2)}
1438
- `, "utf-8"), { from: n, to: A });
1439
- }
1440
- const gt = /^---\s*\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n)?/, vs = 2;
1441
- function we(e) {
1442
- const t = e.content.match(gt);
1443
- if (!t) return null;
1444
- let n = null;
1445
- try {
1446
- const s = V(t[1]);
1447
- s && typeof s == "object" && !Array.isArray(s) && (n = s);
1448
- } catch {
1449
- }
1450
- return { yaml: t[1], data: n };
1451
- }
1452
- function yt(e, t) {
1453
- const n = new RegExp(`^\\s*${t}:`), s = e.split(`
1454
- `).findIndex((a) => n.test(a));
1455
- return s === -1 ? 1 : vs + s;
1456
- }
1457
- function be(e) {
1458
- const t = we(e);
1459
- return t ? yt(t.yaml, "id") : 1;
1460
- }
1461
- function wt(e) {
1462
- var n, s;
1463
- const t = (s = (n = we(e)) == null ? void 0 : n.data) == null ? void 0 : s.id;
1464
- return typeof t == "string" ? t : e.id;
1465
- }
1466
- function R(e, t, n, s) {
1467
- return { file: e.path, line: t, rule: n, message: s };
1468
- }
1469
- const Es = ({ files: e }) => e.flatMap((t) => {
1470
- const n = t.content.match(gt);
1471
- if (!n)
1472
- return [R(t, 1, "frontmatter-schema", "missing YAML frontmatter block")];
1473
- let s;
1474
- try {
1475
- s = V(n[1]);
1476
- } catch (r) {
1477
- return [R(t, 1, "frontmatter-schema", `invalid YAML: ${r.message}`)];
1478
- }
1479
- if (typeof s != "object" || s === null || Array.isArray(s))
1480
- return [R(t, 1, "frontmatter-schema", "frontmatter is not a key/value mapping")];
1481
- const a = dt.safeParse(s);
1482
- return a.success ? [] : a.error.issues.map((r) => {
1483
- const i = String(r.path[0] ?? ""), o = r.path.length > 0 ? `${r.path.join(".")}: ${r.message}` : r.message;
1484
- return R(t, i ? yt(n[1], i) : 1, "frontmatter-schema", o);
1485
- });
1486
- }), ks = ({ files: e }) => e.flatMap((t) => {
1487
- var s, a;
1488
- const n = (a = (s = we(t)) == null ? void 0 : s.data) == null ? void 0 : a.id;
1489
- return typeof n != "string" || n === t.id ? [] : [
1490
- R(
1491
- t,
1492
- be(t),
1493
- "filename-id-mismatch",
1494
- `frontmatter id "${n}" does not match file name ${t.id}.md`
1495
- )
1496
- ];
1497
- }), Ss = ({ files: e }) => {
1498
- const t = /* @__PURE__ */ new Map();
1499
- for (const s of e) {
1500
- const a = wt(s), r = t.get(a);
1501
- r ? r.push(s) : t.set(a, [s]);
1502
- }
1503
- const n = [];
1504
- for (const [s, a] of t) {
1505
- if (a.length < 2) continue;
1506
- const r = a.map((i) => i.path).join(", ");
1507
- for (const i of a)
1508
- n.push(
1509
- R(
1510
- i,
1511
- be(i),
1512
- "duplicate-id",
1513
- `id ${s} is defined by ${a.length} files: ${r}`
1514
- )
1515
- );
1516
- }
1517
- return n;
1518
- }, $s = ({ files: e, config: t }) => {
1519
- var s;
1520
- const n = (s = t == null ? void 0 : t.nextId) == null ? void 0 : s.idea;
1521
- return n == null ? [] : e.flatMap((a) => {
1522
- const r = wt(a), i = r.match(/^IDEA-(\d+)$/);
1523
- return i ? Number(i[1]) < n ? [] : [
1524
- R(
1525
- a,
1526
- be(a),
1527
- "id-counter-stale",
1528
- `id ${r} is >= config nextId.idea (${n}); the next minted id would collide`
1529
- )
1530
- ] : [];
1531
- });
1532
- }, Is = ({ config: e }) => {
1533
- const t = e == null ? void 0 : e.version;
1534
- return typeof t != "number" || t <= A ? [] : [
1535
- {
1536
- file: "papercamp/config.json",
1537
- line: 1,
1538
- rule: "corpus-format-too-new",
1539
- message: `config.version ${t} is newer than the format version ${A} this paper-camp understands; writes are refused until it's upgraded`
1540
- }
1541
- ];
1542
- }, As = [
1543
- Es,
1544
- ks,
1545
- Ss,
1546
- $s,
1547
- Is
1548
- ], Rs = /^---\s*\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n)?/, bt = /^#{1,6}\s+(.+?)\s*$/, vt = /^[-*]\s+\[[ xX]\]\s+(.*)$/, Ps = /^(phases|fixes)$/i, Et = /^#{1,6}\s+Phases\s*$/i, Cs = /^(?:\d{4}-\d{2}-\d{2}\s+)?\[(?:log|clarification|review|note|decision|question|chat)\]/, _s = /^\[(?:phase:\d+|body)\]/, Ns = /\[\[\s*((?:IDEA|TICKET)-\d+)(?:\s*\|[^\]]*)?\s*\]\]/g, Ts = /* @__PURE__ */ new Set(["idea", "planned", "in-progress", "review", "open"]), xs = /* @__PURE__ */ new Set(["done", "dropped"]), Os = /* @__PURE__ */ new Set(["planned", "in-progress", "review"]), Ds = /^\[manual\]\s+/;
1549
- function Q(e) {
1550
- const t = e.match(Rs);
1551
- if (!t) return null;
1552
- try {
1553
- const n = V(t[1]);
1554
- if (n && typeof n == "object" && !Array.isArray(n))
1555
- return n;
1556
- } catch {
1557
- }
1558
- return null;
1559
- }
1560
- function kt(e, t) {
1561
- const n = e.split(`
1562
- `).findIndex((s) => t.test(s));
1563
- return n === -1 ? 1 : n + 1;
1564
- }
1565
- function C(e, t, n, s) {
1566
- return { file: e.path, line: t, rule: n, message: s };
1567
- }
1568
- const js = ({ files: e }) => e.flatMap((t) => {
1569
- const n = t.content.split(`
1570
- `), s = [];
1571
- let a = !1;
1572
- for (let r = 0; r < n.length; r++) {
1573
- const i = n[r].match(bt);
1574
- if (i) {
1575
- a = Ps.test(i[1].trim());
1576
- continue;
1577
- }
1578
- if (a) continue;
1579
- const o = n[r].match(vt);
1580
- if (!o) continue;
1581
- const c = o[1].trim();
1582
- Cs.test(c) || _s.test(c) || s.push(
1583
- C(
1584
- t,
1585
- r + 1,
1586
- "phases-list-split",
1587
- `orphaned phase checkbox outside any Phases section: "${n[r].trim()}"`
1588
- )
1589
- );
1590
- }
1591
- return s;
1592
- }), Ms = ({ files: e }) => e.flatMap((t) => {
1593
- var a;
1594
- if (((a = Q(t.content)) == null ? void 0 : a.kind) !== "note") return [];
1595
- const s = t.content.split(`
1596
- `).findIndex((r) => Et.test(r));
1597
- return s === -1 ? [] : [C(t, s + 1, "note-has-phases", "a kind: note entity carries a Phases section")];
1598
- }), Ls = ({ files: e }) => e.flatMap((t) => t.content.split(`
1599
- `).flatMap((a, r) => Et.test(a) ? [r] : []).slice(1).map(
1600
- (a) => C(
1601
- t,
1602
- a + 1,
1603
- "duplicate-phases-section",
1604
- "a second Phases heading exists — a redraft must replace the existing list in place, not append a new one"
1605
- )
1606
- )), Fs = ({ files: e }) => e.flatMap((t) => {
1607
- const n = Q(t.content), s = typeof (n == null ? void 0 : n.kind) == "string" ? n.kind : void 0;
1608
- if (s === "note" || s === "board") return [];
1609
- const a = typeof (n == null ? void 0 : n.status) == "string" ? n.status : void 0;
1610
- if (!a || !Os.has(a)) return [];
1611
- const r = t.content.split(`
1612
- `);
1613
- let i = !1, o = -1, c = !1;
1614
- for (let l = 0; l < r.length; l++) {
1615
- const f = r[l].match(bt);
1616
- if (f) {
1617
- i = /^phases$/i.test(f[1].trim()), i && o === -1 && (o = l);
1618
- continue;
1619
- }
1620
- if (!i) continue;
1621
- const h = r[l].match(vt);
1622
- h && (Ds.test(h[1].trim()) || (c = !0));
1623
- }
1624
- if (c) return [];
1625
- const u = o === -1 ? kt(t.content, /^status:/) : o + 1;
1626
- return [
1627
- C(
1628
- t,
1629
- u,
1630
- "no-agent-phase",
1631
- `status "${a}" but no agent-runnable phase — only manual phases (or none at all), which reads as a note wearing an idea's clothes`
1632
- )
1633
- ];
1634
- }), Hs = ({ files: e }) => e.flatMap((t) => {
1635
- var a;
1636
- const n = (a = Q(t.content)) == null ? void 0 : a.status;
1637
- if (typeof n != "string") return [];
1638
- const s = kt(t.content, /^status:/);
1639
- return t.archived && Ts.has(n) ? [
1640
- C(
1641
- t,
1642
- s,
1643
- "archive-placement",
1644
- `status "${n}" is active but the file is under ideas/archive/`
1645
- )
1646
- ] : !t.archived && xs.has(n) ? [
1647
- C(
1648
- t,
1649
- s,
1650
- "archive-placement",
1651
- `status "${n}" is closed but the file is still under ideas/ — it should be archived`
1652
- )
1653
- ] : [];
1654
- }), Gs = ({ files: e }) => {
1655
- var n;
1656
- const t = /* @__PURE__ */ new Set();
1657
- for (const s of e) {
1658
- t.add(s.id);
1659
- const a = (n = Q(s.content)) == null ? void 0 : n.id;
1660
- typeof a == "string" && t.add(a);
1661
- }
1662
- return e.flatMap((s) => {
1663
- const a = s.content.split(`
1664
- `), r = [];
1665
- for (let i = 0; i < a.length; i++)
1666
- for (const o of a[i].matchAll(Ns))
1667
- t.has(o[1]) || r.push(
1668
- C(
1669
- s,
1670
- i + 1,
1671
- "dangling-link",
1672
- `[[${o[1]}]] points at an id no entity in the corpus defines`
1673
- )
1674
- );
1675
- return r;
1676
- });
1677
- }, Bs = [
1678
- js,
1679
- Ms,
1680
- Ls,
1681
- Fs,
1682
- Hs,
1683
- Gs
1684
- ], Ys = ".claude/settings.json", qs = ({ hasPermissionsAllow: e }) => e === !1 ? [
1685
- {
1686
- file: Ys,
1687
- line: 1,
1688
- rule: "missing-permissions-allow",
1689
- message: "no permissions.allow list — headless runs deny every edit and shell command outside the defaults; run `paper-camp init --settings` to add it."
1690
- }
1691
- ] : [], Us = ({ config: e }) => e && !e.defaultAgents ? [
1692
- {
1693
- file: "papercamp/config.json",
1694
- line: 1,
1695
- rule: "no-default-agents",
1696
- message: "no defaultAgents — every run uses the built-in agent and model; choose them in Settings → General."
1697
- }
1698
- ] : [], Ks = [
1699
- qs,
1700
- Us
1701
- ], Ws = [
1702
- ...As,
1703
- ...Bs,
1704
- ...Ks
1705
- ];
1706
- function ve(e) {
1707
- return (e == null ? void 0 : e.code) === "ENOENT";
1708
- }
1709
- async function Js(e) {
1710
- try {
1711
- return await z(e);
1712
- } catch (t) {
1713
- if (ve(t)) return [];
1714
- throw t;
1715
- }
1716
- }
1717
- async function zs(e) {
1718
- let t;
1719
- try {
1720
- t = await v(e, "utf-8");
1721
- } catch (n) {
1722
- if (ve(n)) return null;
1723
- throw n;
1724
- }
1725
- try {
1726
- return JSON.parse(t);
1727
- } catch {
1728
- return null;
1729
- }
1730
- }
1731
- async function Vs(e) {
1732
- var n;
1733
- let t;
1734
- try {
1735
- t = await v(p(e, ".claude", "settings.json"), "utf-8");
1736
- } catch (s) {
1737
- if (ve(s)) return !1;
1738
- throw s;
1739
- }
1740
- try {
1741
- const s = JSON.parse(t);
1742
- return Array.isArray((n = s.permissions) == null ? void 0 : n.allow) && s.permissions.allow.length > 0;
1743
- } catch {
1744
- return !1;
1745
- }
1746
- }
1747
- async function Xs(e) {
1748
- const t = p(e, "ideas"), n = [], s = [
1749
- { dir: t, archived: !1, names: await z(t) },
1750
- {
1751
- dir: p(t, "archive"),
1752
- archived: !0,
1753
- names: await Js(p(t, "archive"))
1754
- }
1755
- ];
1756
- for (const { dir: a, archived: r, names: i } of s) {
1757
- const o = i.filter((c) => c.endsWith(".md") && c !== "index.md").sort();
1758
- for (const c of o)
1759
- n.push({
1760
- id: Bt(c, ".md"),
1761
- path: `papercamp/ideas${r ? "/archive" : ""}/${c}`,
1762
- content: await v(p(a, c), "utf-8"),
1763
- archived: r
1764
- });
1765
- }
1766
- return {
1767
- files: n,
1768
- config: await zs(p(e, "config.json")),
1769
- hasPermissionsAllow: await Vs(M(e))
1770
- };
1771
- }
1772
- function Zs(e) {
1773
- return Ws.flatMap((t) => t(e));
1774
- }
1775
- async function ei(e) {
1776
- return Zs(await Xs(e));
1777
- }
1778
- const Qs = {
1779
- rule: "archive-placement",
1780
- fix: (e, t) => ({
1781
- kind: "move",
1782
- from: t.path,
1783
- to: t.archived ? t.path.replace("/archive/", "/") : t.path.replace(/\/(?=[^/]+$)/, "/archive/")
1784
- })
1785
- }, ea = [Qs], ta = new Map(
1786
- ea.map((e) => [e.rule, e])
1787
- );
1788
- function ti(e, t) {
1789
- const n = new Map(e.files.map((o) => [o.path, o])), s = [], a = [], r = [], i = [];
1790
- for (const o of t) {
1791
- const c = ta.get(o.rule), u = n.get(o.file), l = c && u ? c.fix(o, u) : null;
1792
- if (!l) {
1793
- r.push(o);
1794
- continue;
1795
- }
1796
- if (l.kind === "move" && n.has(l.to)) {
1797
- i.push(o);
1798
- continue;
1799
- }
1800
- s.push(l), a.push(o);
1801
- }
1802
- return { actions: s, fixed: a, unfixable: r, rejected: i };
1803
- }
1804
- async function K(e) {
1805
- try {
1806
- return await v(e, "utf-8");
1807
- } catch {
1808
- return "";
1809
- }
1810
- }
1811
- function St(e, t) {
1812
- var n;
1813
- return (n = e.split(`
1814
- `).find((s) => s.includes(`(${t})`))) == null ? void 0 : n.trim();
1815
- }
1816
- const na = /^## \[.+?\]\(.*\/compare\/(.+?)\.\.\.(.+?)\)/;
1817
- function sa(e) {
1818
- return e.split(`
1819
- `).map((t) => na.exec(t)).filter((t) => t !== null).map((t) => ({ version: t[2], range: `${t[1]}..${t[2]}` }));
1820
- }
1821
- function aa(e, t) {
1822
- return e.split(`
1823
- `).find((n) => {
1824
- const s = n.match(/\/commit\/([0-9a-f]+)\)/i);
1825
- return s ? s[1].startsWith(t) || t.startsWith(s[1]) : !1;
1826
- });
1827
- }
1828
- const ra = /^[a-z]+\/([a-z]+-\d+)-/;
1829
- async function ia(e, t) {
1830
- var a;
1831
- const n = await S(e, [
1832
- "for-each-ref",
1833
- "--format=%(refname)",
1834
- "refs/heads",
1835
- "refs/remotes"
1836
- ]), s = t.toLowerCase();
1837
- for (const r of n.split(`
1838
- `)) {
1839
- const i = r.replace(/^refs\/heads\//, "").replace(/^refs\/remotes\/[^/]+\//, "");
1840
- if (((a = ra.exec(i)) == null ? void 0 : a[1]) === s) return i;
1841
- }
1842
- }
1843
- async function oa(e, t) {
1844
- const n = await ia(e, t);
1845
- if (!n) return [];
1846
- const s = await Ke(e);
1847
- return (await S(e, ["log", "--format=%s", `${s}..${n}`])).split(`
1848
- `).map((r) => r.trim()).filter(Boolean);
1849
- }
1850
- async function ca(e, t) {
1851
- if (!ln.test(t)) return null;
1852
- const n = await S(e, ["log", "-1", "--format=%B", t]), s = n ? ne(n) : null;
1853
- if (s) return s;
1854
- const a = await K(p(e, "CHANGELOG.md")), r = aa(a, t);
1855
- return r ? ne(r) : null;
1856
- }
1857
- async function da(e, t) {
1858
- const s = (await S(e, ["log", "--format=%H", "--reverse", t])).split(`
1859
- `).map((i) => i.trim()).filter(Boolean), a = await Promise.all(s.map((i) => ca(e, i))), r = /* @__PURE__ */ new Map();
1860
- return s.forEach((i, o) => {
1861
- const c = a[o];
1862
- if (!c) return;
1863
- const u = r.get(c);
1864
- u ? u.push(i) : r.set(c, [i]);
1865
- }), r;
1866
- }
1867
- async function ni(e, t) {
1868
- const [{ entries: n }, s, a, r, i, o] = await Promise.all([
1869
- x(p(e, "papercamp", "ideas")),
1870
- nt(e),
1871
- K(p(e, "papercamp", "tasks.log")),
1872
- K(p(e, "CHANGELOG.md")),
1873
- oa(e, t),
1874
- We(e)
1875
- ]), c = n.find((h) => h.id === t), u = s == null ? void 0 : s.get(t), l = ps(a).filter((h) => h.planId === t), f = St(r, t);
1876
- return {
1877
- id: t,
1878
- idea: c ? {
1879
- reached: !0,
1880
- data: {
1881
- title: c.title,
1882
- status: X(c, u, s !== void 0, o.has(t)),
1883
- type: c.type
1884
- }
1885
- } : { reached: !1 },
1886
- phases: c ? { reached: c.phases.length > 0, data: c.phases } : { reached: !1 },
1887
- taskRuns: l.length > 0 ? { reached: !0, data: l } : { reached: !1 },
1888
- commits: i.length > 0 ? { reached: !0, data: i } : { reached: !1 },
1889
- pr: u ? { reached: !0, data: u } : { reached: !1 },
1890
- releaseLine: f ? { reached: !0, data: f } : { reached: !1 }
1891
- };
1892
- }
1893
- const B = /^##\s+/, ua = /^##\s+The goal\s*$/i, $t = /^##\s+(Horizon\s+\d+\s*[—-].*)\r?$/i, la = /^##\s+Standing concerns\s*$/i, T = /^-\s+\*\*(.+?)\*\*\s+[—-]\s+(.*)\r?$/, fa = /^\s+-\s+→\s+(.+?)\r?$/, pa = /^([A-Z]+-\d+)/, It = /^\s+-\s+✓\s+shipped\s+(.+?)\r?$/, Ee = /^\s+-\s+(.+?)\r?$/, ke = /^\s+\S/;
1894
- function Oe(e, t, n) {
1895
- var r;
1896
- const s = [];
1897
- let a = t;
1898
- for (; a < n; ) {
1899
- const i = e[a].match(T);
1900
- if (!i) {
1901
- a++;
1902
- continue;
1903
- }
1904
- const o = [i[2].trim()], c = [], u = [];
1905
- let l;
1906
- for (a++; a < n && !T.test(e[a]); ) {
1907
- const f = e[a].match(fa);
1908
- if (f) {
1909
- const w = f[1].trim();
1910
- u.push(((r = w.match(pa)) == null ? void 0 : r[1]) ?? w), a++;
1911
- continue;
1912
- }
1913
- const h = e[a].match(It);
1914
- if (h) {
1915
- l = h[1].trim(), a++;
1916
- continue;
1917
- }
1918
- const g = e[a].match(Ee);
1919
- if (g) {
1920
- c.push(g[1].trim()), a++;
1921
- continue;
1922
- }
1923
- if (!ke.test(e[a])) break;
1924
- o.push(e[a].trim()), a++;
1925
- }
1926
- s.push({
1927
- name: i[1].trim(),
1928
- description: o.join(" "),
1929
- candidates: c,
1930
- linked: u,
1931
- ...l !== void 0 ? { shippedOn: l } : {}
1932
- });
1933
- }
1934
- return s;
1935
- }
1936
- function ha(e) {
1937
- const t = e.split(`
1938
- `);
1939
- let n = "";
1940
- const s = [];
1941
- let a = [];
1942
- for (let r = 0; r < t.length; r++) {
1943
- if (ua.test(t[r])) {
1944
- let o = r + 1;
1945
- for (; o < t.length && !B.test(t[o]); ) o++;
1946
- n = t.slice(r + 1, o).join(`
1947
- `).trim(), r = o - 1;
1948
- continue;
1949
- }
1950
- const i = t[r].match($t);
1951
- if (i) {
1952
- let o = r + 1;
1953
- for (; o < t.length && !B.test(t[o]); ) o++;
1954
- let c = r + 1;
1955
- for (; c < o && !T.test(t[c]); ) c++;
1956
- const u = t.slice(r + 1, c).join(`
1957
- `).trim();
1958
- s.push({
1959
- title: i[1].trim(),
1960
- intro: u,
1961
- items: Oe(t, r + 1, o)
1962
- }), r = o - 1;
1963
- continue;
1964
- }
1965
- if (la.test(t[r])) {
1966
- let o = r + 1;
1967
- for (; o < t.length && !B.test(t[o]); ) o++;
1968
- a = Oe(t, r + 1, o), r = o - 1;
1969
- }
1970
- }
1971
- return { goal: n, horizons: s, standingConcerns: a };
1972
- }
1973
- function W(e, t) {
1974
- for (let n = 0; n < e.length; n++) {
1975
- const s = e[n].match($t);
1976
- if (!s || s[1].trim() !== t) continue;
1977
- let a = n + 1;
1978
- for (; a < e.length && !B.test(e[a]); ) a++;
1979
- return { start: n + 1, end: a };
1980
- }
1981
- }
1982
- function O(e, t, n) {
1983
- const s = W(e, t);
1984
- if (s)
1985
- for (let a = s.start; a < s.end; a++) {
1986
- const r = e[a].match(T);
1987
- if (!r || r[1].trim() !== n) continue;
1988
- let i = a + 1;
1989
- for (; i < s.end && !T.test(e[i]) && ke.test(e[i]); )
1990
- i++;
1991
- return { start: a, end: i };
1992
- }
1993
- }
1994
- function si(e, t, n, s) {
1995
- const a = e.split(`
1996
- `), r = O(a, t, n);
1997
- if (!r) return e;
1998
- if (s === void 0)
1999
- return a.splice(r.start, r.end - r.start), a.join(`
2000
- `);
2001
- for (let i = r.start + 1; i < r.end; i++) {
2002
- const o = a[i].match(Ee);
2003
- if (o && o[1].trim() === s)
2004
- return a.splice(i, 1), a.join(`
2005
- `);
2006
- }
2007
- return e;
2008
- }
2009
- function ai(e, t, n, s) {
2010
- var u, l;
2011
- const a = e.split(`
2012
- `), r = O(a, t, n);
2013
- if (!r) return e;
2014
- const i = a[r.start].match(T);
2015
- if (!i) return e;
2016
- const o = ((u = s.name) == null ? void 0 : u.trim()) ?? i[1].trim(), c = ((l = s.description) == null ? void 0 : l.trim()) ?? i[2].trim();
2017
- if (a[r.start] = `- **${o}** — ${c}`, s.description !== void 0)
2018
- for (let f = r.end - 1; f > r.start; f--)
2019
- ke.test(a[f]) && !Ee.test(a[f]) && a.splice(f, 1);
2020
- return a.join(`
2021
- `);
2022
- }
2023
- function ri(e, t, n, s) {
2024
- if (t === s) return e;
2025
- const a = e.split(`
2026
- `), r = O(a, t, n);
2027
- if (!r || !W(a, s)) return e;
2028
- const i = a.splice(r.start, r.end - r.start), o = W(a, s);
2029
- if (!o) return e;
2030
- let c = o.end;
2031
- for (; c > o.start && a[c - 1].trim() === ""; ) c--;
2032
- return a.splice(c, 0, ...i), a.join(`
2033
- `);
2034
- }
2035
- function ii(e, t, n, s) {
2036
- const a = e.split(`
2037
- `), r = O(a, t, n);
2038
- if (!r) return e;
2039
- let i = r.end;
2040
- for (let o = r.start + 1; o < i; o++)
2041
- if (It.test(a[o])) {
2042
- a.splice(o, 1), i--;
2043
- break;
2044
- }
2045
- return s !== void 0 && a.splice(i, 0, ` - ✓ shipped ${s}`), a.join(`
2046
- `);
2047
- }
2048
- function oi(e, t, n, s) {
2049
- const a = e.split(`
2050
- `), r = W(a, t);
2051
- if (!r) return e;
2052
- let i = r.end;
2053
- for (; i > r.start && a[i - 1].trim() === ""; ) i--;
2054
- return a.splice(i, 0, `- **${n}** — ${s}`), a.join(`
2055
- `);
2056
- }
2057
- function ci(e, t, n, s) {
2058
- const a = e.split(`
2059
- `), r = O(a, t, n);
2060
- return r ? (a.splice(r.end, 0, ` - ${s}`), a.join(`
2061
- `)) : e;
2062
- }
2063
- function di(e, t, n, s) {
2064
- const a = e.split(`
2065
- `), r = O(a, t, n);
2066
- return r ? (a.splice(r.end, 0, ` - → ${s}`), a.join(`
2067
- `)) : e;
2068
- }
2069
- function At(e) {
2070
- return [
2071
- ...e.horizons.flatMap((t) => t.items.map((n) => n.name)),
2072
- ...e.standingConcerns.map((t) => t.name)
2073
- ];
2074
- }
2075
- function ma(e, t, n, s) {
2076
- const a = /* @__PURE__ */ new Map();
2077
- for (const r of t)
2078
- r.id && r.subject === e.name && a.set(r.id, r);
2079
- for (const r of e.linked) {
2080
- const i = n.get(r);
2081
- i && a.set(r, i);
2082
- }
2083
- return [...a.entries()].map(([r, i]) => ({
2084
- id: r,
2085
- title: i.title,
2086
- status: i.status,
2087
- pr: i.pr,
2088
- released: St(s, r) !== void 0
2089
- }));
2090
- }
2091
- function ga(e) {
2092
- const t = e.filter((s) => s.status !== "dropped"), n = t.filter((s) => s.status === "done").length;
2093
- return { total: t.length, done: n, open: t.length - n };
2094
- }
2095
- function ya(e, t) {
2096
- return e.shippedOn !== void 0 ? { state: "shipped", readyToShip: !1 } : t.total === 0 ? { state: "not-started", readyToShip: !1 } : { state: "in-progress", readyToShip: t.done === t.total };
2097
- }
2098
- function De(e, t, n, s) {
2099
- const a = ma(e, t, n, s), r = ga(a), { state: i, readyToShip: o } = ya(e, r);
2100
- return { ...e, ideas: a, rollup: r, state: i, readyToShip: o };
2101
- }
2102
- function ui(e, t, n = "") {
2103
- const s = new Map(t.filter((c) => c.id).map((c) => [c.id, c])), a = e.horizons.map((c) => {
2104
- const u = c.items.map((f) => De(f, t, s, n)), l = u.reduce(
2105
- (f, h) => ({
2106
- total: f.total + h.rollup.total,
2107
- done: f.done + h.rollup.done,
2108
- open: f.open + h.rollup.open
2109
- }),
2110
- { total: 0, done: 0, open: 0 }
2111
- );
2112
- return { title: c.title, intro: c.intro, items: u, rollup: l };
2113
- }), r = e.standingConcerns.map(
2114
- (c) => De(c, t, s, n)
2115
- ), i = new Set(At(e)), o = t.filter(
2116
- (c) => !c.subject || !i.has(c.subject)
2117
- );
2118
- return { goal: e.goal, horizons: a, standingConcerns: r, unfiled: o };
2119
- }
2120
- function wa(e) {
2121
- const t = JSON.stringify({
2122
- body: e.body,
2123
- phases: e.phases.map((n) => ({
2124
- done: n.done,
2125
- text: n.text,
2126
- description: n.description ?? null,
2127
- source: n.source ?? null
2128
- }))
2129
- });
2130
- return Wt("sha256").update(t).digest("hex");
2131
- }
2132
- const Se = /* @__PURE__ */ new Set([
2133
- "core",
2134
- "cli",
2135
- "app",
2136
- "server",
2137
- "agent",
2138
- "audit",
2139
- "plans",
2140
- "ideas",
2141
- "docs",
2142
- "settings",
2143
- "stack",
2144
- "ui",
2145
- "ci",
2146
- "config",
2147
- "deps",
2148
- "repo",
2149
- "release",
2150
- "main"
2151
- ]);
2152
- function ba(e, t) {
2153
- return (e == null ? void 0 : e.find((n) => Se.has(n))) ?? t;
2154
- }
2155
- function Rt(e, t) {
2156
- return new Promise((n) => {
2157
- var r, i;
2158
- const s = b(
2159
- "gh",
2160
- ["pr", "view", t, "--json", "title,body,headRefName,labels,isDraft,state,url,comments"],
2161
- {
2162
- cwd: e,
2163
- stdio: ["ignore", "pipe", "pipe"]
2164
- }
2165
- );
2166
- let a = "";
2167
- (r = s.stdout) == null || r.on("data", (o) => {
2168
- a += o.toString();
2169
- }), (i = s.stderr) == null || i.on("data", () => {
2170
- }), s.on("close", (o) => {
2171
- if (o !== 0) {
2172
- n(void 0);
2173
- return;
2174
- }
2175
- try {
2176
- n(JSON.parse(a));
2177
- } catch {
2178
- n(void 0);
2179
- }
2180
- }), s.on("error", () => n(void 0));
2181
- });
2182
- }
2183
- const va = 6e4;
2184
- function li(e, t, n = va) {
2185
- return new Promise((s) => {
2186
- var i, o;
2187
- const a = b("gh", ["pr", "diff", t], { cwd: e, stdio: ["ignore", "pipe", "pipe"] });
2188
- let r = "";
2189
- (i = a.stdout) == null || i.on("data", (c) => {
2190
- r += c.toString();
2191
- }), (o = a.stderr) == null || o.on("data", () => {
2192
- }), a.on("close", (c) => {
2193
- if (c !== 0) {
2194
- s(void 0);
2195
- return;
2196
- }
2197
- s(
2198
- r.length > n ? `${r.slice(0, n)}
2199
- ... (diff truncated, exceeds size cap)` : r
2200
- );
2201
- }), a.on("error", () => s(void 0));
2202
- });
2203
- }
2204
- async function Ea(e, t) {
2205
- for (const n of ["ideas", p("ideas", "archive")]) {
2206
- const s = p(e, "papercamp", n, `${t}.md`);
2207
- try {
2208
- return await Ht(s), s;
2209
- } catch {
2210
- }
2211
- }
2212
- return null;
2213
- }
2214
- async function _(e, t) {
2215
- const n = await Rt(e, t), s = Ve(n == null ? void 0 : n.body, (n == null ? void 0 : n.headRefName) ?? t);
2216
- if (!s) return;
2217
- const a = await Ea(e, s);
2218
- if (!a) return;
2219
- const { entries: r } = ut(await v(a, "utf-8")), i = r[0];
2220
- if (i)
2221
- return { id: s, view: n, entry: i };
2222
- }
2223
- async function fi(e, t) {
2224
- const n = await _(e, t);
2225
- if (!n) return;
2226
- const { id: s, entry: a } = n;
2227
- return { id: s, kind: a.type, tags: a.tags, phases: a.phases };
2228
- }
2229
- const Pt = "<!-- papercamp:phases:start -->", Ct = "<!-- papercamp:phases:end -->", je = new RegExp(`${Pt}[\\s\\S]*?${Ct}`);
2230
- function ka(e, t) {
2231
- const n = t.map((r) => `- [${r.done ? "x" : " "}] ${r.text}`).join(`
2232
- `), s = `${Pt}
2233
- ### Phases
2234
- ${n}
2235
- ${Ct}`;
2236
- if (je.test(e)) return e.replace(je, () => s);
2237
- const a = e.trimEnd();
2238
- return a.length > 0 ? `${a}
2239
-
2240
- ${s}` : s;
2241
- }
2242
- function Sa(e, t, n) {
2243
- return new Promise((s) => {
2244
- var r, i;
2245
- const a = b("gh", ["pr", "edit", t, "--body-file", "-"], {
2246
- cwd: e,
2247
- stdio: ["pipe", "ignore", "pipe"]
2248
- });
2249
- (r = a.stderr) == null || r.on("data", () => {
2250
- }), a.on("close", (o) => s(o === 0)), a.on("error", () => s(!1)), (i = a.stdin) == null || i.end(n);
2251
- });
2252
- }
2253
- async function pi(e, t) {
2254
- const n = await _(e, t);
2255
- if (!(n != null && n.view)) return "unresolved";
2256
- const { view: s, entry: a } = n, r = ka(s.body, a.phases);
2257
- return r === s.body ? "unchanged" : await Sa(e, t, r) ? "updated" : "unresolved";
2258
- }
2259
- function $a(e) {
2260
- const t = /* @__PURE__ */ new Set();
2261
- e.kind && t.add(e.kind);
2262
- for (const n of e.tags)
2263
- Se.has(n) && t.add(n);
2264
- return [...t];
2265
- }
2266
- function Ia(e) {
2267
- return new Promise((t) => {
2268
- var a, r;
2269
- const n = b("gh", ["label", "list", "--limit", "200", "--json", "name"], {
2270
- cwd: e,
2271
- stdio: ["ignore", "pipe", "pipe"]
2272
- });
2273
- let s = "";
2274
- (a = n.stdout) == null || a.on("data", (i) => {
2275
- s += i.toString();
2276
- }), (r = n.stderr) == null || r.on("data", () => {
2277
- }), n.on("close", (i) => {
2278
- if (i !== 0) {
2279
- t(void 0);
2280
- return;
2281
- }
2282
- try {
2283
- const o = JSON.parse(s);
2284
- t(new Set(o.map((c) => c.name)));
2285
- } catch {
2286
- t(void 0);
2287
- }
2288
- }), n.on("error", () => t(void 0));
2289
- });
2290
- }
2291
- function Aa(e, t) {
2292
- return new Promise((n) => {
2293
- var a;
2294
- const s = b("gh", ["label", "create", t, "--color", "ededed"], {
2295
- cwd: e,
2296
- stdio: ["ignore", "ignore", "pipe"]
2297
- });
2298
- (a = s.stderr) == null || a.on("data", () => {
2299
- }), s.on("close", (r) => n(r === 0)), s.on("error", () => n(!1));
2300
- });
2301
- }
2302
- function Ra(e, t, n) {
2303
- return new Promise((s) => {
2304
- var r;
2305
- const a = b("gh", ["pr", "edit", t, "--add-label", n.join(",")], {
2306
- cwd: e,
2307
- stdio: ["ignore", "ignore", "pipe"]
2308
- });
2309
- (r = a.stderr) == null || r.on("data", () => {
2310
- }), a.on("close", (i) => s(i === 0)), a.on("error", () => s(!1));
2311
- });
2312
- }
2313
- async function hi(e, t) {
2314
- const n = await _(e, t);
2315
- if (!(n != null && n.view)) return "unresolved";
2316
- const { view: s, entry: a } = n, r = $a({ kind: a.type, tags: a.tags }), i = new Set((s.labels ?? []).map((l) => l.name)), o = r.filter((l) => !i.has(l));
2317
- if (o.length === 0) return "unchanged";
2318
- const c = await Ia(e);
2319
- if (c) {
2320
- const l = o.filter((f) => !c.has(f));
2321
- for (const f of l)
2322
- await Aa(e, f);
2323
- }
2324
- return await Ra(e, t, o) ? "updated" : "unresolved";
2325
- }
2326
- function _t(e, t) {
2327
- const n = t.type ?? "feat", s = ba(t.tags, "repo");
2328
- return `${n}(${s}): ${t.title} (${e})`;
2329
- }
2330
- const Pa = new RegExp(`^(${Kt.join("|")})\\(([a-z-]+)\\): .+$`);
2331
- function Ca(e) {
2332
- const t = e.match(Pa);
2333
- return !!t && Se.has(t[2]);
2334
- }
2335
- function _a(e, t, n) {
2336
- return new Promise((s) => {
2337
- var r;
2338
- const a = b("gh", ["pr", "edit", t, "--title", n], {
2339
- cwd: e,
2340
- stdio: ["ignore", "ignore", "pipe"]
2341
- });
2342
- (r = a.stderr) == null || r.on("data", () => {
2343
- }), a.on("close", (i) => s(i === 0)), a.on("error", () => s(!1));
2344
- });
2345
- }
2346
- async function mi(e, t) {
2347
- const n = await _(e, t);
2348
- if (!(n != null && n.view)) return "unresolved";
2349
- const { id: s, view: a, entry: r } = n, i = _t(s, r);
2350
- return a.title === i ? "unchanged" : await _a(e, t, i) ? "updated" : "unresolved";
2351
- }
2352
- async function gi(e, t) {
2353
- const n = await Rt(e, t);
2354
- if (!n) return "no-pr";
2355
- const s = await _(e, t);
2356
- return s ? n.title === _t(s.id, s.entry) ? "valid" : "invalid" : Ca(n.title) ? "valid" : "invalid";
2357
- }
2358
- function yi(e, t) {
2359
- return new Promise((n) => {
2360
- var r;
2361
- const s = b("gh", ["pr", "merge", t, "--squash"], {
2362
- cwd: e,
2363
- stdio: ["ignore", "ignore", "pipe"]
2364
- });
2365
- let a = "";
2366
- (r = s.stderr) == null || r.on("data", (i) => {
2367
- a += i.toString();
2368
- }), s.on("close", (i) => {
2369
- n(i === 0 ? { ok: !0 } : {
2370
- ok: !1,
2371
- message: a.trim() || `gh pr merge ${t} --squash exited with code ${i}`
2372
- });
2373
- }), s.on("error", (i) => n({ ok: !1, message: i.message }));
2374
- });
2375
- }
2376
- function Na(e, t) {
2377
- return new Promise((n) => {
2378
- var a;
2379
- const s = b("gh", ["pr", "ready", t], {
2380
- cwd: e,
2381
- stdio: ["ignore", "ignore", "pipe"]
2382
- });
2383
- (a = s.stderr) == null || a.on("data", () => {
2384
- }), s.on("close", (r) => n(r === 0)), s.on("error", () => n(!1));
2385
- });
2386
- }
2387
- function Ta(e, t) {
2388
- return new Promise((n) => {
2389
- var a;
2390
- const s = b("gh", ["pr", "close", t], {
2391
- cwd: e,
2392
- stdio: ["ignore", "ignore", "pipe"]
2393
- });
2394
- (a = s.stderr) == null || a.on("data", () => {
2395
- }), s.on("close", (r) => n(r === 0)), s.on("error", () => n(!1));
2396
- });
2397
- }
2398
- async function wi(e, t) {
2399
- const n = await _(e, t);
2400
- if (!(n != null && n.view)) return "unresolved";
2401
- const { view: s, entry: a } = n;
2402
- return a.status === "dropped" ? s.state !== "OPEN" ? "unchanged" : await Ta(e, t) ? "closed" : "unresolved" : !(a.phases.length > 0 && a.phases.every((o) => o.done)) || !s.isDraft ? "unchanged" : await Na(e, t) ? "ready" : "unresolved";
2403
- }
2404
- const Nt = "<!-- papercamp:consistency:start -->", xa = "<!-- papercamp:consistency:end -->";
2405
- function Oa(e, t) {
2406
- const n = ["### Paper Camp checks", ""];
2407
- if (e.length === 0)
2408
- n.push("No consistency issues found.");
2409
- else
2410
- for (const s of e)
2411
- n.push(`- **${s.kind}** (${s.section}): ${s.message}`);
2412
- return t && (n.push(""), n.push(
2413
- t.stale ? `Convergence audit: last run \`${t.audited}\`, plan has changed since — due for a re-audit.` : `Convergence audit: last run \`${t.audited}\`, still current.`
2414
- )), `${Nt}
2415
- ${n.join(`
2416
- `)}
2417
- ${xa}`;
2418
- }
2419
- function Da(e, t, n) {
2420
- return new Promise((s) => {
2421
- var r, i;
2422
- const a = b("gh", ["pr", "comment", t, "--body-file", "-"], {
2423
- cwd: e,
2424
- stdio: ["pipe", "ignore", "pipe"]
2425
- });
2426
- (r = a.stderr) == null || r.on("data", () => {
2427
- }), a.on("close", (o) => s(o === 0)), a.on("error", () => s(!1)), (i = a.stdin) == null || i.end(n);
2428
- });
2429
- }
2430
- function ja(e, t, n, s, a) {
2431
- return new Promise((r) => {
2432
- var o, c;
2433
- const i = b(
2434
- "gh",
2435
- [
2436
- "api",
2437
- `repos/${t}/${n}/issues/comments/${s}`,
2438
- "-X",
2439
- "PATCH",
2440
- "-F",
2441
- "body=@-"
2442
- ],
2443
- { cwd: e, stdio: ["pipe", "ignore", "pipe"] }
2444
- );
2445
- (o = i.stderr) == null || o.on("data", () => {
2446
- }), i.on("close", (u) => r(u === 0)), i.on("error", () => r(!1)), (c = i.stdin) == null || c.end(a);
2447
- });
2448
- }
2449
- async function bi(e, t) {
2450
- var w, y;
2451
- const n = await _(e, t);
2452
- if (!((w = n == null ? void 0 : n.view) != null && w.url)) return "unresolved";
2453
- const { view: s, entry: a } = n, [{ entries: r }, i] = await Promise.all([
2454
- x(p(e, "papercamp", "ideas")),
2455
- v(p(e, "ROADMAP.md"), "utf-8").catch(() => "")
2456
- ]), o = r.filter((m) => m.kind !== "note").map((m) => Ue(m)), c = i ? At(ha(i)) : [], u = fs(o, c), l = a.audited && a.auditedHash ? {
2457
- audited: a.audited,
2458
- stale: wa(a) !== a.auditedHash
2459
- } : void 0, f = Oa(u, l), h = (s.comments ?? []).find((m) => m.body.includes(Nt));
2460
- if (h) {
2461
- if (h.body === f) return "unchanged";
2462
- const m = F(s.url), D = (y = h.url.match(/issuecomment-(\d+)/)) == null ? void 0 : y[1];
2463
- return !m || !D ? "unresolved" : await ja(e, m.owner, m.repo, D, f) ? "updated" : "unresolved";
2464
- }
2465
- return await Da(e, t, f) ? "created" : "unresolved";
2466
- }
2467
- function vi(e, t, n) {
2468
- if (!e) return;
2469
- const s = (t ?? "feat").toLowerCase(), a = n.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), r = a.length <= 40 ? a : a[40] === "-" ? a.slice(0, 40) : a.slice(0, 40).replace(/-[^-]*$/, "");
2470
- return `${s}/${e.toLowerCase()}-${r}`;
2471
- }
2472
- function ee(e, t) {
2473
- for (const { entries: n, section: s } of t)
2474
- n && n.length > 0 && e.push(s.formatLines(n).join(`
2475
- `));
2476
- }
2477
- function Tt() {
2478
- return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2479
- }
2480
- function Ei(e, t = "log", n) {
2481
- return {
2482
- kind: t,
2483
- date: Tt(),
2484
- text: e,
2485
- from: "agent",
2486
- ...t === "question" ? { state: "open" } : {},
2487
- ...n ? { entityId: n } : {}
2488
- };
2489
- }
2490
- function ki(e) {
2491
- const t = [`## ${e.title}`, "", `**Status:** ${e.status}`];
2492
- e.kind && t.push(`**Kind:** ${e.kind}`), e.id && t.push(`**Id:** ${e.id}`), e.idea && t.push(`**Idea:** ${e.idea}`), e.agent && t.push(`**Agent:** ${e.agent}`), t.push(`**Created:** ${e.created}`), e.updated && t.push(`**Updated:** ${e.updated}`), e.tags && e.tags.length > 0 && t.push(`**Tags:** ${e.tags.join(", ")}`);
2493
- const n = [t.join(`
2494
- `)];
2495
- return e.body && n.push(e.body), ee(n, [
2496
- { entries: e.clarifications, section: pe },
2497
- { entries: e.phases, section: Z },
2498
- { entries: e.log, section: H },
2499
- { entries: e.notes, section: he }
2500
- ]), n.join(`
2501
-
2502
- `).trimEnd();
2503
- }
2504
- function $e(e) {
2505
- return `---
2506
- ${Yt(e).trimEnd()}
2507
- ---`;
2508
- }
2509
- function Si(e) {
2510
- const t = {
2511
- id: e.id,
2512
- title: e.title,
2513
- kind: e.kind,
2514
- status: e.status,
2515
- created: e.created
2516
- };
2517
- e.idea && (t.idea = e.idea), e.agent && (t.agent = e.agent), e.updated && (t.updated = e.updated), e.audited && (t.audited = e.audited), e.auditedHash && (t["audited-hash"] = e.auditedHash), e.tags && e.tags.length > 0 && (t.tags = e.tags);
2518
- const n = [$e(t)];
2519
- return e.body && n.push(e.body), ee(n, [
2520
- { entries: e.clarifications, section: pe },
2521
- { entries: e.phases, section: Z },
2522
- { entries: e.log, section: H },
2523
- { entries: e.notes, section: he }
2524
- ]), n.join(`
2525
-
2526
- `).trimEnd();
2527
- }
2528
- function Ma(e) {
2529
- const t = {
2530
- id: e.id,
2531
- title: e.title
2532
- };
2533
- e.type && (t.type = e.type), e.kind && (t.kind = e.kind), e.status && (t.status = e.status), e.idea && (t.idea = e.idea), t.created = e.created, e.agent && (t.agent = e.agent), e.updated && (t.updated = e.updated), e.audited && (t.audited = e.audited), e.auditedHash && (t["audited-hash"] = e.auditedHash), e.released && (t.released = e.released), e.tags && e.tags.length > 0 && (t.tags = e.tags), e.subject && (t.subject = e.subject), e.order !== void 0 && (t.order = e.order), e.issueSource && (t.issueSource = e.issueSource);
2534
- for (const s of Object.keys(e.unknownFrontmatter ?? {}).sort())
2535
- t[s] = e.unknownFrontmatter[s];
2536
- const n = [$e(t)];
2537
- return e.body && n.push(e.body), ee(n, [
2538
- { entries: e.phases, section: Z },
2539
- { entries: e.fixes, section: it },
2540
- { entries: e.thread, section: ot }
2541
- ]), n.join(`
2542
-
2543
- `).trimEnd();
2544
- }
2545
- function $i(e) {
2546
- const t = {
2547
- id: e.id,
2548
- title: e.title
2549
- };
2550
- e.kind && (t.kind = e.kind), e.status && (t.status = e.status);
2551
- const n = [$e(t)], s = `## ${e.id}: ${e.title}`;
2552
- return n.push(e.body ? `${s}
2553
-
2554
- ${e.body}` : s), ee(n, [{ entries: e.log, section: H }]), n.join(`
2555
-
2556
- `).trimEnd();
2557
- }
2558
- function Ii(e, t) {
2559
- let n = !1;
2560
- const s = e.split(`
2561
- `).filter((a) => {
2562
- if (!n) {
2563
- const r = a.match(lt);
2564
- if (r && r[1] === t.date && r[2].trim() === t.title && r[3].trim() === t.description)
2565
- return n = !0, !1;
2566
- }
2567
- return !0;
2568
- });
2569
- return n ? s.join(`
2570
- `) : e;
2571
- }
2572
- let J = Promise.resolve();
2573
- async function La(e, t) {
2574
- const n = new RegExp(`^${t.toUpperCase()}-(\\d+)\\.md$`), s = [
2575
- p(e, "ideas"),
2576
- p(e, "ideas", "archive"),
2577
- p(e, "plans"),
2578
- p(e, "plans", "archive")
2579
- ];
2580
- let a = 0;
2581
- for (const r of s) {
2582
- let i;
2583
- try {
2584
- i = await z(r);
2585
- } catch {
2586
- continue;
2587
- }
2588
- for (const o of i) {
2589
- const c = n.exec(o);
2590
- c && (a = Math.max(a, Number(c[1])));
2591
- }
2592
- }
2593
- return a;
2594
- }
2595
- async function xt(e, t) {
2596
- const n = J.then(async () => {
2597
- let s = null;
2598
- try {
2599
- s = JSON.parse(await v(e, "utf-8"));
2600
- } catch {
2601
- return;
2602
- }
2603
- if (!(s != null && s.nextId)) return;
2604
- const a = s.nextId[t] ?? 1, r = await La(M(e), t), i = Math.max(a, r + 1), o = `${t.toUpperCase()}-${i}`;
2605
- return s.nextId[t] = i + 1, await E(e, `${JSON.stringify(s, null, 2)}
2606
- `), o;
2607
- });
2608
- return J = n.catch(() => {
2609
- }), n;
2610
- }
2611
- async function Ai(e) {
2612
- return xt(e, "idea");
2613
- }
2614
- async function Ri(e) {
2615
- return xt(e, "ticket");
2616
- }
2617
- async function Pi(e, t) {
2618
- const n = J.then(async () => {
2619
- let s = null;
2620
- try {
2621
- s = JSON.parse(await v(e, "utf-8"));
2622
- } catch {
2623
- return;
2624
- }
2625
- const a = s.subjects ?? [];
2626
- a.includes(t) || (s.subjects = [...a, t], await E(e, `${JSON.stringify(s, null, 2)}
2627
- `));
2628
- });
2629
- return J = n.catch(() => {
2630
- }), n;
2631
- }
2632
- async function Ci(e, t) {
2633
- const n = p(e, "papercamp", "ideas"), s = p(n, "archive"), a = p(n, `${t}.md`), r = p(s, `${t}.md`);
2634
- await P(s, { recursive: !0 });
2635
- try {
2636
- return await He(a, r), !0;
2637
- } catch (i) {
2638
- if (i.code === "ENOENT") return !1;
2639
- throw i;
2640
- }
2641
- }
2642
- async function _i(e, t) {
2643
- const n = p(e, "papercamp", "ideas"), s = p(n, "archive", `${t}.md`), a = p(n, `${t}.md`);
2644
- try {
2645
- return await He(s, a), !0;
2646
- } catch (r) {
2647
- if (r.code === "ENOENT") return !1;
2648
- throw r;
2649
- }
2650
- }
2651
- const Fa = [
2652
- ["pnpm-lock.yaml", "pnpm"],
2653
- ["yarn.lock", "yarn"],
2654
- ["bun.lockb", "bun"],
2655
- ["package-lock.json", "npm"]
2656
- ];
2657
- function Ot(e) {
2658
- for (const [t, n] of Fa)
2659
- if (ie(p(e, t))) return n;
2660
- return "npm";
2661
- }
2662
- function Ha(e) {
2663
- const t = p(e, "package.json");
2664
- if (ie(t))
2665
- try {
2666
- return JSON.parse(Ge(t, "utf-8"));
2667
- } catch {
2668
- return;
2669
- }
2670
- }
2671
- const Me = "@dendelion/paper-camp";
2672
- function Ga(e) {
2673
- const t = Ha(e);
2674
- if (!t) return !1;
2675
- const n = t.dependencies, s = t.devDependencies;
2676
- return !!(n != null && n[Me] || s != null && s[Me]);
2677
- }
2678
- const Ba = ["*.json", "*.ts", "*.tsx", "*.md", "*.config.*"], Ya = ["src/**", "app/**", "scripts/**", "papercamp/**"], qa = ["git status*", "git diff*", "git log*"], Ua = ["ls*", "cat*", "wc*"], Ka = ["run *", "test*", "install*", "view *", "pack*", "ls*"], Wa = ["npx tsc*", "npx biome check*", "npx vitest*", "npx jest*", "npx expo *"];
2679
- function Dt(e) {
2680
- const t = Ya.flatMap((a) => [`Edit(${a})`, `Write(${a})`]), n = Ba.flatMap((a) => [`Edit(${a})`, `Write(${a})`]), s = [
2681
- ...qa,
2682
- ...Ua,
2683
- ...Ka.map((a) => `${e} ${a}`),
2684
- ...Wa
2685
- ].map((a) => `Bash(${a})`);
2686
- return [...t, ...n, ...s];
2687
- }
2688
- function Ja(e) {
2689
- return e ? `"${Za}" session-focus` : "paper-camp session-focus";
2690
- }
2691
- function za(e, t) {
2692
- let n;
2693
- try {
2694
- n = JSON.parse(e);
2695
- } catch {
2696
- return e;
2697
- }
2698
- const s = Dt(Ot(t)), a = n.permissions ?? {}, r = Array.isArray(a.allow) ? a.allow : [], i = s.filter((o) => !r.includes(o));
2699
- return i.length === 0 && n.permissions !== void 0 ? e : (n.permissions = { ...a, allow: [...r, ...i] }, `${JSON.stringify(n, null, 2)}
2700
- `);
2701
- }
2702
- function Va(e) {
2703
- const t = Ot(e), n = Ja(Ga(e));
2704
- return `${JSON.stringify(
2705
- {
2706
- permissions: {
2707
- allow: Dt(t)
2708
- },
2709
- hooks: {
2710
- SessionStart: [
2711
- {
2712
- matcher: "*",
2713
- hooks: [{ type: "command", command: n }]
2714
- }
2715
- ]
2716
- }
2717
- },
2718
- null,
2719
- 2
2720
- )}
2721
- `;
2722
- }
2723
- const Xa = `---
2724
- name: paper-camp
2725
- description: Work inside a project that has a papercamp/ folder — its unified idea entities (each holding its plan as a section), with decisions and open questions logged as notes on the idea they bind. Use this whenever the working directory contains papercamp/ (papercamp/ideas/), and especially before starting, continuing, or completing any plan phase, drafting an idea, logging a decision, or answering "what are we working on / what's next".
2726
- ---
2727
-
2728
- # Paper Camp
2729
-
2730
- Paper Camp is this project's planning methodology: plans and ideas are one
2731
- thing, a unified entity file under \`papercamp/ideas/\`, with decisions and
2732
- open questions logged as notes bound to the idea they concern. This skill
2733
- tells you how to read that state before acting and how to keep it honest as
2734
- you work.
2735
-
2736
- If a \`papercamp\` MCP server is connected in this session, prefer its tools
2737
- (list/get plans, update phase, etc.) over raw file access —
2738
- they enforce the same guards (id allocation, branch conflicts) that the file
2739
- grammar below assumes. Everything in this skill still applies conceptually;
2740
- only the mechanism changes.
2741
-
2742
- ## Before doing any work
2743
-
2744
- Read, in this order, whatever exists:
2745
-
2746
- 1. \`papercamp/ideas/\` — one file per entity, named by id. Ideas and plans are
2747
- one thing: an entity is an *idea* for its whole life, and its plan is a
2748
- \`### Phases\` section inside the same file.
2749
- 2. The specific entity file at \`papercamp/ideas/<ID>.md\` (e.g. \`IDEA-43.md\`)
2750
- for the work you're about to do — YAML frontmatter (\`id\`, \`title\`,
2751
- \`type\`, \`status\`, \`tags\`, ...) plus prose rationale, then optionally a
2752
- \`### Phases\` list of \`- [ ]\`/\`- [x]\` checkboxes with an indented
2753
- description under each. Done/dropped entities live in
2754
- \`papercamp/ideas/archive/\`.
2755
- Its notes carry any decision or open question bound to it — settled calls
2756
- you shouldn't re-litigate without flagging it to the user first, and
2757
- unresolved questions that might block or redirect the work you're about
2758
- to start.
2759
-
2760
- Skip files that don't exist yet (a fresh project may have empty logs).
2761
-
2762
- ## While working
2763
-
2764
- - Work one plan phase at a time unless told otherwise — don't cascade into
2765
- later phases just because they look quick.
2766
- - If a phase's boundary or intent is unclear, ask before continuing.
2767
- - Prose in plans and ideas is more current than your memory of past
2768
- conversations. If they conflict, the files win — say so and resync.
2769
-
2770
- ## Keep the project current as you go
2771
-
2772
- - **Plan phases**: when you finish a phase, flip its checkbox from \`- [ ]\` to
2773
- \`- [x]\` in the plan's frontmatter file. Change only that line — don't touch
2774
- other phases or prose.
2775
- - **Plan status**: keep the plan's \`status:\` frontmatter honest
2776
- (\`planned\` / \`in-progress\` / \`review\` / \`done\`). When every phase is
2777
- checked, set status to \`review\` — never \`done\`. \`done\` is a human-only
2778
- promotion after review; an agent finishing the last phase does not close
2779
- the plan itself.
2780
- - **Decisions / open questions**: if you settle something ambiguous while
2781
- working, log it as a decision note on the idea it bounds; if you surface a
2782
- question you can't resolve yourself, add it as a question note on that idea
2783
- rather than guessing silently.
2784
-
2785
- ## What this skill deliberately does not do
2786
-
2787
- It does not maintain a separate "current focus" file — that's derived at
2788
- session start from live plan data, not hand-maintained here. It does
2789
- not define the file formats in full; treat the existing files under
2790
- \`papercamp/\` as the grammar reference (mirror their structure exactly rather
2791
- than inventing a new shape).
2792
- `, Za = "$CLAUDE_PROJECT_DIR/node_modules/.bin/paper-camp", Qa = 5;
2793
- function er() {
2794
- try {
2795
- let e = M(Jt(import.meta.url));
2796
- for (let t = 0; t < Qa; t++) {
2797
- const n = p(e, "package.json");
2798
- if (ie(n))
2799
- return JSON.parse(Ge(n, "utf-8")).version ?? "0.0.0";
2800
- e = M(e);
2801
- }
2802
- return "0.0.0";
2803
- } catch {
2804
- return "0.0.0";
2805
- }
2806
- }
2807
- const Ni = er();
2808
- class tr extends Error {
2809
- constructor(t) {
2810
- super(`Paper Camp is already initialized in ${t} (papercamp/config.json exists).`);
2811
- }
2812
- }
2813
- async function N(e) {
2814
- try {
2815
- return await Gt(e), !0;
2816
- } catch {
2817
- return !1;
2818
- }
2819
- }
2820
- const nr = ["suggestions.md"];
2821
- async function Ti(e, t) {
2822
- const n = p(e, "papercamp"), s = p(n, "config.json");
2823
- if (await N(s))
2824
- throw new tr(e);
2825
- const a = {
2826
- version: A,
2827
- projectName: t.projectName,
2828
- initializedAt: (/* @__PURE__ */ new Date()).toISOString(),
2829
- nextId: { idea: 2 }
2830
- };
2831
- ss.parse(a), await P(n, { recursive: !0 });
2832
- const r = p(n, "ideas");
2833
- await P(r, { recursive: !0 });
2834
- const i = p(r, "archive");
2835
- await P(i, { recursive: !0 });
2836
- const o = p(r, "IDEA-1.md");
2837
- if (!await N(o)) {
2838
- const c = Ma({
2839
- id: "IDEA-1",
2840
- title: "Capture your first idea",
2841
- status: "idea",
2842
- created: Tt(),
2843
- body: `This is an example idea, in the format Paper Camp expects: a short title, a status, and a paragraph of context. Read it, then archive or delete it once you've written your own.
2844
-
2845
- Use the New idea button to capture what you're building next, or ask an agent for suggestions.`
2846
- });
2847
- await E(o, `${c}
2848
- `, "utf-8");
2849
- }
2850
- for (const c of nr) {
2851
- const u = p(n, c);
2852
- await N(u) || await E(u, "", "utf-8");
2853
- }
2854
- await E(s, `${JSON.stringify(a, null, 2)}
2855
- `, "utf-8"), await rr(e), await ir(e), await sr(e, t.projectName);
2856
- }
2857
- async function sr(e, t) {
2858
- const n = Xt(), s = await Zt(n), { registry: a, created: r } = Qt(s, e, t);
2859
- r && await en(n, a);
2860
- }
2861
- const ar = [
2862
- "papercamp/.pairing.json",
2863
- "papercamp/.task-logs/",
2864
- "papercamp/tasks.log",
2865
- "papercamp/notifications.log",
2866
- "papercamp/pr-reviews.json",
2867
- "papercamp/pr-map.json",
2868
- "papercamp/night.json",
2869
- "papercamp/run-order.md"
2870
- ];
2871
- async function rr(e) {
2872
- const t = p(e, ".gitignore"), n = await N(t) ? await v(t, "utf-8") : "", s = n.length > 0 ? n.split(`
2873
- `) : [], a = new Set(s.map((o) => o.trim())), r = ar.filter((o) => !a.has(o));
2874
- if (r.length === 0) return;
2875
- const i = s.reduce(
2876
- (o, c, u) => c.startsWith("papercamp/") ? u : o,
2877
- -1
2878
- );
2879
- if (i === -1) {
2880
- const o = n.length > 0 && !n.endsWith(`
2881
- `) ? `
2882
- ` : "";
2883
- await E(t, `${n}${o}${r.join(`
2884
- `)}
2885
- `, "utf-8");
2886
- return;
2887
- }
2888
- s.splice(i + 1, 0, ...r), await E(t, s.join(`
2889
- `), "utf-8");
2890
- }
2891
- async function ir(e) {
2892
- const t = p(e, ".claude", "skills", "paper-camp");
2893
- await P(t, { recursive: !0 });
2894
- const n = p(t, "SKILL.md");
2895
- await N(n) || await E(n, Xa, "utf-8");
2896
- const s = p(e, ".claude");
2897
- await P(s, { recursive: !0 });
2898
- const a = p(s, "settings.json");
2899
- if (await N(a)) {
2900
- const r = await v(a, "utf-8"), i = za(r, e);
2901
- i !== r && await E(a, i, "utf-8");
2902
- } else
2903
- await E(a, Va(e), "utf-8");
2904
- }
2905
- const jt = {
2906
- feat: "Features",
2907
- fix: "Bug Fixes",
2908
- refactor: "Code Refactoring",
2909
- docs: "Documentation",
2910
- chore: "Miscellaneous"
2911
- }, or = Array.from(new Set(Object.values(jt)));
2912
- async function xi(e, t) {
2913
- const n = await K(p(e, "CHANGELOG.md")), s = sa(n).find((o) => o.version === t);
2914
- if (!s) return null;
2915
- const [{ entries: a }, r] = await Promise.all([
2916
- x(p(e, "papercamp", "ideas")),
2917
- da(e, s.range)
2918
- ]), i = /* @__PURE__ */ new Map();
2919
- for (const o of r.keys()) {
2920
- const c = a.find((f) => f.id === o);
2921
- if (!c) continue;
2922
- const u = jt[c.type ?? "chore"], l = i.get(u) ?? [];
2923
- l.push({ id: c.id, title: c.title }), i.set(u, l);
2924
- }
2925
- return or.filter((o) => i.has(o)).map((o) => ({
2926
- label: o,
2927
- ideas: i.get(o)
2928
- }));
2929
- }
2930
- function Oi(e, t) {
2931
- const n = [`## ${e}`];
2932
- for (const s of t) {
2933
- n.push("", `### ${s.label}`, "");
2934
- for (const a of s.ideas)
2935
- n.push(`- ${a.title} (${a.id})`);
2936
- }
2937
- return n.join(`
2938
- `);
2939
- }
2940
- function cr(e) {
2941
- return Math.floor((Date.now() - Date.parse(e)) / 864e5);
2942
- }
2943
- const Mt = (e, t) => e === "question" && (t ?? "open") === "open", Lt = (e) => !!e.archived || e.status === "done" || e.status === "dropped";
2944
- function dr(e) {
2945
- return e.filter((t) => !Lt(t)).flatMap(
2946
- (t) => (t.thread ?? []).filter((n) => Mt(n.kind, n.state)).map((n) => ({
2947
- entityId: t.id,
2948
- entityTitle: t.title,
2949
- text: n.text,
2950
- date: n.date,
2951
- ageDays: n.date ? cr(n.date) : Number.POSITIVE_INFINITY
2952
- }))
2953
- ).sort((t, n) => n.ageDays - t.ageDays);
2954
- }
2955
- async function Di(e) {
2956
- const { entries: t } = await x(e);
2957
- return dr(t);
2958
- }
2959
- function ji(e, t) {
2960
- const n = new Set(t.filter(Lt).map((s) => s.id));
2961
- return e.map((s) => Mt(s.kind, s.state) && s.entityId && n.has(s.entityId) ? { ...s, state: "resolved" } : s);
2962
- }
2963
- const ae = 0.95, Ft = 3e4, re = {
2964
- implement: { from: 0, to: 0.6 },
2965
- verify: { from: 0.6, to: 0.9 },
2966
- checkbox: { from: 0.95, to: 0.95 }
2967
- }, Le = {
2968
- implement: 0,
2969
- verify: 1,
2970
- checkbox: 2
2971
- };
2972
- function Fe(e, ...t) {
2973
- for (const n of t)
2974
- if (typeof (e == null ? void 0 : e[n]) == "string") return e[n];
2975
- return "";
2976
- }
2977
- function Mi(e, t) {
2978
- const n = e.toLowerCase();
2979
- if (n === "bash")
2980
- return (typeof (t == null ? void 0 : t.command) == "string" ? t.command : "").includes("check-types") ? "verify" : null;
2981
- if (n === "edit" || n === "multiedit" || n === "write") {
2982
- const s = Fe(t, "old_string", "oldString"), a = Fe(t, "new_string", "newString");
2983
- return s.includes("- [ ]") && a.includes("- [x]") ? "checkbox" : "implement";
2984
- }
2985
- return null;
2986
- }
2987
- function Li(e, t) {
2988
- return e ? Le[t] > Le[e] ? t : e : t;
2989
- }
2990
- function Fi(e, t) {
2991
- return e === void 0 ? !1 : t - e >= Ft;
2992
- }
2993
- function ur(e) {
2994
- const t = [...e].sort((s, a) => s - a), n = Math.floor(t.length / 2);
2995
- return t.length % 2 === 0 ? (t[n - 1] + t[n]) / 2 : t[n];
2996
- }
2997
- function Hi(e) {
2998
- const t = e.filter((a) => a.taskKind === "phase" && a.outcome === "done").map(
2999
- (a) => (a.endedAt ? Date.parse(a.endedAt) : Number.NaN) - Date.parse(a.startedAt)
3000
- ).filter((a) => Number.isFinite(a) && a > 0);
3001
- if (t.length === 0) return {};
3002
- const n = ur(t), s = {};
3003
- for (const a of ["implement", "verify"]) {
3004
- const r = re[a].to - re[a].from;
3005
- s[a] = n * r / ae;
3006
- }
3007
- return s;
3008
- }
3009
- function Gi({
3010
- anchor: e,
3011
- anchorEnteredAt: t,
3012
- lastStreamAt: n,
3013
- now: s,
3014
- medians: a
3015
- }) {
3016
- if (!e) return 0;
3017
- const r = re[e], i = Math.min(r.from, ae), o = a == null ? void 0 : a[e];
3018
- if (!o || o <= 0 || t === void 0 || r.to === r.from)
3019
- return i;
3020
- const c = n === void 0 ? s : Math.min(s, n + Ft), u = Math.min(1, Math.max(0, c - t) / o);
3021
- return Math.min(r.from + u * (r.to - r.from), ae);
3022
- }
3023
- export {
3024
- Jr as $,
3025
- tr as A,
3026
- wa as B,
3027
- Se as C,
3028
- ar as D,
3029
- _t as E,
3030
- Nr as F,
3031
- I as G,
3032
- $a as H,
3033
- At as I,
3034
- Vr as J,
3035
- zn as K,
3036
- Vn as L,
3037
- Te as M,
3038
- Xn as N,
3039
- Jn as O,
3040
- Ni as P,
3041
- Ot as Q,
3042
- Tr as R,
3043
- Xa as S,
3044
- Pi as T,
3045
- Or as U,
3046
- Wn as V,
3047
- dt as W,
3048
- un as X,
3049
- Ue as Y,
3050
- li as Z,
3051
- Pr as _,
3052
- ln as a,
3053
- xi as a$,
3054
- fs as a0,
3055
- St as a1,
3056
- U as a2,
3057
- Ma as a3,
3058
- $i as a4,
3059
- ki as a5,
3060
- Si as a6,
3061
- Oi as a7,
3062
- Dr as a8,
3063
- Ga as a9,
3064
- Gi as aA,
3065
- ti as aB,
3066
- qn as aC,
3067
- Un as aD,
3068
- Mr as aE,
3069
- Lr as aF,
3070
- x as aG,
3071
- Kr as aH,
3072
- K as aI,
3073
- zr as aJ,
3074
- Di as aK,
3075
- ps as aL,
3076
- Wr as aM,
3077
- si as aN,
3078
- Ii as aO,
3079
- Oa as aP,
3080
- ka as aQ,
3081
- _r as aR,
3082
- Xr as aS,
3083
- ji as aT,
3084
- ca as aU,
3085
- ni as aV,
3086
- ne as aW,
3087
- da as aX,
3088
- fi as aY,
3089
- ba as aZ,
3090
- nt as a_,
3091
- Kn as aa,
3092
- Ti as ab,
3093
- Lt as ac,
3094
- Ca as ad,
3095
- Fi as ae,
3096
- di as af,
3097
- Hi as ag,
3098
- za as ah,
3099
- ri as ai,
3100
- es as aj,
3101
- Qn as ak,
3102
- Zn as al,
3103
- ts as am,
3104
- ss as an,
3105
- ut as ao,
3106
- Er as ap,
3107
- ge as aq,
3108
- Gr as ar,
3109
- Br as as,
3110
- Yr as at,
3111
- Hr as au,
3112
- Fr as av,
3113
- os as aw,
3114
- ha as ax,
3115
- qr as ay,
3116
- xr as az,
3117
- Ws as b,
3118
- sa as b0,
3119
- Cr as b1,
3120
- ui as b2,
3121
- ei as b3,
3122
- Zs as b4,
3123
- ir as b5,
3124
- An as b6,
3125
- $e as b7,
3126
- ii as b8,
3127
- pt as b9,
3128
- yi as ba,
3129
- as as bb,
3130
- bi as bc,
3131
- pi as bd,
3132
- hi as be,
3133
- wi as bf,
3134
- mi as bg,
3135
- Tt as bh,
3136
- _i as bi,
3137
- ai as bj,
3138
- gi as bk,
3139
- ns as bl,
3140
- ot as bm,
3141
- Zr as bn,
3142
- hs as bo,
3143
- Ur as bp,
3144
- Sr as bq,
3145
- Ir as br,
3146
- qe as bs,
3147
- S as bt,
3148
- Ar as bu,
3149
- $r as bv,
3150
- Rr as bw,
3151
- rn as bx,
3152
- Qr as by,
3153
- ea as c,
3154
- ys as d,
3155
- ws as e,
3156
- ae as f,
3157
- Ft as g,
3158
- lt as h,
3159
- ci as i,
3160
- oi as j,
3161
- Li as k,
3162
- $ as l,
3163
- Ei as m,
3164
- kr as n,
3165
- Ci as o,
3166
- Ai as p,
3167
- xt as q,
3168
- Ri as r,
3169
- vi as s,
3170
- Va as t,
3171
- Dt as u,
3172
- Ja as v,
3173
- Mi as w,
3174
- jr as x,
3175
- Xs as y,
3176
- dr as z
3177
- };
3178
- //# sourceMappingURL=phase-progress.2akB88O9.js.map