@appchy/jarvis 0.1.37 → 0.1.39

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 (108) hide show
  1. package/bin/config-change.mjs +1 -1
  2. package/bin/jarvis.mjs +8 -12
  3. package/bin/pre-tool-use.mjs +2 -2
  4. package/bin/session-start.mjs +1 -1
  5. package/bin/stop.mjs +1 -1
  6. package/bin/user-prompt-submit.mjs +1 -1
  7. package/dist/bin.js +10168 -37341
  8. package/dist/bin.js.map +1 -1
  9. package/dist/data/backends.mjs +469 -0
  10. package/dist/data/chunk-7REP35VA.mjs +462 -0
  11. package/dist/data/chunk-AKQQC5IT.mjs +1224 -0
  12. package/dist/data/chunk-AYOJSS2F.mjs +72 -0
  13. package/dist/data/chunk-RRJ6KKYL.mjs +42 -0
  14. package/dist/data/chunk-YWSWQEJN.mjs +138 -0
  15. package/dist/data/embedders.mjs +80 -0
  16. package/dist/data/finders.mjs +35 -0
  17. package/dist/data/index.mjs +137 -0
  18. package/dist/data/linkers.mjs +83 -0
  19. package/dist/data/mcp.mjs +2624 -0
  20. package/dist/data/persistences.mjs +8 -0
  21. package/dist/data/rerankers.mjs +52 -0
  22. package/dist/data/stores.mjs +38 -0
  23. package/dist/hooks/config-change.js +7 -29
  24. package/dist/hooks/config-change.js.map +1 -1
  25. package/dist/hooks/pre-tool-use.js +129 -32
  26. package/dist/hooks/pre-tool-use.js.map +1 -1
  27. package/dist/hooks/session-start.js +72 -37
  28. package/dist/hooks/session-start.js.map +1 -1
  29. package/dist/hooks/stop.js +140 -32
  30. package/dist/hooks/stop.js.map +1 -1
  31. package/dist/hooks/user-prompt-submit.js +7 -29
  32. package/dist/hooks/user-prompt-submit.js.map +1 -1
  33. package/harness/assets/architecture-template.md +53 -0
  34. package/harness/assets/design-template.md +89 -0
  35. package/harness/assets/domains/architecture.md +43 -0
  36. package/harness/assets/domains/commercial.md +42 -0
  37. package/harness/assets/domains/design.md +42 -0
  38. package/harness/assets/domains/legal.md +43 -0
  39. package/harness/assets/domains/operations.md +39 -0
  40. package/harness/assets/domains/product.md +43 -0
  41. package/harness/assets/domains/quality.md +43 -0
  42. package/harness/assets/domains/security.md +44 -0
  43. package/harness/assets/domains/support.md +45 -0
  44. package/harness/assets/epic-template.md +85 -0
  45. package/harness/assets/feature-template.md +86 -0
  46. package/harness/assets/handoff-template.md +26 -0
  47. package/harness/assets/task-template.md +38 -0
  48. package/harness/assets/version-template.md +43 -0
  49. package/harness/harness/__init__.py +7 -0
  50. package/harness/harness/align.py +582 -0
  51. package/harness/harness/architecture.py +254 -0
  52. package/harness/harness/autonomy.py +374 -0
  53. package/harness/harness/branches.py +408 -0
  54. package/harness/harness/config.py +1482 -0
  55. package/harness/harness/coverage.py +199 -0
  56. package/harness/harness/epic.py +220 -0
  57. package/harness/harness/events.py +153 -0
  58. package/harness/harness/extend.py +99 -0
  59. package/harness/harness/frontmatter.py +218 -0
  60. package/harness/harness/gate.py +591 -0
  61. package/harness/harness/generate.py +267 -0
  62. package/harness/harness/git.py +775 -0
  63. package/harness/harness/ids.py +140 -0
  64. package/harness/harness/kickoff.py +231 -0
  65. package/harness/harness/lint.py +505 -0
  66. package/harness/harness/model.py +364 -0
  67. package/harness/harness/peers.py +187 -0
  68. package/harness/harness/product.py +29 -0
  69. package/harness/harness/registry.py +382 -0
  70. package/harness/harness/report.py +227 -0
  71. package/harness/harness/safety.py +387 -0
  72. package/harness/harness/scaffold.py +129 -0
  73. package/harness/harness/shard.py +63 -0
  74. package/harness/harness/shift.py +348 -0
  75. package/harness/harness/task.py +507 -0
  76. package/harness/harness/tree.py +258 -0
  77. package/harness/harness/version.py +305 -0
  78. package/harness/harness/wrap.py +217 -0
  79. package/harness/hooks/guard.py +259 -0
  80. package/harness/presets/appchy/PRESET.md +717 -0
  81. package/harness/presets/appchy/references/artifacts.md +539 -0
  82. package/harness/presets/appchy/references/graph.md +154 -0
  83. package/harness/presets/appchy/references/operations.md +444 -0
  84. package/harness/presets/appchy/references/research.md +216 -0
  85. package/harness/schema/work.config.schema.json +401 -0
  86. package/harness/test_work.py +5002 -0
  87. package/harness/work.py +534 -0
  88. package/package.json +34 -40
  89. package/bin/config-change.dev.mjs +0 -17
  90. package/bin/jarvis-dev.mjs +0 -30
  91. package/bin/pre-tool-use.dev.mjs +0 -23
  92. package/bin/session-start.dev.mjs +0 -17
  93. package/bin/stop.dev.mjs +0 -17
  94. package/bin/user-prompt-submit.dev.mjs +0 -17
  95. package/dev/bin.js +0 -38821
  96. package/dev/bin.js.map +0 -1
  97. package/dev/env.json +0 -1
  98. package/dev/hooks/config-change.js +0 -110
  99. package/dev/hooks/config-change.js.map +0 -1
  100. package/dev/hooks/pre-tool-use.js +0 -120
  101. package/dev/hooks/pre-tool-use.js.map +0 -1
  102. package/dev/hooks/session-start.js +0 -115
  103. package/dev/hooks/session-start.js.map +0 -1
  104. package/dev/hooks/stop.js +0 -112
  105. package/dev/hooks/stop.js.map +0 -1
  106. package/dev/hooks/user-prompt-submit.js +0 -111
  107. package/dev/hooks/user-prompt-submit.js.map +0 -1
  108. package/dist/env.json +0 -1
@@ -0,0 +1,1224 @@
1
+ import {
2
+ localFiles
3
+ } from "./chunk-RRJ6KKYL.mjs";
4
+
5
+ // ../../packages/data/src/graph.ts
6
+ var GraphStore = class {
7
+ nodeById = /* @__PURE__ */ new Map();
8
+ typeIndex = /* @__PURE__ */ new Map();
9
+ edgeList = [];
10
+ pending = [];
11
+ outAdj = /* @__PURE__ */ new Map();
12
+ inAdj = /* @__PURE__ */ new Map();
13
+ hopAdj = /* @__PURE__ */ new Map();
14
+ addNode(input, owner) {
15
+ const node = {
16
+ id: input.id,
17
+ type: input.type,
18
+ metadata: input.metadata ?? {},
19
+ ...input.spec !== void 0 ? { spec: input.spec } : {},
20
+ owner
21
+ };
22
+ const prior = this.nodeById.get(node.id);
23
+ if (prior && prior.type !== node.type) this.unindexType(prior);
24
+ this.nodeById.set(node.id, node);
25
+ let byName = this.typeIndex.get(node.type);
26
+ if (!byName) {
27
+ byName = /* @__PURE__ */ new Map();
28
+ this.typeIndex.set(node.type, byName);
29
+ }
30
+ byName.set(node.id, node);
31
+ }
32
+ /** Drop a node from its type bucket, removing the bucket once empty. */
33
+ unindexType(node) {
34
+ const byName = this.typeIndex.get(node.type);
35
+ if (!byName) return;
36
+ byName.delete(node.id);
37
+ if (byName.size === 0) this.typeIndex.delete(node.type);
38
+ }
39
+ addEdge(input, owner) {
40
+ this.pending.push({ input, owner });
41
+ }
42
+ /** Remove a node and every edge touching it, updating the type index + adjacency. Generic (a node
43
+ * is a node) — the memory-overlay delete path uses it to keep an in-session graph consistent with a
44
+ * persisted removal, mirroring `addNode`. A no-op if the id is absent. */
45
+ removeNode(id) {
46
+ const node = this.nodeById.get(id);
47
+ if (!node) return;
48
+ this.nodeById.delete(id);
49
+ this.unindexType(node);
50
+ this.edgeList = this.edgeList.filter((e) => e.from !== id && e.to !== id);
51
+ this.rebuildAdjacency();
52
+ }
53
+ /** Merge one namespaced annotation into an existing node's metadata (enrichers). Isolated —
54
+ * a no-op if the node is absent, never throws (namespacing the key is the caller's job). */
55
+ annotate(req, _owner) {
56
+ const node = this.nodeById.get(req.nodeId);
57
+ if (!node) return;
58
+ node.metadata[req.key] = req.value;
59
+ }
60
+ /** Resolve pending edges (refs + inverses), rebuild adjacency. Returns any diagnostics. */
61
+ stitch() {
62
+ const diagnostics = [];
63
+ const pending = this.pending;
64
+ this.pending = [];
65
+ for (const { input, owner } of pending) {
66
+ const to = this.resolveTarget(input.to);
67
+ if (to === void 0) {
68
+ diagnostics.push({
69
+ severity: "warn",
70
+ code: "UNRESOLVED_REF",
71
+ message: `edge ${input.from} -[${input.relation}]-> ${describeRef(input.to)} has no matching node`,
72
+ plugin: owner
73
+ });
74
+ continue;
75
+ }
76
+ this.edgeList.push(this.edge(input.from, to, input.relation, input.metadata, owner));
77
+ if (input.inverse !== void 0) {
78
+ this.edgeList.push(this.edge(to, input.from, input.inverse, input.metadata, owner));
79
+ }
80
+ }
81
+ this.rebuildAdjacency();
82
+ return diagnostics;
83
+ }
84
+ // --- ReadonlyGraph ---
85
+ nodes(type) {
86
+ if (type === void 0) return [...this.nodeById.values()];
87
+ const byName = this.typeIndex.get(type);
88
+ return byName ? [...byName.values()] : [];
89
+ }
90
+ node(id) {
91
+ return this.nodeById.get(id);
92
+ }
93
+ edges(query) {
94
+ if (!query) return this.edgeList;
95
+ const candidates = query.from !== void 0 ? this.outAdj.get(query.from) ?? [] : query.to !== void 0 ? this.inAdj.get(query.to) ?? [] : this.edgeList;
96
+ return candidates.filter(
97
+ (e) => (query.from === void 0 || e.from === query.from) && (query.to === void 0 || e.to === query.to) && (query.relation === void 0 || e.relation === query.relation)
98
+ );
99
+ }
100
+ hops(id) {
101
+ return this.hopAdj.get(id) ?? [];
102
+ }
103
+ neighbors(id, query = {}) {
104
+ const direction = query.direction ?? "both";
105
+ const collected = /* @__PURE__ */ new Map();
106
+ const consider = (edges, endpoint) => {
107
+ if (!edges) return;
108
+ for (const e of edges) {
109
+ if (query.relation !== void 0 && e.relation !== query.relation) continue;
110
+ const other = this.nodeById.get(endpoint(e));
111
+ if (other) collected.set(other.id, other);
112
+ }
113
+ };
114
+ if (direction === "out" || direction === "both") consider(this.outAdj.get(id), (e) => e.to);
115
+ if (direction === "in" || direction === "both") consider(this.inAdj.get(id), (e) => e.from);
116
+ const result = [...collected.values()];
117
+ return query.limit !== void 0 ? result.slice(0, query.limit) : result;
118
+ }
119
+ /** Snapshot for serialization (graph.json ⊕ overlay). */
120
+ toJSON() {
121
+ return { nodes: [...this.nodeById.values()], edges: this.edgeList };
122
+ }
123
+ /** Populate from a persisted snapshot — `toJSON`'s inverse. Nodes/edges arrive already resolved and
124
+ * owner-stamped, so they load straight in (no ref resolution, no `stitch`). The MCP serve path
125
+ * presents this frozen built graph without re-running any producer. Replaces all state. */
126
+ load(trace) {
127
+ this.nodeById.clear();
128
+ this.typeIndex.clear();
129
+ this.edgeList = [...trace.edges];
130
+ this.pending = [];
131
+ for (const node of trace.nodes) {
132
+ this.nodeById.set(node.id, node);
133
+ let byName = this.typeIndex.get(node.type);
134
+ if (!byName) {
135
+ byName = /* @__PURE__ */ new Map();
136
+ this.typeIndex.set(node.type, byName);
137
+ }
138
+ byName.set(node.id, node);
139
+ }
140
+ this.rebuildAdjacency();
141
+ }
142
+ // --- internals ---
143
+ resolveTarget(to) {
144
+ if (typeof to === "string") {
145
+ return this.nodeById.has(to) ? to : void 0;
146
+ }
147
+ const byName = this.typeIndex.get(to.type);
148
+ if (!byName) return void 0;
149
+ if (byName.has(to.name)) return to.name;
150
+ for (const node of byName.values()) {
151
+ if (node.metadata.name === to.name || node.metadata.title === to.name) return node.id;
152
+ }
153
+ return void 0;
154
+ }
155
+ edge(from, to, relation, metadata, owner) {
156
+ return { from, to, relation, ...metadata ? { metadata } : {}, owner };
157
+ }
158
+ rebuildAdjacency() {
159
+ this.outAdj = /* @__PURE__ */ new Map();
160
+ this.inAdj = /* @__PURE__ */ new Map();
161
+ this.hopAdj = /* @__PURE__ */ new Map();
162
+ for (const e of this.edgeList) {
163
+ pushInto(this.outAdj, e.from, e);
164
+ pushInto(this.inAdj, e.to, e);
165
+ const metadata = e.metadata ? { metadata: e.metadata } : {};
166
+ pushInto(this.hopAdj, e.from, {
167
+ to: e.to,
168
+ relation: e.relation,
169
+ dir: "out",
170
+ owner: e.owner,
171
+ ...metadata
172
+ });
173
+ pushInto(this.hopAdj, e.to, {
174
+ to: e.from,
175
+ relation: e.relation,
176
+ dir: "in",
177
+ owner: e.owner,
178
+ ...metadata
179
+ });
180
+ }
181
+ }
182
+ };
183
+ function pushInto(map2, key, item) {
184
+ const list = map2.get(key);
185
+ if (list) list.push(item);
186
+ else map2.set(key, [item]);
187
+ }
188
+ function describeRef(to) {
189
+ return typeof to === "string" ? to : `${to.type}:${to.name}`;
190
+ }
191
+
192
+ // ../../packages/data/src/provenance.ts
193
+ var DERIVED_FROM = "data.derivedFrom";
194
+ var TIER = "data.tier";
195
+
196
+ // ../../packages/data/src/map/ledger.ts
197
+ var RESERVED = [
198
+ "type",
199
+ "title",
200
+ "status",
201
+ "createdAt",
202
+ "updatedAt",
203
+ "summary",
204
+ "searchText"
205
+ ];
206
+ function formatLedger(ledgers) {
207
+ const width = Math.max(0, ...ledgers.map((one) => one.name.length));
208
+ return ledgers.map((one) => ` ${one.name.padEnd(width)} ${tally(one)}`).join("\n");
209
+ }
210
+ function tally(ledger) {
211
+ const edges = Object.entries(ledger.edges).sort(([a], [b]) => a.localeCompare(b)).map(([relation, count2]) => `${relation} ${count2}`).join(" \xB7 ");
212
+ const missing = RESERVED.filter((key) => ledger.reserved[key] === 0);
213
+ return [
214
+ `${ledger.items} items \u2192 ${ledger.nodes} nodes`,
215
+ edges,
216
+ missing.length > 0 ? `no ${missing.join("/")}` : ""
217
+ ].filter(Boolean).join(" ");
218
+ }
219
+
220
+ // ../../packages/data/src/map/mint.ts
221
+ function mintId(req) {
222
+ const declared = req.declared?.trim();
223
+ if (declared) return declared;
224
+ const parent = req.parent;
225
+ if (parent) return `${parent.id}/${slug(within(req.ref, parent.ref))}`;
226
+ return `${req.map}:${slug(req.ref)}`;
227
+ }
228
+ function within(ref, parent) {
229
+ return ref.startsWith(parent) ? ref.slice(parent.length) : ref;
230
+ }
231
+ function slug(ref) {
232
+ return ref.replace(/\.[^./\\]+$/, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
233
+ }
234
+
235
+ // ../../packages/data/src/map/status.ts
236
+ var ALIASES = {
237
+ open: "open",
238
+ todo: "open",
239
+ "to-do": "open",
240
+ queue: "open",
241
+ queued: "open",
242
+ backlog: "open",
243
+ planned: "open",
244
+ new: "open",
245
+ ready: "open",
246
+ "in-progress": "in-progress",
247
+ inprogress: "in-progress",
248
+ doing: "in-progress",
249
+ wip: "in-progress",
250
+ active: "in-progress",
251
+ current: "in-progress",
252
+ started: "in-progress",
253
+ done: "done",
254
+ complete: "done",
255
+ completed: "done",
256
+ shipped: "done",
257
+ released: "done",
258
+ closed: "done",
259
+ merged: "done",
260
+ archived: "done",
261
+ dropped: "dropped",
262
+ cancelled: "dropped",
263
+ canceled: "dropped",
264
+ wontfix: "dropped",
265
+ "wont-fix": "dropped",
266
+ abandoned: "dropped",
267
+ superseded: "dropped"
268
+ };
269
+ function categoryOf(raw) {
270
+ if (raw === void 0) return "unknown";
271
+ return ALIASES[raw.trim().toLowerCase()] ?? "unknown";
272
+ }
273
+
274
+ // ../../packages/data/src/map/run.ts
275
+ async function runEntries(req) {
276
+ const maps = req.entries.filter((one) => one.entry === "map");
277
+ const connects = req.entries.filter((one) => one.entry === "connect");
278
+ const run = {
279
+ graph: req.graph,
280
+ repoRoot: req.repoRoot,
281
+ report: req.report,
282
+ code: freeze(req.graph),
283
+ minted: /* @__PURE__ */ new Map()
284
+ };
285
+ const scopes = [];
286
+ for (const spec of maps) {
287
+ const scope = await find(run, spec);
288
+ if (scope) scopes.push(scope);
289
+ }
290
+ await Promise.all(scopes.map((scope) => shape(run, scope)));
291
+ for (const scope of scopes) await mint(run, scope);
292
+ for (const scope of scopes) link(run, scope);
293
+ for (const spec of connects) await join(run, spec);
294
+ for (const diagnostic of silence(scopes)) run.report(diagnostic);
295
+ return scopes.map((scope) => scope.ledger);
296
+ }
297
+ async function find(run, spec) {
298
+ const ctx = finderContext(run, spec.name);
299
+ const listed = await guard(run, spec.name, () => spec.from.list(ctx));
300
+ if (listed === void 0) return void 0;
301
+ const kept = [];
302
+ for (const item of listed) {
303
+ const checked = await validate(run, spec, item);
304
+ if (checked) kept.push(checked);
305
+ }
306
+ return {
307
+ spec,
308
+ entries: kept.map((item) => ({ item, record: {}, items: [] })),
309
+ ledger: { name: spec.name, items: kept.length, nodes: 0, edges: {}, reserved: {} }
310
+ };
311
+ }
312
+ async function validate(run, spec, item) {
313
+ const schema = spec.from.schema;
314
+ if (!schema) return item;
315
+ const checked = await guard(
316
+ run,
317
+ spec.name,
318
+ async () => await schema["~standard"].validate(item.data)
319
+ );
320
+ if (checked === void 0) return void 0;
321
+ if (checked.issues) {
322
+ run.report({
323
+ severity: "error",
324
+ code: "MAP_SCHEMA",
325
+ message: `${spec.name}: ${item.ref} does not match its schema \u2014 ${issueText(checked.issues)}. Keys present: ${keyText(item.data)}`,
326
+ plugin: spec.name,
327
+ ...item.file !== void 0 ? { file: item.file } : {}
328
+ });
329
+ return void 0;
330
+ }
331
+ return { ...item, data: checked.value };
332
+ }
333
+ async function shape(run, scope) {
334
+ const ctx = mapContext(run, scope.spec.name);
335
+ const kept = [];
336
+ for (const entry of scope.entries) {
337
+ const record = await guard(run, scope.spec.name, () => scope.spec.how(entry.item, ctx));
338
+ if (record === void 0) continue;
339
+ entry.record = record;
340
+ entry.items = nest(record.items ?? [], {
341
+ ref: entry.item.ref,
342
+ ...entry.item.file !== void 0 ? { file: entry.item.file } : {}
343
+ });
344
+ kept.push(entry);
345
+ }
346
+ scope.entries = kept;
347
+ scope.ledger.items = kept.length;
348
+ count(scope);
349
+ }
350
+ function nest(items, parent) {
351
+ return items.map((record) => ({
352
+ record,
353
+ parent,
354
+ items: nest(record.items ?? [], {
355
+ ref: record.ref ?? "",
356
+ ...record.file !== void 0 ? { file: record.file } : {}
357
+ })
358
+ }));
359
+ }
360
+ function count(scope) {
361
+ const records = scope.entries.flatMap((entry) => [
362
+ entry.record,
363
+ ...flat(entry.items).map((one) => one.record)
364
+ ]);
365
+ for (const key of RESERVED) {
366
+ scope.ledger.reserved[key] = records.filter((record) => record[key] !== void 0).length;
367
+ }
368
+ }
369
+ function flat(items) {
370
+ return items.flatMap((one) => [one, ...flat(one.items)]);
371
+ }
372
+ async function mint(run, scope) {
373
+ for (const entry of scope.entries) {
374
+ entry.id = await place(run, scope, {
375
+ record: entry.record,
376
+ ref: entry.item.ref,
377
+ ...entry.item.file !== void 0 ? { file: entry.item.file } : {},
378
+ ...entry.item.span !== void 0 ? { span: entry.item.span } : {}
379
+ });
380
+ for (const child of entry.items) await inner(run, scope, child, entry.id, entry.item.ref);
381
+ }
382
+ }
383
+ async function inner(run, scope, node, parentId, parentRef) {
384
+ const record = node.record;
385
+ const file = record.file ?? (record.span !== void 0 ? node.parent.file : void 0);
386
+ if (record.type !== void 0 && record.id === void 0 && record.ref === void 0) {
387
+ run.report({
388
+ severity: "error",
389
+ code: "MAP_ERROR",
390
+ message: `${scope.spec.name}: a nested "${record.type}" record inside ${parentRef} spells neither \`id\` nor \`ref\`, so it has no id to mint`,
391
+ plugin: scope.spec.name
392
+ });
393
+ return;
394
+ }
395
+ node.id = await place(run, scope, {
396
+ record,
397
+ ref: record.ref ?? "",
398
+ ...file !== void 0 ? { file } : {},
399
+ ...record.span !== void 0 ? { span: record.span } : {},
400
+ ...parentId !== void 0 ? { parent: { id: parentId, ref: parentRef } } : {}
401
+ });
402
+ for (const child of node.items) await inner(run, scope, child, node.id, record.ref ?? "");
403
+ }
404
+ async function place(run, scope, req) {
405
+ if (req.record.type === void 0) return void 0;
406
+ const id = mintId({
407
+ map: scope.spec.name,
408
+ ref: req.ref,
409
+ declared: req.record.id,
410
+ ...req.parent !== void 0 ? { parent: req.parent } : {}
411
+ });
412
+ const fresh = { owner: scope.spec.name, record: req.record, ref: req.ref };
413
+ const prior = run.minted.get(id);
414
+ const kept = prior ? await fold(run, scope, id, prior, fresh) : fresh;
415
+ if (kept === void 0) return id;
416
+ run.minted.set(id, kept);
417
+ run.graph.addNode(toNodeInput(id, kept.record, req), kept.owner);
418
+ if (prior === void 0) scope.ledger.nodes += 1;
419
+ return id;
420
+ }
421
+ async function fold(run, scope, id, prior, fresh) {
422
+ const dedupe = scope.spec.dedupe ?? "error";
423
+ if (dedupe === "first") return void 0;
424
+ if (dedupe === "last") return fresh;
425
+ if (dedupe === "error") {
426
+ run.report({
427
+ severity: "error",
428
+ code: "MAP_COLLISION",
429
+ message: `two records mint the id "${id}" \u2014 ${prior.owner}'s ${prior.ref} and ${scope.spec.name}'s ${fresh.ref}. The store keys by id alone, so one of them would simply stop existing; set \`dedupe\` on the map that should decide.`,
430
+ plugin: scope.spec.name
431
+ });
432
+ return void 0;
433
+ }
434
+ const merged = await guard(run, scope.spec.name, () => dedupe(prior.record, fresh.record));
435
+ return merged ? { owner: scope.spec.name, record: merged, ref: fresh.ref } : void 0;
436
+ }
437
+ function toNodeInput(id, record, req) {
438
+ const free = {};
439
+ for (const [key, value] of Object.entries(record)) {
440
+ if (key === "items" || key === "links" || key === "rel" || key === "inverse" || key === "ref")
441
+ continue;
442
+ if (RESERVED.includes(key) || key === "id") continue;
443
+ const json = jsonOf(value);
444
+ if (json !== void 0) free[key] = json;
445
+ }
446
+ return {
447
+ id,
448
+ type: record.type ?? "",
449
+ metadata: {
450
+ ...free,
451
+ title: record.title ?? humanize(req.ref),
452
+ ...req.file !== void 0 ? { sourceFile: req.file } : {},
453
+ ...req.span !== void 0 ? { sourceSpan: req.span } : {},
454
+ // What a record was found INSIDE, as data and not just as an edge: the edge's relation is
455
+ // config-named, so a reader that has not been told the name cannot recognise it, and telling a
456
+ // document's own node from the ones minted inside it is a question every presenter asks.
457
+ ...req.parent !== void 0 ? { [DERIVED_FROM]: req.parent.id } : {},
458
+ ...record.status !== void 0 ? { status: record.status, statusCategory: categoryOf(record.status) } : {},
459
+ ...record.createdAt !== void 0 ? { createdAt: record.createdAt } : {},
460
+ ...record.updatedAt !== void 0 ? { updatedAt: record.updatedAt } : {},
461
+ ...record.summary !== void 0 ? { excerpt: record.summary } : {},
462
+ ...record.searchText !== void 0 ? { embedText: record.searchText } : {}
463
+ }
464
+ };
465
+ }
466
+ function jsonOf(value) {
467
+ if (value === null) return null;
468
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
469
+ return value;
470
+ if (value instanceof Date) return value.toISOString();
471
+ if (Array.isArray(value)) {
472
+ const out = [];
473
+ for (const one of value) {
474
+ const json = jsonOf(one);
475
+ if (json !== void 0) out.push(json);
476
+ }
477
+ return out;
478
+ }
479
+ if (typeof value === "object") {
480
+ const out = {};
481
+ for (const [key, held] of Object.entries(value)) {
482
+ const json = jsonOf(held);
483
+ if (json !== void 0) out[key] = json;
484
+ }
485
+ return out;
486
+ }
487
+ return void 0;
488
+ }
489
+ function link(run, scope) {
490
+ for (const entry of scope.entries) {
491
+ apply(run, scope, entry.record, entry.id, entry.item.ref);
492
+ for (const child of entry.items) descend(run, scope, child, entry.id);
493
+ }
494
+ }
495
+ function descend(run, scope, node, parentId) {
496
+ if (parentId !== void 0 && node.id !== void 0) {
497
+ emit(run, scope, {
498
+ from: parentId,
499
+ rel: node.record.rel,
500
+ to: node.id,
501
+ ...node.record.inverse !== void 0 ? { inverse: node.record.inverse } : {}
502
+ });
503
+ }
504
+ apply(run, scope, node.record, node.id, node.record.ref ?? "");
505
+ for (const child of node.items) descend(run, scope, child, node.id);
506
+ }
507
+ function apply(run, scope, record, id, ref) {
508
+ for (const request of record.links ?? []) {
509
+ const from = request.from ?? id;
510
+ if (from === void 0) {
511
+ run.report({
512
+ severity: "error",
513
+ code: "MAP_ERROR",
514
+ message: `${scope.spec.name}: a "${request.rel}" edge leaves "${ref}", which minted no node \u2014 give the record a \`type\`, or name the id the edge leaves from`,
515
+ plugin: scope.spec.name
516
+ });
517
+ continue;
518
+ }
519
+ emit(run, scope, { ...request, from });
520
+ }
521
+ }
522
+ function emit(run, scope, request) {
523
+ run.graph.addEdge(
524
+ {
525
+ from: request.from,
526
+ to: request.to,
527
+ relation: request.rel,
528
+ ...request.inverse !== void 0 ? { inverse: request.inverse } : {},
529
+ ...request.meta !== void 0 ? { metadata: request.meta } : {}
530
+ },
531
+ scope.spec.name
532
+ );
533
+ scope.ledger.edges[request.rel] = (scope.ledger.edges[request.rel] ?? 0) + 1;
534
+ }
535
+ async function join(run, spec) {
536
+ const ctx = finderContext(run, spec.name);
537
+ const graph = {
538
+ nodes: (type) => run.graph.nodes(type),
539
+ node: (id) => run.graph.node(id),
540
+ edges: (query) => run.graph.edges(query),
541
+ link: (request) => run.graph.addEdge(
542
+ {
543
+ from: request.from,
544
+ to: request.to,
545
+ relation: request.rel,
546
+ ...request.inverse !== void 0 ? { inverse: request.inverse } : {},
547
+ ...request.meta !== void 0 ? { metadata: request.meta } : {}
548
+ },
549
+ spec.name
550
+ ),
551
+ report: ctx.report
552
+ };
553
+ await guard(run, spec.name, () => spec.run(graph, ctx));
554
+ }
555
+ function silence(scopes) {
556
+ const asserted = scopes.filter((scope) => !(scope.spec.lens ?? scope.spec.from.lens));
557
+ if (asserted.length === 0) return [];
558
+ const silent = asserted.filter((scope) => scope.ledger.items === 0);
559
+ if (silent.length === 0) return [];
560
+ const severity = silent.length === asserted.length ? "error" : "warn";
561
+ return silent.map((scope) => ({
562
+ severity,
563
+ code: "MAP_EMPTY",
564
+ message: `map "${scope.spec.name}" found no items \u2014 ${scope.spec.from.name} matches nothing in this repo`,
565
+ plugin: scope.spec.name
566
+ }));
567
+ }
568
+ function freeze(graph) {
569
+ const nodes = [...graph.nodes()];
570
+ const byType = /* @__PURE__ */ new Map();
571
+ const byId = /* @__PURE__ */ new Map();
572
+ for (const node of nodes) {
573
+ byId.set(node.id, node);
574
+ const held = byType.get(node.type);
575
+ if (held) held.push(node);
576
+ else byType.set(node.type, [node]);
577
+ }
578
+ const edges = [...graph.edges()];
579
+ return {
580
+ nodes: (type) => type === void 0 ? nodes : byType.get(type) ?? [],
581
+ node: (id) => byId.get(id),
582
+ edges: (query) => query === void 0 ? edges : edges.filter(
583
+ (edge) => (query.from === void 0 || edge.from === query.from) && (query.to === void 0 || edge.to === query.to) && (query.relation === void 0 || edge.relation === query.relation)
584
+ )
585
+ };
586
+ }
587
+ function finderContext(run, owner) {
588
+ return {
589
+ repoRoot: run.repoRoot,
590
+ report: (diagnostic) => run.report({ ...diagnostic, plugin: owner })
591
+ };
592
+ }
593
+ function mapContext(run, owner) {
594
+ return { ...finderContext(run, owner), code: run.code };
595
+ }
596
+ async function guard(run, owner, fn) {
597
+ try {
598
+ return await fn();
599
+ } catch (err) {
600
+ run.report({ severity: "error", code: "MAP_ERROR", message: reason(err), plugin: owner });
601
+ return void 0;
602
+ }
603
+ }
604
+ function reason(err) {
605
+ return err instanceof Error ? err.message : String(err);
606
+ }
607
+ function issueText(issues) {
608
+ return issues.slice(0, 3).map((issue) => {
609
+ const path = (issue.path ?? []).map((step) => String(typeof step === "object" ? step.key : step)).join(".");
610
+ return path ? `${path}: ${issue.message}` : issue.message;
611
+ }).join("; ");
612
+ }
613
+ function keyText(data) {
614
+ if (typeof data !== "object" || data === null) return "(none)";
615
+ const keys = Object.keys(data);
616
+ return keys.length ? keys.join(", ") : "(none)";
617
+ }
618
+ function humanize(ref) {
619
+ const base = (ref.split("/").pop() ?? ref).replace(/\.[^.]+$/, "");
620
+ return base.split(/[-_\s]+/).filter(Boolean).map((word) => `${(word[0] ?? "").toUpperCase()}${word.slice(1)}`).join(" ");
621
+ }
622
+
623
+ // ../../packages/data/src/engine.ts
624
+ var PanicError = class extends Error {
625
+ constructor(plugin, message) {
626
+ super(message);
627
+ this.plugin = plugin;
628
+ this.name = "PanicError";
629
+ }
630
+ plugin;
631
+ };
632
+ async function buildGraph(config2) {
633
+ const graph = new GraphStore();
634
+ const diagnostics = [];
635
+ const logger = createLogger();
636
+ const code = createCodeBackend(config2.backend);
637
+ let ledger = [];
638
+ const allPlugins = config2.plugins;
639
+ try {
640
+ await runPlugin(config2.backend.name, diagnostics, async () => {
641
+ const ctx = createCtx(config2.backend.name, graph, config2, code, logger, diagnostics);
642
+ const codeGraph = await config2.backend.load(ctx);
643
+ for (const node of codeGraph.nodes) graph.addNode(node, config2.backend.name);
644
+ for (const edge of codeGraph.edges) graph.addEdge(edge, config2.backend.name);
645
+ });
646
+ diagnostics.push(...graph.stitch());
647
+ const sources = order(allPlugins.filter(isSource));
648
+ await Promise.all(
649
+ sources.map(
650
+ (s) => runPlugin(
651
+ s.name,
652
+ diagnostics,
653
+ () => s.apply(createCtx(s.name, graph, config2, code, logger, diagnostics))
654
+ )
655
+ )
656
+ );
657
+ diagnostics.push(...graph.stitch());
658
+ diagnostics.push(...silentSources(sources, graph, diagnostics));
659
+ ledger = await runEntries({
660
+ entries: allPlugins.filter(isMapping),
661
+ graph,
662
+ repoRoot: config2.repoRoot,
663
+ report: (d) => diagnostics.push(d)
664
+ });
665
+ diagnostics.push(...graph.stitch());
666
+ const linkers = order(allPlugins.filter(isLinker));
667
+ for (const l of linkers) {
668
+ await runPlugin(
669
+ l.name,
670
+ diagnostics,
671
+ () => l.apply(createCtx(l.name, graph, config2, code, logger, diagnostics))
672
+ );
673
+ diagnostics.push(...graph.stitch());
674
+ }
675
+ const enrichers = order(allPlugins.filter(isEnricher));
676
+ await Promise.all(
677
+ enrichers.map(
678
+ (e) => runPlugin(
679
+ e.name,
680
+ diagnostics,
681
+ () => e.apply(createCtx(e.name, graph, config2, code, logger, diagnostics))
682
+ )
683
+ )
684
+ );
685
+ diagnostics.push(...graph.stitch());
686
+ } catch (err) {
687
+ if (err instanceof PanicError) {
688
+ diagnostics.push({
689
+ severity: "error",
690
+ code: "PANIC",
691
+ message: err.message,
692
+ plugin: err.plugin
693
+ });
694
+ } else {
695
+ throw err;
696
+ }
697
+ }
698
+ return { graph, diagnostics, ledger };
699
+ }
700
+ function silentSources(sources, graph, reported) {
701
+ const asserted = sources.filter((s) => !s.optional);
702
+ if (asserted.length === 0) return [];
703
+ const produced = new Set(graph.nodes().map((n) => n.owner));
704
+ const spoke = new Set(reported.map((d) => d.plugin));
705
+ const silent = asserted.filter((s) => !produced.has(s.name) && !spoke.has(s.name));
706
+ if (silent.length === 0) return [];
707
+ const severity = silent.length === asserted.length ? "error" : "warn";
708
+ return silent.map((s) => ({
709
+ severity,
710
+ code: "SOURCE_EMPTY",
711
+ message: `source "${s.name}" produced no nodes \u2014 its include patterns match nothing in this repo`,
712
+ plugin: s.name
713
+ }));
714
+ }
715
+ async function runEnforcers(config2, graph) {
716
+ const diagnostics = [];
717
+ const logger = createLogger();
718
+ const code = createCodeBackend(config2.backend);
719
+ const enforcers = config2.plugins.filter(isEnforcer);
720
+ try {
721
+ await Promise.all(
722
+ enforcers.map(
723
+ (e) => runPlugin(
724
+ e.name,
725
+ diagnostics,
726
+ () => e.check(createEnforcerCtx(e, graph, config2, code, logger, diagnostics))
727
+ )
728
+ )
729
+ );
730
+ } catch (err) {
731
+ if (err instanceof PanicError) {
732
+ diagnostics.push({
733
+ severity: "error",
734
+ code: "PANIC",
735
+ message: err.message,
736
+ plugin: err.plugin
737
+ });
738
+ } else {
739
+ throw err;
740
+ }
741
+ }
742
+ return diagnostics;
743
+ }
744
+ function createCtx(owner, graph, config2, code, logger, diagnostics) {
745
+ return {
746
+ config: config2,
747
+ graph,
748
+ code,
749
+ addNode: (node) => graph.addNode(node, owner),
750
+ addEdge: (edge) => graph.addEdge(edge, owner),
751
+ annotate: (req) => graph.annotate(req, owner),
752
+ report: (d) => diagnostics.push({ ...d, plugin: owner }),
753
+ logger,
754
+ panic: (message) => {
755
+ throw new PanicError(owner, message);
756
+ }
757
+ };
758
+ }
759
+ function createEnforcerCtx(e, graph, config2, code, logger, diagnostics) {
760
+ return {
761
+ config: config2,
762
+ graph,
763
+ code,
764
+ logger,
765
+ report: (r) => {
766
+ const nodeId = typeof r.node === "string" ? r.node : r.node?.id;
767
+ diagnostics.push({
768
+ severity: r.severity ?? "error",
769
+ code: r.code ?? e.name,
770
+ message: r.message,
771
+ plugin: e.name,
772
+ ...nodeId !== void 0 ? { nodeId } : {},
773
+ ...r.file !== void 0 ? { file: r.file } : {},
774
+ ...r.line !== void 0 ? { line: r.line } : {},
775
+ ...r.data !== void 0 ? { data: r.data } : {}
776
+ });
777
+ }
778
+ };
779
+ }
780
+ function createCodeBackend(b) {
781
+ return { getNativeContext: () => b.native?.() ?? { type: b.name } };
782
+ }
783
+ async function runPlugin(name, diagnostics, fn) {
784
+ try {
785
+ await fn();
786
+ } catch (err) {
787
+ if (err instanceof PanicError) throw err;
788
+ diagnostics.push({
789
+ severity: "error",
790
+ code: "PLUGIN_ERROR",
791
+ message: err instanceof Error ? err.message : String(err),
792
+ plugin: name
793
+ });
794
+ }
795
+ }
796
+ function order(plugins) {
797
+ const band = (p) => p.enforce === "pre" ? 0 : p.enforce === "post" ? 2 : 1;
798
+ return plugins.map((p, i) => ({ p, i })).sort((a, b) => band(a.p) - band(b.p) || a.i - b.i).map(({ p }) => p);
799
+ }
800
+ function isSource(p) {
801
+ return "seam" in p && p.seam === "source";
802
+ }
803
+ function isLinker(p) {
804
+ return "seam" in p && p.seam === "linker";
805
+ }
806
+ function isEnforcer(p) {
807
+ return "seam" in p && p.seam === "enforcer";
808
+ }
809
+ function isEnricher(p) {
810
+ return "seam" in p && p.seam === "enricher";
811
+ }
812
+ function isMapping(p) {
813
+ return "entry" in p && (p.entry === "map" || p.entry === "connect");
814
+ }
815
+ function createLogger() {
816
+ return {
817
+ info: (m) => process.stderr.write(`[data] ${m}
818
+ `),
819
+ warn: (m) => process.stderr.write(`[data:warn] ${m}
820
+ `),
821
+ error: (m) => process.stderr.write(`[data:error] ${m}
822
+ `)
823
+ };
824
+ }
825
+
826
+ // ../../packages/data/src/diagnostics.ts
827
+ var ICON = { error: "\u2717", warn: "!", info: "\xB7" };
828
+ function printDiagnostics(diagnostics) {
829
+ for (const d of diagnostics) {
830
+ const where = d.file ? ` (${d.file}${d.line !== void 0 ? `:${d.line}` : ""})` : d.nodeId !== void 0 ? ` [${d.nodeId}]` : "";
831
+ const by = d.plugin ? ` \xB7${d.plugin}` : "";
832
+ console.log(` ${ICON[d.severity] ?? "\xB7"} ${d.code}: ${d.message}${where}${by}`);
833
+ }
834
+ }
835
+ function summarize(diagnostics) {
836
+ return {
837
+ errors: diagnostics.filter((d) => d.severity === "error").length,
838
+ warns: diagnostics.filter((d) => d.severity === "warn").length
839
+ };
840
+ }
841
+
842
+ // ../../packages/data/src/parse.ts
843
+ function sections(body, at) {
844
+ const found = [...body.matchAll(everywhere(at))];
845
+ return found.flatMap((match, index) => {
846
+ const id = match[1]?.trim();
847
+ if (!id) return [];
848
+ const start = match.index ?? 0;
849
+ const end = found[index + 1]?.index ?? body.length;
850
+ const heading = (match[0].split(/\r?\n/)[0] ?? "").replace(/^#{1,6}\s+/, "").trim();
851
+ return [{ id, heading, span: [start, end], text: body.slice(start, end) }];
852
+ });
853
+ }
854
+ function matches(body, at) {
855
+ return [...body.matchAll(everywhere(at))].flatMap((match, index) => {
856
+ const found = match.groups;
857
+ if (!found) return [];
858
+ const groups = {};
859
+ for (const [key, value] of Object.entries(found)) {
860
+ if (value !== void 0) groups[key] = value.trim();
861
+ }
862
+ const id = groups["id"] ?? String(index);
863
+ if (!id) return [];
864
+ const start = match.index ?? 0;
865
+ return [
866
+ { id, groups, text: match[0], span: [start, start + match[0].length] }
867
+ ];
868
+ });
869
+ }
870
+ function values(data, key) {
871
+ const held = data?.[key];
872
+ const list = Array.isArray(held) ? held : held === void 0 || held === null ? [] : [held];
873
+ return list.map((raw) => String(raw).trim()).filter(Boolean);
874
+ }
875
+ function everywhere(re) {
876
+ let flags = re.flags;
877
+ if (!flags.includes("g")) flags += "g";
878
+ if (!flags.includes("m")) flags += "m";
879
+ return new RegExp(re.source, flags);
880
+ }
881
+
882
+ // ../../packages/data/src/scan.ts
883
+ function buildIdRegex(req) {
884
+ if (req.tokens.length === 0) return null;
885
+ const escaped = req.tokens.map((token) => token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
886
+ const before = req.notAnExtension ? "[A-Za-z0-9_.]" : "[A-Za-z0-9_]";
887
+ return new RegExp(`(?<!${before})(${escaped.join("|")})(?![A-Za-z0-9_])`, req.loose ? "gi" : "g");
888
+ }
889
+ function countOccurrences(text, regex, resolveToken) {
890
+ const occurrences = /* @__PURE__ */ new Map();
891
+ for (const line of text.split(/\r?\n/)) {
892
+ regex.lastIndex = 0;
893
+ let m;
894
+ while ((m = regex.exec(line)) !== null) {
895
+ const token = m[1];
896
+ const target = token === void 0 ? void 0 : resolveToken(token);
897
+ if (target === void 0) continue;
898
+ occurrences.set(target, (occurrences.get(target) ?? 0) + 1);
899
+ }
900
+ }
901
+ return occurrences;
902
+ }
903
+ function stripExampleSpans(text) {
904
+ return text.replace(/"[^"\n]*"/g, " ").replace(/`[^`\n]*`/g, " ");
905
+ }
906
+
907
+ // ../../packages/data/src/edges.ts
908
+ var COVERAGE_VIA = { colocation: "colocation", import: "import" };
909
+ var IMPORT_RELATIONS = ["imports", "imports_from"];
910
+
911
+ // ../../packages/data/src/confidence.ts
912
+ var FUZZY_RELATIONS = /* @__PURE__ */ new Set([
913
+ "relates_to",
914
+ "touches",
915
+ "touchedBy",
916
+ "bridges",
917
+ "bridgedBy"
918
+ ]);
919
+ var FUZZY_EDGE_META = "data.confidence";
920
+ function isPrecise(edge, backendName) {
921
+ const backendOwned = edge.metadata?.["data.federated.backendOwned"] === true || edge.owner === backendName;
922
+ const declaredFuzzy = edge.metadata?.[FUZZY_EDGE_META] === "fuzzy";
923
+ return !backendOwned && !declaredFuzzy && !FUZZY_RELATIONS.has(edge.relation);
924
+ }
925
+ function edgeMatchesConfidence(edge, backendName, confidence) {
926
+ if (confidence === "all") return true;
927
+ return confidence === "precise" ? isPrecise(edge, backendName) : !isPrecise(edge, backendName);
928
+ }
929
+
930
+ // ../../packages/data/src/code.ts
931
+ function filesOf(graph, req) {
932
+ const index = { code: /* @__PURE__ */ new Map(), docs: /* @__PURE__ */ new Map() };
933
+ for (const node of graph.nodes()) {
934
+ const file = asString(node.metadata.sourceFile);
935
+ if (file === void 0) continue;
936
+ const side = node.owner === req.backend ? index.code : index.docs;
937
+ const held = side.get(file);
938
+ if (held) held.push(node);
939
+ else side.set(file, [node]);
940
+ }
941
+ return index;
942
+ }
943
+ function lineOf(value) {
944
+ if (typeof value !== "string") return Infinity;
945
+ const found = /\d+/.exec(value);
946
+ return found ? Number(found[0]) : Infinity;
947
+ }
948
+ function asString(value) {
949
+ return typeof value === "string" ? value : void 0;
950
+ }
951
+
952
+ // ../../packages/data/src/config.ts
953
+ import { existsSync, readFileSync } from "fs";
954
+ import { readFile } from "fs/promises";
955
+ import { dirname, resolve } from "path";
956
+ import { fileURLToPath } from "url";
957
+ import { createJiti } from "jiti";
958
+ var CONFIG_NAMES = [
959
+ "jarvis.config.ts",
960
+ "jarvis.config.mts",
961
+ "jarvis.config.mjs",
962
+ "jarvis.config.js",
963
+ "jarvis.config.cjs"
964
+ ];
965
+ async function loadConfig(cwd) {
966
+ let dir = resolve(cwd);
967
+ for (; ; ) {
968
+ for (const name of CONFIG_NAMES) {
969
+ const path = resolve(dir, name);
970
+ if (existsSync(path)) {
971
+ const config2 = await importConfig(path);
972
+ validateConfig(config2, path);
973
+ return {
974
+ ...config2,
975
+ persistence: config2.persistence ?? localFiles(),
976
+ repoRoot: dir
977
+ };
978
+ }
979
+ }
980
+ const parent = dirname(dir);
981
+ if (parent === dir) break;
982
+ dir = parent;
983
+ }
984
+ throw new Error(
985
+ `No jarvis.config.{ts,js,mjs} found from ${cwd}. A repo maps its world in one, at its root.`
986
+ );
987
+ }
988
+ async function importConfig(path) {
989
+ const jiti = createJiti(import.meta.url, { moduleCache: false, alias: selfAlias() });
990
+ const mod = await jiti.import(path);
991
+ const config2 = mod.default ?? mod;
992
+ return config2;
993
+ }
994
+ var PACKAGE_NAME = "@jarvis/data";
995
+ function packageRoot() {
996
+ let dir = dirname(fileURLToPath(import.meta.url));
997
+ for (; ; ) {
998
+ if (existsSync(resolve(dir, "package.json"))) return dir;
999
+ const parent = dirname(dir);
1000
+ if (parent === dir) return dir;
1001
+ dir = parent;
1002
+ }
1003
+ }
1004
+ function selfAlias() {
1005
+ const root = packageRoot();
1006
+ const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
1007
+ if (pkg.name !== PACKAGE_NAME) {
1008
+ const vendored = process.env[VENDOR_ENV];
1009
+ if (!vendored) {
1010
+ throw new Error(
1011
+ `${PACKAGE_NAME} is running inside "${pkg.name ?? "an unnamed package"}" and cannot locate its own modules. A host that bundles the engine must set ${VENDOR_ENV} to the directory holding its built copy.`
1012
+ );
1013
+ }
1014
+ return toVendorAlias(vendored);
1015
+ }
1016
+ const alias = {};
1017
+ for (const [subpath, entry] of Object.entries(pkg.exports ?? {})) {
1018
+ const target = typeof entry === "string" ? entry : entry.default;
1019
+ if (!target) continue;
1020
+ alias[toSpecifier(subpath)] = resolve(root, target);
1021
+ }
1022
+ return alias;
1023
+ }
1024
+ var VENDOR_ENV = "JARVIS_DATA_VENDOR";
1025
+ var VENDOR_SUBPATHS = [
1026
+ "mcp",
1027
+ "backends",
1028
+ "embedders",
1029
+ "finders",
1030
+ "linkers",
1031
+ "persistences",
1032
+ "rerankers",
1033
+ "stores"
1034
+ ];
1035
+ function toVendorAlias(dir) {
1036
+ const alias = { [PACKAGE_NAME]: resolve(dir, "index.mjs") };
1037
+ for (const sub of VENDOR_SUBPATHS) alias[`${PACKAGE_NAME}/${sub}`] = resolve(dir, `${sub}.mjs`);
1038
+ return alias;
1039
+ }
1040
+ function toSpecifier(subpath) {
1041
+ return subpath === "." ? PACKAGE_NAME : `${PACKAGE_NAME}/${subpath.replace(/^\.\//, "")}`;
1042
+ }
1043
+ function validateConfig(value, path) {
1044
+ const fail = (msg) => {
1045
+ throw new Error(`${path}: ${msg}`);
1046
+ };
1047
+ if (typeof value !== "object" || value === null) fail("config must export an object");
1048
+ const c = value;
1049
+ const backend2 = c.backend;
1050
+ if (!backend2 || backend2.seam !== "backend")
1051
+ fail("`backend` must be a backend plugin (e.g. graphify({...}))");
1052
+ if (c.persistence !== void 0) {
1053
+ const persistence2 = c.persistence;
1054
+ if (persistence2.seam !== "persistence")
1055
+ fail("`persistence` must be a persistence plugin (e.g. localFiles())");
1056
+ }
1057
+ if (!Array.isArray(c.plugins)) fail("`plugins` must be an array");
1058
+ for (const p of c.plugins) {
1059
+ const routable = p?.entry === "map" || p?.entry === "connect" || p?.seam === "source" || p?.seam === "linker" || p?.seam === "enforcer" || p?.seam === "enricher" || p?.seam === "remote";
1060
+ if (!routable) {
1061
+ fail(
1062
+ `every entry must be a map or a connect, or a source/linker/enforcer/enricher/remote (got ${JSON.stringify(p)})`
1063
+ );
1064
+ }
1065
+ }
1066
+ const search = c.search;
1067
+ if (search !== void 0) {
1068
+ if (typeof search.embedder?.embed !== "function")
1069
+ fail("`search.embedder` must be an embedder (e.g. localEmbeddings())");
1070
+ if (search.store !== void 0 && typeof search.store.query !== "function")
1071
+ fail("`search.store` must be a vector store");
1072
+ if (search.reranker !== void 0 && typeof search.reranker.rerank !== "function")
1073
+ fail("`search.reranker` must be a reranker (e.g. localReranker())");
1074
+ }
1075
+ }
1076
+ async function readJson(path) {
1077
+ return JSON.parse(await readFile(path, "utf8"));
1078
+ }
1079
+
1080
+ // ../../packages/data/src/vector.ts
1081
+ function normalize(vector) {
1082
+ let sum = 0;
1083
+ for (const x of vector) sum += x * x;
1084
+ const mag = Math.sqrt(sum);
1085
+ return mag > 0 ? vector.map((x) => x / mag) : vector;
1086
+ }
1087
+ function dot(a, b) {
1088
+ let sum = 0;
1089
+ const n = Math.min(a.length, b.length);
1090
+ for (let i = 0; i < n; i++) {
1091
+ const x = a[i];
1092
+ const y = b[i];
1093
+ if (x !== void 0 && y !== void 0) sum += x * y;
1094
+ }
1095
+ return sum;
1096
+ }
1097
+
1098
+ // ../../packages/data/src/memory.ts
1099
+ var MEMORY_OWNER = "memory";
1100
+ function emptyStore() {
1101
+ return { version: 1, memories: [], audit: [] };
1102
+ }
1103
+ async function readMemoryStore(config2) {
1104
+ const ctx = { repoRoot: config2.repoRoot };
1105
+ try {
1106
+ const raw = await config2.persistence.load(ctx, "memory");
1107
+ if (raw) {
1108
+ const parsed = JSON.parse(raw);
1109
+ if (parsed.version === 1 && Array.isArray(parsed.memories) && Array.isArray(parsed.audit)) {
1110
+ return parsed;
1111
+ }
1112
+ }
1113
+ } catch {
1114
+ }
1115
+ return emptyStore();
1116
+ }
1117
+ async function writeMemoryStore(config2, store2) {
1118
+ const ctx = { repoRoot: config2.repoRoot };
1119
+ await config2.persistence.save(ctx, "memory", `${JSON.stringify(store2, null, 2)}
1120
+ `);
1121
+ }
1122
+ function deleteMemory(store2, id) {
1123
+ const index = store2.memories.findIndex((m) => m.id === id);
1124
+ if (index < 0) return void 0;
1125
+ const [removed] = store2.memories.splice(index, 1);
1126
+ return removed;
1127
+ }
1128
+ function memoryNode(rec) {
1129
+ return {
1130
+ id: rec.id,
1131
+ type: rec.type,
1132
+ metadata: { title: rec.text, "data.origin": "agent", origin: "agent" }
1133
+ };
1134
+ }
1135
+ function resolveLinkTarget(graph, backendName, to) {
1136
+ const direct = graph.node(to);
1137
+ if (direct) return direct.id;
1138
+ let suffixMatch;
1139
+ for (const n of graph.nodes()) {
1140
+ if (n.owner !== backendName) continue;
1141
+ const f = n.metadata.sourceFile;
1142
+ if (typeof f !== "string") continue;
1143
+ if (f === to) return n.id;
1144
+ if (suffixMatch === void 0 && (f.endsWith(`/${to}`) || f.endsWith(to))) suffixMatch = n.id;
1145
+ }
1146
+ return suffixMatch;
1147
+ }
1148
+ async function applyMemoryOverlay(config2, graph, options = {}) {
1149
+ const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
1150
+ const store2 = await readMemoryStore(config2);
1151
+ const backendName = config2.backend.name;
1152
+ const flipped = [];
1153
+ let orphanedCount = 0;
1154
+ for (const rec of store2.memories) {
1155
+ graph.addNode(memoryNode(rec), MEMORY_OWNER);
1156
+ let anyResolved = false;
1157
+ for (const link2 of rec.links) {
1158
+ const targetId = resolveLinkTarget(graph, backendName, link2.to);
1159
+ if (targetId === void 0) continue;
1160
+ graph.addEdge({ from: rec.id, to: targetId, relation: link2.relation }, MEMORY_OWNER);
1161
+ anyResolved = true;
1162
+ }
1163
+ const wasOrphaned = rec.orphaned === true;
1164
+ const nowOrphaned = rec.links.length > 0 && !anyResolved;
1165
+ if (nowOrphaned !== wasOrphaned) flipped.push(rec);
1166
+ rec.orphaned = nowOrphaned;
1167
+ if (nowOrphaned) orphanedCount++;
1168
+ }
1169
+ graph.stitch();
1170
+ if (flipped.length > 0) {
1171
+ for (const rec of flipped) {
1172
+ store2.audit.push({
1173
+ at: now(),
1174
+ actor: "system",
1175
+ action: "reconcile",
1176
+ id: rec.id,
1177
+ rationale: rec.orphaned ? "no link target resolved on rebuild" : "a link target resolved again"
1178
+ });
1179
+ }
1180
+ await writeMemoryStore(config2, store2);
1181
+ }
1182
+ return { loaded: store2.memories.length, orphaned: orphanedCount };
1183
+ }
1184
+
1185
+ export {
1186
+ GraphStore,
1187
+ DERIVED_FROM,
1188
+ TIER,
1189
+ RESERVED,
1190
+ formatLedger,
1191
+ mintId,
1192
+ slug,
1193
+ categoryOf,
1194
+ buildGraph,
1195
+ runEnforcers,
1196
+ printDiagnostics,
1197
+ summarize,
1198
+ sections,
1199
+ matches,
1200
+ values,
1201
+ everywhere,
1202
+ buildIdRegex,
1203
+ countOccurrences,
1204
+ stripExampleSpans,
1205
+ COVERAGE_VIA,
1206
+ IMPORT_RELATIONS,
1207
+ FUZZY_RELATIONS,
1208
+ FUZZY_EDGE_META,
1209
+ isPrecise,
1210
+ edgeMatchesConfidence,
1211
+ filesOf,
1212
+ lineOf,
1213
+ asString,
1214
+ loadConfig,
1215
+ validateConfig,
1216
+ readJson,
1217
+ normalize,
1218
+ dot,
1219
+ readMemoryStore,
1220
+ writeMemoryStore,
1221
+ deleteMemory,
1222
+ memoryNode,
1223
+ applyMemoryOverlay
1224
+ };