@dendelion/paper-camp 0.44.0 → 0.45.1

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