@shiyuphan/follow-me 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -69
- package/dist/src/cli.js +1069 -140
- package/dist/src/comments.js +361 -0
- package/dist/src/git.js +32 -3
- package/dist/src/host.js +106 -9
- package/dist/src/install.js +22 -1
- package/dist/src/naive.js +26 -0
- package/dist/src/plan.js +282 -26
- package/dist/src/prompt.js +711 -98
- package/dist/src/server.js +573 -140
- package/dist/src/session.js +106 -104
- package/dist/src/syntax.js +199 -0
- package/dist/src/types.js +3 -1
- package/dist/ui/dist/assets/abnfDiagram-WEYOIA3C-DcmzRkVZ.js +1 -0
- package/dist/ui/dist/assets/arc-T5QB0T1C.js +1 -0
- package/dist/ui/dist/assets/architecture-WOLXFQ4H-DUIkQTeE.js +1 -0
- package/dist/ui/dist/assets/architectureDiagram-ZHVYKT53-BUYi4-bA.js +36 -0
- package/dist/ui/dist/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/dist/assets/blockDiagram-RJAE7QA3-dyJxtT9e.js +140 -0
- package/dist/ui/dist/assets/c4Diagram-XDPPPE54-CrAgiaZR.js +38 -0
- package/dist/ui/dist/assets/channel-wW7ebm0i.js +1 -0
- package/dist/ui/dist/assets/chunk-2E7WHVMD-C6fAf4Vj.js +88 -0
- package/dist/ui/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/dist/assets/chunk-4EMNKR2T-BLZjwZ4Q.js +1 -0
- package/dist/ui/dist/assets/chunk-55XCU76K-DGLK2Q-L.js +1 -0
- package/dist/ui/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/dist/assets/chunk-7S6XSTFO-BablszTX.js +2 -0
- package/dist/ui/dist/assets/chunk-AQDTSKZB-Cuk3B_vb.js +72 -0
- package/dist/ui/dist/assets/chunk-DSNLSHJZ-DiC78RqE.js +127 -0
- package/dist/ui/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/dist/ui/dist/assets/chunk-HAGFOTED-X_RbvixX.js +62 -0
- package/dist/ui/dist/assets/chunk-HNE4Q36P-C14_x5ck.js +1 -0
- package/dist/ui/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/dist/ui/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/ui/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
- package/dist/ui/dist/assets/chunk-NQOKRVIF-Dxd0dtYR.js +213 -0
- package/dist/ui/dist/assets/chunk-UHW7PFBP-BIxePNUS.js +1 -0
- package/dist/ui/dist/assets/chunk-VQ5W62QI-CKSKLbj4.js +1 -0
- package/dist/ui/dist/assets/chunk-WQQL4CDK-CWNmZGlR.js +10 -0
- package/dist/ui/dist/assets/chunk-XXDRQBXY-D-I5QHgG.js +1 -0
- package/dist/ui/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/dist/assets/chunk-YZO67HFG-DFP68CFd.js +1 -0
- package/dist/ui/dist/assets/chunk-ZAUX2LTX-DY90d4QG.js +1 -0
- package/dist/ui/dist/assets/chunk-ZCHMQSOR-Dlpr-5UC.js +2 -0
- package/dist/ui/dist/assets/classDiagram-v2-4MYWR3UA-CH9uhoLJ.js +217 -0
- package/dist/ui/dist/assets/cose-bilkent-JH36ORCC-DtYHrp8D.js +1 -0
- package/dist/ui/dist/assets/cynefin-EF2NZ3EQ-BtslbtrY.js +1 -0
- package/dist/ui/dist/assets/cynefinDiagram-WF24UDRP-DUU1Jcc7.js +62 -0
- package/dist/ui/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/dist/ui/dist/assets/dagre-NK4GIZVX-CDlP-dRW.js +4 -0
- package/dist/ui/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/dist/assets/diagram-CBK3MWTC-DiV2fn-r.js +24 -0
- package/dist/ui/dist/assets/diagram-I5XIIV2D-Bg5RehGi.js +24 -0
- package/dist/ui/dist/assets/diagram-ODIL2UCW-CFmazz3f.js +30 -0
- package/dist/ui/dist/assets/diagram-PFBEJUXE-uw3ZBqDp.js +3 -0
- package/dist/ui/dist/assets/diagram-RJA3I4VV-uEOYYRYa.js +41 -0
- package/dist/ui/dist/assets/diagram-TYUNFSNR-BN58LF_3.js +200 -0
- package/dist/ui/dist/assets/dist-tWS_lXDS.js +1 -0
- package/dist/ui/dist/assets/ebnfDiagram-V4P3TTPT-B68Tz-w8.js +1 -0
- package/dist/ui/dist/assets/elk-QONTBCTQ-DZZohNea.js +27 -0
- package/dist/ui/dist/assets/erDiagram-KYQIGQAA-BH_m74pv.js +99 -0
- package/dist/ui/dist/assets/eventmodeling-K75KTNOO-CTp5FHos.js +1 -0
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-CsCvexry.js +1 -0
- package/dist/ui/dist/assets/ganttDiagram-R7GPNTJY-BDau7_0f.js +292 -0
- package/dist/ui/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/ui/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/ui/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/ui/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/ui/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/ui/dist/assets/gitGraph-VSP46ZUC-Bv84IoCm.js +1 -0
- package/dist/ui/dist/assets/gitGraphDiagram-PDYL7XHY-DzXcMlm8.js +106 -0
- package/dist/ui/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/dist/assets/index-BTJoqVVN.js +133 -0
- package/dist/ui/dist/assets/index-D60U3smc.css +1 -0
- package/dist/ui/dist/assets/info-OHQRW6UA-222J0O_j.js +1 -0
- package/dist/ui/dist/assets/infoDiagram-MVDRKAX5-DE2sa3Vz.js +2 -0
- package/dist/ui/dist/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/dist/assets/ishikawaDiagram-HPEHV2GE-BJDk0Zcj.js +70 -0
- package/dist/ui/dist/assets/journeyDiagram-BOSHW7O7-JSUXX5Hb.js +139 -0
- package/dist/ui/dist/assets/kanban-definition-FNSA4HC5-Bg79b49M.js +89 -0
- package/dist/ui/dist/assets/katex-B_JcIwqj.js +257 -0
- package/dist/ui/dist/assets/line-Bk5qQSIa.js +1 -0
- package/dist/ui/dist/assets/linear-PFnVMru5.js +1 -0
- package/dist/ui/dist/assets/mermaid-parser.core-Dcxlzc-S.js +7 -0
- package/dist/ui/dist/assets/mermaid.core-TeOHQJ00.js +44 -0
- package/dist/ui/dist/assets/mindmap-definition-6OY57UG3-IaNk8NnW.js +98 -0
- package/dist/ui/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/dist/assets/packet-JDAUHWVQ-DMff5cVK.js +1 -0
- package/dist/ui/dist/assets/path-fybaL0A-.js +1 -0
- package/dist/ui/dist/assets/pegDiagram-ECIUJNBX-66Xu4YRc.js +1 -0
- package/dist/ui/dist/assets/pie-XZMESJXO-GEtzLCIM.js +1 -0
- package/dist/ui/dist/assets/pieDiagram-GNMPY74T-7jMBi-kU.js +39 -0
- package/dist/ui/dist/assets/prism-bash-D6zCJ74D.js +1 -0
- package/dist/ui/dist/assets/prism-c-04YixN25.js +1 -0
- package/dist/ui/dist/assets/prism-clike-DapgWyxx.js +1 -0
- package/dist/ui/dist/assets/prism-core-FDHlWQ43.js +1 -0
- package/dist/ui/dist/assets/prism-cpp-C-lJbC-6.js +1 -0
- package/dist/ui/dist/assets/prism-css-CPgOzxXp.js +1 -0
- package/dist/ui/dist/assets/prism-go-C_8qAH5n.js +1 -0
- package/dist/ui/dist/assets/prism-java-6wtusvIl.js +1 -0
- package/dist/ui/dist/assets/prism-javascript-BnO-swvr.js +1 -0
- package/dist/ui/dist/assets/prism-json-Dp_-W-Hv.js +1 -0
- package/dist/ui/dist/assets/prism-jsx-D-D0NWtC.js +1 -0
- package/dist/ui/dist/assets/prism-kotlin-BUlJgC-o.js +1 -0
- package/dist/ui/dist/assets/prism-markdown-Bc9-36OC.js +1 -0
- package/dist/ui/dist/assets/prism-markup-Cp04rIa1.js +1 -0
- package/dist/ui/dist/assets/prism-markup-templating-KkSMBj6Y.js +1 -0
- package/dist/ui/dist/assets/prism-php-Co4fM40K.js +1 -0
- package/dist/ui/dist/assets/prism-python-CfiSVtM6.js +1 -0
- package/dist/ui/dist/assets/prism-ruby-DFCASkuD.js +1 -0
- package/dist/ui/dist/assets/prism-rust-Bu9N8X3N.js +1 -0
- package/dist/ui/dist/assets/prism-sql-CYhVDMEE.js +1 -0
- package/dist/ui/dist/assets/prism-swift-B0kWc-ne.js +1 -0
- package/dist/ui/dist/assets/prism-toml-DKb54GAT.js +1 -0
- package/dist/ui/dist/assets/prism-tsx-p6J2Kc_4.js +1 -0
- package/dist/ui/dist/assets/prism-typescript-BMqONzxf.js +1 -0
- package/dist/ui/dist/assets/prism-yaml-C0JHu8gr.js +1 -0
- package/dist/ui/dist/assets/quadrantDiagram-D57K4MC3-8hIWayC2.js +7 -0
- package/dist/ui/dist/assets/radar-ABXABTNO-BTdwUOOF.js +1 -0
- package/dist/ui/dist/assets/railroad-I3PHUGI6-DcMbDqBL.js +1 -0
- package/dist/ui/dist/assets/railroad-abnf-LNEFI6M7-gTzUTWeI.js +1 -0
- package/dist/ui/dist/assets/railroad-ebnf-DWYD2UWJ-CNtdUPtk.js +1 -0
- package/dist/ui/dist/assets/railroad-peg-JR7BVNR7-DOiExAtE.js +1 -0
- package/dist/ui/dist/assets/railroadDiagram-Y72NRBL7-2kE_ygvM.js +1 -0
- package/dist/ui/dist/assets/requirementDiagram-EQYSW5RA-CoIAHRq2.js +84 -0
- package/dist/ui/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/dist/ui/dist/assets/sankeyDiagram-SY24JHO5-7p8D3wim.js +40 -0
- package/dist/ui/dist/assets/sequenceDiagram-BLEHNHQU-C6QFnMvn.js +169 -0
- package/dist/ui/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/ui/dist/assets/src-FgijoBVs.js +1 -0
- package/dist/ui/dist/assets/stateDiagram-v2-A6IESKWZ-n9eAb_bT.js +291 -0
- package/dist/ui/dist/assets/swimlanes-B3ENRKFQ-U18mtUzX.js +1 -0
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DX07QVy9.js +8 -0
- package/dist/ui/dist/assets/timeline-definition-JXYKGLGB-CD2Z8QGP.js +120 -0
- package/dist/ui/dist/assets/treeView-D4JQ5SDB-sqUjH5gB.js +1 -0
- package/dist/ui/dist/assets/treemap-SAJKECNS-DvZ38FP7.js +1 -0
- package/dist/ui/dist/assets/usecaseDiagram-WPOLBNNX-CqOGidaZ.js +328 -0
- package/dist/ui/dist/assets/vennDiagram-Q3Y3IDCW-D1SXSBQ5.js +34 -0
- package/dist/ui/dist/assets/wardley-7MLQ67FV-Bpwio-Dx.js +1 -0
- package/dist/ui/dist/assets/wardleyDiagram-IVAIDKWA-JSUXHMnk.js +78 -0
- package/dist/ui/dist/assets/xychartDiagram-4URNHDU4-B4IFqQTN.js +7 -0
- package/dist/ui/dist/index.html +15 -0
- package/dist/ui/dist/style.css +121 -0
- package/package.json +31 -8
- package/skills/follow-me/SKILL.md +15 -61
- package/dist/src/anthropic.js +0 -101
- package/dist/ui/app.js +0 -797
- package/dist/ui/index.html +0 -40
- package/dist/ui/playback.js +0 -175
- package/dist/ui/style.css +0 -466
package/dist/src/session.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { existsSync, linkSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { existsSync, linkSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { parseDraft, parseReview } from "./comments.js";
|
|
5
|
+
import { isSha } from "./diff.js";
|
|
6
|
+
import { branchExists, currentBranch, defaultBaseRef, GitError, loadDiff, objectsDir, QUARANTINE_PREFIX, repoRoot, resolveCommit, } from "./git.js";
|
|
7
|
+
import { isFields, isPlanVersion, parsePlanFile, planHash, serializePlanFile, } from "./plan.js";
|
|
7
8
|
export class BranchMovedError extends Error {
|
|
8
9
|
name = "BranchMovedError";
|
|
9
10
|
}
|
|
@@ -11,6 +12,21 @@ export class BranchMovedError extends Error {
|
|
|
11
12
|
export class SessionFileError extends Error {
|
|
12
13
|
name = "SessionFileError";
|
|
13
14
|
}
|
|
15
|
+
/** draft.json has not been written yet. */
|
|
16
|
+
export class MissingDraftError extends SessionFileError {
|
|
17
|
+
path;
|
|
18
|
+
constructor(path) {
|
|
19
|
+
super(`${path} does not exist yet`);
|
|
20
|
+
this.path = path;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** The command ran outside any git work tree. */
|
|
24
|
+
export class NotARepositoryError extends Error {
|
|
25
|
+
name = "NotARepositoryError";
|
|
26
|
+
constructor() {
|
|
27
|
+
super("not a git repository (run it inside the repo you want to split)");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
14
30
|
/** Another live follow-me process holds the session's lock. */
|
|
15
31
|
export class SessionLockedError extends Error {
|
|
16
32
|
name = "SessionLockedError";
|
|
@@ -118,12 +134,15 @@ function adoptLock(dir, from, command) {
|
|
|
118
134
|
export function slugOf(branch) {
|
|
119
135
|
return branch.replaceAll("/", "-");
|
|
120
136
|
}
|
|
121
|
-
function
|
|
137
|
+
function writeText(path, text) {
|
|
122
138
|
mkdirSync(dirname(path), { recursive: true });
|
|
123
139
|
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
124
|
-
writeFileSync(temp,
|
|
140
|
+
writeFileSync(temp, text);
|
|
125
141
|
renameSync(temp, path);
|
|
126
142
|
}
|
|
143
|
+
function writeJson(path, value) {
|
|
144
|
+
writeText(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
145
|
+
}
|
|
127
146
|
function readJson(path) {
|
|
128
147
|
let text;
|
|
129
148
|
try {
|
|
@@ -169,101 +188,25 @@ function parseAnchor(v) {
|
|
|
169
188
|
function parseApplied(v) {
|
|
170
189
|
if (!isFields(v))
|
|
171
190
|
return null;
|
|
172
|
-
const { schema, version, branch, head } = v;
|
|
191
|
+
const { schema, version, branch, head, hash = null, signed = null } = v;
|
|
173
192
|
if (schema !== "follow-me/applied@1" || !isPlanVersion(version))
|
|
174
193
|
return null;
|
|
175
194
|
if (typeof branch !== "string" || typeof head !== "string" || !isSha(head))
|
|
176
195
|
return null;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
function parseCommentAnchor(v) {
|
|
183
|
-
if (!isFields(v))
|
|
184
|
-
return "anchor is not an object";
|
|
185
|
-
if (v.kind === "plan")
|
|
186
|
-
return { kind: "plan" };
|
|
187
|
-
if (!isCommitIndex(v.commit))
|
|
188
|
-
return "anchor.commit is not a commit index";
|
|
189
|
-
const commit = v.commit;
|
|
190
|
-
if (v.kind === "message" || v.kind === "position")
|
|
191
|
-
return { kind: v.kind, commit };
|
|
192
|
-
if (v.kind !== "hunk")
|
|
193
|
-
return "anchor.kind is unknown";
|
|
194
|
-
if (typeof v.unit !== "string" || !isUnitId(v.unit))
|
|
195
|
-
return "anchor.unit is not a unit id";
|
|
196
|
-
if (v.changes === null)
|
|
197
|
-
return { kind: "hunk", unit: v.unit, commit, changes: null };
|
|
198
|
-
if (!isArray(v.changes) || !v.changes.every(isCommitIndex)) {
|
|
199
|
-
return "anchor.changes is not null or a list of change indices";
|
|
200
|
-
}
|
|
201
|
-
return { kind: "hunk", unit: v.unit, commit, changes: v.changes.filter(isCommitIndex) };
|
|
202
|
-
}
|
|
203
|
-
function parseComment(v) {
|
|
204
|
-
if (!isFields(v))
|
|
205
|
-
return "not an object";
|
|
206
|
-
if (typeof v.id !== "string" || !isCommentId(v.id))
|
|
207
|
-
return "id is not a comment id";
|
|
208
|
-
const anchor = parseCommentAnchor(v.anchor);
|
|
209
|
-
if (typeof anchor === "string")
|
|
210
|
-
return anchor;
|
|
211
|
-
const ctx = v.context;
|
|
212
|
-
if (!isFields(ctx) || typeof ctx.commitMessage !== "string") {
|
|
213
|
-
return "context.commitMessage is not a string";
|
|
214
|
-
}
|
|
215
|
-
const path = ctx.path;
|
|
216
|
-
if (path !== null && (typeof path !== "string" || !isRepoPath(path))) {
|
|
217
|
-
return "context.path is not null or a path";
|
|
218
|
-
}
|
|
219
|
-
if (typeof v.body !== "string" || typeof v.createdAt !== "string") {
|
|
220
|
-
return "body and createdAt must be strings";
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
id: v.id,
|
|
224
|
-
anchor,
|
|
225
|
-
context: { commitMessage: ctx.commitMessage, path },
|
|
226
|
-
body: v.body,
|
|
227
|
-
createdAt: v.createdAt,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
/** Boundary parse of reviews/vN.json. Files written before walk mode existed have no mode. */
|
|
231
|
-
export function parseReview(v) {
|
|
232
|
-
if (!isFields(v)) {
|
|
233
|
-
return "not an object";
|
|
234
|
-
}
|
|
235
|
-
if (v.schema !== "follow-me/review@1")
|
|
236
|
-
return 'schema is not "follow-me/review@1"';
|
|
237
|
-
const mode = v.mode ?? "split";
|
|
238
|
-
if (mode !== "split" && mode !== "walk")
|
|
239
|
-
return 'mode is not "split" or "walk"';
|
|
240
|
-
if (!isPlanVersion(v.planVersion))
|
|
241
|
-
return "planVersion is not a positive integer";
|
|
242
|
-
if (typeof v.planHash !== "string" || typeof v.endedAt !== "string") {
|
|
243
|
-
return "planHash and endedAt must be strings";
|
|
244
|
-
}
|
|
245
|
-
if (!isArray(v.comments))
|
|
246
|
-
return "comments is not an array";
|
|
247
|
-
const comments = [];
|
|
248
|
-
for (const [i, raw] of v.comments.entries()) {
|
|
249
|
-
const c = parseComment(raw);
|
|
250
|
-
if (typeof c === "string")
|
|
251
|
-
return `comments[${i}]: ${c}`;
|
|
252
|
-
comments.push(c);
|
|
253
|
-
}
|
|
254
|
-
return {
|
|
255
|
-
schema: v.schema,
|
|
256
|
-
mode,
|
|
257
|
-
planVersion: v.planVersion,
|
|
258
|
-
planHash: v.planHash,
|
|
259
|
-
endedAt: v.endedAt,
|
|
260
|
-
comments,
|
|
261
|
-
};
|
|
196
|
+
if (hash !== null && typeof hash !== "string")
|
|
197
|
+
return null;
|
|
198
|
+
if (signed !== null && typeof signed !== "boolean")
|
|
199
|
+
return null;
|
|
200
|
+
return { schema, version, branch, head, hash, signed };
|
|
262
201
|
}
|
|
263
202
|
function makeSession(root, dir, anchor, diff) {
|
|
264
203
|
const plansDir = join(dir, "plans");
|
|
265
|
-
const
|
|
204
|
+
const splitReviews = join(dir, "reviews");
|
|
205
|
+
// Older builds wrote reviewer reviews to reviews/walk/; they stay readable, nothing new goes there.
|
|
206
|
+
const legacyWalkDir = join(splitReviews, "walk");
|
|
207
|
+
const reviewsDir = () => splitReviews;
|
|
266
208
|
const draftPath = join(dir, "draft.json");
|
|
209
|
+
const promptPath = join(dir, "prompt.md");
|
|
267
210
|
const appliedPath = join(dir, "applied.json");
|
|
268
211
|
const plans = () => versions(plansDir).map(({ version, path }) => {
|
|
269
212
|
const file = parsePlanFile(readJson(path));
|
|
@@ -274,30 +217,75 @@ function makeSession(root, dir, anchor, diff) {
|
|
|
274
217
|
}
|
|
275
218
|
return { version, file, hash: planHash(file) };
|
|
276
219
|
});
|
|
277
|
-
const
|
|
220
|
+
const reviewsIn = (dir) => versions(dir).map(({ version, path }) => {
|
|
278
221
|
const review = parseReview(readJson(path));
|
|
279
222
|
if (typeof review === "string")
|
|
280
223
|
throw new SessionFileError(`${path}: ${review}`);
|
|
281
224
|
return { version, review };
|
|
282
225
|
});
|
|
226
|
+
const reviews = () => reviewsIn(splitReviews);
|
|
227
|
+
const walkReviews = () => [...reviews().filter((r) => r.review.mode === "walk"), ...reviewsIn(legacyWalkDir)].sort((a, b) => a.version - b.version);
|
|
228
|
+
const draftVersions = () => {
|
|
229
|
+
const dir = reviewsDir();
|
|
230
|
+
if (!existsSync(dir))
|
|
231
|
+
return [];
|
|
232
|
+
return readdirSync(dir).flatMap((name) => {
|
|
233
|
+
const n = Number(/^v([1-9][0-9]*)\.draft\.json$/.exec(name)?.[1]);
|
|
234
|
+
return isPlanVersion(n) ? [n] : [];
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
const draftReview = (mode, version) => {
|
|
238
|
+
const path = join(reviewsDir(), `v${version}.draft.json`);
|
|
239
|
+
const draft = parseDraft(readJson(path));
|
|
240
|
+
if (typeof draft === "string")
|
|
241
|
+
throw new SessionFileError(`${path}: ${draft}`);
|
|
242
|
+
const { comments, concerns, resolved, excluded } = draft;
|
|
243
|
+
return {
|
|
244
|
+
schema: "follow-me/review@1",
|
|
245
|
+
mode,
|
|
246
|
+
planVersion: version,
|
|
247
|
+
planHash: plans().find((p) => p.version === version)?.hash ?? "",
|
|
248
|
+
endedAt: statSync(path).mtime.toISOString(),
|
|
249
|
+
comments,
|
|
250
|
+
...(concerns.length === 0 ? {} : { concerns }),
|
|
251
|
+
...(resolved.length === 0 ? {} : { resolved }),
|
|
252
|
+
...(excluded.comments.length + excluded.concerns.length === 0 ? {} : { excluded }),
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
const priorReview = (mode, before) => {
|
|
256
|
+
const below = (v) => before === null || v < before;
|
|
257
|
+
const done = (mode === "walk" ? walkReviews() : reviews().filter((r) => r.review.mode === mode))
|
|
258
|
+
.filter((r) => below(r.version))
|
|
259
|
+
.at(-1);
|
|
260
|
+
const unfinished = draftVersions()
|
|
261
|
+
.filter((v) => below(v) && v > (done?.version ?? 0))
|
|
262
|
+
.sort((a, b) => a - b)
|
|
263
|
+
.at(-1);
|
|
264
|
+
return unfinished === undefined ? (done?.review ?? null) : draftReview(mode, unfinished);
|
|
265
|
+
};
|
|
283
266
|
return {
|
|
284
267
|
root,
|
|
285
268
|
dir,
|
|
286
269
|
anchor,
|
|
287
270
|
diff,
|
|
288
271
|
draftPath,
|
|
272
|
+
promptPath,
|
|
289
273
|
plans,
|
|
274
|
+
reviewsDir,
|
|
290
275
|
reviews,
|
|
291
|
-
|
|
276
|
+
walkReviews,
|
|
277
|
+
lastReview: () => priorReview("split", null),
|
|
278
|
+
priorReview,
|
|
292
279
|
readDraft: () => {
|
|
293
280
|
const draft = readJson(draftPath);
|
|
294
281
|
if (draft === undefined)
|
|
295
|
-
throw new
|
|
282
|
+
throw new MissingDraftError(draftPath);
|
|
296
283
|
return draft;
|
|
297
284
|
},
|
|
298
285
|
writeDraft: (draft) => writeJson(draftPath, draft),
|
|
286
|
+
writePrompt: (text) => writeText(promptPath, text),
|
|
299
287
|
writeReview: (review) => {
|
|
300
|
-
const path = join(reviewsDir, `v${review.planVersion}.json`);
|
|
288
|
+
const path = join(reviewsDir(), `v${review.planVersion}.json`);
|
|
301
289
|
if (existsSync(path))
|
|
302
290
|
throw new SessionFileError(`${path} is already written`);
|
|
303
291
|
writeJson(path, review);
|
|
@@ -324,16 +312,30 @@ function makeSession(root, dir, anchor, diff) {
|
|
|
324
312
|
throw new SessionFileError(`${appliedPath} is not an applied record`);
|
|
325
313
|
return applied;
|
|
326
314
|
},
|
|
327
|
-
recordApplied: (
|
|
315
|
+
recordApplied: (plan, result) => writeJson(appliedPath, {
|
|
328
316
|
schema: "follow-me/applied@1",
|
|
329
|
-
version,
|
|
317
|
+
version: plan.version,
|
|
330
318
|
branch: result.branch,
|
|
331
319
|
head: result.head,
|
|
320
|
+
hash: plan.hash,
|
|
321
|
+
signed: result.signed,
|
|
332
322
|
}),
|
|
333
323
|
};
|
|
334
324
|
}
|
|
325
|
+
/** The top level of the work tree holding cwd. */
|
|
326
|
+
async function rootOf(cwd) {
|
|
327
|
+
try {
|
|
328
|
+
return await repoRoot(cwd);
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
if (error instanceof GitError && error.message.includes("not a git repository")) {
|
|
332
|
+
throw new NotARepositoryError();
|
|
333
|
+
}
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
335
337
|
async function locate(repo, branch) {
|
|
336
|
-
const root = await
|
|
338
|
+
const root = await rootOf(repo);
|
|
337
339
|
const name = branch ?? (await currentBranch(root));
|
|
338
340
|
if (name === null)
|
|
339
341
|
throw new Error("HEAD is detached; pass --branch <name>");
|
|
@@ -421,8 +423,7 @@ function abandonedTemps(dir) {
|
|
|
421
423
|
.map((name) => join(dir, name));
|
|
422
424
|
}
|
|
423
425
|
/** Review drafts for a version at or below the latest finished review; none can be resumed. */
|
|
424
|
-
function deadReviewDrafts(
|
|
425
|
-
const reviewsDir = join(dir, "reviews");
|
|
426
|
+
function deadReviewDrafts(reviewsDir) {
|
|
426
427
|
const latest = versions(reviewsDir).at(-1)?.version ?? 0;
|
|
427
428
|
if (!existsSync(reviewsDir))
|
|
428
429
|
return [];
|
|
@@ -441,7 +442,7 @@ function deadReviewDrafts(dir) {
|
|
|
441
442
|
* a live process are left alone, and all refuses while one exists.
|
|
442
443
|
*/
|
|
443
444
|
export async function cleanSessions(repo, opts) {
|
|
444
|
-
const root = await
|
|
445
|
+
const root = await rootOf(repo);
|
|
445
446
|
const top = join(root, ".follow-me");
|
|
446
447
|
const sessions = existsSync(top)
|
|
447
448
|
? readdirSync(top, { withFileTypes: true })
|
|
@@ -466,7 +467,8 @@ export async function cleanSessions(repo, opts) {
|
|
|
466
467
|
}
|
|
467
468
|
if (lock.kind === "stale")
|
|
468
469
|
found.push(join(dir, "lock"));
|
|
469
|
-
|
|
470
|
+
const reviews = join(dir, "reviews");
|
|
471
|
+
found.push(...abandonedTemps(dir), ...deadReviewDrafts(reviews), ...deadReviewDrafts(join(reviews, "walk")));
|
|
470
472
|
}
|
|
471
473
|
if (opts.all && existsSync(top))
|
|
472
474
|
found.push(top);
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { extname, join } from "node:path";
|
|
5
|
+
const LIMIT = 4;
|
|
6
|
+
const TIMEOUT_MS = 30_000;
|
|
7
|
+
/**
|
|
8
|
+
* node --check does not strip types, so .ts goes through module.stripTypeScriptTypes, which
|
|
9
|
+
* parses the whole file and throws on a syntax error. An enum or namespace parses but cannot be
|
|
10
|
+
* stripped, which is node's limit and not the file's, so that counts as parsing.
|
|
11
|
+
*/
|
|
12
|
+
const STRIP_TYPES = `
|
|
13
|
+
const { stripTypeScriptTypes } = require("node:module");
|
|
14
|
+
const { readFileSync } = require("node:fs");
|
|
15
|
+
try {
|
|
16
|
+
stripTypeScriptTypes(readFileSync(process.argv[1], "utf8"), { mode: "strip" });
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error.code === "ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX") process.exit(0);
|
|
19
|
+
console.error(error.message);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}`;
|
|
22
|
+
/** Runs argv with {file} replaced; a program that is not installed skips the file. */
|
|
23
|
+
function command(argv) {
|
|
24
|
+
return (file) => run(argv.map((a) => (a === "{file}" ? file : a)), file, argv[0] ?? "");
|
|
25
|
+
}
|
|
26
|
+
function run(argv, file, what) {
|
|
27
|
+
const [program, ...rest] = argv;
|
|
28
|
+
if (program === undefined)
|
|
29
|
+
return Promise.resolve({ kind: "parses" });
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
execFile(program, rest, { timeout: TIMEOUT_MS, encoding: "utf8" }, (error, stdout, stderr) => {
|
|
32
|
+
if (error === null)
|
|
33
|
+
return resolve({ kind: "parses" });
|
|
34
|
+
if (error.code === "ENOENT") {
|
|
35
|
+
return resolve({ kind: "skipped", reason: `${what} is not installed` });
|
|
36
|
+
}
|
|
37
|
+
const output = stderr.trim() === "" ? stdout : stderr;
|
|
38
|
+
const message = error.killed === true
|
|
39
|
+
? `checker timed out after ${TIMEOUT_MS / 1000}s`
|
|
40
|
+
: output.trim() === ""
|
|
41
|
+
? `checker exited ${error.code}`
|
|
42
|
+
: complaint(output, file);
|
|
43
|
+
resolve({ kind: "fails", message });
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The line of a parser's output that names the problem. node prints `file:LINE`, the source line,
|
|
49
|
+
* a caret and then `SyntaxError: ...`; python prints a traceback ending in `SyntaxError: ...`;
|
|
50
|
+
* sh and bash print one line. The temp path never leaks into the message.
|
|
51
|
+
*/
|
|
52
|
+
export function complaint(output, file) {
|
|
53
|
+
const lines = output
|
|
54
|
+
.replaceAll(file, "file")
|
|
55
|
+
.split("\n")
|
|
56
|
+
.map((l) => l.trimEnd())
|
|
57
|
+
.filter((l) => l !== "");
|
|
58
|
+
const named = lines.find((l) => /^[A-Za-z]*Error\b/.test(l));
|
|
59
|
+
if (named === undefined)
|
|
60
|
+
return lines[0] ?? "does not parse";
|
|
61
|
+
const where = /^file:(\d+)$/.exec(lines[0] ?? "")?.[1];
|
|
62
|
+
return where === undefined ? named : `line ${where}: ${named}`;
|
|
63
|
+
}
|
|
64
|
+
const json = async (_file, text) => {
|
|
65
|
+
try {
|
|
66
|
+
JSON.parse(text);
|
|
67
|
+
return { kind: "parses" };
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
return { kind: "fails", message: message.split("\n")[0] ?? message };
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/** Bash scripts need bash -n; a plain .sh is held to POSIX unless its shebang names bash. */
|
|
75
|
+
const sh = command(["sh", "-n", "{file}"]);
|
|
76
|
+
const bash = command(["bash", "-n", "{file}"]);
|
|
77
|
+
const shell = (file, text) => /^#!.*\bbash\b/.test(text) || file.endsWith(".bash") ? bash(file, text) : sh(file, text);
|
|
78
|
+
const node = command(["node", "--check", "{file}"]);
|
|
79
|
+
const types = command([
|
|
80
|
+
"node",
|
|
81
|
+
"--disable-warning=ExperimentalWarning",
|
|
82
|
+
"-e",
|
|
83
|
+
STRIP_TYPES,
|
|
84
|
+
"{file}",
|
|
85
|
+
]);
|
|
86
|
+
const python = command(["python3", "-m", "py_compile", "{file}"]);
|
|
87
|
+
/**
|
|
88
|
+
* Extension to checker. A missing extension is skipped, not guessed: .jsx and .tsx need a JSX
|
|
89
|
+
* parser node does not have, and yaml has none installed.
|
|
90
|
+
*/
|
|
91
|
+
const CHECKERS = new Map([
|
|
92
|
+
[".js", node],
|
|
93
|
+
[".mjs", node],
|
|
94
|
+
[".cjs", node],
|
|
95
|
+
[".ts", types],
|
|
96
|
+
[".mts", types],
|
|
97
|
+
[".cts", types],
|
|
98
|
+
[".json", json],
|
|
99
|
+
[".sh", shell],
|
|
100
|
+
[".bash", shell],
|
|
101
|
+
[".py", python],
|
|
102
|
+
]);
|
|
103
|
+
function quote(s) {
|
|
104
|
+
return `'${s.replaceAll("'", `'\\''`)}'`;
|
|
105
|
+
}
|
|
106
|
+
/** The --syntax override: one shell command for every text file, {file} replaced by its path. */
|
|
107
|
+
function overrideChecker(template) {
|
|
108
|
+
return (file) => run(["sh", "-c", template.replaceAll("{file}", quote(file))], file, "the --syntax command");
|
|
109
|
+
}
|
|
110
|
+
async function mapLimit(items, limit, fn) {
|
|
111
|
+
const out = new Array(items.length);
|
|
112
|
+
let next = 0;
|
|
113
|
+
const worker = async () => {
|
|
114
|
+
for (let i = next++; i < items.length; i = next++) {
|
|
115
|
+
const item = items[i];
|
|
116
|
+
if (item !== undefined)
|
|
117
|
+
out[i] = await fn(item);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
function byPath(a, b) {
|
|
124
|
+
return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Checks every file each commit writes. The last commit equals the tip by construction, so a
|
|
128
|
+
* file that fails there is reported as the tip's own state and is not checked in earlier commits,
|
|
129
|
+
* where the same failure would be blamed on the split.
|
|
130
|
+
*/
|
|
131
|
+
export async function checkSyntax(plan, opts) {
|
|
132
|
+
const last = plan.commits.length - 1;
|
|
133
|
+
const jobs = plan.commits.flatMap((c) => c.changes.flatMap((ch) => ch.op === "write" ? [{ commit: c.index, path: ch.path, bytes: ch.bytes }] : []));
|
|
134
|
+
// node prints the resolved path, so the dir is resolved too for the message to find it.
|
|
135
|
+
const dir = await realpath(await mkdtemp(join(tmpdir(), "follow-me-syntax-")));
|
|
136
|
+
try {
|
|
137
|
+
const cache = new Map();
|
|
138
|
+
let serial = 0;
|
|
139
|
+
const outcomeOf = (job) => {
|
|
140
|
+
const ext = extname(job.path).toLowerCase();
|
|
141
|
+
const checker = opts.override === undefined ? CHECKERS.get(ext) : overrideChecker(opts.override);
|
|
142
|
+
if (checker === undefined) {
|
|
143
|
+
return Promise.resolve({
|
|
144
|
+
kind: "skipped",
|
|
145
|
+
reason: `no checker for ${ext === "" ? "files without an extension" : ext}`,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const key = `${ext}\0${job.bytes}`;
|
|
149
|
+
const known = cache.get(key);
|
|
150
|
+
if (known !== undefined)
|
|
151
|
+
return known;
|
|
152
|
+
const file = join(dir, `f${serial++}${ext}`);
|
|
153
|
+
const fresh = writeFile(file, Buffer.from(job.bytes, "latin1")).then(() => checker(file, job.bytes));
|
|
154
|
+
cache.set(key, fresh);
|
|
155
|
+
return fresh;
|
|
156
|
+
};
|
|
157
|
+
const tipJobs = jobs.filter((j) => j.commit === last);
|
|
158
|
+
const tipOutcomes = await mapLimit(tipJobs, LIMIT, outcomeOf);
|
|
159
|
+
const tip = [];
|
|
160
|
+
const broken = new Set();
|
|
161
|
+
tipJobs.forEach((job, i) => {
|
|
162
|
+
const outcome = tipOutcomes[i];
|
|
163
|
+
if (outcome?.kind !== "fails")
|
|
164
|
+
return;
|
|
165
|
+
tip.push({ commit: job.commit, path: job.path, message: outcome.message });
|
|
166
|
+
broken.add(job.path);
|
|
167
|
+
});
|
|
168
|
+
const rest = jobs.filter((j) => j.commit !== last && !broken.has(j.path));
|
|
169
|
+
const outcomes = await mapLimit(rest, LIMIT, outcomeOf);
|
|
170
|
+
const issues = [];
|
|
171
|
+
const skipped = new Map();
|
|
172
|
+
let checked = 0;
|
|
173
|
+
const all = [
|
|
174
|
+
...tipJobs.map((j, i) => [j, tipOutcomes[i]]),
|
|
175
|
+
...rest.map((j, i) => [j, outcomes[i]]),
|
|
176
|
+
];
|
|
177
|
+
for (const [job, outcome] of all) {
|
|
178
|
+
if (outcome === undefined)
|
|
179
|
+
continue;
|
|
180
|
+
if (outcome.kind === "skipped")
|
|
181
|
+
skipped.set(job.path, outcome.reason);
|
|
182
|
+
else
|
|
183
|
+
checked++;
|
|
184
|
+
if (outcome.kind === "fails" && job.commit !== last) {
|
|
185
|
+
issues.push({ commit: job.commit, path: job.path, message: outcome.message });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
issues.sort((a, b) => a.commit - b.commit || byPath(a, b));
|
|
189
|
+
return {
|
|
190
|
+
issues,
|
|
191
|
+
tip,
|
|
192
|
+
skipped: [...skipped].map(([path, reason]) => ({ path, reason })).sort(byPath),
|
|
193
|
+
checked,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
await rm(dir, { recursive: true, force: true });
|
|
198
|
+
}
|
|
199
|
+
}
|
package/dist/src/types.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* project every commit, prove the last one equals the tip. Pure.
|
|
9
9
|
* git.ts the only module that spawns git: load a Diff, write a branch from a ValidPlan.
|
|
10
10
|
* session.ts the .follow-me/<slug>/ layout: session anchor, plans, reviews, draft.
|
|
11
|
-
*
|
|
11
|
+
* comments.ts the wire formats of review state: comments, replies, concern decisions, the
|
|
12
|
+
* review file, the autosaved draft and the server's events. Pure parsers.
|
|
13
|
+
* prompt.ts the planner prompt for an agent, the feedback and the consolidated report.
|
|
12
14
|
* server.ts the review server: a registry of hosted sessions, their JSON API, and ui/.
|
|
13
15
|
* host.ts the one server per user: its record in the state dir, attach, start, stop.
|
|
14
16
|
* cli.ts argv -> one call into the modules above.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-Dcxlzc-S.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-FgijoBVs.js";import"./chunk-DSNLSHJZ-DiC78RqE.js";import{n as i,r as a,t as o}from"./chunk-2E7WHVMD-C6fAf4Vj.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-TeOHQJ00.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};
|
|
@@ -0,0 +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-tWS_lXDS.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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-NGNAAXSQ-C2Wy9Xmz.js";export{M as createArchitectureServices}from"./mermaid-parser.core-Dcxlzc-S.js";
|