@shiyuphan/follow-me 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli.js +202 -34
- package/dist/src/comments.js +49 -7
- package/dist/src/diff.js +11 -0
- package/dist/src/evolve.js +123 -0
- package/dist/src/host.js +24 -4
- package/dist/src/prompt.js +43 -14
- package/dist/src/server.js +91 -72
- package/dist/src/session.js +40 -1
- package/dist/ui/dist/assets/{abnfDiagram-WEYOIA3C-DcmzRkVZ.js → abnfDiagram-WEYOIA3C-byISxePP.js} +1 -1
- package/dist/ui/dist/assets/{arc-T5QB0T1C.js → arc-CBzsAsP2.js} +1 -1
- package/dist/ui/dist/assets/{architecture-WOLXFQ4H-DUIkQTeE.js → architecture-WOLXFQ4H-0OSkIwdB.js} +1 -1
- package/dist/ui/dist/assets/{architectureDiagram-ZHVYKT53-BUYi4-bA.js → architectureDiagram-ZHVYKT53-ixHybNqO.js} +1 -1
- package/dist/ui/dist/assets/{blockDiagram-RJAE7QA3-dyJxtT9e.js → blockDiagram-RJAE7QA3-1F8XL_Ou.js} +1 -1
- package/dist/ui/dist/assets/{c4Diagram-XDPPPE54-CrAgiaZR.js → c4Diagram-XDPPPE54-D9PFBXnc.js} +1 -1
- package/dist/ui/dist/assets/channel-B-fwsHf1.js +1 -0
- package/dist/ui/dist/assets/{chunk-2E7WHVMD-C6fAf4Vj.js → chunk-2E7WHVMD-zZdNfMkj.js} +1 -1
- package/dist/ui/dist/assets/{chunk-4EMNKR2T-BLZjwZ4Q.js → chunk-4EMNKR2T-CTKoxSSj.js} +1 -1
- package/dist/ui/dist/assets/{chunk-55XCU76K-DGLK2Q-L.js → chunk-55XCU76K-ifFnDlcS.js} +1 -1
- package/dist/ui/dist/assets/{chunk-7S6XSTFO-BablszTX.js → chunk-7S6XSTFO-BCMVdi_1.js} +1 -1
- package/dist/ui/dist/assets/{chunk-AQDTSKZB-Cuk3B_vb.js → chunk-AQDTSKZB-DC-5Whju.js} +1 -1
- package/dist/ui/dist/assets/{chunk-DSNLSHJZ-DiC78RqE.js → chunk-DSNLSHJZ-DASbe_gb.js} +1 -1
- package/dist/ui/dist/assets/{chunk-HAGFOTED-X_RbvixX.js → chunk-HAGFOTED-CArytDjj.js} +1 -1
- package/dist/ui/dist/assets/{chunk-HNE4Q36P-C14_x5ck.js → chunk-HNE4Q36P-Dnl-Wtud.js} +1 -1
- package/dist/ui/dist/assets/{chunk-NQOKRVIF-Dxd0dtYR.js → chunk-NQOKRVIF-wvSLaeaK.js} +1 -1
- package/dist/ui/dist/assets/{chunk-UHW7PFBP-BIxePNUS.js → chunk-UHW7PFBP-CckaOGiQ.js} +1 -1
- package/dist/ui/dist/assets/{chunk-VQ5W62QI-CKSKLbj4.js → chunk-VQ5W62QI-kQBMDJ7V.js} +1 -1
- package/dist/ui/dist/assets/{chunk-WQQL4CDK-CWNmZGlR.js → chunk-WQQL4CDK-CMg6bbLU.js} +1 -1
- package/dist/ui/dist/assets/{chunk-XXDRQBXY-D-I5QHgG.js → chunk-XXDRQBXY-CYlFr19K.js} +1 -1
- package/dist/ui/dist/assets/{chunk-YZO67HFG-DFP68CFd.js → chunk-YZO67HFG-CqkG1vVk.js} +1 -1
- package/dist/ui/dist/assets/{chunk-ZAUX2LTX-DY90d4QG.js → chunk-ZAUX2LTX-BUNEdx-e.js} +1 -1
- package/dist/ui/dist/assets/{chunk-ZCHMQSOR-Dlpr-5UC.js → chunk-ZCHMQSOR-CFTbvcrm.js} +1 -1
- package/dist/ui/dist/assets/{classDiagram-v2-4MYWR3UA-CH9uhoLJ.js → classDiagram-v2-4MYWR3UA-t2OdJ0LA.js} +1 -1
- package/dist/ui/dist/assets/{cose-bilkent-JH36ORCC-DtYHrp8D.js → cose-bilkent-JH36ORCC-FGm030gc.js} +1 -1
- package/dist/ui/dist/assets/{cynefin-EF2NZ3EQ-BtslbtrY.js → cynefin-EF2NZ3EQ-BtU33Lsu.js} +1 -1
- package/dist/ui/dist/assets/{cynefinDiagram-WF24UDRP-DUU1Jcc7.js → cynefinDiagram-WF24UDRP-DUr-I7hS.js} +1 -1
- package/dist/ui/dist/assets/{dagre-NK4GIZVX-CDlP-dRW.js → dagre-NK4GIZVX-BWTHwJKt.js} +1 -1
- package/dist/ui/dist/assets/{diagram-CBK3MWTC-DiV2fn-r.js → diagram-CBK3MWTC-PcHrsEBn.js} +1 -1
- package/dist/ui/dist/assets/{diagram-I5XIIV2D-Bg5RehGi.js → diagram-I5XIIV2D-CkGtKjYj.js} +1 -1
- package/dist/ui/dist/assets/{diagram-ODIL2UCW-CFmazz3f.js → diagram-ODIL2UCW-B_HdBB1w.js} +1 -1
- package/dist/ui/dist/assets/{diagram-PFBEJUXE-uw3ZBqDp.js → diagram-PFBEJUXE-DL9zayE6.js} +1 -1
- package/dist/ui/dist/assets/{diagram-RJA3I4VV-uEOYYRYa.js → diagram-RJA3I4VV-By5wl8nK.js} +1 -1
- package/dist/ui/dist/assets/{diagram-TYUNFSNR-BN58LF_3.js → diagram-TYUNFSNR-ziWPVayL.js} +1 -1
- package/dist/ui/dist/assets/{dist-tWS_lXDS.js → dist-NDVqCM7k.js} +1 -1
- package/dist/ui/dist/assets/{ebnfDiagram-V4P3TTPT-B68Tz-w8.js → ebnfDiagram-V4P3TTPT-DH4YGkPc.js} +1 -1
- package/dist/ui/dist/assets/{elk-QONTBCTQ-DZZohNea.js → elk-QONTBCTQ-BL2RlCEv.js} +1 -1
- package/dist/ui/dist/assets/{erDiagram-KYQIGQAA-BH_m74pv.js → erDiagram-KYQIGQAA-BKbTMaRp.js} +1 -1
- package/dist/ui/dist/assets/{eventmodeling-K75KTNOO-CTp5FHos.js → eventmodeling-K75KTNOO-Dk5RCWc-.js} +1 -1
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-Dkjb3PCK.js +1 -0
- package/dist/ui/dist/assets/{ganttDiagram-R7GPNTJY-BDau7_0f.js → ganttDiagram-R7GPNTJY-3vM9gxeT.js} +1 -1
- package/dist/ui/dist/assets/{gitGraph-VSP46ZUC-Bv84IoCm.js → gitGraph-VSP46ZUC-CXGFhz54.js} +1 -1
- package/dist/ui/dist/assets/{gitGraphDiagram-PDYL7XHY-DzXcMlm8.js → gitGraphDiagram-PDYL7XHY-Cl4fwUuA.js} +1 -1
- package/dist/ui/dist/assets/index-B8TYcxF8.css +1 -0
- package/dist/ui/dist/assets/index-CBbx_pJk.js +134 -0
- package/dist/ui/dist/assets/{info-OHQRW6UA-222J0O_j.js → info-OHQRW6UA-DfSsZ8ib.js} +1 -1
- package/dist/ui/dist/assets/{infoDiagram-MVDRKAX5-DE2sa3Vz.js → infoDiagram-MVDRKAX5-B_7tyrL5.js} +1 -1
- package/dist/ui/dist/assets/{ishikawaDiagram-HPEHV2GE-BJDk0Zcj.js → ishikawaDiagram-HPEHV2GE-DUvHljpg.js} +1 -1
- package/dist/ui/dist/assets/{journeyDiagram-BOSHW7O7-JSUXX5Hb.js → journeyDiagram-BOSHW7O7-BCgqzrb8.js} +1 -1
- package/dist/ui/dist/assets/{kanban-definition-FNSA4HC5-Bg79b49M.js → kanban-definition-FNSA4HC5-DOzZ3NmE.js} +1 -1
- package/dist/ui/dist/assets/{line-Bk5qQSIa.js → line-B-mPFVll.js} +1 -1
- package/dist/ui/dist/assets/{linear-PFnVMru5.js → linear-k7o0jout.js} +1 -1
- package/dist/ui/dist/assets/{mermaid-parser.core-Dcxlzc-S.js → mermaid-parser.core-CAyLSQpH.js} +3 -3
- package/dist/ui/dist/assets/{mermaid.core-TeOHQJ00.js → mermaid.core-vfVMjgx2.js} +4 -4
- package/dist/ui/dist/assets/{mindmap-definition-6OY57UG3-IaNk8NnW.js → mindmap-definition-6OY57UG3-B6y-B_r4.js} +1 -1
- package/dist/ui/dist/assets/{packet-JDAUHWVQ-DMff5cVK.js → packet-JDAUHWVQ-C_r4ed30.js} +1 -1
- package/dist/ui/dist/assets/{pegDiagram-ECIUJNBX-66Xu4YRc.js → pegDiagram-ECIUJNBX-B8VUm3Bh.js} +1 -1
- package/dist/ui/dist/assets/{pie-XZMESJXO-GEtzLCIM.js → pie-XZMESJXO-DcwtbH9y.js} +1 -1
- package/dist/ui/dist/assets/{pieDiagram-GNMPY74T-7jMBi-kU.js → pieDiagram-GNMPY74T-CvUNBi2F.js} +1 -1
- package/dist/ui/dist/assets/{prism-core-FDHlWQ43.js → prism-core-_2ju85Ya.js} +1 -1
- package/dist/ui/dist/assets/{quadrantDiagram-D57K4MC3-8hIWayC2.js → quadrantDiagram-D57K4MC3-Dg2eVS3j.js} +1 -1
- package/dist/ui/dist/assets/{radar-ABXABTNO-BTdwUOOF.js → radar-ABXABTNO-xCNTcZE9.js} +1 -1
- package/dist/ui/dist/assets/{railroad-I3PHUGI6-DcMbDqBL.js → railroad-I3PHUGI6-BI-sIL2Q.js} +1 -1
- package/dist/ui/dist/assets/{railroad-abnf-LNEFI6M7-gTzUTWeI.js → railroad-abnf-LNEFI6M7-Ddvk40Wx.js} +1 -1
- package/dist/ui/dist/assets/{railroad-ebnf-DWYD2UWJ-CNtdUPtk.js → railroad-ebnf-DWYD2UWJ-h35gE183.js} +1 -1
- package/dist/ui/dist/assets/{railroad-peg-JR7BVNR7-DOiExAtE.js → railroad-peg-JR7BVNR7-Zim9iPdp.js} +1 -1
- package/dist/ui/dist/assets/{railroadDiagram-Y72NRBL7-2kE_ygvM.js → railroadDiagram-Y72NRBL7-Cz6fdmOQ.js} +1 -1
- package/dist/ui/dist/assets/{requirementDiagram-EQYSW5RA-CoIAHRq2.js → requirementDiagram-EQYSW5RA-DTJkCrcD.js} +1 -1
- package/dist/ui/dist/assets/{sankeyDiagram-SY24JHO5-7p8D3wim.js → sankeyDiagram-SY24JHO5-Bg1iiVla.js} +1 -1
- package/dist/ui/dist/assets/{sequenceDiagram-BLEHNHQU-C6QFnMvn.js → sequenceDiagram-BLEHNHQU-9XfetsRA.js} +1 -1
- package/dist/ui/dist/assets/{src-FgijoBVs.js → src-Dym5afQ5.js} +1 -1
- package/dist/ui/dist/assets/{stateDiagram-v2-A6IESKWZ-n9eAb_bT.js → stateDiagram-v2-A6IESKWZ-CMmcMV8z.js} +1 -1
- package/dist/ui/dist/assets/{swimlanes-B3ENRKFQ-U18mtUzX.js → swimlanes-B3ENRKFQ-B2PcshTY.js} +1 -1
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DfB3XTTn.js +8 -0
- package/dist/ui/dist/assets/{timeline-definition-JXYKGLGB-CD2Z8QGP.js → timeline-definition-JXYKGLGB-Cr1hCNtW.js} +1 -1
- package/dist/ui/dist/assets/{treeView-D4JQ5SDB-sqUjH5gB.js → treeView-D4JQ5SDB-jH5ksKnh.js} +1 -1
- package/dist/ui/dist/assets/{treemap-SAJKECNS-DvZ38FP7.js → treemap-SAJKECNS-BV90e8QL.js} +1 -1
- package/dist/ui/dist/assets/{usecaseDiagram-WPOLBNNX-CqOGidaZ.js → usecaseDiagram-WPOLBNNX-BNwq5aJV.js} +1 -1
- package/dist/ui/dist/assets/{vennDiagram-Q3Y3IDCW-D1SXSBQ5.js → vennDiagram-Q3Y3IDCW-D-lLyZCL.js} +1 -1
- package/dist/ui/dist/assets/{wardley-7MLQ67FV-Bpwio-Dx.js → wardley-7MLQ67FV-CWfHQUGt.js} +1 -1
- package/dist/ui/dist/assets/{wardleyDiagram-IVAIDKWA-JSUXHMnk.js → wardleyDiagram-IVAIDKWA-BL_qNKVd.js} +1 -1
- package/dist/ui/dist/assets/{xychartDiagram-4URNHDU4-B4IFqQTN.js → xychartDiagram-4URNHDU4-BHPK5Snp.js} +1 -1
- package/dist/ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/skills/follow-me/SKILL.md +8 -4
- package/dist/ui/dist/assets/channel-wW7ebm0i.js +0 -1
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-CsCvexry.js +0 -1
- package/dist/ui/dist/assets/index-BTJoqVVN.js +0 -133
- package/dist/ui/dist/assets/index-D60U3smc.css +0 -1
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DX07QVy9.js +0 -8
package/dist/src/host.js
CHANGED
|
@@ -3,10 +3,11 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { closeSync, linkSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
-
import { parseEvent, parseEventBatch } from "./comments.js";
|
|
6
|
+
import { parseCarried, parseEvent, parseEventBatch } from "./comments.js";
|
|
7
|
+
import { carryComments } from "./evolve.js";
|
|
7
8
|
import { isPlanVersion, serializePlanFile, validatePlan } from "./plan.js";
|
|
8
9
|
import { HEALTH_APP, isSessionId, loadDraftComments, PortInUseError, RegistrationError, startServer, } from "./server.js";
|
|
9
|
-
import { BranchMovedError, isAlive, openSessionAt, SessionFileError, SessionLockedError, } from "./session.js";
|
|
10
|
+
import { BranchMovedError, diffOfPlan, isAlive, openSessionAt, SessionFileError, SessionLockedError, } from "./session.js";
|
|
10
11
|
const RECORD = "server.json";
|
|
11
12
|
const DEFAULT_IDLE_MS = 30_000;
|
|
12
13
|
const START_TIMEOUT_MS = 10_000;
|
|
@@ -135,6 +136,11 @@ async function loadHosted(registration, held) {
|
|
|
135
136
|
comments: prior.comments,
|
|
136
137
|
responses: stored.file.responses,
|
|
137
138
|
};
|
|
139
|
+
const priorPlan = session.plans().find((p) => p.version === previous?.version);
|
|
140
|
+
const before = priorPlan === undefined ? null : await diffOfPlan(session, priorPlan);
|
|
141
|
+
const carried = previous === null
|
|
142
|
+
? []
|
|
143
|
+
: carryComments(previous.comments, before?.units ?? new Map(), session.diff.units);
|
|
138
144
|
const command = "review";
|
|
139
145
|
// A version swap runs inside the server that already owns this session's lock.
|
|
140
146
|
const release = held ??
|
|
@@ -144,6 +150,7 @@ async function loadHosted(registration, held) {
|
|
|
144
150
|
reviewsDir,
|
|
145
151
|
release,
|
|
146
152
|
previous,
|
|
153
|
+
carried,
|
|
147
154
|
state: {
|
|
148
155
|
mode,
|
|
149
156
|
anchor: session.anchor,
|
|
@@ -275,9 +282,22 @@ export async function swapVersion(port, summary, version, hash) {
|
|
|
275
282
|
throw new RegistrationError("the server answered with a malformed session");
|
|
276
283
|
return swapped;
|
|
277
284
|
}
|
|
285
|
+
/** The previous version's comments as the open page places them on its plan. */
|
|
286
|
+
export async function fetchCarried(port, summary) {
|
|
287
|
+
const state = await call(port, "GET", `${summary.url}api/state`);
|
|
288
|
+
const raw = isFields(state) ? state["carried"] : undefined;
|
|
289
|
+
if (!Array.isArray(raw))
|
|
290
|
+
throw new RegistrationError("the server sent no carried comments");
|
|
291
|
+
return raw.flatMap((item) => {
|
|
292
|
+
const carried = parseCarried(item);
|
|
293
|
+
if (typeof carried === "string")
|
|
294
|
+
throw new RegistrationError(`carried: ${carried}`);
|
|
295
|
+
return [carried];
|
|
296
|
+
});
|
|
297
|
+
}
|
|
278
298
|
/** One long poll of the session's event log: what happened after `since`, or nothing at timeout. */
|
|
279
299
|
export async function pollEvents(port, summary, since, timeoutMs) {
|
|
280
|
-
const path = `${summary.url}api/events?since=${since}&timeout=${timeoutMs}
|
|
300
|
+
const path = `${summary.url}api/events?since=${since}&timeout=${timeoutMs}`;
|
|
281
301
|
const batch = parseEventBatch(await call(port, "GET", path));
|
|
282
302
|
if (typeof batch === "string")
|
|
283
303
|
throw new RegistrationError(`the server sent bad events: ${batch}`);
|
|
@@ -288,7 +308,7 @@ export async function pollEvents(port, summary, since, timeoutMs) {
|
|
|
288
308
|
* the server closes the stream, which it does after the `ended` event.
|
|
289
309
|
*/
|
|
290
310
|
export async function followEvents(port, summary, since, onEvent) {
|
|
291
|
-
const res = await fetch(`${sessionUrl(port, summary)}api/events/stream?since=${since}
|
|
311
|
+
const res = await fetch(`${sessionUrl(port, summary)}api/events/stream?since=${since}`);
|
|
292
312
|
if (!res.ok || res.body === null) {
|
|
293
313
|
throw new RegistrationError(`GET api/events/stream failed with ${res.status}`);
|
|
294
314
|
}
|
package/dist/src/prompt.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tipStartOf } from "./diff.js";
|
|
1
2
|
/** Consecutive runs collapse: [0,1,2,3,7] becomes "0-3,7". */
|
|
2
3
|
function formatRanges(changes) {
|
|
3
4
|
const sorted = [...new Set(changes)].sort((a, b) => a - b);
|
|
@@ -269,6 +270,35 @@ const RULES = (checkCommand) => `- A unit is "h_…" (a text hunk), "f_…" (a w
|
|
|
269
270
|
- Every commit needs a non-empty message and at least one unit.
|
|
270
271
|
- Keep every file syntactically whole in every commit: a function with its closing brace, a block with both ends, an import before or with its first use. Commits need not build or pass tests, but each file a commit writes must parse.
|
|
271
272
|
- Check your work with \`${checkCommand}\` and fix every issue it prints, syntax errors included.`;
|
|
273
|
+
function renderUnit(unit) {
|
|
274
|
+
return unit.kind === "hunk" ? renderHunk(unit) : renderOtherUnit(unit);
|
|
275
|
+
}
|
|
276
|
+
/** The vanished and new units, and the reviewer's comments on hunks that vanished, quoted. */
|
|
277
|
+
function renderChanges(changes, input, ctx) {
|
|
278
|
+
const { since, vanished, added } = changes;
|
|
279
|
+
const gone = new Set(vanished.map((v) => v.unit.id));
|
|
280
|
+
const review = input.mode === "split" ? input.review : null;
|
|
281
|
+
const orphans = (review?.comments ?? []).filter((c) => c.author !== "agent" && c.anchor.kind === "hunk" && gone.has(c.anchor.unit));
|
|
282
|
+
const parts = [
|
|
283
|
+
`## Changes since v${since}\n`,
|
|
284
|
+
`The branch moved after plan v${since}. ${input.draftPath} already holds that plan on the new tip: every unit whose hunk did not change keeps its commit, and any commit left with no unit was dropped. Put each new unit below into a commit, an existing one or a new one, and keep the rest as it is.`,
|
|
285
|
+
];
|
|
286
|
+
if (vanished.length > 0) {
|
|
287
|
+
parts.push(`\nThese units of v${since} changed, so their ids are gone:`, ...vanished.map(({ unit, commits }) => {
|
|
288
|
+
const where = commits
|
|
289
|
+
.map((c) => `commit ${c.index + 1} ${JSON.stringify(c.message.split("\n")[0] ?? "")}`)
|
|
290
|
+
.join(", ");
|
|
291
|
+
return `- ${unit.id} ${unit.path}, was in ${where}`;
|
|
292
|
+
}));
|
|
293
|
+
}
|
|
294
|
+
if (added.length > 0) {
|
|
295
|
+
parts.push("\nNew units, in no commit yet:\n", added.map(renderUnit).join("\n\n"));
|
|
296
|
+
}
|
|
297
|
+
if (orphans.length > 0) {
|
|
298
|
+
parts.push(`\nThe reviewer commented on hunks that changed. Write a response for each saying what you changed, and post the same with \`follow-me reply <id>\`:\n`, orphans.map((c) => renderComment(c, ctx)).join("\n"));
|
|
299
|
+
}
|
|
300
|
+
return parts.join("\n");
|
|
301
|
+
}
|
|
272
302
|
/** The planner prompt for a coding agent. Pure and deterministic for the same input. */
|
|
273
303
|
export function renderPrompt(input) {
|
|
274
304
|
const { diff, draftPath } = input;
|
|
@@ -282,13 +312,24 @@ export function renderPrompt(input) {
|
|
|
282
312
|
if (input.mode === "walk") {
|
|
283
313
|
const sections = [
|
|
284
314
|
...head,
|
|
315
|
+
...(input.changes === undefined
|
|
316
|
+
? []
|
|
317
|
+
: [renderChanges(input.changes, input, { commits: [], units: diff.units })]),
|
|
285
318
|
`## Concerns\n\n${CONCERNS}`,
|
|
286
319
|
`## Questions from the reviewer\n\n${QUESTIONS}\n\n${CONTEXT_RULE}`,
|
|
287
320
|
units,
|
|
288
321
|
];
|
|
289
322
|
return `${sections.join("\n\n")}\n`;
|
|
290
323
|
}
|
|
291
|
-
const
|
|
324
|
+
const ctx = {
|
|
325
|
+
commits: input.previousCommits ?? [],
|
|
326
|
+
units: diff.units,
|
|
327
|
+
};
|
|
328
|
+
const sections = [
|
|
329
|
+
...head,
|
|
330
|
+
...(input.changes === undefined ? [] : [renderChanges(input.changes, input, ctx)]),
|
|
331
|
+
units,
|
|
332
|
+
];
|
|
292
333
|
const { previousPlan, review } = input;
|
|
293
334
|
if (previousPlan) {
|
|
294
335
|
sections.push(`## Previous plan\n\nCommits are numbered from 1.\n\n${renderPreviousPlan(previousPlan)}`);
|
|
@@ -296,7 +337,6 @@ export function renderPrompt(input) {
|
|
|
296
337
|
const theirs = review?.comments.filter((c) => c.author !== "agent") ?? [];
|
|
297
338
|
const asked = theirs.filter((c) => c.kind === "question");
|
|
298
339
|
const changes = theirs.filter((c) => c.kind !== "question");
|
|
299
|
-
const ctx = { commits: input.previousCommits ?? [], units: diff.units };
|
|
300
340
|
const questions = [`## Questions from the reviewer\n`, QUESTIONS, `\n${CONTEXT_RULE}`];
|
|
301
341
|
if (review && asked.length > 0) {
|
|
302
342
|
questions.push(`\nThe reviewer asked ${asked.length === 1 ? "this question" : "these questions"} about plan v${review.planVersion}. Commits are numbered from 1. Write one response for each.\n`, asked.map((c) => renderComment(c, ctx)).join("\n"));
|
|
@@ -350,17 +390,6 @@ function excerpt(hunk, tipStart, owners, k) {
|
|
|
350
390
|
}
|
|
351
391
|
return lines;
|
|
352
392
|
}
|
|
353
|
-
/** Where the hunk starts in the tip: its base position shifted by earlier hunks of the file. */
|
|
354
|
-
function tipStartOf(hunk, units) {
|
|
355
|
-
let shift = 0;
|
|
356
|
-
for (const u of units.values()) {
|
|
357
|
-
if (u.kind !== "hunk" || u.path !== hunk.path || u.oldStart >= hunk.oldStart)
|
|
358
|
-
continue;
|
|
359
|
-
for (const line of u.body)
|
|
360
|
-
shift += line.op === "+" ? 1 : line.op === "-" ? -1 : 0;
|
|
361
|
-
}
|
|
362
|
-
return hunk.oldStart + shift + 1;
|
|
363
|
-
}
|
|
364
393
|
/** A fence longer than any backtick run in the content, so quoted code cannot close it. */
|
|
365
394
|
function fence(lines) {
|
|
366
395
|
const longest = Math.max(0, ...lines.flatMap((l) => (l.match(/`+/g) ?? []).map((r) => r.length)));
|
|
@@ -927,7 +956,7 @@ function renderEvent(event, ctx) {
|
|
|
927
956
|
case "version":
|
|
928
957
|
return [`- plan v${event.version} is now open`];
|
|
929
958
|
case "submitted":
|
|
930
|
-
return ["- the reviewer submitted the review
|
|
959
|
+
return ["- the reviewer submitted the review"];
|
|
931
960
|
case "ended":
|
|
932
961
|
return [`- the reviewer ended the review; the report is next to ${event.review}`];
|
|
933
962
|
default: {
|
package/dist/src/server.js
CHANGED
|
@@ -82,7 +82,13 @@ export async function loadDraft(reviewsDir, version) {
|
|
|
82
82
|
}
|
|
83
83
|
catch (error) {
|
|
84
84
|
if (isRecord(error) && error["code"] === "ENOENT") {
|
|
85
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
comments: [],
|
|
87
|
+
concerns: [],
|
|
88
|
+
resolved: [],
|
|
89
|
+
excluded: { comments: [], concerns: [] },
|
|
90
|
+
earlierReplies: [],
|
|
91
|
+
};
|
|
86
92
|
}
|
|
87
93
|
throw error;
|
|
88
94
|
}
|
|
@@ -240,6 +246,7 @@ function viewOf(registration, loaded, draft) {
|
|
|
240
246
|
resolved: new Set(draft.resolved),
|
|
241
247
|
excludedComments: new Set(draft.excluded.comments),
|
|
242
248
|
excludedConcerns: new Set(draft.excluded.concerns),
|
|
249
|
+
earlierReplies: new Map(draft.earlierReplies.map((e) => [e.comment, e.replies])),
|
|
243
250
|
unitIds: new Set(state.units.map((u) => u.id)),
|
|
244
251
|
hunkRows: new Map(state.units.flatMap((u) => u.kind === "hunk" ? [[u.id, u.body.length]] : [])),
|
|
245
252
|
concernIds: new Set(state.commits.flatMap((c) => (c.concerns ?? []).map((k) => k.id))),
|
|
@@ -251,6 +258,47 @@ function withReplies(comment, replies) {
|
|
|
251
258
|
const { replies: _dropped, ...rest } = comment;
|
|
252
259
|
return replies === undefined || replies.length === 0 ? rest : { ...rest, replies };
|
|
253
260
|
}
|
|
261
|
+
/** A comment of the previous version with the replies it got during this one. */
|
|
262
|
+
function withEarlier(view, comment) {
|
|
263
|
+
const later = view.earlierReplies.get(comment.id) ?? [];
|
|
264
|
+
return later.length === 0
|
|
265
|
+
? comment
|
|
266
|
+
: withReplies(comment, [...(comment.replies ?? []), ...later]);
|
|
267
|
+
}
|
|
268
|
+
function previousOf(view) {
|
|
269
|
+
const { previous } = view.loaded;
|
|
270
|
+
if (previous === null)
|
|
271
|
+
return null;
|
|
272
|
+
return { ...previous, comments: previous.comments.map((c) => withEarlier(view, c)) };
|
|
273
|
+
}
|
|
274
|
+
function carriedOf(view) {
|
|
275
|
+
return view.loaded.carried.map((c) => ({ ...c, comment: withEarlier(view, c.comment) }));
|
|
276
|
+
}
|
|
277
|
+
function earlierOf(view) {
|
|
278
|
+
return [...view.earlierReplies].map(([comment, replies]) => ({ comment, replies }));
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* What a restarted server's log begins with: each saved comment and each reply as the event that
|
|
282
|
+
* first carried it, oldest first, so a client that reads from 0 sees the whole conversation.
|
|
283
|
+
*/
|
|
284
|
+
function replayOf(view) {
|
|
285
|
+
const said = [];
|
|
286
|
+
for (const comment of view.comments.values()) {
|
|
287
|
+
said.push({
|
|
288
|
+
at: comment.createdAt,
|
|
289
|
+
event: { type: "comment", comment: withReplies(comment, undefined) },
|
|
290
|
+
});
|
|
291
|
+
for (const reply of comment.replies ?? []) {
|
|
292
|
+
said.push({ at: reply.createdAt, event: { type: "reply", commentId: comment.id, reply } });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
for (const [commentId, replies] of view.earlierReplies) {
|
|
296
|
+
for (const reply of replies) {
|
|
297
|
+
said.push({ at: reply.createdAt, event: { type: "reply", commentId, reply } });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return said.sort((a, b) => a.at.localeCompare(b.at)).map((s) => s.event);
|
|
301
|
+
}
|
|
254
302
|
function mintReplyId(taken) {
|
|
255
303
|
for (;;) {
|
|
256
304
|
let id = "r_";
|
|
@@ -260,38 +308,6 @@ function mintReplyId(taken) {
|
|
|
260
308
|
return id;
|
|
261
309
|
}
|
|
262
310
|
}
|
|
263
|
-
/**
|
|
264
|
-
* What the agent may see of the log: a pending flag is held back with everything said about it,
|
|
265
|
-
* and reaches the agent as an ordinary "comment" event when the reviewer submits.
|
|
266
|
-
*/
|
|
267
|
-
function forAgent(log) {
|
|
268
|
-
const held = new Set();
|
|
269
|
-
const out = [];
|
|
270
|
-
for (const event of log) {
|
|
271
|
-
if (event.type === "comment" || event.type === "comment-updated") {
|
|
272
|
-
const { comment } = event;
|
|
273
|
-
if (comment.pending === true) {
|
|
274
|
-
held.add(comment.id);
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
if (held.delete(comment.id))
|
|
278
|
-
out.push({ ...event, type: "comment" });
|
|
279
|
-
else
|
|
280
|
-
out.push(event);
|
|
281
|
-
}
|
|
282
|
-
else if (event.type === "comment-deleted") {
|
|
283
|
-
if (!held.delete(event.id))
|
|
284
|
-
out.push(event);
|
|
285
|
-
}
|
|
286
|
-
else if (event.type === "reply") {
|
|
287
|
-
if (!held.has(event.commentId))
|
|
288
|
-
out.push(event);
|
|
289
|
-
}
|
|
290
|
-
else
|
|
291
|
-
out.push(event);
|
|
292
|
-
}
|
|
293
|
-
return out;
|
|
294
|
-
}
|
|
295
311
|
const DEFAULT_POLL_MS = 25_000;
|
|
296
312
|
const MAX_POLL_MS = 60_000;
|
|
297
313
|
const STREAM_PING_MS = 15_000;
|
|
@@ -314,9 +330,10 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
314
330
|
for (const listener of [...listeners])
|
|
315
331
|
listener();
|
|
316
332
|
};
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
333
|
+
for (const event of replayOf(view))
|
|
334
|
+
emit(event);
|
|
335
|
+
const waitForEvents = (since, timeoutMs, signal) => new Promise((resolve) => {
|
|
336
|
+
const news = () => events.filter((e) => e.seq > since);
|
|
320
337
|
const answer = () => resolve({ seq: events.length, events: news() });
|
|
321
338
|
if (news().length > 0 || timeoutMs === 0 || signal.aborted)
|
|
322
339
|
return answer();
|
|
@@ -356,6 +373,7 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
356
373
|
const concerns = [...view.decisions.values()];
|
|
357
374
|
const resolved = [...view.resolved];
|
|
358
375
|
const excluded = excludedOf();
|
|
376
|
+
const earlierReplies = earlierOf(view);
|
|
359
377
|
const snapshot = JSON.stringify({
|
|
360
378
|
schema: "follow-me/review-draft@1",
|
|
361
379
|
planVersion: state.version,
|
|
@@ -363,6 +381,7 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
363
381
|
...(concerns.length === 0 ? {} : { concerns }),
|
|
364
382
|
...(resolved.length === 0 ? {} : { resolved }),
|
|
365
383
|
...(excluded === null ? {} : { excluded }),
|
|
384
|
+
...(earlierReplies.length === 0 ? {} : { earlierReplies }),
|
|
366
385
|
}, null, 2);
|
|
367
386
|
const dir = view.loaded.reviewsDir;
|
|
368
387
|
writes = writes.then(async () => {
|
|
@@ -393,33 +412,22 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
393
412
|
}
|
|
394
413
|
return parsed;
|
|
395
414
|
};
|
|
396
|
-
/** Clears `pending` on every held flag at once and tells the agent what it missed. */
|
|
397
|
-
const release = () => {
|
|
398
|
-
const held = [...view.comments.values()].filter((c) => c.pending === true);
|
|
399
|
-
if (held.length === 0)
|
|
400
|
-
return 0;
|
|
401
|
-
emit({ type: "submitted" });
|
|
402
|
-
for (const comment of held) {
|
|
403
|
-
const { pending: _held, ...open } = comment;
|
|
404
|
-
view.comments.set(comment.id, open);
|
|
405
|
-
emit({ type: "comment", comment: open });
|
|
406
|
-
}
|
|
407
|
-
return held.length;
|
|
408
|
-
};
|
|
409
415
|
const review = () => {
|
|
410
416
|
const concerns = [...view.decisions.values()];
|
|
411
417
|
const resolved = [...view.resolved];
|
|
412
418
|
const excluded = excludedOf();
|
|
419
|
+
const earlierReplies = earlierOf(view);
|
|
413
420
|
return {
|
|
414
421
|
schema: "follow-me/review@1",
|
|
415
422
|
mode: view.loaded.state.mode,
|
|
416
423
|
planVersion: view.loaded.state.version,
|
|
417
424
|
planHash: view.loaded.state.planHash,
|
|
418
425
|
endedAt: new Date().toISOString(),
|
|
419
|
-
comments: [...view.comments.values()]
|
|
426
|
+
comments: [...view.comments.values()],
|
|
420
427
|
...(concerns.length === 0 ? {} : { concerns }),
|
|
421
428
|
...(resolved.length === 0 ? {} : { resolved }),
|
|
422
429
|
...(excluded === null ? {} : { excluded }),
|
|
430
|
+
...(earlierReplies.length === 0 ? {} : { earlierReplies }),
|
|
423
431
|
};
|
|
424
432
|
};
|
|
425
433
|
const report = (finished) => {
|
|
@@ -439,7 +447,7 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
439
447
|
await rm(view.draftFile, { force: true });
|
|
440
448
|
return view.reviewFile;
|
|
441
449
|
};
|
|
442
|
-
const hasWork = () => view.comments.size > 0 || view.decisions.size > 0;
|
|
450
|
+
const hasWork = () => view.comments.size > 0 || view.decisions.size > 0 || view.earlierReplies.size > 0;
|
|
443
451
|
return {
|
|
444
452
|
id,
|
|
445
453
|
done,
|
|
@@ -464,17 +472,18 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
464
472
|
resolved: [...view.resolved],
|
|
465
473
|
excluded: { comments: [...view.excludedComments], concerns: [...view.excludedConcerns] },
|
|
466
474
|
seq: events.length,
|
|
467
|
-
previous: view
|
|
475
|
+
previous: previousOf(view),
|
|
476
|
+
carried: carriedOf(view),
|
|
468
477
|
}),
|
|
469
478
|
/** Events after `since`, now and as they happen, until unfollowed or the review ends. */
|
|
470
479
|
latest: () => events.length,
|
|
471
|
-
follow: (since, send, stop
|
|
480
|
+
follow: (since, send, stop) => {
|
|
472
481
|
if (since > events.length) {
|
|
473
482
|
throw new HttpError(409, `since ${since} is ahead of this session's log (${events.length})`);
|
|
474
483
|
}
|
|
475
484
|
let last = since;
|
|
476
485
|
const flush = () => {
|
|
477
|
-
for (const event of
|
|
486
|
+
for (const event of events.filter((e) => e.seq > last)) {
|
|
478
487
|
send(event);
|
|
479
488
|
last = event.seq;
|
|
480
489
|
if (event.type === "ended") {
|
|
@@ -488,11 +497,11 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
488
497
|
flush();
|
|
489
498
|
return () => listeners.delete(flush);
|
|
490
499
|
},
|
|
491
|
-
poll: (since, timeoutMs, signal
|
|
500
|
+
poll: (since, timeoutMs, signal) => {
|
|
492
501
|
if (since > events.length) {
|
|
493
502
|
throw new HttpError(409, `since ${since} is ahead of this session's log (${events.length})`);
|
|
494
503
|
}
|
|
495
|
-
return waitForEvents(since, timeoutMs, signal
|
|
504
|
+
return waitForEvents(since, timeoutMs, signal);
|
|
496
505
|
},
|
|
497
506
|
report: () => report(review()),
|
|
498
507
|
reportItems: () => {
|
|
@@ -536,22 +545,33 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
536
545
|
emit({ type: "comment-deleted", id: gone.id });
|
|
537
546
|
await saveDraft();
|
|
538
547
|
},
|
|
548
|
+
/** Replies under a comment of this version, or of the previous one, as after a swap. */
|
|
539
549
|
reply: async (target, raw) => {
|
|
540
550
|
open();
|
|
541
551
|
const comment = view.comments.get(target);
|
|
542
|
-
|
|
552
|
+
const earlier = view.loaded.previous?.comments.find((c) => c.id === target);
|
|
553
|
+
const owner = comment ?? earlier;
|
|
554
|
+
if (owner === undefined)
|
|
543
555
|
throw new HttpError(404, "no such comment");
|
|
544
556
|
const parsed = parseNewReply(raw);
|
|
545
557
|
if (typeof parsed === "string")
|
|
546
558
|
throw new HttpError(400, `malformed reply: ${parsed}`);
|
|
547
|
-
const taken = new Set([...view.comments.values()
|
|
559
|
+
const taken = new Set([...view.comments.values(), ...(previousOf(view)?.comments ?? [])]
|
|
560
|
+
.flatMap((c) => c.replies ?? [])
|
|
561
|
+
.map((r) => r.id));
|
|
548
562
|
const reply = {
|
|
549
563
|
id: mintReplyId(taken),
|
|
550
564
|
...parsed,
|
|
551
565
|
createdAt: new Date().toISOString(),
|
|
552
566
|
};
|
|
553
|
-
|
|
554
|
-
|
|
567
|
+
if (comment !== undefined) {
|
|
568
|
+
view.comments.set(comment.id, { ...comment, replies: [...(comment.replies ?? []), reply] });
|
|
569
|
+
}
|
|
570
|
+
else if (earlier !== undefined) {
|
|
571
|
+
const later = view.earlierReplies.get(earlier.id) ?? [];
|
|
572
|
+
view.earlierReplies.set(earlier.id, [...later, reply]);
|
|
573
|
+
}
|
|
574
|
+
emit({ type: "reply", commentId: owner.id, reply });
|
|
555
575
|
await saveDraft();
|
|
556
576
|
return reply.id;
|
|
557
577
|
},
|
|
@@ -599,12 +619,14 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
599
619
|
await saveDraft();
|
|
600
620
|
return { comments: [...view.excludedComments], concerns: [...view.excludedConcerns] };
|
|
601
621
|
},
|
|
602
|
-
/**
|
|
603
|
-
|
|
622
|
+
/**
|
|
623
|
+
* The reviewer pressed Submit. Every comment already reached the agent, so this only tells it
|
|
624
|
+
* the reviewer is done; `released` stays for pages that still read it.
|
|
625
|
+
*/
|
|
626
|
+
submit: () => {
|
|
604
627
|
open();
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
return { released };
|
|
628
|
+
emit({ type: "submitted" });
|
|
629
|
+
return { released: 0 };
|
|
608
630
|
},
|
|
609
631
|
resolve: async (raw) => {
|
|
610
632
|
open();
|
|
@@ -642,13 +664,12 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
642
664
|
try {
|
|
643
665
|
await writes;
|
|
644
666
|
const next = await load({ ...view.registration, version, hash, lockToken: null }, view.loaded.release);
|
|
645
|
-
release();
|
|
646
667
|
if (hasWork())
|
|
647
668
|
await writeReview(review());
|
|
648
669
|
else
|
|
649
670
|
await rm(view.draftFile, { force: true });
|
|
650
671
|
view = viewOf({ ...view.registration, version, hash }, next, await loadDraft(next.reviewsDir, version));
|
|
651
|
-
emit({ type: "version", version, hash });
|
|
672
|
+
emit({ type: "version", version, hash, carried: carriedOf(view) });
|
|
652
673
|
}
|
|
653
674
|
finally {
|
|
654
675
|
swapping = false;
|
|
@@ -658,7 +679,6 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
658
679
|
end: async () => {
|
|
659
680
|
open();
|
|
660
681
|
ending = true;
|
|
661
|
-
release();
|
|
662
682
|
const finished = review();
|
|
663
683
|
const file = await writeReview(finished);
|
|
664
684
|
view.loaded.release();
|
|
@@ -832,7 +852,7 @@ export function startServer(opts) {
|
|
|
832
852
|
});
|
|
833
853
|
res.write(": follow-me\n\n");
|
|
834
854
|
const ping = setInterval(() => res.write(": ping\n\n"), STREAM_PING_MS);
|
|
835
|
-
const unfollow = session.follow(since, (event) => res.write(`id: ${event.seq}\ndata: ${JSON.stringify(event)}\n\n`), () => res.end()
|
|
855
|
+
const unfollow = session.follow(since, (event) => res.write(`id: ${event.seq}\ndata: ${JSON.stringify(event)}\n\n`), () => res.end());
|
|
836
856
|
res.on("close", () => {
|
|
837
857
|
clearInterval(ping);
|
|
838
858
|
unfollow();
|
|
@@ -844,8 +864,7 @@ export function startServer(opts) {
|
|
|
844
864
|
res.on("close", () => abort.abort());
|
|
845
865
|
const since = queryInt(url, "since", 0, Number.MAX_SAFE_INTEGER, 0);
|
|
846
866
|
const timeout = queryInt(url, "timeout", 0, MAX_POLL_MS, DEFAULT_POLL_MS);
|
|
847
|
-
|
|
848
|
-
return sendJson(res, 200, await session.poll(since, timeout, abort.signal, agent));
|
|
867
|
+
return sendJson(res, 200, await session.poll(since, timeout, abort.signal));
|
|
849
868
|
}
|
|
850
869
|
if (method === "GET" && rest === "/api/report" && url.searchParams.get("format") === "json") {
|
|
851
870
|
return sendJson(res, 200, session.reportItems());
|
|
@@ -866,7 +885,7 @@ export function startServer(opts) {
|
|
|
866
885
|
return sendJson(res, 200, session.summary());
|
|
867
886
|
}
|
|
868
887
|
if (method === "POST" && rest === "/api/submit") {
|
|
869
|
-
return sendJson(res, 200,
|
|
888
|
+
return sendJson(res, 200, session.submit());
|
|
870
889
|
}
|
|
871
890
|
if (method === "POST" && rest === "/api/excluded") {
|
|
872
891
|
return sendJson(res, 200, await session.exclude(await readJson(req)));
|
package/dist/src/session.js
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync, linkSync, mkdirSync, readdirSync, readFileSync, renameSync,
|
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
4
|
import { parseDraft, parseReview } from "./comments.js";
|
|
5
5
|
import { isSha } from "./diff.js";
|
|
6
|
+
import { evolvePlan } from "./evolve.js";
|
|
6
7
|
import { branchExists, currentBranch, defaultBaseRef, GitError, loadDiff, objectsDir, QUARANTINE_PREFIX, repoRoot, resolveCommit, } from "./git.js";
|
|
7
8
|
import { isFields, isPlanVersion, parsePlanFile, planHash, serializePlanFile, } from "./plan.js";
|
|
8
9
|
export class BranchMovedError extends Error {
|
|
@@ -261,7 +262,16 @@ function makeSession(root, dir, anchor, diff) {
|
|
|
261
262
|
.filter((v) => below(v) && v > (done?.version ?? 0))
|
|
262
263
|
.sort((a, b) => a - b)
|
|
263
264
|
.at(-1);
|
|
264
|
-
|
|
265
|
+
if (unfinished === undefined)
|
|
266
|
+
return done?.review ?? null;
|
|
267
|
+
// A draft answers comments from every earlier version, so the finished review's
|
|
268
|
+
// comments stay visible beside the newer autosave's.
|
|
269
|
+
const current = draftReview(mode, unfinished);
|
|
270
|
+
if (done === undefined)
|
|
271
|
+
return current;
|
|
272
|
+
const seen = new Set(current.comments.map((c) => c.id));
|
|
273
|
+
const earlier = done.review.comments.filter((c) => !seen.has(c.id));
|
|
274
|
+
return { ...current, comments: [...earlier, ...current.comments] };
|
|
265
275
|
};
|
|
266
276
|
return {
|
|
267
277
|
root,
|
|
@@ -410,6 +420,35 @@ export async function openSessionAt(dir) {
|
|
|
410
420
|
}
|
|
411
421
|
return openSession(dirname(top), { branch: anchor.branch });
|
|
412
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* The diff a stored plan was made on: the session's own when the plan is for this tip, else the
|
|
425
|
+
* plan's base and tip loaded again. Null when those commits are gone from the repo.
|
|
426
|
+
*/
|
|
427
|
+
export async function diffOfPlan(session, plan) {
|
|
428
|
+
const { base, tip } = plan.file;
|
|
429
|
+
if (base === session.anchor.base && tip === session.anchor.tip)
|
|
430
|
+
return session.diff;
|
|
431
|
+
try {
|
|
432
|
+
return await loadDiff(session.root, base, tip);
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
if (error instanceof GitError)
|
|
436
|
+
return null;
|
|
437
|
+
throw error;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* How the latest plan carries over to the branch as it is now, or null when that plan was made
|
|
442
|
+
* for this tip. The old diff is loaded from the plan's own base and tip, so this gives the same
|
|
443
|
+
* answer however many times it runs after the move.
|
|
444
|
+
*/
|
|
445
|
+
export async function sinceLastPlan(session) {
|
|
446
|
+
const latest = session.plans().at(-1);
|
|
447
|
+
if (latest === undefined || latest.file.tip === session.anchor.tip)
|
|
448
|
+
return null;
|
|
449
|
+
const oldDiff = await loadDiff(session.root, latest.file.base, latest.file.tip);
|
|
450
|
+
return { since: latest.version, ...evolvePlan(latest.file, oldDiff, session.diff) };
|
|
451
|
+
}
|
|
413
452
|
/** "<name>.<pid>.<random>.tmp" as written by writeJson, the lock and the review server. */
|
|
414
453
|
function abandonedTemps(dir) {
|
|
415
454
|
if (!existsSync(dir))
|
package/dist/ui/dist/assets/{abnfDiagram-WEYOIA3C-DcmzRkVZ.js → abnfDiagram-WEYOIA3C-byISxePP.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,t}from"./mermaid-parser.core-
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-CAyLSQpH.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-Dym5afQ5.js";import"./chunk-DSNLSHJZ-DASbe_gb.js";import{n as i,r as a,t as o}from"./chunk-2E7WHVMD-zZdNfMkj.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-vfVMjgx2.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-
|
|
1
|
+
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-NDVqCM7k.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
|
package/dist/ui/dist/assets/{architecture-WOLXFQ4H-DUIkQTeE.js → architecture-WOLXFQ4H-0OSkIwdB.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./chunk-NGNAAXSQ-C2Wy9Xmz.js";export{M as createArchitectureServices}from"./mermaid-parser.core-
|
|
1
|
+
import"./chunk-NGNAAXSQ-C2Wy9Xmz.js";export{M as createArchitectureServices}from"./mermaid-parser.core-CAyLSQpH.js";
|