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