@oevery/rsp 4.0.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/chunk-AUZYUJ46.mjs +418 -0
  2. package/dist/chunk-DNRGBQS7.mjs +108 -0
  3. package/dist/chunk-LGDCIQYL.mjs +725 -0
  4. package/dist/chunk-P3KO3GZW.mjs +1023 -0
  5. package/dist/{chunk-HFSTLCCK.mjs → chunk-VDRXFKBP.mjs} +133 -1800
  6. package/dist/cli.mjs +286 -294
  7. package/dist/client-U2PN3VS4.mjs +108 -0
  8. package/dist/{entry-RIJQ2X46.mjs → entry-NDASTAI5.mjs} +13 -9
  9. package/dist/viewer/NOTICE.txt +299 -0
  10. package/dist/viewer/app.css +2 -0
  11. package/dist/viewer/app.js +670 -0
  12. package/dist/viewer/chunks/abnfDiagram-S7VZAUFO-K5EOZOEF.js +1 -0
  13. package/dist/viewer/chunks/architecture-WOLXFQ4H-UAT5D5BF.js +1 -0
  14. package/dist/viewer/chunks/architectureDiagram-YGSQEFNW-UNPSYM2L.js +36 -0
  15. package/dist/viewer/chunks/blockDiagram-APKKGZFW-2F7TTPAV.js +140 -0
  16. package/dist/viewer/chunks/bpmnIcons-APHXDYXF-ZWPFFN3O.js +1 -0
  17. package/dist/viewer/chunks/c4Diagram-KJ2TOQU5-KNLN6CME.js +38 -0
  18. package/dist/viewer/chunks/chunk-274PZN6B.js +1 -0
  19. package/dist/viewer/chunks/chunk-2VGDQLQD.js +1 -0
  20. package/dist/viewer/chunks/chunk-33XESOCS.js +1 -0
  21. package/dist/viewer/chunks/chunk-3ZSL2TGQ.js +1 -0
  22. package/dist/viewer/chunks/chunk-432I4U3B.js +1 -0
  23. package/dist/viewer/chunks/chunk-4PJNGS3V.js +1 -0
  24. package/dist/viewer/chunks/chunk-54NLSLTF.js +9 -0
  25. package/dist/viewer/chunks/chunk-6QRHYJ5Q.js +1 -0
  26. package/dist/viewer/chunks/chunk-6RCFKBVR.js +1 -0
  27. package/dist/viewer/chunks/chunk-6Y2HP7DC.js +213 -0
  28. package/dist/viewer/chunks/chunk-7MAT7NHP.js +1 -0
  29. package/dist/viewer/chunks/chunk-AKHBD6LT.js +2 -0
  30. package/dist/viewer/chunks/chunk-CZIM56BV.js +1 -0
  31. package/dist/viewer/chunks/chunk-DN4Y7LXJ.js +1 -0
  32. package/dist/viewer/chunks/chunk-E2UQN267.js +1 -0
  33. package/dist/viewer/chunks/chunk-GBARLDDT.js +1 -0
  34. package/dist/viewer/chunks/chunk-H4YEV7VR.js +1 -0
  35. package/dist/viewer/chunks/chunk-HUVVDQHZ.js +1 -0
  36. package/dist/viewer/chunks/chunk-IAKC32WV.js +72 -0
  37. package/dist/viewer/chunks/chunk-JTW4SY4L.js +1 -0
  38. package/dist/viewer/chunks/chunk-JZV675LF.js +1 -0
  39. package/dist/viewer/chunks/chunk-KDUDYTWO.js +1 -0
  40. package/dist/viewer/chunks/chunk-KHX3A5RZ.js +331 -0
  41. package/dist/viewer/chunks/chunk-KPXJJSUR.js +15 -0
  42. package/dist/viewer/chunks/chunk-LEGNM6PH.js +1 -0
  43. package/dist/viewer/chunks/chunk-LYY4ZY72.js +1 -0
  44. package/dist/viewer/chunks/chunk-MKDEK2LU.js +1 -0
  45. package/dist/viewer/chunks/chunk-MYJRFART.js +129 -0
  46. package/dist/viewer/chunks/chunk-NUXZBURC.js +1 -0
  47. package/dist/viewer/chunks/chunk-NXHT5I2L.js +1 -0
  48. package/dist/viewer/chunks/chunk-OF62XRVM.js +62 -0
  49. package/dist/viewer/chunks/chunk-ONCOEVBS.js +1 -0
  50. package/dist/viewer/chunks/chunk-P44SH3OO.js +1 -0
  51. package/dist/viewer/chunks/chunk-PDXKDBIX.js +1 -0
  52. package/dist/viewer/chunks/chunk-PHKIAKHS.js +1 -0
  53. package/dist/viewer/chunks/chunk-PJDMAGPE.js +2 -0
  54. package/dist/viewer/chunks/chunk-RC2NPHSX.js +88 -0
  55. package/dist/viewer/chunks/chunk-T6DM5RSK.js +10 -0
  56. package/dist/viewer/chunks/chunk-U5STX66M.js +2 -0
  57. package/dist/viewer/chunks/chunk-UIAJQPOF.js +1 -0
  58. package/dist/viewer/chunks/chunk-UX2S3LP3.js +1 -0
  59. package/dist/viewer/chunks/chunk-VEMTQQ7Y.js +2 -0
  60. package/dist/viewer/chunks/chunk-WKCPBR3C.js +1 -0
  61. package/dist/viewer/chunks/chunk-WMQ4Y2MS.js +32 -0
  62. package/dist/viewer/chunks/chunk-YPWIQAHX.js +3 -0
  63. package/dist/viewer/chunks/chunk-Z4ZDIMO2.js +136 -0
  64. package/dist/viewer/chunks/chunk-ZATQCNXH.js +1 -0
  65. package/dist/viewer/chunks/chunk-ZE5RDJX6.js +1 -0
  66. package/dist/viewer/chunks/chunk-ZO62R4L2.js +1 -0
  67. package/dist/viewer/chunks/classDiagram-v2-L25IHNXF-5F2M2CZ4.js +217 -0
  68. package/dist/viewer/chunks/cose-bilkent-JH36ORCC-S56HJ5WN.js +1 -0
  69. package/dist/viewer/chunks/cynefin-EF2NZ3EQ-6WY2NCLE.js +1 -0
  70. package/dist/viewer/chunks/cynefinDiagram-L36ESFBY-DE6PZNQA.js +62 -0
  71. package/dist/viewer/chunks/dagre-SPEB5RXH-QEOTKFV2.js +4 -0
  72. package/dist/viewer/chunks/diagram-3B7GL73J-RZPIU7FY.js +200 -0
  73. package/dist/viewer/chunks/diagram-DNEMPZML-YD26TJKC.js +24 -0
  74. package/dist/viewer/chunks/diagram-L3MUGCDN-TJUEX4QI.js +41 -0
  75. package/dist/viewer/chunks/diagram-LA4NIPWU-3PUQS6UW.js +24 -0
  76. package/dist/viewer/chunks/diagram-MHCOC4E2-YBTBD4PG.js +3 -0
  77. package/dist/viewer/chunks/diagram-XPFHEONP-VF2TQBNR.js +30 -0
  78. package/dist/viewer/chunks/ebnfDiagram-UZSYDL2S-QE7DIEME.js +1 -0
  79. package/dist/viewer/chunks/elk-XV6XMXQE-X5OZRIL3.js +27 -0
  80. package/dist/viewer/chunks/erDiagram-EYBAPDEH-5B2S7VB4.js +99 -0
  81. package/dist/viewer/chunks/eventmodeling-K75KTNOO-CHCQYZBI.js +1 -0
  82. package/dist/viewer/chunks/flowDiagram-MALGAFMO-XJFC5NTN.js +1 -0
  83. package/dist/viewer/chunks/ganttDiagram-23REXITY-TUUZ3QYO.js +292 -0
  84. package/dist/viewer/chunks/gitGraph-VSP46ZUC-VFZPJQ6A.js +1 -0
  85. package/dist/viewer/chunks/gitGraphDiagram-STJD7YCW-4CVV5LSH.js +106 -0
  86. package/dist/viewer/chunks/info-OHQRW6UA-P4744SJ6.js +1 -0
  87. package/dist/viewer/chunks/infoDiagram-KBXO4MBG-Q2WYYNNN.js +2 -0
  88. package/dist/viewer/chunks/ishikawaDiagram-M35NCXHK-PLQPTX2U.js +70 -0
  89. package/dist/viewer/chunks/journeyDiagram-OPLOXDXO-FB73RE6F.js +139 -0
  90. package/dist/viewer/chunks/kanban-definition-BZKZHSQY-6FU4TXYJ.js +89 -0
  91. package/dist/viewer/chunks/katex-IN4S3RHS.js +257 -0
  92. package/dist/viewer/chunks/mindmap-definition-I2QAS2VK-ZQ36ABVW.js +98 -0
  93. package/dist/viewer/chunks/packet-JDAUHWVQ-NYXIQQVA.js +1 -0
  94. package/dist/viewer/chunks/pegDiagram-FD4XPPHP-5XXVT6XN.js +1 -0
  95. package/dist/viewer/chunks/pie-XZMESJXO-32CUE6KZ.js +1 -0
  96. package/dist/viewer/chunks/pieDiagram-J4AKIIQI-WRTBDFM5.js +39 -0
  97. package/dist/viewer/chunks/quadrantDiagram-XTYBLID2-YPBDQIAK.js +7 -0
  98. package/dist/viewer/chunks/radar-ABXABTNO-272FILRO.js +1 -0
  99. package/dist/viewer/chunks/railroad-I3PHUGI6-6EGAH4XA.js +1 -0
  100. package/dist/viewer/chunks/railroad-abnf-LNEFI6M7-G6E7ATER.js +1 -0
  101. package/dist/viewer/chunks/railroad-ebnf-DWYD2UWJ-MR4P6BDR.js +1 -0
  102. package/dist/viewer/chunks/railroad-peg-JR7BVNR7-WXLQUGUO.js +1 -0
  103. package/dist/viewer/chunks/railroadDiagram-MRQ6NCDL-XHZQ2BLY.js +1 -0
  104. package/dist/viewer/chunks/requirementDiagram-Y7LWUY3S-W5FHRRML.js +84 -0
  105. package/dist/viewer/chunks/sankeyDiagram-WGJFYUXA-OJJIRVTI.js +40 -0
  106. package/dist/viewer/chunks/sequenceDiagram-5NDRBG5M-D6T4DRNG.js +169 -0
  107. package/dist/viewer/chunks/sizeCapture-INFHLROL-WL3FTUJX.js +1 -0
  108. package/dist/viewer/chunks/stateDiagram-v2-FJ4FJNEA-LAXL7KGG.js +291 -0
  109. package/dist/viewer/chunks/swimlanes-UMQ2AFYF-XIJBD726.js +1 -0
  110. package/dist/viewer/chunks/swimlanesDiagram-OMPLPG4O-7O7MHFW7.js +8 -0
  111. package/dist/viewer/chunks/timeline-definition-SEUHKTQE-BAZQA47I.js +120 -0
  112. package/dist/viewer/chunks/treeView-D4JQ5SDB-6YMDAAPA.js +1 -0
  113. package/dist/viewer/chunks/treemap-SAJKECNS-2X573PPS.js +1 -0
  114. package/dist/viewer/chunks/usecaseDiagram-AFA6YXN4-4YCEMWJB.js +370 -0
  115. package/dist/viewer/chunks/vennDiagram-NNMJR3RB-GEMROLAE.js +34 -0
  116. package/dist/viewer/chunks/wardley-7MLQ67FV-DGTGHYYC.js +1 -0
  117. package/dist/viewer/chunks/wardleyDiagram-S7YFUSEH-K4HVD3SS.js +78 -0
  118. package/dist/viewer/chunks/xychartDiagram-GGNKY7IX-HVLWCUEO.js +7 -0
  119. package/dist/viewer/mermaid.js +12 -0
  120. package/dist/viewer-build.json +1 -0
  121. package/dist/viewer-service.mjs +607 -0
  122. package/package.json +27 -8
  123. package/skills/rsp/SKILL.md +13 -10
  124. package/skills/rsp/references/architecture.md +39 -0
  125. package/skills/rsp/references/closeout.md +4 -4
  126. package/skills/rsp/references/durable-review.md +6 -9
  127. package/skills/rsp/references/focus-continuation.md +6 -2
  128. package/skills/rsp/references/setup-repair.md +11 -6
  129. package/skills/rsp/references/work-model.md +14 -4
  130. package/skills/rsp-commit/SKILL.md +2 -2
  131. package/skills/rsp-doc/SKILL.md +13 -10
  132. package/skills/rsp-doc/references/skill-authoring.md +2 -2
  133. package/skills/rsp-implement/SKILL.md +16 -12
  134. package/skills/rsp-review/SKILL.md +12 -12
  135. package/skills/rsp-review/references/code-review.md +11 -5
  136. package/skills/rsp-review/references/document-review.md +8 -10
  137. package/skills/rsp-shape/SKILL.md +3 -1
  138. package/skills/rsp-shape/references/planning.md +4 -4
  139. package/skills/rsp-verify/SKILL.md +9 -14
  140. package/skills/rsp-doc/references/artifact-lenses.md +0 -43
  141. package/skills/rsp-review/references/structural-review.md +0 -41
@@ -0,0 +1,418 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ readCurrentDocument
4
+ } from "./chunk-LGDCIQYL.mjs";
5
+ import {
6
+ PKG_ROOT,
7
+ RSP_DIR,
8
+ getVersion,
9
+ inspectManagedDirectory,
10
+ inspectManagedFile,
11
+ inspectRspConfig,
12
+ markdownText,
13
+ parseMarkdown,
14
+ walkMarkdown
15
+ } from "./chunk-P3KO3GZW.mjs";
16
+
17
+ // src/architecture/model.ts
18
+ import { createHash } from "crypto";
19
+ import { realpath } from "fs/promises";
20
+ import { isAbsolute, relative, resolve } from "path";
21
+
22
+ // src/architecture/selection.ts
23
+ function selectArchitecture(model, selection) {
24
+ const view = selection.kind === "view" ? model.views.find((view2) => view2.id === selection.id) : void 0;
25
+ const selected = new Set(model.nodes.map((node) => node.id));
26
+ if (selection.kind === "focus") {
27
+ if (!selected.has(selection.id))
28
+ throw new Error(`Unknown architecture node: ${selection.id}`);
29
+ selected.clear();
30
+ selected.add(selection.id);
31
+ }
32
+ if (selection.kind === "node") {
33
+ if (!selected.has(selection.id))
34
+ throw new Error(`Unknown architecture node: ${selection.id}`);
35
+ selected.clear();
36
+ selected.add(selection.id);
37
+ let added = true;
38
+ while (added) {
39
+ added = false;
40
+ for (const node of model.nodes) {
41
+ if (node.parent && selected.has(node.parent) && !selected.has(node.id)) {
42
+ selected.add(node.id);
43
+ added = true;
44
+ }
45
+ }
46
+ }
47
+ }
48
+ if (selection.kind === "view") {
49
+ if (!view)
50
+ throw new Error(`Unknown architecture view: ${selection.id}`);
51
+ selected.clear();
52
+ view.include.forEach((id) => selected.add(id));
53
+ }
54
+ const relations = view?.scenario ? [] : model.relations.filter(
55
+ (edge) => (selected.has(edge.source) || selected.has(edge.target)) && (view?.boundary !== "exclude" || selected.has(edge.source) && selected.has(edge.target)) && (!view?.relationTypes || view.relationTypes.includes(edge.type)) && (!view?.relationIds || view.relationIds.includes(edge.id))
56
+ );
57
+ const visible = /* @__PURE__ */ new Set([...selected, ...relations.flatMap((edge) => [edge.source, edge.target])]);
58
+ return { selected, visible, relations };
59
+ }
60
+
61
+ // src/architecture/model.ts
62
+ var ARCHITECTURE_FILE = ".rsp/architecture.json";
63
+ var MAX_CONTENT_BYTES = 16 * 1024 * 1024;
64
+ var digest = (value) => createHash("sha256").update(value).digest("hex");
65
+ function projectPath(file) {
66
+ if (!file || isAbsolute(file) || file.includes("\\") || file.split("/").some((part) => !part || part === "." || part === "..") || file.includes("\0"))
67
+ throw new Error(`Unsafe project-relative path: ${file}`);
68
+ if (file.split("/").some((part) => part === ".git" || part === "node_modules"))
69
+ throw new Error(`Private/runtime path is not readable: ${file}`);
70
+ return file;
71
+ }
72
+ async function readProjectFile(root, file) {
73
+ projectPath(file);
74
+ return (await readCurrentDocument(resolve(root, file), root, file, MAX_CONTENT_BYTES)).content;
75
+ }
76
+ function object(value) {
77
+ if (!value || typeof value !== "object" || Array.isArray(value))
78
+ throw new Error("Architecture entries must be objects");
79
+ return value;
80
+ }
81
+ function text(value, field) {
82
+ if (typeof value !== "string" || !value.trim())
83
+ throw new Error(`Architecture ${field} must be a non-empty string`);
84
+ return value;
85
+ }
86
+ function strings(value, field) {
87
+ if (!Array.isArray(value))
88
+ throw new Error(`Architecture ${field} must be an array`);
89
+ return value.map((item) => text(item, field));
90
+ }
91
+ function collection(value, field) {
92
+ if (!Array.isArray(value))
93
+ throw new Error(`Architecture ${field} must be an array`);
94
+ return value.map(object);
95
+ }
96
+ function unique(items, field) {
97
+ if (new Set(items.map((item) => item.id)).size !== items.length)
98
+ throw new Error(`Duplicate architecture ${field} identity`);
99
+ }
100
+ function parseArchitecture(content) {
101
+ const value = object(JSON.parse(content));
102
+ if (value.schemaVersion !== 1)
103
+ throw new Error(`Unsupported architecture schemaVersion: ${String(value.schemaVersion)}`);
104
+ const nodes = collection(value.nodes, "nodes").map((node) => ({ id: text(node.id, "node.id"), title: text(node.title, "node.title"), description: text(node.description, "node.description"), ...node.parent === void 0 ? {} : { parent: text(node.parent, "node.parent") } }));
105
+ const relations = collection(value.relations, "relations").map((edge) => ({ id: text(edge.id, "relation.id"), source: text(edge.source, "relation.source"), target: text(edge.target, "relation.target"), type: text(edge.type, "relation.type"), ...edge.label === void 0 ? {} : { label: text(edge.label, "relation.label") }, description: text(edge.description, "relation.description") }));
106
+ const views = collection(value.views, "views").map((view) => {
107
+ if (view.boundary !== void 0 && view.boundary !== "include" && view.boundary !== "exclude")
108
+ throw new Error("Architecture view.boundary must be include or exclude");
109
+ const result = {
110
+ id: text(view.id, "view.id"),
111
+ title: text(view.title, "view.title"),
112
+ node: text(view.node, "view.node"),
113
+ include: view.scenario === void 0 ? strings(view.include, "view.include") : [],
114
+ ...view.relationTypes === void 0 ? {} : { relationTypes: strings(view.relationTypes, "view.relationTypes") },
115
+ ...view.relationIds === void 0 ? {} : { relationIds: strings(view.relationIds, "view.relationIds") },
116
+ ...view.boundary === void 0 ? {} : { boundary: view.boundary }
117
+ };
118
+ if (view.scenario !== void 0) {
119
+ if (view.include !== void 0 || view.relationTypes !== void 0 || view.relationIds !== void 0 || view.boundary !== void 0)
120
+ throw new Error("Scenario views use participants instead of structural selectors");
121
+ const scenario = object(view.scenario);
122
+ const participants = collection(scenario.participants, "scenario.participants").map((participant) => ({
123
+ id: text(participant.id, "participant.id"),
124
+ node: text(participant.node, "participant.node"),
125
+ ...participant.title === void 0 ? {} : { title: text(participant.title, "participant.title") }
126
+ }));
127
+ const steps = collection(scenario.steps, "scenario.steps").map((step) => {
128
+ if (!["call", "return", "event"].includes(String(step.type)))
129
+ throw new Error("Scenario step.type must be call, return or event");
130
+ return {
131
+ id: text(step.id, "step.id"),
132
+ source: text(step.source, "step.source"),
133
+ target: text(step.target, "step.target"),
134
+ type: step.type,
135
+ label: text(step.label, "step.label"),
136
+ ...step.description === void 0 ? {} : { description: text(step.description, "step.description") },
137
+ ...step.replyTo === void 0 ? {} : { replyTo: text(step.replyTo, "step.replyTo") }
138
+ };
139
+ });
140
+ unique(participants, "participant");
141
+ unique(steps, "step");
142
+ if (!participants.length || !steps.length)
143
+ throw new Error("Scenarios require participants and steps");
144
+ const participantIds = new Set(participants.map((participant) => participant.id));
145
+ steps.forEach((step, index) => {
146
+ if (!participantIds.has(step.source) || !participantIds.has(step.target))
147
+ throw new Error(`Missing scenario participant in step: ${step.id}`);
148
+ if (step.replyTo !== void 0) {
149
+ const call = steps.slice(0, index).find((candidate) => candidate.id === step.replyTo);
150
+ if (step.type !== "return" || call?.type !== "call" || call.source !== step.target || call.target !== step.source)
151
+ throw new Error(`Invalid earlier call reference in step: ${step.id}`);
152
+ }
153
+ });
154
+ result.scenario = { participants, steps };
155
+ result.include = [...new Set(participants.map((participant) => participant.node))];
156
+ }
157
+ return result;
158
+ });
159
+ const bindings = collection(value.bindings, "bindings").map((binding) => {
160
+ const file = projectPath(text(binding.file, "binding.file"));
161
+ if (!/\.md$/i.test(file))
162
+ throw new Error("Architecture bindings must reference Markdown files");
163
+ const scope = binding.scope ?? "node";
164
+ if (scope !== "node" && scope !== "subtree")
165
+ throw new Error("Architecture binding.scope must be node or subtree");
166
+ const headings = binding.headings === void 0 ? void 0 : strings(binding.headings, "binding.headings");
167
+ if (headings?.length === 0)
168
+ throw new Error("Omit headings for a whole-file binding");
169
+ return { node: text(binding.node, "binding.node"), file, ...headings ? { headings } : {}, scope };
170
+ });
171
+ unique(nodes, "node");
172
+ unique(relations, "relation");
173
+ unique(views, "view");
174
+ const ids = new Set(nodes.map((node) => node.id));
175
+ const requireNode = (id) => {
176
+ if (!ids.has(id))
177
+ throw new Error(`Missing architecture node: ${id}`);
178
+ };
179
+ for (const node of nodes) {
180
+ const visited = /* @__PURE__ */ new Set([node.id]);
181
+ let parent = node.parent;
182
+ while (parent !== void 0) {
183
+ requireNode(parent);
184
+ if (visited.has(parent))
185
+ throw new Error(`Architecture hierarchy cycle: ${parent}`);
186
+ visited.add(parent);
187
+ parent = nodes.find((node2) => node2.id === parent).parent;
188
+ }
189
+ }
190
+ relations.forEach((edge) => {
191
+ requireNode(edge.source);
192
+ requireNode(edge.target);
193
+ });
194
+ views.forEach((view) => {
195
+ requireNode(view.node);
196
+ view.include.forEach(requireNode);
197
+ view.relationIds?.forEach((id) => {
198
+ if (!relations.some((edge) => edge.id === id))
199
+ throw new Error(`Missing architecture relation: ${id}`);
200
+ });
201
+ });
202
+ bindings.forEach((binding) => requireNode(binding.node));
203
+ return { schemaVersion: 1, nodes, relations, views, bindings };
204
+ }
205
+ function documentHeadings(content) {
206
+ const result = [];
207
+ const stack = [];
208
+ const counts = /* @__PURE__ */ new Map();
209
+ const anchors = /* @__PURE__ */ new Set();
210
+ walkMarkdown(parseMarkdown(content).root, (node) => {
211
+ if (node.type !== "heading")
212
+ return;
213
+ const title = markdownText(node);
214
+ while (stack.length && stack.at(-1).depth >= node.depth) stack.pop();
215
+ stack.push({ title, depth: node.depth });
216
+ const base = title.toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s/g, "-") || "section";
217
+ let count = counts.get(base) ?? 0;
218
+ let anchor = count ? `${base}-${count}` : base;
219
+ while (anchors.has(anchor)) anchor = `${base}-${++count}`;
220
+ counts.set(base, count + 1);
221
+ anchors.add(anchor);
222
+ result.push({ title, depth: node.depth, anchor, hierarchy: stack.map((item) => item.title) });
223
+ });
224
+ return result;
225
+ }
226
+ function resolveHeading(headings, hierarchy) {
227
+ const matches = headings.filter((heading) => hierarchy.length <= heading.hierarchy.length && hierarchy.every((title, i) => title === heading.hierarchy[heading.hierarchy.length - hierarchy.length + i]));
228
+ if (matches.length !== 1)
229
+ throw new Error(`${matches.length ? "Ambiguous" : "Missing"} chapter: ${hierarchy.join(" > ")}`);
230
+ return matches[0];
231
+ }
232
+ async function queryArchitecture(options = {}) {
233
+ if (options.node !== void 0 && !options.node.trim())
234
+ throw new Error("--node requires a non-empty ID");
235
+ if (options.view !== void 0 && !options.view.trim())
236
+ throw new Error("--view requires a non-empty ID");
237
+ if (options.node && options.view)
238
+ throw new Error("--node and --view are mutually exclusive");
239
+ const root = await realpath(resolve(options.root ?? process.cwd()));
240
+ const config = await inspectRspConfig(root);
241
+ if (config.issues.length)
242
+ throw new Error(config.issues.join("; "));
243
+ let content;
244
+ try {
245
+ content = await readProjectFile(root, ARCHITECTURE_FILE);
246
+ } catch (error) {
247
+ const { lstat } = await import("fs/promises");
248
+ try {
249
+ await lstat(resolve(root, ARCHITECTURE_FILE));
250
+ } catch (missing) {
251
+ if (missing.code === "ENOENT")
252
+ return { configured: false, source: { root, file: ARCHITECTURE_FILE, revision: "" }, selection: { kind: "full" }, nodes: [], relations: [], views: [], documents: [], diagnostics: [{ code: "not_configured", message: `Architecture is not configured: ${ARCHITECTURE_FILE}` }] };
253
+ }
254
+ throw error;
255
+ }
256
+ const model = parseArchitecture(content);
257
+ const selection = options.node ? { kind: "node", id: options.node } : options.view ? { kind: "view", id: options.view } : { kind: "full" };
258
+ const { selected, visible, relations } = selectArchitecture(model, selection);
259
+ const diagnostics = [];
260
+ const chapterMaps = /* @__PURE__ */ new Map();
261
+ const sourceInputs = [content, JSON.stringify(config.config)];
262
+ for (const file of new Set(model.bindings.map((binding) => binding.file))) {
263
+ try {
264
+ const body = await readProjectFile(root, file);
265
+ chapterMaps.set(file, documentHeadings(body));
266
+ sourceInputs.push(file, body);
267
+ } catch (error) {
268
+ diagnostics.push({ code: "document_unavailable", file, message: error.message });
269
+ }
270
+ }
271
+ const nodes = model.nodes.filter((node) => visible.has(node.id)).map((node) => {
272
+ const ancestors = /* @__PURE__ */ new Set();
273
+ let parent = node.parent;
274
+ while (parent) {
275
+ ancestors.add(parent);
276
+ parent = model.nodes.find((node2) => node2.id === parent)?.parent;
277
+ }
278
+ const bindings = model.bindings.filter((binding) => binding.node === node.id || binding.scope === "subtree" && ancestors.has(binding.node)).map((binding) => {
279
+ const reference = { ...binding, origin: binding.node, inherited: binding.node !== node.id };
280
+ if (!binding.headings || !chapterMaps.has(binding.file))
281
+ return reference;
282
+ try {
283
+ return { ...reference, anchor: resolveHeading(chapterMaps.get(binding.file), binding.headings).anchor };
284
+ } catch (error) {
285
+ diagnostics.push({ code: "chapter_unresolved", file: binding.file, message: error.message });
286
+ return reference;
287
+ }
288
+ });
289
+ return { ...node, boundary: !selected.has(node.id), bindings };
290
+ });
291
+ return { configured: true, source: { root, file: ARCHITECTURE_FILE, revision: digest(sourceInputs.join("\0")) }, selection, nodes, relations, views: model.views.filter((view) => selection.kind === "full" || view.id === options.view || selected.has(view.node)), documents: [...new Set(nodes.flatMap((node) => node.bindings.map((binding) => binding.file)))], diagnostics };
292
+ }
293
+ function isWithin(root, target) {
294
+ const path = relative(root, target);
295
+ return path !== ".." && !path.startsWith("../") && !isAbsolute(path);
296
+ }
297
+
298
+ // src/core/project-version.ts
299
+ import { readFile, realpath as realpath2 } from "fs/promises";
300
+ import { join } from "path";
301
+ var PROJECT_VERSION_PATH = ".rsp/version";
302
+ var PROJECT_SOURCE_PATH = ".rsp/source";
303
+ var SOURCE_INVOCATION = "mise exec -- node dist/cli.mjs";
304
+ function isExactVersion(value) {
305
+ return /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?$/i.test(value);
306
+ }
307
+ function exactInvocation(version) {
308
+ if (!isExactVersion(version))
309
+ throw new Error(`Invalid exact RSP version: ${version}`);
310
+ return `npx -y @oevery/rsp@${version}`;
311
+ }
312
+ async function readProjectVersion(projectRoot = ".") {
313
+ const directory = inspectManagedDirectory(join(projectRoot, RSP_DIR), "RSP root", { allowMissing: true });
314
+ if (directory.issue)
315
+ throw directory.issue;
316
+ if (!directory.exists)
317
+ return null;
318
+ const path = join(projectRoot, PROJECT_VERSION_PATH);
319
+ const inspection = inspectManagedFile(path, "project CLI version", { allowMissing: true });
320
+ if (inspection.issue)
321
+ throw inspection.issue;
322
+ if (!inspection.exists)
323
+ return null;
324
+ const version = (await readFile(path, "utf8")).trim();
325
+ if (!isExactVersion(version))
326
+ throw new Error(`${PROJECT_VERSION_PATH} must contain one exact package version, not a tag or range`);
327
+ return version;
328
+ }
329
+ async function readProjectRuntime(projectRoot = ".") {
330
+ const version = await readProjectVersion(projectRoot);
331
+ const source = inspectManagedFile(join(projectRoot, PROJECT_SOURCE_PATH), "project source selection", { allowMissing: true });
332
+ if (source.issue)
333
+ throw source.issue;
334
+ if (!source.exists)
335
+ return { mode: "package", version };
336
+ if (version !== null)
337
+ throw new Error(`${PROJECT_SOURCE_PATH} and ${PROJECT_VERSION_PATH} are mutually exclusive; explicitly select one runtime.`);
338
+ if ((await readFile(join(projectRoot, PROJECT_SOURCE_PATH), "utf8")).trim() !== ".")
339
+ throw new Error(`${PROJECT_SOURCE_PATH} must contain . to select this RSP source checkout.`);
340
+ if (await realpath2(projectRoot) !== await realpath2(PKG_ROOT))
341
+ throw new Error(`Source mode requires this checkout's local CLI. Build here, then use ${SOURCE_INVOCATION} <command>; a registry package or another checkout cannot substitute.`);
342
+ const sourceDirectory = inspectManagedDirectory(join(projectRoot, "src"), "RSP source directory");
343
+ const sourceEntry = inspectManagedFile(join(projectRoot, "src/cli.ts"), "RSP source entry");
344
+ if (sourceDirectory.issue || sourceEntry.issue)
345
+ throw sourceDirectory.issue ?? sourceEntry.issue;
346
+ return { mode: "source" };
347
+ }
348
+ async function selectProjectRuntime(operation = "ordinary") {
349
+ const version = await getVersion();
350
+ exactInvocation(version);
351
+ const selected = await readProjectRuntime();
352
+ if (selected.mode === "source")
353
+ return { mode: "source", version };
354
+ if (selected.version && selected.version !== version && operation !== "update") {
355
+ throw new Error(`Project pins RSP ${selected.version}; running CLI is ${version}. Use ${exactInvocation(selected.version)} <command>. To migrate deliberately, inspect the target version and run ${exactInvocation(version)} update.`);
356
+ }
357
+ if (selected.version && operation === "update" && compareVersions(version, selected.version) < 0)
358
+ throw new Error(`Downgrading RSP from ${selected.version} to ${version} is unsupported. Use ${exactInvocation(selected.version)} <command>.`);
359
+ return { mode: "package", version };
360
+ }
361
+ function compareVersions(left, right) {
362
+ const parts = (value) => {
363
+ const [core, ...pre] = value.split("+", 1)[0].split("-");
364
+ return { core: core.split(".").map(BigInt), pre: pre.length ? pre.join("-").split(".") : [] };
365
+ };
366
+ const a = parts(left);
367
+ const b = parts(right);
368
+ for (let i = 0; i < 3; i++) {
369
+ if (a.core[i] !== b.core[i])
370
+ return a.core[i] > b.core[i] ? 1 : -1;
371
+ }
372
+ if (!a.pre.length || !b.pre.length)
373
+ return Number(!a.pre.length) - Number(!b.pre.length);
374
+ for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {
375
+ const x = a.pre[i];
376
+ const y = b.pre[i];
377
+ if (x === y)
378
+ continue;
379
+ if (x === void 0 || y === void 0)
380
+ return x === void 0 ? -1 : 1;
381
+ const nx = /^\d+$/.test(x);
382
+ const ny = /^\d+$/.test(y);
383
+ if (nx && ny)
384
+ return BigInt(x) > BigInt(y) ? 1 : -1;
385
+ if (nx !== ny)
386
+ return nx ? -1 : 1;
387
+ return x > y ? 1 : -1;
388
+ }
389
+ return 0;
390
+ }
391
+ async function guardProjectVersion(rawArgs) {
392
+ if (rawArgs.some((arg) => ["--help", "-h"].includes(arg)) || rawArgs.length === 1 && ["--version", "-v"].includes(rawArgs[0]))
393
+ return;
394
+ if (["doctor", "config", "update"].includes(rawArgs[0] ?? ""))
395
+ return;
396
+ await selectProjectRuntime();
397
+ }
398
+
399
+ export {
400
+ selectArchitecture,
401
+ ARCHITECTURE_FILE,
402
+ MAX_CONTENT_BYTES,
403
+ digest,
404
+ projectPath,
405
+ readProjectFile,
406
+ documentHeadings,
407
+ queryArchitecture,
408
+ isWithin,
409
+ PROJECT_VERSION_PATH,
410
+ SOURCE_INVOCATION,
411
+ isExactVersion,
412
+ exactInvocation,
413
+ readProjectVersion,
414
+ readProjectRuntime,
415
+ selectProjectRuntime,
416
+ compareVersions,
417
+ guardProjectVersion
418
+ };
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ PKG_ROOT
4
+ } from "./chunk-P3KO3GZW.mjs";
5
+
6
+ // src/viewer/build-assets.mjs
7
+ import { createHash } from "crypto";
8
+ import { lstat, readdir, readFile } from "fs/promises";
9
+ import { join, sep } from "path";
10
+ var viewerEntries = { app: "app.js", style: "app.css", mermaid: "mermaid.js" };
11
+ var mismatch = () => new Error("Viewer assets do not match the startup build; rebuild or reinstall and retry");
12
+ function validateAssetPaths(paths) {
13
+ if (!Array.isArray(paths) || paths.some((path) => typeof path !== "string" || !/^(?:[\w-]+\/)*[\w-]+\.(?:js|css)$/.test(path)) || new Set(paths).size !== paths.length || Object.values(viewerEntries).some((path) => !paths.includes(path))) {
14
+ throw mismatch();
15
+ }
16
+ return (
17
+ /** @type {string[]} */
18
+ paths
19
+ );
20
+ }
21
+ function parseBuildManifest(source) {
22
+ const manifest = JSON.parse(source);
23
+ if (!manifest || typeof manifest.identity !== "string" || !/^[a-f0-9]{64}$/.test(manifest.identity))
24
+ throw mismatch();
25
+ return { identity: (
26
+ /** @type {string} */
27
+ manifest.identity
28
+ ), assets: validateAssetPaths(manifest.assets) };
29
+ }
30
+ async function readBuildSnapshot(root, paths) {
31
+ const allowed = new Set(validateAssetPaths(paths));
32
+ const hash = createHash("sha256");
33
+ const assets = /* @__PURE__ */ new Map();
34
+ const directory = join(root, "dist");
35
+ for (const file of (await readdir(directory, { recursive: true })).map((file2) => file2.split(sep).join("/")).sort()) {
36
+ if (!/\.(?:mjs|js|css)$/.test(file))
37
+ continue;
38
+ const path = join(directory, file);
39
+ if (!(await lstat(path)).isFile())
40
+ throw mismatch();
41
+ const content = await readFile(path);
42
+ hash.update(file).update(content);
43
+ if (file.startsWith("viewer/") && allowed.has(file.slice("viewer/".length))) {
44
+ assets.set(`/${file.slice("viewer/".length)}`, {
45
+ content,
46
+ type: file.endsWith(".css") ? "text/css" : "text/javascript"
47
+ });
48
+ }
49
+ }
50
+ if (assets.size !== allowed.size)
51
+ throw mismatch();
52
+ return { identity: hash.digest("hex"), assets };
53
+ }
54
+
55
+ // src/viewer/runtime.ts
56
+ import { createHash as createHash2 } from "crypto";
57
+ import { readFile as readFile2, realpath } from "fs/promises";
58
+ import { homedir } from "os";
59
+ import { join as join2 } from "path";
60
+ var VIEWER_PROTOCOL = 1;
61
+ async function viewerRuntime(installation = PKG_ROOT) {
62
+ const root = await realpath(installation);
63
+ const { version } = JSON.parse(await readFile2(join2(root, "package.json"), "utf8"));
64
+ const build = (await readFile2(join2(root, "dist/viewer-build.json"), "utf8")).trim();
65
+ const identity = createHash2("sha256").update(JSON.stringify({ root, version, build, protocol: VIEWER_PROTOCOL })).digest("hex");
66
+ const directory = join2(homedir(), ".cache", "rsp", "viewer");
67
+ return { root, version, build, identity, directory, recordPath: join2(directory, `${identity}.json`), lockPath: join2(directory, `${identity}.lock`) };
68
+ }
69
+ async function snapshotViewerRuntime(root, identity) {
70
+ const runtime = await viewerRuntime(root);
71
+ if (runtime.identity !== identity)
72
+ throw new Error("Viewer build changed during startup; retry");
73
+ const manifest = parseBuildManifest(runtime.build);
74
+ const snapshot = await readBuildSnapshot(root, manifest.assets);
75
+ if (snapshot.identity !== manifest.identity || (await viewerRuntime(root)).identity !== identity) {
76
+ throw new Error("Viewer assets do not match the startup build; rebuild or reinstall and retry");
77
+ }
78
+ return { version: runtime.version, assets: snapshot.assets };
79
+ }
80
+ async function serviceRequest(record, path, body, timeoutMs = 2500) {
81
+ const response = await fetch(`http://127.0.0.1:${record.port}${path}`, { method: body === void 0 ? "GET" : "POST", headers: { Authorization: `Bearer ${record.adminToken}`, ...body === void 0 ? {} : { "Content-Type": "application/json" } }, ...body === void 0 ? {} : { body: JSON.stringify(body) }, signal: AbortSignal.timeout(timeoutMs) });
82
+ if (!response.ok)
83
+ throw new Error(await response.text());
84
+ const result = await response.json();
85
+ if (result.identity !== record.identity || result.protocol !== VIEWER_PROTOCOL)
86
+ throw new Error("Viewer identity/protocol mismatch");
87
+ return result;
88
+ }
89
+ async function discoverService(path, identity) {
90
+ try {
91
+ const record = JSON.parse(await readFile2(path, "utf8"));
92
+ if (record.identity !== identity || record.protocol !== VIEWER_PROTOCOL || !Number.isInteger(record.port) || record.port < 1 || record.port > 65535 || typeof record.adminToken !== "string")
93
+ return null;
94
+ await serviceRequest(record, "/control/status");
95
+ return record;
96
+ } catch {
97
+ return null;
98
+ }
99
+ }
100
+
101
+ export {
102
+ viewerEntries,
103
+ VIEWER_PROTOCOL,
104
+ viewerRuntime,
105
+ snapshotViewerRuntime,
106
+ serviceRequest,
107
+ discoverService
108
+ };