@coopcli/specplan 5.5.7 → 2026.817.2

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 (101) hide show
  1. package/dist/cli/index.js +399 -26
  2. package/dist/client/assets/{abnfDiagram-VRR7QNED-DYWyHZ9F.js → abnfDiagram-VRR7QNED-BYxvjjLu.js} +1 -1
  3. package/dist/client/assets/architecture-TIHT7OUA-Cz2n_8dt.js +1 -0
  4. package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-C_eOLe63.js → architectureDiagram-ZJ3FMSHR-DaKJ9vFA.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-677ZJIJ3-tsATc02E.js → blockDiagram-677ZJIJ3-xETyUDB6.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-LMCZKHZV-DYnH3lWl.js → c4Diagram-LMCZKHZV-CJGUNAXt.js} +1 -1
  7. package/dist/client/assets/channel-CuBMvOiT.js +1 -0
  8. package/dist/client/assets/{chunk-32BRIVSS-BXANaFgi.js → chunk-32BRIVSS-6Rh4CCUQ.js} +1 -1
  9. package/dist/client/assets/{chunk-52WLFC77-Kb6jbJaM.js → chunk-52WLFC77-A0SkaTYM.js} +1 -1
  10. package/dist/client/assets/{chunk-C7G6YPKG-Dx3Quqbt.js → chunk-C7G6YPKG-BMphmVWl.js} +1 -1
  11. package/dist/client/assets/{chunk-EX3LRPZG-Cvj4C7h5.js → chunk-EX3LRPZG-xUcXnDRE.js} +1 -1
  12. package/dist/client/assets/{chunk-FWX5IMBZ-Dwmh6LfQ.js → chunk-FWX5IMBZ-C_mfbGNa.js} +2 -2
  13. package/dist/client/assets/{chunk-HOUHSVGY-B9wU44cg.js → chunk-HOUHSVGY-yNNNImSs.js} +1 -1
  14. package/dist/client/assets/{chunk-ICXQ74PX-BSyEFY66.js → chunk-ICXQ74PX-CyZZ3kFK.js} +1 -1
  15. package/dist/client/assets/{chunk-K2UTITRG-CoqZrE_Y.js → chunk-K2UTITRG-CL9ravuf.js} +3 -3
  16. package/dist/client/assets/{chunk-MOJQB5TN-CHtb7Cmu.js → chunk-MOJQB5TN-Bio7zOcN.js} +1 -1
  17. package/dist/client/assets/{chunk-OGEWGWER-1nrDWRbg.js → chunk-OGEWGWER-vwCurcM9.js} +1 -1
  18. package/dist/client/assets/{chunk-PUDLZKDR-COVK5AOh.js → chunk-PUDLZKDR-BWhPp8B4.js} +1 -1
  19. package/dist/client/assets/{chunk-Q4XR5HBZ-Bte6Z4Y6.js → chunk-Q4XR5HBZ-CvZ-Pz5B.js} +1 -1
  20. package/dist/client/assets/{chunk-RYQCIY6F-Y7iEjw2i.js → chunk-RYQCIY6F-CtK1BTrZ.js} +1 -1
  21. package/dist/client/assets/{chunk-V7JOEXUC-CsT2riud.js → chunk-V7JOEXUC-BP5dRCwG.js} +1 -1
  22. package/dist/client/assets/chunk-VAUOI2AC-D7taetBr.js +1 -0
  23. package/dist/client/assets/{chunk-VR4S4FIN-m_oV_gzU.js → chunk-VR4S4FIN-Q-TN3CuI.js} +1 -1
  24. package/dist/client/assets/{chunk-WYO6CB5R-DWSMU6gp.js → chunk-WYO6CB5R-qHfws5Na.js} +1 -1
  25. package/dist/client/assets/chunk-XXDRQBXY-BSmtn7NR.js +1 -0
  26. package/dist/client/assets/{chunk-ZGVPDNZ5-CEFMjdw9.js → chunk-ZGVPDNZ5-CHDpAKTq.js} +1 -1
  27. package/dist/client/assets/classDiagram-OUVF2IWQ-B8_i7rCq.js +1 -0
  28. package/dist/client/assets/classDiagram-v2-EOCWNBFH-B8_i7rCq.js +1 -0
  29. package/dist/client/assets/{cose-bilkent-JH36ORCC-Df934yzX.js → cose-bilkent-JH36ORCC-dsywqjX4.js} +1 -1
  30. package/dist/client/assets/{cynefin-VYW2F7L2-ClnIw26J.js → cynefin-VYW2F7L2-CeEsCHXm.js} +1 -1
  31. package/dist/client/assets/{cynefinDiagram-TSTJHNR4-CPRYiRX_.js → cynefinDiagram-TSTJHNR4-DXw0GuLE.js} +1 -1
  32. package/dist/client/assets/{dagre-VKFMJZFB-C4nF9q2L.js → dagre-VKFMJZFB-D7iVblaF.js} +1 -1
  33. package/dist/client/assets/{diagram-FQU43EPY-CGFM396M.js → diagram-FQU43EPY-D8k_N9yq.js} +1 -1
  34. package/dist/client/assets/{diagram-G47NLZAW-BXvyFAv9.js → diagram-G47NLZAW-CH7ECldl.js} +1 -1
  35. package/dist/client/assets/{diagram-NH7WQ7WH-2R_WWGMz.js → diagram-NH7WQ7WH-eJ3LFOko.js} +1 -1
  36. package/dist/client/assets/{diagram-OA4YK3LP-WUv5u0Y5.js → diagram-OA4YK3LP-BcBLwxof.js} +1 -1
  37. package/dist/client/assets/{diagram-WEI45ONY-CNb7I-i_.js → diagram-WEI45ONY-g4Ydc6Nh.js} +1 -1
  38. package/dist/client/assets/{dist-DK2USxRt.js → dist-D2xnLDqk.js} +4 -4
  39. package/dist/client/assets/{ebnfDiagram-CCIWWBDH-DlMvqVKR.js → ebnfDiagram-CCIWWBDH-nijnOCKy.js} +1 -1
  40. package/dist/client/assets/{erDiagram-Q63AITRT-nQNZerKT.js → erDiagram-Q63AITRT-Dj7bPJ8h.js} +1 -1
  41. package/dist/client/assets/eventmodeling-45OFAUF4-D-h9umx1.js +1 -0
  42. package/dist/client/assets/flowDiagram-23GEKE2U-C4Gtyi00.js +1 -0
  43. package/dist/client/assets/{ganttDiagram-NO4QXBWP-LgOQ3f8i.js → ganttDiagram-NO4QXBWP-BKwUgAKS.js} +1 -1
  44. package/dist/client/assets/{gitGraph-TEB2WS4Q-h8YftM84.js → gitGraph-TEB2WS4Q-C3nx5-er.js} +1 -1
  45. package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-C-2lUGWN.js → gitGraphDiagram-IHSO6WYX-xX_vEW-n.js} +1 -1
  46. package/dist/client/assets/image-GAAHSSAO-BS5tZem_.js +1 -0
  47. package/dist/client/assets/{index-Dgo32IwH.css → index-Cc2C1ER1.css} +1 -1
  48. package/dist/client/assets/index-CcRUbBLK.js +92 -0
  49. package/dist/client/assets/{info-DKCQHKI2-CPjn8MeM.js → info-DKCQHKI2-DtWLyMEA.js} +1 -1
  50. package/dist/client/assets/{infoDiagram-FWYZ7A6U-BWNia170.js → infoDiagram-FWYZ7A6U-uJrkO1jE.js} +1 -1
  51. package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-DzGb1nwn.js → ishikawaDiagram-FXEZZL3T-grtc_5Gw.js} +1 -1
  52. package/dist/client/assets/{journeyDiagram-5HDEW3XC-DCQtd1UV.js → journeyDiagram-5HDEW3XC-D5mpwyTK.js} +1 -1
  53. package/dist/client/assets/{kanban-definition-HUTT4EX6-TU2lEHmm.js → kanban-definition-HUTT4EX6-C8FJz-rK.js} +1 -1
  54. package/dist/client/assets/{line-Bhnb99qa.js → line-FNoxgdds.js} +1 -1
  55. package/dist/client/assets/{linear-6pbkJ0-_.js → linear-CmlO4_aU.js} +1 -1
  56. package/dist/client/assets/{mermaid-parser.core-OvIPAtjO.js → mermaid-parser.core-CWtD7LBQ.js} +3 -3
  57. package/dist/client/assets/{mindmap-definition-LN4V7U3C-CVP_9PTJ.js → mindmap-definition-LN4V7U3C-c0zWDYkC.js} +1 -1
  58. package/dist/client/assets/{packet-7NZHBO7P-DZU_wayH.js → packet-7NZHBO7P-CFjBM6rf.js} +1 -1
  59. package/dist/client/assets/{pegDiagram-2B236MQR-B5h_0epx.js → pegDiagram-2B236MQR-D5EdJTGE.js} +1 -1
  60. package/dist/client/assets/{pie-RZYD4A2V-D1Ivmk2g.js → pie-RZYD4A2V-DzXcKfJc.js} +1 -1
  61. package/dist/client/assets/{pieDiagram-ENE6RG2P-DFFO1w4T.js → pieDiagram-ENE6RG2P-CfJdJPbc.js} +1 -1
  62. package/dist/client/assets/{prod-CkjOyc2K.js → prod-DxrTzQHE.js} +5 -5
  63. package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-CV3J7KXU.js → quadrantDiagram-ABIIQ3AL-BERLxAab.js} +1 -1
  64. package/dist/client/assets/{radar-I7S5WNFK-8MrgTeSz.js → radar-I7S5WNFK-BTKCUdOD.js} +1 -1
  65. package/dist/client/assets/{railroad-3IZDKUUU-C5HUKwxx.js → railroad-3IZDKUUU-BqlJYKVR.js} +1 -1
  66. package/dist/client/assets/railroad-abnf-AHOZXSZD-sY1lWP7G.js +1 -0
  67. package/dist/client/assets/railroad-ebnf-EBAXGLYW-D9qoZ2zn.js +1 -0
  68. package/dist/client/assets/railroad-peg-LSFZ7HO6-ChaYlBF0.js +1 -0
  69. package/dist/client/assets/{railroadDiagram-RFXS5EU6-CKh8T4lJ.js → railroadDiagram-RFXS5EU6-CjYvoaz7.js} +1 -1
  70. package/dist/client/assets/{requirementDiagram-TGXJPOKE-DjXuDke9.js → requirementDiagram-TGXJPOKE-CBOHTTAP.js} +1 -1
  71. package/dist/client/assets/{sankeyDiagram-HTMAVEWB-BejNjkdX.js → sankeyDiagram-HTMAVEWB-BhhsYxus.js} +1 -1
  72. package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-BvdwpZEb.js → sequenceDiagram-DBY2YBRQ-tBrxwb5x.js} +1 -1
  73. package/dist/client/assets/{src-_g_WvJI1.js → src-B56W74gN.js} +1 -1
  74. package/dist/client/assets/{stateDiagram-2N3HPSRC-BnGJpSFl.js → stateDiagram-2N3HPSRC-CETKX_oh.js} +1 -1
  75. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-Q_oKpxae.js +1 -0
  76. package/dist/client/assets/{swimlanes-5IMT3BWC-B0KYjtx_.js → swimlanes-5IMT3BWC-Cp3oOe65.js} +1 -1
  77. package/dist/client/assets/swimlanesDiagram-G3AALYLV-BlNqO3f1.js +8 -0
  78. package/dist/client/assets/{timeline-definition-FHXFAJF6-CeR9YvHk.js → timeline-definition-FHXFAJF6-D4yGmEXE.js} +1 -1
  79. package/dist/client/assets/{treeView-QDETBFTQ-7aggPVAO.js → treeView-QDETBFTQ-Bq0uOqri.js} +1 -1
  80. package/dist/client/assets/{treemap-6X3UGDF4-BIYXi5iS.js → treemap-6X3UGDF4-DEB6PYX-.js} +1 -1
  81. package/dist/client/assets/{vennDiagram-L72KCM5P-CfmtNUHK.js → vennDiagram-L72KCM5P-D5QdiCle.js} +1 -1
  82. package/dist/client/assets/{wardley-OPB4EBWU-D_cAHc5x.js → wardley-OPB4EBWU-DySMTzU2.js} +1 -1
  83. package/dist/client/assets/{wardleyDiagram-EHGQE667-BNoQXjla.js → wardleyDiagram-EHGQE667-B2KHXQxE.js} +1 -1
  84. package/dist/client/assets/{xychartDiagram-FW5EYKEG-CPMslibf.js → xychartDiagram-FW5EYKEG-HxzAIqAC.js} +1 -1
  85. package/dist/client/index.html +2 -2
  86. package/package.json +3 -4
  87. package/dist/client/assets/architecture-TIHT7OUA-CPH7BDQd.js +0 -1
  88. package/dist/client/assets/channel-aEQHEbQr.js +0 -1
  89. package/dist/client/assets/chunk-VAUOI2AC-D14Dfnyc.js +0 -1
  90. package/dist/client/assets/chunk-XXDRQBXY-3irJBvUF.js +0 -1
  91. package/dist/client/assets/classDiagram-OUVF2IWQ-DvYYcX0E.js +0 -1
  92. package/dist/client/assets/classDiagram-v2-EOCWNBFH-DvYYcX0E.js +0 -1
  93. package/dist/client/assets/eventmodeling-45OFAUF4-BghYzyif.js +0 -1
  94. package/dist/client/assets/flowDiagram-23GEKE2U-Ds32-z1u.js +0 -1
  95. package/dist/client/assets/image-GAAHSSAO-8w7tFoBO.js +0 -1
  96. package/dist/client/assets/index-CX5vrCEC.js +0 -92
  97. package/dist/client/assets/railroad-abnf-AHOZXSZD-CmbAkvRY.js +0 -1
  98. package/dist/client/assets/railroad-ebnf-EBAXGLYW-B46ilc8r.js +0 -1
  99. package/dist/client/assets/railroad-peg-LSFZ7HO6-JRE4sO_Q.js +0 -1
  100. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-mYNG_j99.js +0 -1
  101. package/dist/client/assets/swimlanesDiagram-G3AALYLV-CdcAToWt.js +0 -8
package/dist/cli/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // server/cli.ts
4
4
  import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync5 } from "node:fs";
5
5
  import { homedir as homedir3 } from "node:os";
6
- import { dirname as dirname7, extname, join as join12, normalize as normalize2, resolve as resolve6 } from "node:path";
6
+ import { dirname as dirname8, extname, join as join14, normalize as normalize2, resolve as resolve6 } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { parseArgs as parseArgs2 } from "node:util";
9
9
  import { serve } from "@hono/node-server";
@@ -121,7 +121,11 @@ function* mapMessage(message, expectStructuredOutput) {
121
121
  switch (message.type) {
122
122
  case "system": {
123
123
  if (message.subtype === "init") {
124
- yield { type: "start", sessionId: message.session_id };
124
+ yield {
125
+ type: "start",
126
+ sessionId: message.session_id,
127
+ ...message.apiKeySource !== void 0 ? { apiKeySource: message.apiKeySource } : {}
128
+ };
125
129
  }
126
130
  return;
127
131
  }
@@ -891,6 +895,9 @@ var nodeSyncFs = {
891
895
  removeFile(path) {
892
896
  rmSync(path, { force: true });
893
897
  },
898
+ removeDir(path) {
899
+ rmSync(path, { force: true, recursive: true });
900
+ },
894
901
  exists(path) {
895
902
  return existsSync5(path);
896
903
  },
@@ -918,11 +925,11 @@ function readTaskProgress(changeDir, fsx = nodeSyncFs) {
918
925
  return md === void 0 ? null : parseTaskProgress(md);
919
926
  }
920
927
  function parseLedgerStatus(md, specId) {
921
- const cellsOf2 = (line) => line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
928
+ const cellsOf3 = (line) => line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
922
929
  let statusIdx = 2;
923
930
  for (const line of md.split(/\r?\n/)) {
924
931
  if (!line.trim().startsWith("|")) continue;
925
- const cells = cellsOf2(line);
932
+ const cells = cellsOf3(line);
926
933
  if (cells.every((c) => /^:?-+:?$/.test(c))) continue;
927
934
  const lower = cells.map((c) => c.toLowerCase());
928
935
  if (lower.includes("status") && lower.includes("change")) {
@@ -1078,10 +1085,10 @@ var PlanJournal = class {
1078
1085
  }
1079
1086
  if (ledgerMd !== void 0) {
1080
1087
  const status = parseLedgerStatus(ledgerMd, targetId);
1081
- if (status === void 0 || !/^approved\b/i.test(status)) {
1088
+ if (status === void 0 || !/^(archived|approved)\b/i.test(status)) {
1082
1089
  reports.push({
1083
1090
  targetId,
1084
- expected: "SPECS.md row status approved",
1091
+ expected: "SPECS.md row status archived",
1085
1092
  actual: status === void 0 ? "no SPECS.md row" : `SPECS.md row status "${status}"`,
1086
1093
  detail: `journal records approved: true for ${targetId} but its SPECS.md ledger row ${status === void 0 ? "is missing" : `reads "${status}"`}`
1087
1094
  });
@@ -1701,7 +1708,7 @@ function locateArchivedDir(store, specId) {
1701
1708
  const newest = names[names.length - 1];
1702
1709
  return newest ? join7("changes", "archive", newest) : null;
1703
1710
  }
1704
- var APPROVED_LEDGER_STATUS = "approved `[not-implemented]`";
1711
+ var ARCHIVED_LEDGER_STATUS = "archived";
1705
1712
  function cellsOf(line) {
1706
1713
  return line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
1707
1714
  }
@@ -1765,12 +1772,12 @@ function maintainLedgerRow(store, specId, archivedRelDir) {
1765
1772
  continue;
1766
1773
  }
1767
1774
  if ((cells[0] ?? "").includes(token)) {
1768
- lines[i] = replaceStatusCell(line, statusIdx, APPROVED_LEDGER_STATUS);
1775
+ lines[i] = replaceStatusCell(line, statusIdx, ARCHIVED_LEDGER_STATUS);
1769
1776
  store.fsx.writeAtomicFile(ledgerPath, lines.join("\n"));
1770
1777
  return "updated";
1771
1778
  }
1772
1779
  }
1773
- const row2 = `| ${token} (archived \`${archivedBase}\`) | ${deriveDomains(store, archivedRelDir)} | ${APPROVED_LEDGER_STATUS} | (auto-created by specplan apply) |`;
1780
+ const row2 = `| ${token} (archived \`${archivedBase}\`) | ${deriveDomains(store, archivedRelDir)} | ${ARCHIVED_LEDGER_STATUS} | (auto-created by specplan apply) |`;
1774
1781
  const insertAt = activeTableInsertIndex(lines);
1775
1782
  if (insertAt !== null) {
1776
1783
  lines.splice(insertAt, 0, row2);
@@ -1782,7 +1789,7 @@ function maintainLedgerRow(store, specId, archivedRelDir) {
1782
1789
  ${freshLedger(row2)}`);
1783
1790
  return "created";
1784
1791
  }
1785
- const row = `| ${token} (archived \`${archivedBase}\`) | ${deriveDomains(store, archivedRelDir)} | ${APPROVED_LEDGER_STATUS} | (auto-created by specplan apply) |`;
1792
+ const row = `| ${token} (archived \`${archivedBase}\`) | ${deriveDomains(store, archivedRelDir)} | ${ARCHIVED_LEDGER_STATUS} | (auto-created by specplan apply) |`;
1786
1793
  store.fsx.writeAtomicFile(ledgerPath, freshLedger(row));
1787
1794
  return "created";
1788
1795
  }
@@ -2094,6 +2101,44 @@ var PlanStore = class {
2094
2101
  }
2095
2102
  return edge;
2096
2103
  }
2104
+ /**
2105
+ * Drop a proposal node and every edge touching it — the counterpart to
2106
+ * `adoptExistingChanges`, which adds a node for every change directory and
2107
+ * (until this existed) never removed one.
2108
+ *
2109
+ * Deleting a change without this leaves a node whose `specFile` points at a
2110
+ * directory that is gone: a dangling reference on the canvas, which is the
2111
+ * SAME failure as the stale roadmap row this change exists to fix. Observed
2112
+ * live — deleting a scratch change left its node behind in `specplan.yaml`.
2113
+ *
2114
+ * Returns false when no such node exists, which is not an error: a change can
2115
+ * be deleted before the plan ever adopted it.
2116
+ */
2117
+ removeProposal(nodeId) {
2118
+ const doc = this.loadDoc();
2119
+ const plan = this.planOf(doc);
2120
+ const idx = plan.nodes.findIndex((n) => n.id === nodeId);
2121
+ if (idx < 0) return false;
2122
+ const doomed = plan.edges.map((edge, i) => ({ edge, i })).filter(({ edge }) => edge.from === nodeId || edge.to === nodeId).map(({ i }) => i).reverse();
2123
+ for (const i of doomed) doc.deleteIn(["edges", i]);
2124
+ doc.deleteIn(["nodes", idx]);
2125
+ this.save(doc);
2126
+ const next = {
2127
+ ...plan,
2128
+ nodes: plan.nodes.filter((_, i) => i !== idx),
2129
+ edges: plan.edges.filter((e) => e.from !== nodeId && e.to !== nodeId)
2130
+ };
2131
+ for (const source of new Set(
2132
+ plan.edges.filter((e) => e.to === nodeId).map((e) => e.from)
2133
+ )) {
2134
+ if (next.nodes.find((n) => n.id === source)?.type !== "proposal") continue;
2135
+ try {
2136
+ this.writeSpecMeta(source, containedStories(next, source), specDependencies(next, source));
2137
+ } catch {
2138
+ }
2139
+ }
2140
+ return true;
2141
+ }
2097
2142
  /** Update one node's sibling `order` hint. Never touches edges. */
2098
2143
  reorder(nodeId, order) {
2099
2144
  const doc = this.loadDoc();
@@ -2570,6 +2615,265 @@ function registerPlanRoutes(app, store, workspace, opts = {}) {
2570
2615
  });
2571
2616
  }
2572
2617
 
2618
+ // server/app.ts
2619
+ import { dirname as dirname6, join as join11 } from "node:path";
2620
+
2621
+ // server/gallery.ts
2622
+ import { join as join10 } from "node:path";
2623
+ var SNIPPET_LIMIT = 180;
2624
+ var REQUIRED_PROPOSAL_SECTIONS = [
2625
+ { title: "Why", level: 2 },
2626
+ { title: "What Changes", level: 2 },
2627
+ { title: "System context", level: 3 },
2628
+ { title: "Containers", level: 3 },
2629
+ { title: "Components", level: 3 },
2630
+ { title: "Impact", level: 2 }
2631
+ ];
2632
+ function titleOf(proposal, id) {
2633
+ const heading = /^#[ \t]+(.+)$/m.exec(proposal ?? "");
2634
+ const text = heading?.[1]?.trim();
2635
+ return text === void 0 || text === "" ? id : text;
2636
+ }
2637
+ function snippetOf(proposal, limit = SNIPPET_LIMIT) {
2638
+ if (!proposal) return "";
2639
+ const why = /^##[ \t]+Why[ \t]*$/m.exec(proposal);
2640
+ if (!why) return "";
2641
+ const after = proposal.slice(why.index + why[0].length);
2642
+ const untilNextHeading = after.split(/^#{1,6}[ \t]/m)[0] ?? "";
2643
+ const paragraph = untilNextHeading.split(/\n\s*\n/).map((p) => p.trim()).find((p) => p !== "");
2644
+ if (paragraph === void 0) return "";
2645
+ const flat = paragraph.replace(/\s+/g, " ").trim();
2646
+ return truncateOnWord(flat, limit);
2647
+ }
2648
+ function truncateOnWord(text, limit) {
2649
+ if (text.length <= limit) return text;
2650
+ const clipped = text.slice(0, limit);
2651
+ const lastSpace = clipped.lastIndexOf(" ");
2652
+ return `${(lastSpace > 0 ? clipped.slice(0, lastSpace) : clipped).trimEnd()}\u2026`;
2653
+ }
2654
+ function taskCounts(tasks) {
2655
+ if (!tasks) return { done: 0, total: 0 };
2656
+ const items = tasks.match(/^[ \t]*-[ \t]+\[[ xX]\]/gm) ?? [];
2657
+ const done = items.filter((i) => /\[[xX]\]/.test(i)).length;
2658
+ return { done, total: items.length };
2659
+ }
2660
+ function proposalState(proposal) {
2661
+ if (!proposal || proposal.trim() === "") return "absent";
2662
+ let filled = 0;
2663
+ for (const section of REQUIRED_PROPOSAL_SECTIONS) {
2664
+ const hashes = "#".repeat(section.level);
2665
+ const heading = new RegExp(`^${hashes}[ \\t]+${section.title}[ \\t]*$`, "m").exec(proposal);
2666
+ if (!heading) continue;
2667
+ const after = proposal.slice(heading.index + heading[0].length);
2668
+ const body = (after.split(/^#{1,6}[ \t]/m)[0] ?? "").trim();
2669
+ if (body !== "") filled += 1;
2670
+ }
2671
+ if (filled === 0) return "absent";
2672
+ return filled === REQUIRED_PROPOSAL_SECTIONS.length ? "complete" : "partial";
2673
+ }
2674
+ function designState(design) {
2675
+ if (!design || design.trim() === "") return "absent";
2676
+ return /^##[ \t]+\S/m.test(design) ? "complete" : "absent";
2677
+ }
2678
+ var DATE_PREFIX = /^\d{4}-\d{2}-\d{2}-/;
2679
+ function archivedIds(deps) {
2680
+ const dir = join10(deps.root, "changes", "archive");
2681
+ if (!deps.fsx.exists(dir)) return /* @__PURE__ */ new Set();
2682
+ const ids = /* @__PURE__ */ new Set();
2683
+ for (const entry of deps.fsx.readdir(dir)) {
2684
+ if (!entry.directory || entry.name.startsWith(".")) continue;
2685
+ ids.add(entry.name.replace(DATE_PREFIX, ""));
2686
+ ids.add(entry.name);
2687
+ }
2688
+ return ids;
2689
+ }
2690
+ function listChanges(deps) {
2691
+ const changesDir = join10(deps.root, "changes");
2692
+ if (!deps.fsx.exists(changesDir)) return [];
2693
+ const cards = [];
2694
+ for (const entry of deps.fsx.readdir(changesDir).sort((a, b) => a.name.localeCompare(b.name))) {
2695
+ if (!entry.directory || entry.name === "archive" || entry.name.startsWith(".")) continue;
2696
+ cards.push(readChange(deps, changesDir, entry.name));
2697
+ }
2698
+ return cards;
2699
+ }
2700
+ function readChange(deps, changesDir, id) {
2701
+ const dir = join10(changesDir, id);
2702
+ const proposal = deps.fsx.readIfExists(join10(dir, "proposal.md"));
2703
+ const design = deps.fsx.readIfExists(join10(dir, "design.md"));
2704
+ const tasks = deps.fsx.readIfExists(join10(dir, "tasks.md"));
2705
+ const specsDir = join10(dir, "specs");
2706
+ const capabilities = deps.fsx.exists(specsDir) ? deps.fsx.readdir(specsDir).filter((e) => e.directory && deps.fsx.exists(join10(specsDir, e.name, "spec.md"))).map((e) => e.name).sort() : [];
2707
+ const counts = taskCounts(tasks);
2708
+ const states = {
2709
+ proposal: proposalState(proposal),
2710
+ design: designState(design),
2711
+ // Deltas have no middle state either.
2712
+ deltas: capabilities.length > 0 ? "complete" : "absent",
2713
+ tasks: counts.total === 0 ? "absent" : counts.done === counts.total ? "complete" : "partial"
2714
+ };
2715
+ return {
2716
+ id,
2717
+ title: titleOf(proposal, id),
2718
+ snippet: snippetOf(proposal),
2719
+ capabilities,
2720
+ ...states,
2721
+ tasksDone: counts.done,
2722
+ tasksTotal: counts.total,
2723
+ tasksOpen: counts.total - counts.done,
2724
+ // Derived from the four states rather than re-read, so "empty" cannot disagree with
2725
+ // the indicators sitting next to it on the same card.
2726
+ empty: Object.values(states).every((state) => state === "absent")
2727
+ };
2728
+ }
2729
+ function deleteChange(deps, id) {
2730
+ const name = id.trim();
2731
+ if (name === "") return { outcome: "refused", reason: "no change was named" };
2732
+ if (name === "archive") {
2733
+ return { outcome: "refused", reason: "the archive is not a change and cannot be deleted" };
2734
+ }
2735
+ if (name.startsWith(".") || /[/\\]/.test(name) || name.split(".").includes("..")) {
2736
+ return { outcome: "refused", reason: `\`${name}\` is not a change directory name` };
2737
+ }
2738
+ const dir = join10(deps.root, "changes", name);
2739
+ if (!deps.fsx.exists(dir)) {
2740
+ return { outcome: "refused", reason: `\`${name}\` has no directory under openspec/changes/` };
2741
+ }
2742
+ deps.fsx.removeDir(dir);
2743
+ return { outcome: "deleted" };
2744
+ }
2745
+ function readChangePreview(deps, id) {
2746
+ const dir = join10(deps.root, "changes", id);
2747
+ if (!deps.fsx.exists(dir)) return null;
2748
+ const documents = ["proposal.md", "design.md", "tasks.md"].map((path) => ({
2749
+ path,
2750
+ content: deps.fsx.readIfExists(join10(dir, path)) ?? null
2751
+ }));
2752
+ const specsDir = join10(dir, "specs");
2753
+ if (deps.fsx.exists(specsDir)) {
2754
+ for (const entry of deps.fsx.readdir(specsDir).sort((a, b) => a.name.localeCompare(b.name))) {
2755
+ if (!entry.directory) continue;
2756
+ const path = `specs/${entry.name}/spec.md`;
2757
+ const content = deps.fsx.readIfExists(join10(specsDir, entry.name, "spec.md"));
2758
+ if (content !== void 0) documents.push({ path, content });
2759
+ }
2760
+ }
2761
+ return {
2762
+ id,
2763
+ title: titleOf(deps.fsx.readIfExists(join10(dir, "proposal.md")), id),
2764
+ // The real id, never a placeholder: a command the reader has to edit is one they
2765
+ // will mistype.
2766
+ command: `specsketch ${id}`,
2767
+ documents
2768
+ };
2769
+ }
2770
+
2771
+ // server/roadmap.ts
2772
+ var ROW_ID = /^\|[^|]*\|\s*`([a-z0-9][a-z0-9-]*)`\s*\|/;
2773
+ function readOrder(roadmap) {
2774
+ const rows = [];
2775
+ roadmap.split("\n").forEach((text, line) => {
2776
+ const match = ROW_ID.exec(text);
2777
+ if (match?.[1]) rows.push({ id: match[1], line, text });
2778
+ });
2779
+ return { rows, order: rows.map((r) => r.id) };
2780
+ }
2781
+ function reorder(roadmap, id, beforeId) {
2782
+ const lines = roadmap.split("\n");
2783
+ const { rows } = readOrder(roadmap);
2784
+ const moving = rows.find((r) => r.id === id);
2785
+ if (!moving) return { outcome: "refused", reason: `\`${id}\` has no row in the roadmap` };
2786
+ if (beforeId !== null && !rows.some((r) => r.id === beforeId)) {
2787
+ return { outcome: "refused", reason: `\`${beforeId}\` has no row in the roadmap` };
2788
+ }
2789
+ if (id === beforeId) {
2790
+ return { outcome: "refused", reason: "a change cannot be moved before itself" };
2791
+ }
2792
+ const row = lines[moving.line];
2793
+ lines.splice(moving.line, 1);
2794
+ let insertAt;
2795
+ if (beforeId === null) {
2796
+ const remaining = readOrder(lines.join("\n")).rows;
2797
+ const last = remaining[remaining.length - 1];
2798
+ insertAt = last === void 0 ? lines.length : last.line + 1;
2799
+ } else {
2800
+ const target = readOrder(lines.join("\n")).rows.find((r) => r.id === beforeId);
2801
+ if (!target) return { outcome: "refused", reason: `\`${beforeId}\` vanished while reordering` };
2802
+ insertAt = target.line;
2803
+ }
2804
+ lines.splice(insertAt, 0, row);
2805
+ const content = lines.join("\n");
2806
+ const before = roadmap.split("\n");
2807
+ const withoutMoved = (all) => {
2808
+ const copy = [...all];
2809
+ const at = copy.indexOf(row);
2810
+ if (at >= 0) copy.splice(at, 1);
2811
+ return copy;
2812
+ };
2813
+ const kept = withoutMoved(before);
2814
+ const now = withoutMoved(lines);
2815
+ if (kept.length !== now.length || kept.some((line, i) => line !== now[i])) {
2816
+ return {
2817
+ outcome: "refused",
2818
+ reason: "the reorder would have changed a line it did not move"
2819
+ };
2820
+ }
2821
+ return { outcome: "reordered", content };
2822
+ }
2823
+ function cellsOf2(line) {
2824
+ return line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|");
2825
+ }
2826
+ function isSeparator(line) {
2827
+ return line.trim().startsWith("|") && cellsOf2(line).every((c) => /^\s*:?-+:?\s*$/.test(c));
2828
+ }
2829
+ function governingHeader(lines, index) {
2830
+ for (let i = Math.min(index, lines.length) - 1; i >= 1; i -= 1) {
2831
+ if (!isSeparator(lines[i])) continue;
2832
+ return (lines[i - 1] ?? "").trim().startsWith("|") ? i - 1 : null;
2833
+ }
2834
+ return null;
2835
+ }
2836
+ function addRow(roadmap, id) {
2837
+ const lines = roadmap.split("\n");
2838
+ const { rows } = readOrder(roadmap);
2839
+ if (rows.some((r) => r.id === id)) {
2840
+ return { outcome: "refused", reason: `\`${id}\` already has a roadmap row` };
2841
+ }
2842
+ const firstSeparator = lines.findIndex(isSeparator);
2843
+ const insertAt = rows.length > 0 ? rows[rows.length - 1].line + 1 : firstSeparator + 1;
2844
+ const headerAt = governingHeader(lines, insertAt);
2845
+ if (headerAt === null) {
2846
+ return { outcome: "refused", reason: "the roadmap carries no table to append a row to" };
2847
+ }
2848
+ const header = cellsOf2(lines[headerAt]).map((c) => c.trim());
2849
+ const rowsHere = rows.filter((r) => governingHeader(lines, r.line) === headerAt);
2850
+ const idColumn = rowsHere.length > 0 ? cellsOf2(rowsHere[0].text).findIndex((c) => /^\s*`[^`]+`\s*$/.test(c)) : header.findIndex((h) => /change[- ]?id|^id$/i.test(h));
2851
+ if (idColumn < 0) {
2852
+ return { outcome: "refused", reason: "the roadmap table declares no change-id column" };
2853
+ }
2854
+ const cells = header.map(() => "");
2855
+ cells[idColumn] = `\`${id}\``;
2856
+ if (/^#$/.test(header[0] ?? "") && idColumn !== 0) {
2857
+ const written = rows.map((r) => Number.parseInt((cellsOf2(r.text)[0] ?? "").trim(), 10)).filter((n) => Number.isFinite(n));
2858
+ cells[0] = String(written.length > 0 ? Math.max(...written) + 1 : rows.length + 1);
2859
+ }
2860
+ const row = `| ${cells.join(" | ")} |`;
2861
+ lines.splice(insertAt, 0, row);
2862
+ const content = lines.join("\n");
2863
+ const before = roadmap.split("\n");
2864
+ const after = lines.filter((_, i) => i !== insertAt);
2865
+ if (before.length !== after.length || before.some((line, i) => line !== after[i])) {
2866
+ return { outcome: "refused", reason: "the append would have changed a line it did not add" };
2867
+ }
2868
+ if (cellsOf2(row).length !== header.length) {
2869
+ return {
2870
+ outcome: "refused",
2871
+ reason: `the appended row would carry ${cellsOf2(row).length} cells in a ${header.length}-column table`
2872
+ };
2873
+ }
2874
+ return { outcome: "reordered", content };
2875
+ }
2876
+
2573
2877
  // server/tools.ts
2574
2878
  import { z as z4 } from "zod";
2575
2879
  var CreateSpecInput = z4.object({ title: z4.string().min(1) });
@@ -2774,6 +3078,68 @@ function createApp(anthropic, workspace, store, options) {
2774
3078
  const powerProfile = readMostlyProfile("specplan-assistant");
2775
3079
  const app = new Hono();
2776
3080
  app.get("/api/health", (c) => c.json({ ok: true }));
3081
+ if (store) {
3082
+ const roadmapPath = () => {
3083
+ const beside = join11(dirname6(store.dir), "roadmap.md");
3084
+ return store.fsx.exists(beside) ? beside : join11(store.dir, "roadmap.md");
3085
+ };
3086
+ app.get("/api/gallery", (c) => {
3087
+ const cards = listChanges({ fsx: store.fsx, root: store.dir });
3088
+ const roadmap = store.fsx.readIfExists(roadmapPath());
3089
+ const order = roadmap === void 0 ? [] : readOrder(roadmap).order;
3090
+ const rank = new Map(order.map((id, i) => [id, i]));
3091
+ const roadmapped = cards.filter((card) => rank.has(card.id)).sort((a, b) => rank.get(a.id) - rank.get(b.id));
3092
+ const backlog2 = cards.filter((card) => !rank.has(card.id));
3093
+ const present = new Set(cards.map((card) => card.id));
3094
+ const archive = archivedIds({ fsx: store.fsx, root: store.dir });
3095
+ const absent = order.filter((id) => !present.has(id));
3096
+ const archived = absent.filter((id) => archive.has(id));
3097
+ const missing = absent.filter((id) => !archive.has(id));
3098
+ return c.json({
3099
+ roadmap: roadmapped,
3100
+ backlog: backlog2,
3101
+ archived,
3102
+ missing,
3103
+ hasRoadmap: roadmap !== void 0
3104
+ });
3105
+ });
3106
+ app.get("/api/gallery/:id", (c) => {
3107
+ const preview = readChangePreview({ fsx: store.fsx, root: store.dir }, c.req.param("id"));
3108
+ if (preview === null) return c.json({ error: "no-such-change" }, 404);
3109
+ return c.json(preview);
3110
+ });
3111
+ app.post("/api/gallery/roadmap", async (c) => {
3112
+ const body = await c.req.json();
3113
+ const path = roadmapPath();
3114
+ const roadmap = store.fsx.readIfExists(path);
3115
+ if (roadmap === void 0) {
3116
+ return c.json({ outcome: "refused", reason: "no roadmap.md to add to" }, 409);
3117
+ }
3118
+ const result = addRow(roadmap, body.id ?? "");
3119
+ if (result.outcome !== "reordered") return c.json(result, 409);
3120
+ store.fsx.writeAtomicFile(path, result.content);
3121
+ return c.json({ outcome: "added" });
3122
+ });
3123
+ app.delete("/api/gallery/:id", (c) => {
3124
+ const id = c.req.param("id");
3125
+ const result = deleteChange({ fsx: store.fsx, root: store.dir }, id);
3126
+ if (result.outcome !== "deleted") return c.json(result, 409);
3127
+ store.removeProposal(id);
3128
+ return c.json(result);
3129
+ });
3130
+ app.post("/api/gallery/reorder", async (c) => {
3131
+ const body = await c.req.json();
3132
+ const path = roadmapPath();
3133
+ const roadmap = store.fsx.readIfExists(path);
3134
+ if (roadmap === void 0) {
3135
+ return c.json({ outcome: "refused", reason: "no roadmap.md to reorder" }, 409);
3136
+ }
3137
+ const result = reorder(roadmap, body.id ?? "", body.before ?? null);
3138
+ if (result.outcome !== "reordered") return c.json(result, 409);
3139
+ store.fsx.writeAtomicFile(path, result.content);
3140
+ return c.json({ outcome: "reordered" });
3141
+ });
3142
+ }
2777
3143
  app.get(
2778
3144
  "/api/config",
2779
3145
  (c) => c.json({
@@ -2997,7 +3363,7 @@ import { parseArgs } from "node:util";
2997
3363
  // ../shared/src/cli-config.ts
2998
3364
  import { readFile, writeFile, mkdir, chmod } from "node:fs/promises";
2999
3365
  import { homedir as homedir2, hostname } from "node:os";
3000
- import { join as join10, dirname as dirname6 } from "node:path";
3366
+ import { join as join12, dirname as dirname7 } from "node:path";
3001
3367
 
3002
3368
  // ../shared/src/schemas.ts
3003
3369
  import { z as z5 } from "zod";
@@ -3196,8 +3562,8 @@ var poolCancelInputSchema = z5.object({
3196
3562
  });
3197
3563
 
3198
3564
  // ../shared/src/cli-config.ts
3199
- var DEFAULT_CONFIG_DIR = join10(homedir2(), ".coopcli");
3200
- var DEFAULT_CONFIG_PATH = join10(DEFAULT_CONFIG_DIR, "config.json");
3565
+ var DEFAULT_CONFIG_DIR = join12(homedir2(), ".coopcli");
3566
+ var DEFAULT_CONFIG_PATH = join12(DEFAULT_CONFIG_DIR, "config.json");
3201
3567
  var PROD_API_URL = "https://api.coopcli.com";
3202
3568
  function emptyProfile(apiUrl) {
3203
3569
  return {
@@ -3243,7 +3609,7 @@ var ConfigStore = class {
3243
3609
  configDir;
3244
3610
  constructor(configPath) {
3245
3611
  this.configPath = configPath ?? DEFAULT_CONFIG_PATH;
3246
- this.configDir = dirname6(this.configPath);
3612
+ this.configDir = dirname7(this.configPath);
3247
3613
  }
3248
3614
  /**
3249
3615
  * Select the profile this store operates on for the rest of the process
@@ -3620,14 +3986,15 @@ function deriveApiUrl(webUrl) {
3620
3986
  async function login(input, deps) {
3621
3987
  const state = randomBytes(16).toString("hex");
3622
3988
  const machineId = input.machine ?? hostname2();
3989
+ const log = deps.log ?? ((message) => void process.stderr.write(message));
3623
3990
  const server = await startLocalhostServer({ expectedState: state, loginCommand: input.loginCommand });
3624
3991
  try {
3625
3992
  const webUrl = input.webUrl ?? "https://coopcli.com";
3626
3993
  const authUrl = `${webUrl}/cli-auth?port=${server.port}&state=${state}&machine=${encodeURIComponent(machineId)}`;
3627
- process.stderr.write(`Opening browser for authentication...
3994
+ log(`Opening browser for authentication...
3628
3995
  `);
3629
3996
  await deps.openBrowser(authUrl);
3630
- process.stderr.write(`Waiting for callback on port ${server.port}...
3997
+ log(`Waiting for callback on port ${server.port}...
3631
3998
  `);
3632
3999
  const params = await server.waitForCallback();
3633
4000
  const displayName = resolveDisplayName(params.displayName, params.email, params.userId);
@@ -3644,7 +4011,7 @@ async function login(input, deps) {
3644
4011
  });
3645
4012
  const config = await deps.config.load();
3646
4013
  if (params.apiUrl && config.apiUrl && params.apiUrl !== config.apiUrl) {
3647
- process.stderr.write(
4014
+ log(
3648
4015
  `
3649
4016
  Backend mismatch: you authenticated against ${params.apiUrl}
3650
4017
  but your config points to ${config.apiUrl}
@@ -3762,7 +4129,7 @@ function validateCommand(rootDir, log = console.log, logError = console.error) {
3762
4129
  }
3763
4130
 
3764
4131
  // server/workspace.ts
3765
- import { join as join11, resolve as resolve5 } from "node:path";
4132
+ import { join as join13, resolve as resolve5 } from "node:path";
3766
4133
  var SIDECAR_FILE = "specplan-ui.json";
3767
4134
  var PlanWorkspace = class {
3768
4135
  /** Resolved openspec root directory. */
@@ -3776,7 +4143,7 @@ var PlanWorkspace = class {
3776
4143
  }
3777
4144
  // ── Sidecar ──────────────────────────────────────────────────────────────
3778
4145
  readSidecar() {
3779
- const raw = this.fsx.readIfExists(join11(this.dir, SIDECAR_FILE));
4146
+ const raw = this.fsx.readIfExists(join13(this.dir, SIDECAR_FILE));
3780
4147
  if (!raw) return {};
3781
4148
  try {
3782
4149
  return JSON.parse(raw);
@@ -3791,7 +4158,7 @@ var PlanWorkspace = class {
3791
4158
  delete next.positions;
3792
4159
  next.createdAt ??= now;
3793
4160
  next.updatedAt = now;
3794
- this.fsx.writeAtomicFile(join11(this.dir, SIDECAR_FILE), JSON.stringify(next, null, 2));
4161
+ this.fsx.writeAtomicFile(join13(this.dir, SIDECAR_FILE), JSON.stringify(next, null, 2));
3795
4162
  }
3796
4163
  readModel() {
3797
4164
  return this.readSidecar().model;
@@ -3839,7 +4206,9 @@ var PlanWorkspace = class {
3839
4206
  };
3840
4207
 
3841
4208
  // server/cli.ts
4209
+ var CLI_VERSION = true ? "2026.08.17.2" : "dev";
3842
4210
  var USAGE = `Usage: specplan [rootDir] [--port <n>]
4211
+ specplan --version
3843
4212
  specplan generate <specId> [rootDir]
3844
4213
  specplan apply <proposalId> [rootDir] [--confirm-hand-edit]
3845
4214
  specplan validate [rootDir]
@@ -3974,15 +4343,15 @@ var MIME = {
3974
4343
  ".woff": "font/woff"
3975
4344
  };
3976
4345
  function resolveClientDir(moduleUrl) {
3977
- const here = dirname7(fileURLToPath(moduleUrl));
4346
+ const here = dirname8(fileURLToPath(moduleUrl));
3978
4347
  const candidates = [
3979
- join12(here, "..", "client"),
4348
+ join14(here, "..", "client"),
3980
4349
  // packed: dist/cli -> dist/client
3981
- join12(here, "..", "dist", "client")
4350
+ join14(here, "..", "dist", "client")
3982
4351
  // repo: server/ -> dist/client
3983
4352
  ];
3984
4353
  for (const dir of candidates) {
3985
- if (existsSync7(join12(dir, "index.html"))) return dir;
4354
+ if (existsSync7(join14(dir, "index.html"))) return dir;
3986
4355
  }
3987
4356
  return null;
3988
4357
  }
@@ -3990,7 +4359,7 @@ function staticResponse(clientDir, pathname) {
3990
4359
  const rel = normalize2(decodeURIComponent(pathname)).replace(/^\/+/, "");
3991
4360
  const target = resolve6(clientDir, rel === "" ? "index.html" : rel);
3992
4361
  if (!target.startsWith(resolve6(clientDir))) return null;
3993
- const file = existsSync7(target) && extname(target) ? target : join12(clientDir, "index.html");
4362
+ const file = existsSync7(target) && extname(target) ? target : join14(clientDir, "index.html");
3994
4363
  if (!existsSync7(file)) return null;
3995
4364
  return new Response(readFileSync5(file), {
3996
4365
  headers: { "Content-Type": MIME[extname(file)] ?? "application/octet-stream" }
@@ -4008,7 +4377,7 @@ function detectCredentialSource(env = process.env, home = homedir3()) {
4008
4377
  if (env.ANTHROPIC_API_KEY) return "ANTHROPIC_API_KEY";
4009
4378
  if (env.ANTHROPIC_AUTH_TOKEN) return "ANTHROPIC_AUTH_TOKEN";
4010
4379
  try {
4011
- const dir = join12(home, ".config", "anthropic", "credentials");
4380
+ const dir = join14(home, ".config", "anthropic", "credentials");
4012
4381
  if (readdirSync5(dir).some((f) => f.endsWith(".json"))) return "anthropic profile";
4013
4382
  } catch {
4014
4383
  }
@@ -4023,6 +4392,10 @@ function preflight(log = console.log) {
4023
4392
  }
4024
4393
  }
4025
4394
  async function main(argv) {
4395
+ if (argv[0] === "--version") {
4396
+ console.log(CLI_VERSION);
4397
+ return;
4398
+ }
4026
4399
  preflight();
4027
4400
  if (argv[0] === "login") {
4028
4401
  let loginArgs;
@@ -1 +1 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{t}from"./src-_g_WvJI1.js";import"./chunk-WYO6CB5R-DWSMU6gp.js";import"./chunk-VAUOI2AC-D14Dfnyc.js";import{m as n,t as r}from"./mermaid-parser.core-OvIPAtjO.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CHtb7Cmu.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=n().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(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=e(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=e(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=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{o.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new r(n);let i=n.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
1
+ import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{t}from"./src-B56W74gN.js";import"./chunk-WYO6CB5R-qHfws5Na.js";import"./chunk-VAUOI2AC-D7taetBr.js";import{m as n,t as r}from"./mermaid-parser.core-CWtD7LBQ.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-Bio7zOcN.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=n().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(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=e(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=e(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=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{o.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new r(n);let i=n.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
@@ -0,0 +1 @@
1
+ import"./chunk-KEIR6QF5-BfrZ3jm6.js";import{M as e}from"./mermaid-parser.core-CWtD7LBQ.js";export{e as createArchitectureServices};