@davidbalzan/groundwork-console 0.1.7 → 0.1.8
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 +1 -1
- package/dist/gitinfo.js +114 -0
- package/dist/kitresolve.js +11 -9
- package/dist/mcp.js +39 -1
- package/dist/server.js +119 -8
- package/package.json +1 -1
- package/public/assets/{abnfDiagram-N423BO3Z-B0nns4cA.js → abnfDiagram-N423BO3Z-oco1yJr8.js} +1 -1
- package/public/assets/{arc-DsDgWIU7.js → arc-D-FYRBZ0.js} +1 -1
- package/public/assets/architecture-TIHT7OUA-bBJswTmy.js +1 -0
- package/public/assets/{architectureDiagram-T3A2C74G-Bqo7zIaB.js → architectureDiagram-T3A2C74G-CIG6iOMV.js} +1 -1
- package/public/assets/{blockDiagram-VBNYF7ZC-CvNPqlbt.js → blockDiagram-VBNYF7ZC-B6dDKm7d.js} +1 -1
- package/public/assets/{c4Diagram-5PPSVZJV-BqjkNgv6.js → c4Diagram-5PPSVZJV-Y3UQgn94.js} +1 -1
- package/public/assets/channel-BeJg4QSq.js +1 -0
- package/public/assets/{chunk-2GRJ4B5K-CWJJzbol.js → chunk-2GRJ4B5K-BoWCLegZ.js} +1 -1
- package/public/assets/{chunk-4I5QYGJK-CuHGN-lT.js → chunk-4I5QYGJK-DZTRMmZG.js} +1 -1
- package/public/assets/{chunk-5RXB4S5H-Cl92nmIs.js → chunk-5RXB4S5H-Dw1IdYaS.js} +1 -1
- package/public/assets/{chunk-6Q2QTUOP-C3vYg7rX.js → chunk-6Q2QTUOP-nWUr6zuz.js} +1 -1
- package/public/assets/{chunk-7Z6QIM7H-jkpbvAhi.js → chunk-7Z6QIM7H-DxXQZe2t.js} +1 -1
- package/public/assets/{chunk-GF5L2VYU-Bo-zB54d.js → chunk-GF5L2VYU-DbHTRiGa.js} +1 -1
- package/public/assets/{chunk-I66GZJ75-BD9h95Vq.js → chunk-I66GZJ75-C97VIOWu.js} +1 -1
- package/public/assets/{chunk-JQJVKLGR-C6TRkyf7.js → chunk-JQJVKLGR-CeOq7ul_.js} +1 -1
- package/public/assets/{chunk-KBJHAD2P-RgoL4Nto.js → chunk-KBJHAD2P-D_xgFhfD.js} +1 -1
- package/public/assets/{chunk-NSK5VX7P-C6TsGnGA.js → chunk-NSK5VX7P-DjNVQuCo.js} +1 -1
- package/public/assets/{chunk-QR6OTTB3-Bq5akaxk.js → chunk-QR6OTTB3-CGPHl8Ev.js} +1 -1
- package/public/assets/{chunk-RYQCIY6F-DtVCesJ4.js → chunk-RYQCIY6F-Ca_NSSUq.js} +1 -1
- package/public/assets/{chunk-UBXNYLIW-BjnaZZH0.js → chunk-UBXNYLIW-BH4KEg7B.js} +1 -1
- package/public/assets/{chunk-W5SLKNZC-D1KW-lQn.js → chunk-W5SLKNZC-CzSVKrQX.js} +1 -1
- package/public/assets/{chunk-WRU74C26-YZFoWy6X.js → chunk-WRU74C26--qTXNfid.js} +1 -1
- package/public/assets/{chunk-XXDRQBXY-CyrI_Mox.js → chunk-XXDRQBXY-0XJhmQM-.js} +1 -1
- package/public/assets/classDiagram-JCYQIIEL-B6RcDz49.js +1 -0
- package/public/assets/classDiagram-v2-OCEON4UE-B6RcDz49.js +1 -0
- package/public/assets/{cose-bilkent-JH36ORCC-v6lDOu7m.js → cose-bilkent-JH36ORCC-CeMP2vk-.js} +1 -1
- package/public/assets/{cynefin-VYW2F7L2-BtzRQs_J.js → cynefin-VYW2F7L2-NtSbHkFO.js} +1 -1
- package/public/assets/{cynefinDiagram-MW4NZA55--R8S0KoS.js → cynefinDiagram-MW4NZA55-B4uWxM0-.js} +1 -1
- package/public/assets/{dagre-VZM6K2ZE-D6mRFOfB.js → dagre-VZM6K2ZE-C8wHGLeh.js} +1 -1
- package/public/assets/{diagram-7IWD3JNH-kfCoRMpW.js → diagram-7IWD3JNH-DKe15FkE.js} +1 -1
- package/public/assets/{diagram-B4RE2ZJO-sxUW6rzT.js → diagram-B4RE2ZJO-B-pgDq0Z.js} +1 -1
- package/public/assets/{diagram-LBJQPF4R-B926bgCA.js → diagram-LBJQPF4R-16bhvJKB.js} +1 -1
- package/public/assets/{diagram-Q27KOJAE-CcVi6njJ.js → diagram-Q27KOJAE-BuzXd81m.js} +1 -1
- package/public/assets/{diagram-UB23O5K3-BtW4XOuK.js → diagram-UB23O5K3-CZzMkkP7.js} +1 -1
- package/public/assets/{dist-BnkmMC7M.js → dist-DoiwlY9_.js} +1 -1
- package/public/assets/{ebnfDiagram-BXEA7PRR-BNGfWHYj.js → ebnfDiagram-BXEA7PRR-BGUXNic-.js} +1 -1
- package/public/assets/{erDiagram-JOGREHBK-BGrhe741.js → erDiagram-JOGREHBK--mo3pLi7.js} +1 -1
- package/public/assets/eventmodeling-45OFAUF4-Bv0pQfuo.js +1 -0
- package/public/assets/flowDiagram-UKHOOZJN-cF6VTIV-.js +1 -0
- package/public/assets/{ganttDiagram-PKOTCBZU-CR5BNdXR.js → ganttDiagram-PKOTCBZU-DbHv-Q1F.js} +1 -1
- package/public/assets/{gitGraph-TEB2WS4Q-CfvC-iZD.js → gitGraph-TEB2WS4Q-CU8msIuE.js} +1 -1
- package/public/assets/{gitGraphDiagram-DS77QQ5N-D45EWdll.js → gitGraphDiagram-DS77QQ5N-CmVui6qG.js} +1 -1
- package/public/assets/index-BZBf_g4s.css +2 -0
- package/public/assets/index-CJ73bGme.js +41 -0
- package/public/assets/{info-DKCQHKI2-stahmZKW.js → info-DKCQHKI2-CcA5kzgP.js} +1 -1
- package/public/assets/{infoDiagram-6WML65LV-nWu8ujif.js → infoDiagram-6WML65LV-Drd9lSSV.js} +1 -1
- package/public/assets/{ishikawaDiagram-WSZJBQD7-Ccwzv-xZ.js → ishikawaDiagram-WSZJBQD7-CcnlbfwE.js} +1 -1
- package/public/assets/{journeyDiagram-NVQOT4AX-R7gZw1Qw.js → journeyDiagram-NVQOT4AX-CYcWG8V9.js} +1 -1
- package/public/assets/{kanban-definition-27J2QSJJ-By8aTzkI.js → kanban-definition-27J2QSJJ-Ca5F0ySI.js} +1 -1
- package/public/assets/{line-zJXbSVxf.js → line-BFnVTMoX.js} +1 -1
- package/public/assets/{linear-B8U-jWnO.js → linear-BVUPf0HH.js} +1 -1
- package/public/assets/{mermaid-parser.core-B1iqIz20.js → mermaid-parser.core-BXY3JF8d.js} +3 -3
- package/public/assets/{mermaid.core-CuFK5CuK.js → mermaid.core-DqjaFy6t.js} +4 -4
- package/public/assets/{mindmap-definition-FAOFIHXS-qJ2DuBku.js → mindmap-definition-FAOFIHXS-m3-vScki.js} +1 -1
- package/public/assets/{packet-7NZHBO7P-CzQJdD0e.js → packet-7NZHBO7P-BTm7636C.js} +1 -1
- package/public/assets/{pegDiagram-VL7TDLO6-CKmHw89b.js → pegDiagram-VL7TDLO6-C3kirtKa.js} +1 -1
- package/public/assets/{pie-RZYD4A2V-Dt3CA16n.js → pie-RZYD4A2V-BsF__qFU.js} +1 -1
- package/public/assets/{pieDiagram-7S7Q4E2Y-D9vwefN9.js → pieDiagram-7S7Q4E2Y-YEbSx31D.js} +1 -1
- package/public/assets/{quadrantDiagram-CIZ2JOQS-jtSAmOcp.js → quadrantDiagram-CIZ2JOQS-qY1c3Wr-.js} +1 -1
- package/public/assets/{radar-I7S5WNFK-BtDfUAqO.js → radar-I7S5WNFK-BaQRhfKS.js} +1 -1
- package/public/assets/{railroad-3IZDKUUU-D5DNw8qB.js → railroad-3IZDKUUU-C4riB9xs.js} +1 -1
- package/public/assets/railroad-abnf-AHOZXSZD-CBcG7kXE.js +1 -0
- package/public/assets/railroad-ebnf-EBAXGLYW-EqgxHSrX.js +1 -0
- package/public/assets/railroad-peg-LSFZ7HO6-C5-qxTK4.js +1 -0
- package/public/assets/{railroadDiagram-AXF67PYL-C-S9VT4Z.js → railroadDiagram-AXF67PYL-BDXHJJin.js} +1 -1
- package/public/assets/{requirementDiagram-LRYGKXZP-Bmv6EbmT.js → requirementDiagram-LRYGKXZP-5N_4nQH3.js} +1 -1
- package/public/assets/{sankeyDiagram-W5VNT64P-Cm2dLWH9.js → sankeyDiagram-W5VNT64P-C0zqsu_g.js} +1 -1
- package/public/assets/{sequenceDiagram-SI44F4Z6-DPslkAXf.js → sequenceDiagram-SI44F4Z6-DonYJ8_h.js} +1 -1
- package/public/assets/{src-l8EnJwXz.js → src-D5IewQLB.js} +1 -1
- package/public/assets/{stateDiagram-OKZ733FA-BBXMNQQ7.js → stateDiagram-OKZ733FA-CEY8i14f.js} +1 -1
- package/public/assets/stateDiagram-v2-UEYNNEHI-C13FXtls.js +1 -0
- package/public/assets/{swimlanes-SLNWSIFB-BeiwtQg-.js → swimlanes-SLNWSIFB-BBKzhZ8g.js} +1 -1
- package/public/assets/swimlanesDiagram-ULZ7WXOC-D0VKK_TP.js +8 -0
- package/public/assets/{timeline-definition-Z64GVDOM-Drd7Un_g.js → timeline-definition-Z64GVDOM-RI7P7P5y.js} +1 -1
- package/public/assets/{treeView-QDETBFTQ-BT2KyIoB.js → treeView-QDETBFTQ-Bpw_kGf5.js} +1 -1
- package/public/assets/{treemap-6X3UGDF4-gdZw2F6X.js → treemap-6X3UGDF4-Deq82US6.js} +1 -1
- package/public/assets/{vennDiagram-T6HMQDX7-Bbk8OMR0.js → vennDiagram-T6HMQDX7-DetRcU6h.js} +1 -1
- package/public/assets/{wardley-OPB4EBWU-BI0xutNp.js → wardley-OPB4EBWU-Iwt8nlFq.js} +1 -1
- package/public/assets/{wardleyDiagram-T6FBY63Y-DQvEcd8r.js → wardleyDiagram-T6FBY63Y-iVT5tLSy.js} +1 -1
- package/public/assets/{xychartDiagram-ELKLHX3M-CRWoUpzf.js → xychartDiagram-ELKLHX3M-B7_cZ8Wg.js} +1 -1
- package/public/index.html +2 -2
- package/public/assets/architecture-TIHT7OUA-okwBKksv.js +0 -1
- package/public/assets/channel-CI1-SNMv.js +0 -1
- package/public/assets/classDiagram-JCYQIIEL-zdEoGtpz.js +0 -1
- package/public/assets/classDiagram-v2-OCEON4UE-zdEoGtpz.js +0 -1
- package/public/assets/eventmodeling-45OFAUF4-Cn6RDMEG.js +0 -1
- package/public/assets/flowDiagram-UKHOOZJN-DOMsEKL1.js +0 -1
- package/public/assets/index-CxNe802C.css +0 -2
- package/public/assets/index-yHI4kvM-.js +0 -41
- package/public/assets/railroad-abnf-AHOZXSZD-DuJUfLMt.js +0 -1
- package/public/assets/railroad-ebnf-EBAXGLYW-CiLIN42p.js +0 -1
- package/public/assets/railroad-peg-LSFZ7HO6-DKtIAsvs.js +0 -1
- package/public/assets/stateDiagram-v2-UEYNNEHI-BxOobbWc.js +0 -1
- package/public/assets/swimlanesDiagram-ULZ7WXOC-BEW9yZ9H.js +0 -8
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ groundwork-console token # print the token
|
|
|
18
18
|
groundwork-console config # print the config path
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Requires on the host: Node ≥ 22, tmux (pane monitor / spawn), and either the three kit bins above on PATH or a kit checkout at `kitPath` in the config. `doctor` tells you which it found. Console requires: seam `^0.1.0` · agent-coord-mcp `^0.
|
|
21
|
+
Requires on the host: Node ≥ 22, tmux (pane monitor / spawn), and either the three kit bins above on PATH or a kit checkout at `kitPath` in the config. `doctor` tells you which it found. Console requires: seam `^0.1.0` · agent-coord-mcp `^0.24.0` · groundwork `^0.3.0` · agent-coordination `^0.5.0`.
|
|
22
22
|
|
|
23
23
|
## From another machine
|
|
24
24
|
|
package/dist/gitinfo.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* Read-only git views for a project's docs: uncommitted hunks (working tree vs HEAD, staged included), last commit touching the file + its hunks, and a docs/ status map. */
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
4
6
|
const run = promisify(execFile);
|
|
5
7
|
async function git(cwd, args) { try {
|
|
6
8
|
const r = await run("git", args, { cwd, maxBuffer: 8 << 20 });
|
|
@@ -67,3 +69,115 @@ export async function lastDocsCommit(projectPath) {
|
|
|
67
69
|
const files = ((await git(projectPath, ["show", "--format=", "--name-only", short, "--", "docs"])) ?? "").split("\n").filter(Boolean).map((f) => f.replace(/^docs\//, ""));
|
|
68
70
|
return { short, subject, date, files };
|
|
69
71
|
}
|
|
72
|
+
const GIT_CACHE_MS = 3_000;
|
|
73
|
+
const STALE_DAYS = 14;
|
|
74
|
+
const GRAPH_MAX = 500;
|
|
75
|
+
const cache = new Map();
|
|
76
|
+
function cached(key, fn) {
|
|
77
|
+
const hit = cache.get(key);
|
|
78
|
+
if (hit && Date.now() - hit.at < GIT_CACHE_MS)
|
|
79
|
+
return hit.value;
|
|
80
|
+
const value = fn();
|
|
81
|
+
cache.set(key, { at: Date.now(), value });
|
|
82
|
+
value.catch(() => cache.delete(key));
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
const splitRecords = (out) => (out ?? "").split("\n").filter(Boolean).map((l) => l.split("\x1f"));
|
|
86
|
+
export function gitGraph(projectPath, limit = 200) {
|
|
87
|
+
const n = Math.max(1, Math.min(GRAPH_MAX, Math.floor(limit) || 200));
|
|
88
|
+
return cached(`graph:${projectPath}:${n}`, async () => {
|
|
89
|
+
const head = (await git(projectPath, ["rev-parse", "HEAD"]))?.trim() ?? "";
|
|
90
|
+
const rows = splitRecords(await git(projectPath, ["log", "--all", "--date-order", `-n${n}`, "--format=%H%x1f%h%x1f%P%x1f%s%x1f%an%x1f%aI%x1f%D"]));
|
|
91
|
+
const commits = rows.map(([sha, short, parents, subject, author, date, refs]) => ({ sha, short, parents: (parents ?? "").split(" ").filter(Boolean), subject: subject ?? "", author: author ?? "", date: date ?? "", refs: (refs ?? "").split(",").map((r) => r.trim().replace(/^HEAD -> /, "HEAD -> ")).filter(Boolean) }));
|
|
92
|
+
return { head, commits };
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** `git worktree list --porcelain` → entries; dirty = `git status --porcelain` non-empty (skipped for prunable/missing paths); agent from the fleet sibling dir name `<repo>-<agentId>[-<slice>]`, else board owner whose Branch·Worktree column names the branch/path. */
|
|
96
|
+
export function gitWorktrees(projectPath, hints = {}) {
|
|
97
|
+
return cached(`worktrees:${projectPath}:${(hints.agentIds ?? []).join(",")}`, async () => {
|
|
98
|
+
const out = (await git(projectPath, ["worktree", "list", "--porcelain"])) ?? "";
|
|
99
|
+
const blocks = out.split(/\n\s*\n/).map((b) => b.split("\n").filter(Boolean)).filter((b) => b.length);
|
|
100
|
+
const repoBase = path.basename(projectPath.replace(/\/+$/, ""));
|
|
101
|
+
const agentIds = [...(hints.agentIds ?? [])].sort((a, b) => b.length - a.length); // longest first: "<project>-worker-1" before "<project>"
|
|
102
|
+
const worktrees = await Promise.all(blocks.map(async (b, i) => {
|
|
103
|
+
const get = (k) => b.find((l) => l === k || l.startsWith(k + " "))?.slice(k.length).trim();
|
|
104
|
+
const wt = get("worktree") ?? "";
|
|
105
|
+
const sha = get("HEAD") ?? "";
|
|
106
|
+
const branchRef = get("branch");
|
|
107
|
+
const prunable = get("prunable") !== undefined;
|
|
108
|
+
const detached = b.includes("detached") || !branchRef;
|
|
109
|
+
const branch = branchRef ? branchRef.replace(/^refs\/heads\//, "") : null;
|
|
110
|
+
const dirty = !prunable && wt && existsSync(wt) ? ((await git(wt, ["status", "--porcelain"])) ?? "").trim().length > 0 : false;
|
|
111
|
+
const dir = path.basename(wt);
|
|
112
|
+
let agent;
|
|
113
|
+
if (i > 0 && dir.startsWith(repoBase + "-")) {
|
|
114
|
+
const suffix = dir.slice(repoBase.length + 1);
|
|
115
|
+
agent = agentIds.find((id) => suffix === id || suffix.startsWith(id + "-"));
|
|
116
|
+
}
|
|
117
|
+
if (!agent) {
|
|
118
|
+
const row = hints.boardRows?.find((r) => r.branchWorktree && ((branch && r.branchWorktree.includes(branch)) || r.branchWorktree.includes(dir)));
|
|
119
|
+
if (row?.owner)
|
|
120
|
+
agent = row.owner;
|
|
121
|
+
}
|
|
122
|
+
return { path: wt, branch, sha, short: sha.slice(0, 7), detached, dirty, prunable, ...(agent ? { agent } : {}), isMain: i === 0 };
|
|
123
|
+
}));
|
|
124
|
+
return { worktrees };
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/** gh (optional): open/merged/closed PRs by head branch. Missing/unauthenticated gh → null (envelope `ghAvailable:false`). */
|
|
128
|
+
async function ghPrs(projectPath) {
|
|
129
|
+
try {
|
|
130
|
+
const r = await run("gh", ["pr", "list", "--state", "all", "--limit", "50", "--json", "number,url,state,title,headRefName"], { cwd: projectPath, maxBuffer: 8 << 20, timeout: 15_000 });
|
|
131
|
+
const list = JSON.parse(r.stdout);
|
|
132
|
+
const m = new Map();
|
|
133
|
+
for (const p of list)
|
|
134
|
+
if (!m.has(p.headRefName))
|
|
135
|
+
m.set(p.headRefName, { number: p.number, url: p.url, state: p.state, title: p.title });
|
|
136
|
+
return m;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export function gitBranches(projectPath, hints = {}) {
|
|
143
|
+
return cached(`branches:${projectPath}:${(hints.agentIds ?? []).join(",")}`, async () => {
|
|
144
|
+
const defaultBranch = "main";
|
|
145
|
+
const base = `origin/${defaultBranch}`;
|
|
146
|
+
const hasBase = (await git(projectPath, ["rev-parse", "--verify", "-q", `${base}^{commit}`]))?.trim() ? true : false;
|
|
147
|
+
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
|
+
.filter(([ref]) => !/^refs\/remotes\/[^/]+\/HEAD$/.test(ref));
|
|
149
|
+
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; };
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
const branches = await Promise.all(rows.map(async ([ref, sha, short, subject, date]) => {
|
|
154
|
+
const remote = ref.startsWith("refs/remotes/");
|
|
155
|
+
const name = ref.replace(/^refs\/(heads|remotes)\//, "");
|
|
156
|
+
let ahead = null, behind = null, merged = false;
|
|
157
|
+
if (hasBase) {
|
|
158
|
+
const lr = (await git(projectPath, ["rev-list", "--left-right", "--count", `${base}...${ref}`]))?.trim().split(/\s+/);
|
|
159
|
+
if (lr && lr.length === 2) {
|
|
160
|
+
behind = +lr[0];
|
|
161
|
+
ahead = +lr[1];
|
|
162
|
+
}
|
|
163
|
+
merged = (await gitOk(projectPath, ["merge-base", "--is-ancestor", ref, base]));
|
|
164
|
+
}
|
|
165
|
+
const stale = date ? now - Date.parse(date) > STALE_DAYS * 86_400_000 : false;
|
|
166
|
+
const localName = remote ? name.replace(/^[^/]+\//, "") : name;
|
|
167
|
+
const wt = remote ? undefined : worktrees.find((w) => w.branch === name);
|
|
168
|
+
const boardRow = hints.boardRows?.find((r) => r.branchWorktree && r.branchWorktree.includes(localName));
|
|
169
|
+
const owner = boardRow?.owner || wt?.agent;
|
|
170
|
+
const pr = prs?.get(localName);
|
|
171
|
+
const prOut = pr ? { ...pr, ...(qaOf(pr.number) ? { qa: qaOf(pr.number) } : {}) } : undefined;
|
|
172
|
+
return { name, remote, sha, short, subject: subject ?? "", date: date ?? "", ahead, behind, merged, stale, ...(wt ? { worktree: wt.path } : {}), ...(owner ? { owner } : {}), ...(prOut ? { pr: prOut } : {}) };
|
|
173
|
+
}));
|
|
174
|
+
return { defaultBranch, base, baseFound: hasBase, ghAvailable: prs !== null, branches };
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async function gitOk(cwd, args) { try {
|
|
178
|
+
await run("git", args, { cwd });
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return false;
|
|
183
|
+
} }
|
package/dist/kitresolve.js
CHANGED
|
@@ -2,8 +2,9 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
//
|
|
6
|
-
|
|
5
|
+
// mcp ^0.24.0 ships send_message.inReplyTo (npm 2026-08-19) — that is the floor. send_command's allowlist (/reload-skills since 0.25.0) is NOT a floor:
|
|
6
|
+
// it is feature-detected per connection (BusClient.sendCommands → /health.requires.mcp.capabilities.sendCommand) and the console degrades gracefully without it.
|
|
7
|
+
export const REQUIRES = { seam: "^0.1.0", mcp: "^0.24.0", groundwork: "^0.3.0", playbook: "^0.5.0" };
|
|
7
8
|
const which = (bin) => { try {
|
|
8
9
|
return execFileSync("which", [bin], { encoding: "utf8" }).trim() || null;
|
|
9
10
|
}
|
|
@@ -57,25 +58,26 @@ export function installedVersions(kitPath) {
|
|
|
57
58
|
const gw = groundworkCli(kitPath), pb = playbookCli(kitPath), mcp = mcpServer(kitPath);
|
|
58
59
|
return { seam, groundwork: pkgVersionNear(gw), playbook: pkgVersionNear(pb), mcp: pkgVersionNear(mcp), paths: { groundwork: gw, playbook: pb, mcp, kitCheckout: hasKitCheckout(kitPath) ? kitPath : null } };
|
|
59
60
|
}
|
|
60
|
-
/**
|
|
61
|
+
/** Floor check: the console's requirement is a MINIMUM within the same major (kit packages bump minors often; 0.25.1 must satisfy "^0.24.0").
|
|
62
|
+
Written as ^a.b.c for familiarity, evaluated as >= a.b.c and < (a+1).0.0 — NOT strict caret semantics for 0.x. */
|
|
61
63
|
export function satisfies(v, range) {
|
|
62
64
|
if (!v)
|
|
63
65
|
return null;
|
|
64
66
|
const m = range.match(/^\^(\d+)\.(\d+)\.(\d+)$/);
|
|
65
67
|
if (!m)
|
|
66
68
|
return null;
|
|
67
|
-
const [a, b, c] = v.split(".").map(
|
|
69
|
+
const [a, b, c] = v.split(".").map((n) => parseInt(n, 10));
|
|
68
70
|
const [x, y, z] = m.slice(1).map(Number);
|
|
71
|
+
if (![a, b, c].every(Number.isFinite))
|
|
72
|
+
return null;
|
|
69
73
|
if (a !== x)
|
|
70
|
-
return false;
|
|
71
|
-
|
|
72
|
-
return false;
|
|
73
|
-
return a > x || b > y || (b === y && c >= z);
|
|
74
|
+
return false; // different major: not satisfied (breaking either way)
|
|
75
|
+
return b > y || (b === y && c >= z);
|
|
74
76
|
}
|
|
75
77
|
export function requiresReport(kitPath, caps) {
|
|
76
78
|
const inst = installedVersions(kitPath);
|
|
77
79
|
const check = (k) => ({ required: REQUIRES[k], installed: inst[k], ok: satisfies(inst[k], REQUIRES[k]) });
|
|
78
|
-
const r = { seam: check("seam"), mcp: { ...check("mcp"), capabilities: caps ?? { inReplyTo: null }, warnings: caps?.inReplyTo === false ? ["inReplyTo missing — decision replies will not be linked/cleared; upgrade agent-coord-mcp"] : [] }, groundwork: check("groundwork"), playbook: check("playbook") };
|
|
80
|
+
const r = { seam: check("seam"), mcp: { ...check("mcp"), capabilities: caps ?? { inReplyTo: null, sendCommand: null }, warnings: caps?.inReplyTo === false ? ["inReplyTo missing — decision replies will not be linked/cleared; upgrade agent-coord-mcp"] : [] }, groundwork: check("groundwork"), playbook: check("playbook") };
|
|
79
81
|
return { ...r, paths: inst.paths, ok: Object.values(r).every((x) => x.ok !== false) };
|
|
80
82
|
}
|
|
81
83
|
/** Package root dir for a kit package: the checkout's packages/<dir> if present, else the installed npm package (found from its bin). Null when neither exists. */
|
package/dist/mcp.js
CHANGED
|
@@ -74,12 +74,17 @@ export class BusClient {
|
|
|
74
74
|
const reg = await client.callTool({ name: "register", arguments: { agentId: id.agentId, project: id.project, role: id.role, force: true } });
|
|
75
75
|
log?.(`register → ${JSON.stringify(reg.structuredContent ?? textOf(reg)).slice(0, 160)}`);
|
|
76
76
|
// Feature detection: which args each tool accepts (e.g. send_message.inReplyTo landed in kit 18ffbf4; the live bin may predate it).
|
|
77
|
+
let sendCommandDesc = "";
|
|
77
78
|
try {
|
|
78
79
|
const tools = await client.listTools();
|
|
79
|
-
for (const t of tools.tools)
|
|
80
|
+
for (const t of tools.tools) {
|
|
80
81
|
this.toolArgs.set(t.name, new Set(Object.keys(t.inputSchema?.properties ?? {})));
|
|
82
|
+
if (t.name === "send_command")
|
|
83
|
+
sendCommandDesc = t.description ?? "";
|
|
84
|
+
}
|
|
81
85
|
}
|
|
82
86
|
catch { /* older server: no detection, pass nothing extra */ }
|
|
87
|
+
this.commands = await detectCommands(client, sendCommandDesc, log);
|
|
83
88
|
this.client = client;
|
|
84
89
|
return client;
|
|
85
90
|
})();
|
|
@@ -91,6 +96,11 @@ export class BusClient {
|
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
toolArgs = new Map();
|
|
99
|
+
commands = null;
|
|
100
|
+
/** send_command allowlist detected on the connected server (e.g. ["/clear","/compact"] on 0.24.0, +"/reload-skills" on 0.25.0); null = not connected / undetectable. */
|
|
101
|
+
get sendCommands() { return this.client ? this.commands : null; }
|
|
102
|
+
/** True when the connected server accepts this raw control command. */
|
|
103
|
+
acceptsCommand(cmd) { return this.sendCommands?.includes(cmd) ?? false; }
|
|
94
104
|
/** True when the connected server's tool schema lists this argument (false when unknown or not connected yet). */
|
|
95
105
|
supports(tool, arg) { return this.toolArgs.get(tool)?.has(arg) ?? false; }
|
|
96
106
|
get connected() { return !!this.client; }
|
|
@@ -109,6 +119,34 @@ export class BusClient {
|
|
|
109
119
|
}
|
|
110
120
|
async close() { await this.client?.close(); this.client = null; }
|
|
111
121
|
}
|
|
122
|
+
/* send_command allowlist detection. The MCP does not expose the allowlist in the tool schema (command is a plain string), so:
|
|
123
|
+
1. probe once per connection with a command that can never be valid — the server rejects it BEFORE looking at to/room or touching any pane
|
|
124
|
+
(transport.ts sendCommandTool: normalizeControlCommand → `unsupported command '…'. Allowed: /clear, /compact[, /reload-skills]`), so this is
|
|
125
|
+
side-effect-free and needs no bus-writes gate; 2. fall back to the tool description ("allowlist is locked to /clear, /compact and /reload-skills"). */
|
|
126
|
+
const PROBE_COMMAND = "/__groundwork-console-probe__";
|
|
127
|
+
async function detectCommands(client, description, log) {
|
|
128
|
+
const fromText = (t) => { const m = t.match(/Allowed:\s*([^\n]+)/i) ?? t.match(/locked to ([^.;\n]+)/i); return m ? [...m[1].matchAll(/\/[a-z][\w-]*/g)].map((x) => x[0]) : []; };
|
|
129
|
+
try {
|
|
130
|
+
const res = await client.callTool({ name: "send_command", arguments: { from: "groundwork-console", command: PROBE_COMMAND } });
|
|
131
|
+
const out = (res.structuredContent ?? parseText(textOf(res)));
|
|
132
|
+
const list = fromText(typeof out === "string" ? out : out?.error ?? textOf(res));
|
|
133
|
+
if (list.length) {
|
|
134
|
+
log?.(`send_command allowlist (probe): ${list.join(" ")}`);
|
|
135
|
+
return list;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
const list = fromText(e.message ?? "");
|
|
140
|
+
if (list.length)
|
|
141
|
+
return list;
|
|
142
|
+
}
|
|
143
|
+
const list = fromText(description);
|
|
144
|
+
if (list.length) {
|
|
145
|
+
log?.(`send_command allowlist (description): ${list.join(" ")}`);
|
|
146
|
+
return list;
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
112
150
|
function textOf(r) { return (r.content ?? []).map((c) => c.text ?? "").join("\n"); }
|
|
113
151
|
function parseText(t) { try {
|
|
114
152
|
return JSON.parse(t);
|
package/dist/server.js
CHANGED
|
@@ -20,7 +20,7 @@ import { detectedHarnesses, groundworkSkills, playbookCards } from "./skills.js"
|
|
|
20
20
|
import { extensionsOf } from "./extensions.js";
|
|
21
21
|
import { parseWorkDoc, workstreamsExtensionsOf } from "@davidbalzan/groundwork-seam";
|
|
22
22
|
import { EventEngine } from "./events.js";
|
|
23
|
-
import { docGit, docsStatus, lastDocsCommit } from "./gitinfo.js";
|
|
23
|
+
import { docGit, docsStatus, lastDocsCommit, gitGraph, gitBranches, gitWorktrees } from "./gitinfo.js";
|
|
24
24
|
import { Notifier } from "./notify.js";
|
|
25
25
|
import { Runs } from "./runs.js";
|
|
26
26
|
import { BusClient, AGENT_ID, busWrites, seamWrites, saveSwitches, SWITCHES_FILE } from "./mcp.js";
|
|
@@ -46,7 +46,9 @@ const notifier = new Notifier(cfg, runs, events);
|
|
|
46
46
|
const busDavid = new BusClient(cfg.kitPath, cfg.coordDir, "david", process.env.AGENT_COORD_MCP);
|
|
47
47
|
const clientFor = (id) => (id === "david" ? busDavid : bus);
|
|
48
48
|
/** Capabilities of the connected MCP (null until a client has connected and listed tools). */
|
|
49
|
-
const mcpCaps = () =>
|
|
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
|
+
/* 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);
|
|
50
52
|
const PUBLIC_DIR = path.resolve(HERE, "../public");
|
|
51
53
|
const root = new Hono();
|
|
52
54
|
const app = new Hono().basePath("/api");
|
|
@@ -113,13 +115,24 @@ app.post("/actions/send", async (c) => {
|
|
|
113
115
|
return c.json({ error: "text required" }, 400);
|
|
114
116
|
if (!b.room && !b.to)
|
|
115
117
|
return c.json({ error: "room or to required" }, 400);
|
|
116
|
-
// Raw control commands:
|
|
117
|
-
//
|
|
118
|
-
|
|
118
|
+
// Raw control commands: a bare `/<cmd>` (no args) to ONE agent goes through MCP send_command — the only path that types a slash command into a live CLI.
|
|
119
|
+
// The allowlist is the CONNECTED server's (feature-detected: 0.24.0 = /clear /compact; 0.25.0 adds /reload-skills — BusClient.sendCommands). A bare slash
|
|
120
|
+
// command the server does not accept degrades to the text wrapper `AGENT_ACTION: run /<cmd>` (never a 4xx; the agent runs it from the request, #14).
|
|
119
121
|
const cmd = b.text.trim();
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
let degraded;
|
|
123
|
+
if (/^\/[a-z][\w-]*$/i.test(cmd) && b.to) {
|
|
124
|
+
const cl = clientFor(who);
|
|
125
|
+
try {
|
|
126
|
+
await cl.connect();
|
|
127
|
+
}
|
|
128
|
+
catch { /* not connected → no raw path; fall through to the text DM */ }
|
|
129
|
+
if (!cl.acceptsCommand(cmd)) {
|
|
130
|
+
const have = cl.sendCommands;
|
|
131
|
+
degraded = `${cmd} is not in the connected agent-coord-mcp's send_command allowlist (${have ? have.join(" ") : "unknown"})${cmd === "/reload-skills" ? " — needs agent-coord-mcp ≥0.25.0 + MCP session restart" : ""}; sent as a text request instead`;
|
|
132
|
+
b.text = `AGENT_ACTION: run ${cmd}`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (/^\/[a-z][\w-]*$/i.test(cmd) && !degraded && (b.to ? clientFor(who).acceptsCommand(cmd) : false)) {
|
|
123
136
|
const r = await runs.exec({ kind: "bus", label: `command ${cmd} → ${b.to}`, sideEffects: true, args: { as: who, to: b.to, command: cmd } }, async (log) => {
|
|
124
137
|
const out = (await clientFor(who).call("send_command", { to: b.to, command: cmd, waitForDelivery: true, deliveryTimeoutMs: 15_000 }, log));
|
|
125
138
|
log(`delivery: ${out.delivery ?? "?"}${out.reason ? ` · ${out.reason}` : ""}${out.note ? ` · ${out.note}` : ""}`);
|
|
@@ -130,6 +143,8 @@ app.post("/actions/send", async (c) => {
|
|
|
130
143
|
const out = (r.result ?? {});
|
|
131
144
|
return c.json({ ...r, raw: true, delivery: out.delivery ?? null, warning: out.delivery === "pending" ? `pusher has not confirmed typing ${cmd} yet${out.reason ? ` — ${out.reason}` : ""}` : undefined }, r.status === "ok" ? 200 : 403);
|
|
132
145
|
}
|
|
146
|
+
if (/^\/[a-z][\w-]*$/i.test(cmd) && b.room && !b.to)
|
|
147
|
+
return c.json({ error: `${cmd} is unicast — pick an agent (rooms get text only)` }, 400);
|
|
133
148
|
const PREFIX_RE = /^(BLOCKER|RISK|DONE|FYI|AGENT_ACTION|GO|SCOPE CHANGE|VERDICT|DAVID_DECISION)\b/;
|
|
134
149
|
if (b.room && !PREFIX_RE.test(b.text))
|
|
135
150
|
return c.json({ error: "room posts need a prefix (FYI / AGENT_ACTION / DAVID_DECISION / BLOCKER / RISK / DONE / GO / SCOPE CHANGE / VERDICT)" }, 400);
|
|
@@ -137,6 +152,8 @@ app.post("/actions/send", async (c) => {
|
|
|
137
152
|
const r = await runs.exec({ kind: "bus", label: `send as ${who} ${target}`, sideEffects: true, args: { as: who, room: b.room, to: b.to, kind: b.kind, text: b.text.slice(0, 200) } }, async (log) => {
|
|
138
153
|
const cl = clientFor(who);
|
|
139
154
|
await cl.connect(log);
|
|
155
|
+
if (degraded)
|
|
156
|
+
log(`⚠ ${degraded}`);
|
|
140
157
|
// Reply linkage = bus inReplyTo only (console-owned answered.json retired 2026-08-19). Parent: explicit from the UI (answers[0] / inReplyTo);
|
|
141
158
|
// for a DM as David with no explicit parent, the latest still-open DAVID_DECISION DM from that agent — so "reply to the agent" clears its packet.
|
|
142
159
|
let parent = b.inReplyTo ?? b.answers?.[0];
|
|
@@ -164,6 +181,56 @@ app.post("/actions/send", async (c) => {
|
|
|
164
181
|
}
|
|
165
182
|
return out;
|
|
166
183
|
});
|
|
184
|
+
return c.json({ ...r, ...(degraded ? { raw: false, degraded: true, warning: degraded } : {}) }, r.status === "ok" ? 200 : 403);
|
|
185
|
+
});
|
|
186
|
+
/* Ops › Skills: after a playbook update, push `/reload-skills` RAW to every live tmux-attached agent (send_command, bus-writes gated, only when the connected
|
|
187
|
+
MCP's allowlist carries it — 0.25.0+). Per-agent delivery confirmed|pending in the Run. Targets may be narrowed with `agentIds`. */
|
|
188
|
+
app.get("/actions/reload-skills", async (c) => {
|
|
189
|
+
const cl = bus;
|
|
190
|
+
try {
|
|
191
|
+
await cl.connect();
|
|
192
|
+
}
|
|
193
|
+
catch { /* report what we know */ }
|
|
194
|
+
return c.json({ supported: cl.acceptsCommand("/reload-skills"), allowlist: cl.sendCommands, agents: liveAttached(), hint: cl.acceptsCommand("/reload-skills") ? null : "needs agent-coord-mcp ≥0.25.0 (send_command allowlist with /reload-skills) + a restart of the console's MCP session and the fleet's" });
|
|
195
|
+
});
|
|
196
|
+
app.post("/actions/reload-skills", async (c) => {
|
|
197
|
+
const b = (await c.req.json().catch(() => ({})));
|
|
198
|
+
if (b.confirm !== "RELOAD")
|
|
199
|
+
return c.json({ error: "confirm must be RELOAD" }, 400);
|
|
200
|
+
if (!busWrites.enabled)
|
|
201
|
+
return c.json({ error: "bus writes are off — switch them on in the header (WRITES) first" }, 403);
|
|
202
|
+
const cl = bus;
|
|
203
|
+
try {
|
|
204
|
+
await cl.connect();
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
return c.json({ error: e.message }, 500);
|
|
208
|
+
}
|
|
209
|
+
if (!cl.acceptsCommand("/reload-skills"))
|
|
210
|
+
return c.json({ error: `connected agent-coord-mcp does not accept /reload-skills (allowlist ${cl.sendCommands?.join(" ") ?? "unknown"}) — needs ≥0.25.0 + MCP session restart` }, 409);
|
|
211
|
+
const live = liveAttached();
|
|
212
|
+
const targets = (b.agentIds?.length ? b.agentIds.filter((id) => live.includes(id)) : live);
|
|
213
|
+
if (!targets.length)
|
|
214
|
+
return c.json({ error: "no live tmux-attached agents to reload" }, 400);
|
|
215
|
+
const r = await runs.exec({ kind: "bus", label: `reload skills → ${targets.length} session${targets.length === 1 ? "" : "s"}`, sideEffects: true, args: { agentIds: targets } }, async (log) => {
|
|
216
|
+
const results = [];
|
|
217
|
+
for (const to of targets) {
|
|
218
|
+
try {
|
|
219
|
+
const out = (await cl.call("send_command", { to, command: "/reload-skills", waitForDelivery: true, deliveryTimeoutMs: 15_000 }, log));
|
|
220
|
+
const delivery = out.error ? `error: ${out.error}` : out.delivery ?? "?";
|
|
221
|
+
results.push({ agentId: to, delivery, ...(out.reason ? { reason: out.reason } : {}) });
|
|
222
|
+
log(`${to}: ${delivery}${out.reason ? ` · ${out.reason}` : ""}`);
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
results.push({ agentId: to, delivery: `error: ${e.message}` });
|
|
226
|
+
log(`${to}: error ${e.message}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const confirmed = results.filter((x) => x.delivery === "confirmed").length, pending = results.filter((x) => x.delivery === "pending").length;
|
|
230
|
+
if (confirmed + pending === 0)
|
|
231
|
+
throw new Error(`no session accepted /reload-skills (${results.map((x) => `${x.agentId}: ${x.delivery}`).join("; ")})`); // an all-error push is a failed Run, never ok
|
|
232
|
+
return { results, confirmed, pending };
|
|
233
|
+
});
|
|
167
234
|
return c.json(r, r.status === "ok" ? 200 : 403);
|
|
168
235
|
});
|
|
169
236
|
/* Uploads — pasted images for DMs. Path + served URL both go into the message text as `[image: <path> · <url>]`.
|
|
@@ -307,6 +374,50 @@ app.get("/projects/:name/status", async (c) => {
|
|
|
307
374
|
return c.json({ error: "unknown project" }, 404);
|
|
308
375
|
return c.json(await status(cfg.kitPath, p.path));
|
|
309
376
|
});
|
|
377
|
+
/* ---- Git branches screen v1 (docs/contracts/git-screen-v1.md): read-only git/gh, 3 s cache, errors → { error } ---- */
|
|
378
|
+
const gitHints = (p) => {
|
|
379
|
+
const src = existsSync(path.join(p.path, "docs/WORKSTREAMS.md")) ? readFileSync(path.join(p.path, "docs/WORKSTREAMS.md"), "utf8") : null;
|
|
380
|
+
const rows = boardOf(p.path).workstreams.rows.flatMap((r) => ("branchWorktree" in r ? [{ owner: r.owner, branchWorktree: r.branchWorktree }] : []));
|
|
381
|
+
let openPrs = [];
|
|
382
|
+
try {
|
|
383
|
+
openPrs = src ? workstreamsExtensionsOf(parseWorkDoc(src)).openPrs : [];
|
|
384
|
+
}
|
|
385
|
+
catch { /* board unparsable → no qa column */ }
|
|
386
|
+
return { agentIds: agents(cfg.coordDir).map((a) => a.agentId), boardRows: rows, openPrs };
|
|
387
|
+
};
|
|
388
|
+
app.get("/projects/:name/git/graph", async (c) => {
|
|
389
|
+
const p = project(c.req.param("name"));
|
|
390
|
+
if (!p)
|
|
391
|
+
return c.json({ error: "unknown project" }, 404);
|
|
392
|
+
try {
|
|
393
|
+
return c.json(await gitGraph(p.path, Number(c.req.query("limit") ?? 200)));
|
|
394
|
+
}
|
|
395
|
+
catch (e) {
|
|
396
|
+
return c.json({ error: e.message }, 500);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
app.get("/projects/:name/git/branches", async (c) => {
|
|
400
|
+
const p = project(c.req.param("name"));
|
|
401
|
+
if (!p)
|
|
402
|
+
return c.json({ error: "unknown project" }, 404);
|
|
403
|
+
try {
|
|
404
|
+
return c.json(await gitBranches(p.path, gitHints(p)));
|
|
405
|
+
}
|
|
406
|
+
catch (e) {
|
|
407
|
+
return c.json({ error: e.message }, 500);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
app.get("/projects/:name/git/worktrees", async (c) => {
|
|
411
|
+
const p = project(c.req.param("name"));
|
|
412
|
+
if (!p)
|
|
413
|
+
return c.json({ error: "unknown project" }, 404);
|
|
414
|
+
try {
|
|
415
|
+
return c.json(await gitWorktrees(p.path, gitHints(p)));
|
|
416
|
+
}
|
|
417
|
+
catch (e) {
|
|
418
|
+
return c.json({ error: e.message }, 500);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
310
421
|
app.get("/projects/:name/skills", async (c) => {
|
|
311
422
|
const p = project(c.req.param("name"));
|
|
312
423
|
if (!p)
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,t}from"./mermaid-parser.core-
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-BXY3JF8d.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-D5IewQLB.js";import"./chunk-I66GZJ75-C97VIOWu.js";import{n as i,r as a,t as o}from"./chunk-6Q2QTUOP-nWUr6zuz.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-DqjaFy6t.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-DoiwlY9_.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-BXY3JF8d.js";export{e as createArchitectureServices};
|