@davidbalzan/groundwork-console 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/bus.js +54 -3
  2. package/dist/events.js +1 -1
  3. package/dist/gitinfo.js +42 -2
  4. package/dist/seam.js +27 -10
  5. package/dist/seamwrite.js +5 -1
  6. package/dist/server.js +10 -10
  7. package/dist/tmux.js +1 -1
  8. package/package.json +7 -4
  9. package/public/assets/{abnfDiagram-N423BO3Z-C7_Zpec2.js → abnfDiagram-N423BO3Z-2g9UHdfw.js} +1 -1
  10. package/public/assets/{arc-Bbq8pKhn.js → arc-EEINoaIY.js} +1 -1
  11. package/public/assets/architecture-TIHT7OUA-CFm6TN4T.js +1 -0
  12. package/public/assets/{architectureDiagram-T3A2C74G-tEkFMAHO.js → architectureDiagram-T3A2C74G-D4F9rIiu.js} +1 -1
  13. package/public/assets/{blockDiagram-VBNYF7ZC-CZNrU4_z.js → blockDiagram-VBNYF7ZC-CQZAhVXz.js} +1 -1
  14. package/public/assets/{c4Diagram-5PPSVZJV-D1-T-b--.js → c4Diagram-5PPSVZJV-BlBzSIXs.js} +1 -1
  15. package/public/assets/channel-CHLtdsMA.js +1 -0
  16. package/public/assets/{chunk-2GRJ4B5K-DMzvnmxY.js → chunk-2GRJ4B5K-ZVZxINuZ.js} +1 -1
  17. package/public/assets/{chunk-4I5QYGJK-D03m5a1e.js → chunk-4I5QYGJK-8hqVPXHL.js} +1 -1
  18. package/public/assets/{chunk-5RXB4S5H-B5SxHfnz.js → chunk-5RXB4S5H-D1s6L_89.js} +1 -1
  19. package/public/assets/{chunk-6Q2QTUOP-BQYOf-YY.js → chunk-6Q2QTUOP-BWLeyKxS.js} +1 -1
  20. package/public/assets/{chunk-7Z6QIM7H-DGtHjZJL.js → chunk-7Z6QIM7H-DjvOr6wh.js} +1 -1
  21. package/public/assets/{chunk-GF5L2VYU-6_YTDzGo.js → chunk-GF5L2VYU-BCFrJAqb.js} +1 -1
  22. package/public/assets/{chunk-I66GZJ75-CfWGFqqO.js → chunk-I66GZJ75-XdTCBJhL.js} +1 -1
  23. package/public/assets/{chunk-JQJVKLGR-DrCjH1AL.js → chunk-JQJVKLGR-BwEUHZBv.js} +1 -1
  24. package/public/assets/{chunk-KBJHAD2P-DovMVsOt.js → chunk-KBJHAD2P-HjBUqR8F.js} +1 -1
  25. package/public/assets/{chunk-NSK5VX7P-qSMdOab2.js → chunk-NSK5VX7P-DkHwHrKE.js} +1 -1
  26. package/public/assets/{chunk-QR6OTTB3-Iraem5u-.js → chunk-QR6OTTB3-DF1_EWFH.js} +1 -1
  27. package/public/assets/{chunk-RYQCIY6F-8rrSNAgX.js → chunk-RYQCIY6F-BaBhRLGF.js} +1 -1
  28. package/public/assets/{chunk-UBXNYLIW-CyzK1RD5.js → chunk-UBXNYLIW-BNV3VrZA.js} +1 -1
  29. package/public/assets/{chunk-W5SLKNZC-BVK4MUw1.js → chunk-W5SLKNZC-5ymyg6B_.js} +1 -1
  30. package/public/assets/{chunk-WRU74C26-C_K_UiUW.js → chunk-WRU74C26-oznLCI2s.js} +1 -1
  31. package/public/assets/{chunk-XXDRQBXY-0nuSRCEm.js → chunk-XXDRQBXY-sauNIYLc.js} +1 -1
  32. package/public/assets/classDiagram-JCYQIIEL-Wf8lPn9O.js +1 -0
  33. package/public/assets/classDiagram-v2-OCEON4UE-Wf8lPn9O.js +1 -0
  34. package/public/assets/{cose-bilkent-JH36ORCC-R9H6hDjY.js → cose-bilkent-JH36ORCC-B0A4Je-o.js} +1 -1
  35. package/public/assets/{cynefin-VYW2F7L2-DnWlTifu.js → cynefin-VYW2F7L2-BtmksekA.js} +1 -1
  36. package/public/assets/{cynefinDiagram-MW4NZA55-B-7owUyc.js → cynefinDiagram-MW4NZA55-DxlB0XWy.js} +1 -1
  37. package/public/assets/{dagre-VZM6K2ZE-B3ZoPe5S.js → dagre-VZM6K2ZE-BwX2jQEP.js} +1 -1
  38. package/public/assets/{diagram-7IWD3JNH-BI26fzwd.js → diagram-7IWD3JNH-CRw4eKmV.js} +1 -1
  39. package/public/assets/{diagram-B4RE2ZJO-BiSCmkOd.js → diagram-B4RE2ZJO-BhSHl_HC.js} +1 -1
  40. package/public/assets/{diagram-LBJQPF4R-BQ35r2wn.js → diagram-LBJQPF4R-BLNDbOa-.js} +1 -1
  41. package/public/assets/{diagram-Q27KOJAE-258JzS88.js → diagram-Q27KOJAE-OI-WVr2z.js} +1 -1
  42. package/public/assets/{diagram-UB23O5K3-BQLuDWQ5.js → diagram-UB23O5K3-Dw4v-DRV.js} +1 -1
  43. package/public/assets/{dist-BhEZ2d35.js → dist-CkCHUIyI.js} +1 -1
  44. package/public/assets/{ebnfDiagram-BXEA7PRR-D6cGU6FA.js → ebnfDiagram-BXEA7PRR-_CCtytYQ.js} +1 -1
  45. package/public/assets/{erDiagram-JOGREHBK-x-fYPfbp.js → erDiagram-JOGREHBK-DPwktZ67.js} +1 -1
  46. package/public/assets/eventmodeling-45OFAUF4-COOjM2XZ.js +1 -0
  47. package/public/assets/flowDiagram-UKHOOZJN-ipIqOWay.js +1 -0
  48. package/public/assets/{ganttDiagram-PKOTCBZU-B44E_1OW.js → ganttDiagram-PKOTCBZU-ChHOAX0R.js} +1 -1
  49. package/public/assets/{gitGraph-TEB2WS4Q-B_J5fmci.js → gitGraph-TEB2WS4Q-DTKjAplS.js} +1 -1
  50. package/public/assets/{gitGraphDiagram-DS77QQ5N-oi1EtSGf.js → gitGraphDiagram-DS77QQ5N-CetQtIP1.js} +1 -1
  51. package/public/assets/{index-D4kIibeU.js → index-CMeHHiB8.js} +11 -11
  52. package/public/assets/{info-DKCQHKI2-CvqAVyoT.js → info-DKCQHKI2-CEZZoshL.js} +1 -1
  53. package/public/assets/{infoDiagram-6WML65LV-BroSi0D-.js → infoDiagram-6WML65LV-Dn0Ci1fh.js} +1 -1
  54. package/public/assets/{ishikawaDiagram-WSZJBQD7-BRQ47rb2.js → ishikawaDiagram-WSZJBQD7-C0yvCWyv.js} +1 -1
  55. package/public/assets/{journeyDiagram-NVQOT4AX-DB6Xo-9Q.js → journeyDiagram-NVQOT4AX-CpTSt3oh.js} +1 -1
  56. package/public/assets/{kanban-definition-27J2QSJJ-BBzsco6z.js → kanban-definition-27J2QSJJ-Co4MhtWb.js} +1 -1
  57. package/public/assets/{line-m55PflQc.js → line-CKdpDvRy.js} +1 -1
  58. package/public/assets/{linear-Dgtz6Nrl.js → linear-BAY9SBNB.js} +1 -1
  59. package/public/assets/{mermaid-parser.core-B98Rk-oq.js → mermaid-parser.core-B-yKcreq.js} +3 -3
  60. package/public/assets/{mermaid.core-CqRCrVqJ.js → mermaid.core-CW_jKbCL.js} +4 -4
  61. package/public/assets/{mindmap-definition-FAOFIHXS-BPP33G4r.js → mindmap-definition-FAOFIHXS-CejNgzQn.js} +1 -1
  62. package/public/assets/{packet-7NZHBO7P-tO4S6ouz.js → packet-7NZHBO7P-B53cyWv4.js} +1 -1
  63. package/public/assets/{pegDiagram-VL7TDLO6-Bw6mp9l7.js → pegDiagram-VL7TDLO6-DAh-rohm.js} +1 -1
  64. package/public/assets/{pie-RZYD4A2V-C437Vvln.js → pie-RZYD4A2V-Bbl6CzP-.js} +1 -1
  65. package/public/assets/{pieDiagram-7S7Q4E2Y-CBxtUqZy.js → pieDiagram-7S7Q4E2Y-BoiaUX85.js} +1 -1
  66. package/public/assets/{quadrantDiagram-CIZ2JOQS-Bqsvmpf9.js → quadrantDiagram-CIZ2JOQS-2EgkGrDN.js} +1 -1
  67. package/public/assets/{radar-I7S5WNFK-C6dNsRn9.js → radar-I7S5WNFK-1Piyk_Oc.js} +1 -1
  68. package/public/assets/{railroad-3IZDKUUU-KaVebqGG.js → railroad-3IZDKUUU-DcQG7hTA.js} +1 -1
  69. package/public/assets/railroad-abnf-AHOZXSZD-CaviTNom.js +1 -0
  70. package/public/assets/railroad-ebnf-EBAXGLYW-K7l-WN0j.js +1 -0
  71. package/public/assets/railroad-peg-LSFZ7HO6-Bu22X3mO.js +1 -0
  72. package/public/assets/{railroadDiagram-AXF67PYL-DKq4X6Uj.js → railroadDiagram-AXF67PYL-kPPDLWet.js} +1 -1
  73. package/public/assets/{requirementDiagram-LRYGKXZP-B9fsvyjq.js → requirementDiagram-LRYGKXZP-B0wNE6wF.js} +1 -1
  74. package/public/assets/{sankeyDiagram-W5VNT64P-BsfVDMI2.js → sankeyDiagram-W5VNT64P-CyKEUx9w.js} +1 -1
  75. package/public/assets/{sequenceDiagram-SI44F4Z6-OgERYEKg.js → sequenceDiagram-SI44F4Z6-DT7UmiRF.js} +1 -1
  76. package/public/assets/{src-fnYA6irH.js → src-EbLSa_z9.js} +1 -1
  77. package/public/assets/{stateDiagram-OKZ733FA-CQbkYnuQ.js → stateDiagram-OKZ733FA-BNk8YOH4.js} +1 -1
  78. package/public/assets/stateDiagram-v2-UEYNNEHI-8Oafa2Rm.js +1 -0
  79. package/public/assets/{swimlanes-SLNWSIFB-Bjko0riO.js → swimlanes-SLNWSIFB-B7eUL04h.js} +1 -1
  80. package/public/assets/swimlanesDiagram-ULZ7WXOC-BT5Ebr7W.js +8 -0
  81. package/public/assets/{timeline-definition-Z64GVDOM-yK0hLw8V.js → timeline-definition-Z64GVDOM-CDLh-h94.js} +1 -1
  82. package/public/assets/{treeView-QDETBFTQ-BGpcfDZf.js → treeView-QDETBFTQ-BAscBSG9.js} +1 -1
  83. package/public/assets/{treemap-6X3UGDF4-QT7YcofM.js → treemap-6X3UGDF4-B2ahLgfF.js} +1 -1
  84. package/public/assets/{vennDiagram-T6HMQDX7-BuWvWEXb.js → vennDiagram-T6HMQDX7-62WQ_264.js} +1 -1
  85. package/public/assets/{wardley-OPB4EBWU-hYpU_cIv.js → wardley-OPB4EBWU-DUrXideD.js} +1 -1
  86. package/public/assets/{wardleyDiagram-T6FBY63Y-CgKHf2Kc.js → wardleyDiagram-T6FBY63Y-Ci4DF612.js} +1 -1
  87. package/public/assets/{xychartDiagram-ELKLHX3M-CoNyQg1m.js → xychartDiagram-ELKLHX3M-D--O5thj.js} +1 -1
  88. package/public/index.html +1 -1
  89. package/public/assets/architecture-TIHT7OUA-CJFVglth.js +0 -1
  90. package/public/assets/channel-ClF6sQKu.js +0 -1
  91. package/public/assets/classDiagram-JCYQIIEL-z-TVRcqJ.js +0 -1
  92. package/public/assets/classDiagram-v2-OCEON4UE-z-TVRcqJ.js +0 -1
  93. package/public/assets/eventmodeling-45OFAUF4-CsZX8d8q.js +0 -1
  94. package/public/assets/flowDiagram-UKHOOZJN-B-BoRx9A.js +0 -1
  95. package/public/assets/railroad-abnf-AHOZXSZD-vLN4fXap.js +0 -1
  96. package/public/assets/railroad-ebnf-EBAXGLYW-B70X8CrM.js +0 -1
  97. package/public/assets/railroad-peg-LSFZ7HO6-DXKtxJxs.js +0 -1
  98. package/public/assets/stateDiagram-v2-UEYNNEHI-BDBAarNU.js +0 -1
  99. package/public/assets/swimlanesDiagram-ULZ7WXOC-BhW0icZR.js +0 -8
package/dist/bus.js CHANGED
@@ -19,6 +19,54 @@ const tailJsonl = (p, limit) => {
19
19
  return { malformed: true, raw: l };
20
20
  } });
21
21
  };
22
+ /* Thresholds are judgement, not measurement, and are stated here rather than buried: "producing"
23
+ is the working cadence, and >8h is the shape of an overnight stall. The 2h boundary exists
24
+ because the previous model put a coffee break and a seventeen-hour stall in one bucket. */
25
+ const PRODUCING_MS = 2 * 3_600_000;
26
+ const STALLED_MS = 8 * 3_600_000;
27
+ const SCAN_LINES = 4000;
28
+ /** Newest emission per agent, from the room logs and the status stream. */
29
+ export function outputIndex(root, scanLines = SCAN_LINES) {
30
+ const out = new Map();
31
+ const note = (from, ts, basis) => {
32
+ if (typeof from !== "string" || typeof ts !== "number")
33
+ return;
34
+ const prev = out.get(from);
35
+ if (!prev || ts > prev.ts)
36
+ out.set(from, { ts, basis });
37
+ };
38
+ const roomDir = path.join(root, "rooms");
39
+ if (existsSync(roomDir))
40
+ for (const f of readdirSync(roomDir).filter((x) => x.endsWith(".jsonl"))) {
41
+ for (const m of tailJsonl(path.join(roomDir, f), scanLines))
42
+ note(m.from, m.ts, "room");
43
+ }
44
+ for (const m of tailJsonl(path.join(root, "status.jsonl"), scanLines))
45
+ note(m.agentId ?? m.from, m.ts, "status");
46
+ /* DMs count as output and are NOT in the room logs — they are rows in the RECIPIENT's inbox,
47
+ carrying the sender in `from`. Omitting them made an agent working entirely by DM read as
48
+ stalled; that was caught on the first live run, where this console's own worker showed
49
+ `stalled` while it had been reporting by DM all morning. Scanning inboxes attributes each
50
+ message to its sender, not its owner. */
51
+ const inboxDir = path.join(root, "inbox");
52
+ if (existsSync(inboxDir))
53
+ for (const f of readdirSync(inboxDir).filter((x) => x.endsWith(".jsonl"))) {
54
+ for (const m of tailJsonl(path.join(inboxDir, f), scanLines))
55
+ note(m.from, m.ts, "dm");
56
+ }
57
+ return out;
58
+ }
59
+ /* `unknown` is NOT "no output" — it is "none within the scanned window", and the window is reported
60
+ so a caller can tell the two apart. Collapsing them would be the same defect one level down. */
61
+ const progressOf = (hit, now, scanLines) => {
62
+ if (!hit)
63
+ return { lastOutputAt: null, ageMs: null, state: "unknown", basis: null, scannedLines: scanLines };
64
+ const ageMs = now - hit.ts;
65
+ return { lastOutputAt: hit.ts, ageMs, state: ageMs < PRODUCING_MS ? "producing" : ageMs < STALLED_MS ? "quiet" : "stalled", basis: hit.basis, scannedLines: scanLines };
66
+ };
67
+ /* Reachability buckets. `idle` used to span 15 MINUTES to 24 HOURS, so a coffee break and an
68
+ overnight stall rendered identically; `dormant` splits that. */
69
+ export const reachOf = (age) => age < 15 * 60_000 ? "live" : age < 2 * 3_600_000 ? "idle" : age < 24 * 3_600_000 ? "dormant" : "dead";
22
70
  export async function agentsAsync(root) {
23
71
  const panes = await listPanes();
24
72
  const base = agentsSync(root);
@@ -33,11 +81,14 @@ export async function agentsAsync(root) {
33
81
  const paneActivity = ct === undefined || ct === null ? windowActivity : ct || null; // 0 = seeded, no change observed yet → treated as unknown, not as "now"
34
82
  const lastSeen = Math.max(a.lastSeen ?? 0, paneActivity ?? 0);
35
83
  const age = lastSeen ? Date.now() - lastSeen : Infinity;
36
- return { ...a, paneActivity, windowActivity, windowName: pane?.windowName ?? null, paneCommand: pane?.command ?? null, paneAlive: pane ? !pane.dead : null, lastSeen: lastSeen || null, heartbeatAgeMs: Number.isFinite(age) ? age : null, liveness: age < 15 * 60_000 ? "live" : age < 24 * 3_600_000 ? "idle" : "dead", active: paneActivity ? Date.now() - paneActivity < 60_000 : false };
84
+ /* paneActivity is another REACHABILITY input, not progress: a pane redraws when a message is pasted into
85
+ it. It is deliberately not folded into `progress`, which stays as agentsSync computed it. */
86
+ return { ...a, paneActivity, windowActivity, windowName: pane?.windowName ?? null, paneCommand: pane?.command ?? null, paneAlive: pane ? !pane.dead : null, lastSeen: lastSeen || null, heartbeatAgeMs: Number.isFinite(age) ? age : null, reachability: reachOf(age), active: paneActivity ? Date.now() - paneActivity < 60_000 : false };
37
87
  }).sort((a, b) => (b.lastSeen ?? 0) - (a.lastSeen ?? 0));
38
88
  }
39
89
  export const agents = agentsSync;
40
- export function agentsSync(root) {
90
+ export function agentsSync(root, scanLines = SCAN_LINES) {
91
+ const output = outputIndex(root, scanLines);
41
92
  const reg = readJson(path.join(root, "agents.json"), {});
42
93
  const transports = existsSync(path.join(root, "transports")) ? new Set(readdirSync(path.join(root, "transports")).map((f) => f.replace(/\.json$/, ""))) : new Set();
43
94
  const now = Date.now();
@@ -48,7 +99,7 @@ export function agentsSync(root) {
48
99
  const mtimes = [tPath, path.join(root, "inbox", `${a.agentId}.jsonl`), path.join(root, "cursors", `${a.agentId}.json`)].filter(existsSync).map((f) => statSync(f).mtimeMs);
49
100
  const lastSeen = Math.max(a.lastHeartbeat ?? 0, ...mtimes);
50
101
  const age = lastSeen ? now - lastSeen : Infinity;
51
- return { ...a, transport: t?.transport ?? t?.kind ?? (t ? "tmux-push" : null), pane: t?.tmuxTarget ?? null, liveness: age < 15 * 60_000 ? "live" : age < 24 * 3_600_000 ? "idle" : "dead", heartbeatAgeMs: Number.isFinite(age) ? age : null, lastSeen: lastSeen || null };
102
+ return { ...a, transport: t?.transport ?? t?.kind ?? (t ? "tmux-push" : null), pane: t?.tmuxTarget ?? null, reachability: reachOf(age), heartbeatAgeMs: Number.isFinite(age) ? age : null, lastSeen: lastSeen || null, progress: progressOf(output.get(a.agentId), now, scanLines) };
52
103
  }).sort((a, b) => (b.lastSeen ?? 0) - (a.lastSeen ?? 0));
53
104
  }
54
105
  export function rooms(root) {
package/dist/events.js CHANGED
@@ -103,7 +103,7 @@ export class EventEngine {
103
103
  }
104
104
  /** The live coordinator agent for a project/room (role coordinator, project == room or name), else null. */
105
105
  coordinatorFor(project, room) {
106
- const a = agents(this.cfg.coordDir).find((x) => /coordinator/i.test(x.role ?? "") && (x.project === room || x.project === project) && x.liveness !== "dead");
106
+ const a = agents(this.cfg.coordDir).find((x) => /coordinator/i.test(x.role ?? "") && (x.project === room || x.project === project) && x.reachability !== "dead");
107
107
  return a?.agentId ?? null;
108
108
  }
109
109
  emitRaw(type, project, data) {
package/dist/gitinfo.js CHANGED
@@ -139,6 +139,46 @@ async function ghPrs(projectPath) {
139
139
  return null;
140
140
  }
141
141
  }
142
+ /* Which PR a board row is ABOUT, from its `pr` CELL — never from a `#n` appearing anywhere in the
143
+ row. The openPrs extension declares fields ["repo","pr","owner","status","mergeGate"], so the
144
+ answer has a designated column and reading the others is a guess.
145
+
146
+ The old matcher was `Object.values(r).some(v => /#n/.test(v))`, which meant a Status cell reading
147
+ "blocked by #39" made THAT row answer for PR 39 — and `find` returns the first hit, so a mention
148
+ beat the real row. Measured on a two-row fixture: qaOf(39) returned "FAIL — held" from #57's row
149
+ while #39's own row said PASS. A passing PR shown as failing, in the alarming direction.
150
+
151
+ Same defect the kit found in queue-done-loop the same afternoon: a ref MENTIONED in a row is a
152
+ different fact from the ref that IDENTIFIES it. */
153
+ export const prCellNumber = (cell) => {
154
+ const m = /#(\d+)\b/.exec(cell ?? "");
155
+ return m ? Number(m[1]) : null;
156
+ };
157
+ /* `owner/name` from a remote URL, ssh or https. null when there is no origin — in which case the
158
+ repo check below is SKIPPED rather than guessed, and matching falls back to the pr cell alone. */
159
+ export const slugFromRemoteUrl = (url) => {
160
+ const m = url ? /(?:[:/])([^/:]+\/[^/]+?)(?:\.git)?$/.exec(url.trim()) : null;
161
+ return m ? m[1] : null;
162
+ };
163
+ const originSlug = async (projectPath) => slugFromRemoteUrl(await git(projectPath, ["remote", "get-url", "origin"]));
164
+ /* A row's repo, when it states one. Boards legitimately carry several repos — the kit's own board
165
+ lists `kit#11` beside console PRs — so a bare number is ambiguous across them. Only rows that
166
+ name a DIFFERENT repo are rejected; a row with no repo cell is accepted, because absence is not
167
+ a claim. */
168
+ /* The gate a board reports for PR `n` in THIS repo, or undefined. Exported and used by
169
+ gitBranches directly, so the test exercises this function rather than a copy of it — a test that
170
+ re-implements the matcher pins the fixture, not the code. */
171
+ export const gateForPr = (rows, n, slug) => {
172
+ const row = rows?.find((r) => prCellNumber(r.pr) === n && repoMatches(r.repo, slug));
173
+ const qa = row?.mergeGate ?? row?.status;
174
+ return qa ? String(qa) : undefined;
175
+ };
176
+ export const repoMatches = (rowRepo, slug) => {
177
+ const cell = (rowRepo ?? "").trim();
178
+ if (!cell || !slug)
179
+ return true;
180
+ return cell === slug || cell.endsWith(`/${slug.split("/").pop()}`) || slug.endsWith(`/${cell}`) || cell === slug.split("/").pop();
181
+ };
142
182
  export function gitBranches(projectPath, hints = {}) {
143
183
  return cached(`branches:${projectPath}:${(hints.agentIds ?? []).join(",")}`, async () => {
144
184
  const defaultBranch = "main";
@@ -147,8 +187,8 @@ export function gitBranches(projectPath, hints = {}) {
147
187
  const rows = splitRecords(await git(projectPath, ["for-each-ref", "--sort=-committerdate", "--format=%(refname)%1f%(objectname)%1f%(objectname:short)%1f%(subject)%1f%(committerdate:iso-strict)", "refs/heads", "refs/remotes"]))
148
188
  .filter(([ref]) => !/^refs\/remotes\/[^/]+\/HEAD$/.test(ref));
149
189
  const [{ worktrees }, prs] = await Promise.all([gitWorktrees(projectPath, hints), ghPrs(projectPath)]);
150
- const qaOf = (n) => { const row = hints.openPrs?.find((r) => Object.values(r).some((v) => new RegExp(`(^|[^0-9])#${n}([^0-9]|$)`).test(v))); if (!row)
151
- return undefined; const qa = row.mergeGate ?? row.status ?? row.state; return qa ? String(qa) : undefined; };
190
+ const slug = await originSlug(projectPath);
191
+ const qaOf = (n) => gateForPr(hints.openPrs, n, slug);
152
192
  const now = Date.now();
153
193
  const branches = await Promise.all(rows.map(async ([ref, sha, short, subject, date]) => {
154
194
  const remote = ref.startsWith("refs/remotes/");
package/dist/seam.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /* Read-only views over a project's seam files, parsed ONLY through @davidbalzan/groundwork-seam. */
2
2
  import { existsSync, readFileSync, readdirSync } from "node:fs";
3
3
  import path from "node:path";
4
- import { parseWorkDoc, queueItemsOf, doneEntriesOf, listWorkBoardOf, workDocIssues, SCHEMA } from "@davidbalzan/groundwork-seam";
4
+ import { parseWorkDoc, queueItemsOf, doneEntriesOf, listWorkBoardOf, workDocIssues, boardSchemaOf, SCHEMA } from "@davidbalzan/groundwork-seam";
5
5
  import { parseFactsDoc } from "@davidbalzan/groundwork-seam";
6
6
  const read = (p) => (existsSync(p) ? readFileSync(p, "utf8") : null);
7
7
  const FACT_STALE_DAYS = 14; // groundwork doctor rule
@@ -13,14 +13,22 @@ export function boardOf(projectPath) {
13
13
  const f = read(path.join(docs, "FACTS.md"));
14
14
  const wsDoc = ws ? parseWorkDoc(ws) : null;
15
15
  const board = wsDoc ? listWorkBoardOf(wsDoc) : [];
16
- /* Read the schema off the Active Streams BLOCK, not off its rows. A board with a valid
17
- workstreams.v1 header and zero streams yields zero rows, and deriving the schema from
18
- rows made "no work in flight" indistinguishable from "grammar not recognised" the
19
- console then rendered the alarming one. Only the two board grammars count here; the
20
- workstreams.ext.* blocks are separate tables and must never answer this question. */
21
- const boardSchemas = [SCHEMA.workstreams, SCHEMA.workstreamsLanesV0];
22
- const blockSchema = wsDoc?.blocks.find((bl) => bl.kind === "board" && typeof bl.schema === "string" && boardSchemas.includes(bl.schema))?.schema ?? null;
23
- const schema = blockSchema ?? board.find((r) => "schema" in r)?.schema ?? null;
16
+ /* Schema comes from the seam's boardSchemaOf, not from a local re-derivation.
17
+ The console used to work this out itself, and console#35 fixed the first defect in that
18
+ copy: it read the schema off the first parsed ROW, so an empty-but-valid board yielded
19
+ null and rendered "unknown workstreams grammar" the alarming reading of an empty one.
20
+ The seam now exports the answer, and its version is better than ours on two counts we
21
+ had wrong:
22
+ - it EXCLUDES extension tables by deriving their ids from WORKSTREAMS_EXT, so
23
+ registering a new extension cannot forget to exclude it. Ours allow-listed the two
24
+ board grammars we knew, which meant any FUTURE board schema read as unrecognised.
25
+ - it returns "unknown" only when a header was actually REFUSED, and null when there
26
+ is simply no board block. Ours collapsed those: `schema ?? (ws ? "unknown" : null)`
27
+ reported "unknown grammar" for a WORKSTREAMS.md that merely has no Active Streams
28
+ table — the same conflation console#35 fixed, one case over, still live until now.
29
+ So `null` here means "no board table", NOT "no file"; the caller must not render those
30
+ as the same thing. `files.workstreams` is what distinguishes them. */
31
+ const schema = wsDoc ? boardSchemaOf(wsDoc) : null;
24
32
  const qDoc = q ? parseWorkDoc(q) : null;
25
33
  const dDoc = d ? parseWorkDoc(d) : null;
26
34
  const facts = f ? parseFactsDoc(f) : null;
@@ -31,9 +39,18 @@ export function boardOf(projectPath) {
31
39
  return {
32
40
  files: { workstreams: !!ws, queue: !!q, done: !!d, facts: !!f },
33
41
  prd,
34
- workstreams: { schema: schema ?? (ws ? "unknown" : null), rows: board, issues: wsDoc ? workDocIssues(wsDoc) : [] },
42
+ workstreams: { schema, rows: board, issues: wsDoc ? workDocIssues(wsDoc) : [] },
35
43
  queue: qDoc ? queueItemsOf(qDoc) : [],
36
44
  done: dDoc ? doneEntriesOf(dDoc) : [],
45
+ /* QUEUE.md and DONE.md parse issues. Only WORKSTREAMS.md's were ever surfaced, so a malformed
46
+ queue line was parsed, kept, rendered — and reported NOWHERE. That gap is the console's half
47
+ of seam 0.1.6: the seam stopped DROPPING an out-of-range priority precisely so a consumer
48
+ could say something about it, and this consumer said nothing. `workDocIssues` already carries
49
+ the message (the seam builds it with invalidPriorityIssue), so nothing here re-derives it.
50
+ Siblings rather than a reshape of `queue`/`done`: those are arrays every caller maps over,
51
+ and the gap is a missing signal, not a wrong shape. */
52
+ queueIssues: qDoc ? workDocIssues(qDoc) : [],
53
+ doneIssues: dDoc ? workDocIssues(dDoc) : [],
37
54
  facts: facts
38
55
  ? {
39
56
  entries: facts.entries.map((e) => {
package/dist/seamwrite.js CHANGED
@@ -65,7 +65,11 @@ export function addQueueItem(projectPath, item, log) {
65
65
  log(`wrote docs/QUEUE.md (+1 item, ${out.length - src.length} bytes)`);
66
66
  return { file: "docs/QUEUE.md", line: renderWorkDocLine(newItem), bytesDelta: out.length - src.length };
67
67
  }
68
- const renderWorkDocLine = (q) => `- [ ] (${q.priority}) ${q.text}`;
68
+ /* Display-only echo of the line we appended. `QueueItem.priority` is nullable since seam 0.1.6, and
69
+ interpolating a null renders the literal "(null)" — so fall back to the raw tag the seam kept. The
70
+ console never CREATES such an item (addQueueItem's input is narrowed to P1|P2|P3); this guards the
71
+ echo against a shape the type now permits. */
72
+ const renderWorkDocLine = (q) => `- [${q.done ? "x" : " "}] (${q.priority ?? q.priorityRaw ?? "?"}) ${q.text}`;
69
73
  /** Re-verify (or add) a fact: seam upsertFact with a fresh timestamp. */
70
74
  export function reverifyFact(projectPath, entry, log) {
71
75
  const file = path.join(projectPath, "docs/FACTS.md");
package/dist/server.js CHANGED
@@ -48,7 +48,7 @@ const clientFor = (id) => (id === "david" ? busDavid : bus);
48
48
  /** Capabilities of the connected MCP (null until a client has connected and listed tools). */
49
49
  const mcpCaps = () => { const cl = bus.connected ? bus : busDavid.connected ? busDavid : null; return { inReplyTo: cl ? cl.supports("send_message", "inReplyTo") : null, sendCommand: cl ? cl.sendCommands : null }; };
50
50
  /* Live tmux-attached agents — the only valid send_command targets (the MCP hard-gates to tmux-push/-remote). */
51
- const liveAttached = () => agents(cfg.coordDir).filter((a) => a.liveness === "live" && (a.transport === "tmux-push" || a.transport === "tmux-push-remote")).map((a) => a.agentId);
51
+ const liveAttached = () => agents(cfg.coordDir).filter((a) => a.reachability === "live" && (a.transport === "tmux-push" || a.transport === "tmux-push-remote")).map((a) => a.agentId);
52
52
  const PUBLIC_DIR = path.resolve(HERE, "../public");
53
53
  const root = new Hono();
54
54
  const app = new Hono().basePath("/api");
@@ -286,7 +286,7 @@ app.get("/projects", (c) => {
286
286
  catch {
287
287
  return null;
288
288
  } })();
289
- const liveAgents = agents(cfg.coordDir).filter((a) => a.liveness !== "dead");
289
+ const liveAgents = agents(cfg.coordDir).filter((a) => a.reachability !== "dead");
290
290
  return c.json(cfg.projects.map((p) => {
291
291
  const b = boardOf(p.path);
292
292
  const fleet = fleetStatus(liveAgents.filter((a) => a.project === p.room || a.project === p.name));
@@ -723,7 +723,7 @@ app.get("/projects/:name/fleet", async (c) => {
723
723
  const p = project(c.req.param("name"));
724
724
  if (!p)
725
725
  return c.json({ error: "unknown project" }, 404);
726
- const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.liveness !== "dead" && (a.project === p.room || a.project === p.name));
726
+ const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.reachability !== "dead" && (a.project === p.room || a.project === p.name));
727
727
  return c.json({ ...fleetStatus(live), templates: FLEET_TEMPLATES, window: `fleet-${p.name}` });
728
728
  });
729
729
  const roleOfAgent = (id) => agents(cfg.coordDir).find((a) => a.agentId === id)?.role ?? null;
@@ -788,7 +788,7 @@ app.post("/actions/prune", async (c) => {
788
788
  });
789
789
  app.post("/actions/unregister-dead", async (c) => {
790
790
  const b = (await c.req.json().catch(() => ({})));
791
- const dead = agents(cfg.coordDir).filter((a) => a.liveness === "dead" && !["groundwork-console", "david"].includes(a.agentId)).map((a) => a.agentId);
791
+ const dead = agents(cfg.coordDir).filter((a) => a.reachability === "dead" && !["groundwork-console", "david"].includes(a.agentId)).map((a) => a.agentId);
792
792
  const ids = (b.agentIds?.length ? b.agentIds.filter((id) => dead.includes(id)) : dead);
793
793
  if (b.confirm !== "UNREGISTER")
794
794
  return c.json({ error: "confirm must be UNREGISTER", wouldUnregister: ids }, 400);
@@ -810,7 +810,7 @@ app.post("/actions/unregister", async (c) => {
810
810
  return c.json(r, r.status === "ok" ? 200 : 403);
811
811
  });
812
812
  /* ---- Roadmap actions: message the project's aide (plan) or coordinator (kick off) as david ---- */
813
- const roleAgent = (projectName, room, roleRe) => agents(cfg.coordDir).find((a) => roleRe.test(a.role ?? "") && (a.project === room || a.project === projectName) && a.liveness !== "dead")?.agentId ?? null;
813
+ const roleAgent = (projectName, room, roleRe) => agents(cfg.coordDir).find((a) => roleRe.test(a.role ?? "") && (a.project === room || a.project === projectName) && a.reachability !== "dead")?.agentId ?? null;
814
814
  app.get("/projects/:name/agents", (c) => {
815
815
  const p = project(c.req.param("name"));
816
816
  if (!p)
@@ -855,7 +855,7 @@ app.post("/actions/task/:name/:phase/:line", async (c) => {
855
855
  return c.json({ error: "no task at that line" }, 404);
856
856
  if (task.done)
857
857
  return c.json({ error: "already ticked" }, 409);
858
- const live = agents(cfg.coordDir).filter((a) => a.liveness !== "dead");
858
+ const live = agents(cfg.coordDir).filter((a) => a.reachability !== "dead");
859
859
  const owner = b.owner && live.some((a) => a.agentId === b.owner) ? b.owner : roleAgent(p.name, p.room, /aide|liaison|curator/i);
860
860
  if (!owner)
861
861
  return c.json({ error: `no live owner or aide for ${p.name}` }, 409);
@@ -897,7 +897,7 @@ app.get("/projects/:name/doctor/fixers", async (c) => {
897
897
  const p = project(c.req.param("name"));
898
898
  if (!p)
899
899
  return c.json({ error: "unknown project" }, 404);
900
- const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.liveness !== "dead" && (a.project === p.room || a.project === p.name));
900
+ const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.reachability !== "dead" && (a.project === p.room || a.project === p.name));
901
901
  const out = {};
902
902
  for (const [check, o] of Object.entries(DOCTOR_OWNER)) {
903
903
  const a = live.find((x) => o.re.test(x.role ?? ""));
@@ -913,7 +913,7 @@ app.post("/actions/doctor-fix/:name", async (c) => {
913
913
  const o = DOCTOR_OWNER[b.check];
914
914
  if (!o)
915
915
  return c.json({ error: "no fixer role for this check" }, 400);
916
- const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.liveness !== "dead" && (a.project === p.room || a.project === p.name));
916
+ const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.reachability !== "dead" && (a.project === p.room || a.project === p.name));
917
917
  const target = live.find((x) => o.re.test(x.role ?? ""))?.agentId;
918
918
  if (!target)
919
919
  return c.json({ error: `no live ${o.role} for ${p.name}` }, 409);
@@ -1036,9 +1036,9 @@ app.get("/bus/panes", async (c) => {
1036
1036
  const lines = Number(c.req.query("lines") ?? 40);
1037
1037
  const p = proj ? project(proj) : undefined;
1038
1038
  const all = await agentsAsync(cfg.coordDir);
1039
- const scoped = all.filter((a) => a.pane && a.liveness !== "dead" && (!p || a.project === p.name || (p.room && a.project === p.room)));
1039
+ const scoped = all.filter((a) => a.pane && a.reachability !== "dead" && (!p || a.project === p.name || (p.room && a.project === p.room)));
1040
1040
  const colors = c.req.query("colors") === "1";
1041
- const out = await Promise.all(scoped.map(async (a) => ({ agentId: a.agentId, role: a.role ?? null, project: a.project ?? null, pane: a.pane, windowName: a.windowName ?? null, command: a.paneCommand, active: a.active, paneActivity: a.paneActivity, liveness: a.liveness, capture: await capturePane(a.pane, lines, colors) })));
1041
+ const out = await Promise.all(scoped.map(async (a) => ({ agentId: a.agentId, role: a.role ?? null, project: a.project ?? null, pane: a.pane, windowName: a.windowName ?? null, command: a.paneCommand, active: a.active, paneActivity: a.paneActivity, reachability: a.reachability, capture: await capturePane(a.pane, lines, colors) })));
1042
1042
  return c.json(out);
1043
1043
  });
1044
1044
  /* Pane viewer: read-only capture of an agent's tmux pane (never sends keys). */
package/dist/tmux.js CHANGED
@@ -1,4 +1,4 @@
1
- /* Read-only tmux views: pane activity (for liveness + activity indicators) and pane capture (for the pane viewer). Never sends keys. */
1
+ /* Read-only tmux views: pane activity (for reachability + activity indicators) and pane capture (for the pane viewer). Never sends keys. */
2
2
  import { execFile } from "node:child_process";
3
3
  import { promisify } from "node:util";
4
4
  const run = promisify(execFile);
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@davidbalzan/groundwork-console",
3
3
  "private": false,
4
- "version": "0.1.12",
4
+ "version": "0.1.13",
5
5
  "type": "module",
6
6
  "dependencies": {
7
- "@davidbalzan/groundwork-seam": "^0.1.0",
7
+ "@davidbalzan/groundwork-seam": "0.1.7",
8
8
  "@hono/node-server": "^2.1.1",
9
9
  "@modelcontextprotocol/sdk": "^1.30.0",
10
10
  "hono": "^4.13.2",
@@ -48,8 +48,11 @@
48
48
  "publish-check": "node scripts/publish-check.mjs",
49
49
  "test:inject": "tsx test/inject.test.mts",
50
50
  "test:skills": "tsx test/skills.test.mts",
51
- "test": "pnpm run test:inject && pnpm run test:skills && pnpm run test:board && pnpm run test:panes",
51
+ "test": "pnpm run test:inject && pnpm run test:skills && pnpm run test:board && pnpm run test:panes && pnpm run test:seam-contract && pnpm run test:prref && pnpm run test:progress",
52
52
  "test:board": "tsx test/board.test.mts",
53
- "test:panes": "tsx test/panes.test.mts"
53
+ "test:panes": "tsx test/panes.test.mts",
54
+ "test:seam-contract": "tsx test/seam-contract.test.mts",
55
+ "test:prref": "tsx test/prref.test.mts",
56
+ "test:progress": "tsx test/progress.test.mts"
54
57
  }
55
58
  }
@@ -1 +1 @@
1
- import{m as e,t}from"./mermaid-parser.core-B98Rk-oq.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-fnYA6irH.js";import"./chunk-I66GZJ75-CfWGFqqO.js";import{n as i,r as a,t as o}from"./chunk-6Q2QTUOP-BQYOf-YY.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-CqRCrVqJ.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
+ import{m as e,t}from"./mermaid-parser.core-B-yKcreq.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-EbLSa_z9.js";import"./chunk-I66GZJ75-XdTCBJhL.js";import{n as i,r as a,t as o}from"./chunk-6Q2QTUOP-BWLeyKxS.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-CW_jKbCL.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-BhEZ2d35.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};
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-CkCHUIyI.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-KEIR6QF5-BICK3FdT.js";import{M as e}from"./mermaid-parser.core-B-yKcreq.js";export{e as createArchitectureServices};