@inerrata-corporation/errata 2.0.0-dev.35 → 2.0.0-dev.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/errata.mjs CHANGED
@@ -15268,6 +15268,85 @@ var init_edge_rules = __esm({
15268
15268
  }
15269
15269
  });
15270
15270
 
15271
+ // ../../packages/shared/src/symbol-intent.ts
15272
+ function extractIdentifierTokens(text) {
15273
+ const out2 = [];
15274
+ const seen = /* @__PURE__ */ new Set();
15275
+ for (const m of text.matchAll(TOKEN_RE2)) {
15276
+ if (!seen.has(m[0])) {
15277
+ seen.add(m[0]);
15278
+ out2.push(m[0]);
15279
+ }
15280
+ }
15281
+ return out2;
15282
+ }
15283
+ function summaryRejectionReason(summary, isKnownToken) {
15284
+ if (!summary.trim()) return "empty summary";
15285
+ if (summary.length > MAX_SYMBOL_SUMMARY_CHARS) return "summary too long";
15286
+ if (PATH_SHAPE_RE.test(summary)) return "contains a path shape";
15287
+ if (QUOTED_LITERAL_RE.test(summary.replace(CONTRACTION_RE, "$1$2")))
15288
+ return "contains a quoted literal";
15289
+ for (const tok of extractIdentifierTokens(summary)) {
15290
+ if (isKnownToken(tok)) return `contains verbatim identifier '${tok}'`;
15291
+ if (tok.includes(".")) {
15292
+ const segs = tok.split(".");
15293
+ for (let i2 = 0; i2 < segs.length; i2++) {
15294
+ let span = "";
15295
+ for (let j = i2; j < segs.length; j++) {
15296
+ span = span ? `${span}.${segs[j]}` : segs[j];
15297
+ if (span !== tok && span && isKnownToken(span))
15298
+ return `contains verbatim identifier '${span}'`;
15299
+ }
15300
+ }
15301
+ }
15302
+ }
15303
+ return null;
15304
+ }
15305
+ function substituteSymbolSummaries(text, summaryByToken) {
15306
+ if (summaryByToken.size === 0) return text;
15307
+ const present = extractIdentifierTokens(text).filter((t) => summaryByToken.has(t));
15308
+ present.sort((a, b) => b.length - a.length);
15309
+ let out2 = text;
15310
+ for (const key of present) {
15311
+ const re = new RegExp(`(?<![A-Za-z0-9_$.])${escapeRe2(key)}(?![A-Za-z0-9_$])`, "g");
15312
+ out2 = out2.replace(re, summaryByToken.get(key));
15313
+ }
15314
+ return out2;
15315
+ }
15316
+ function vetSidecarSummaries(sidecar) {
15317
+ if (!sidecar) return null;
15318
+ const keys = new Set(Object.keys(sidecar));
15319
+ if (keys.size === 0 || keys.size > MAX_SIDECAR_SYMBOLS) return null;
15320
+ const clean = /* @__PURE__ */ new Map();
15321
+ for (const [symbol2, entry] of Object.entries(sidecar)) {
15322
+ if (!entry || typeof entry.summary !== "string") continue;
15323
+ if (summaryRejectionReason(entry.summary, (t) => keys.has(t)) !== null) continue;
15324
+ clean.set(symbol2, entry.summary);
15325
+ }
15326
+ return clean.size > 0 ? clean : null;
15327
+ }
15328
+ var SYMBOL_SUMMARY_KINDS, MAX_SYMBOL_SUMMARY_CHARS, MAX_SIDECAR_SYMBOLS, TOKEN_RE2, RE_RESERVED, escapeRe2, PATH_SHAPE_RE, CONTRACTION_RE, QUOTED_LITERAL_RE;
15329
+ var init_symbol_intent = __esm({
15330
+ "../../packages/shared/src/symbol-intent.ts"() {
15331
+ "use strict";
15332
+ SYMBOL_SUMMARY_KINDS = [
15333
+ "function",
15334
+ "method",
15335
+ "class",
15336
+ "interface",
15337
+ "constant"
15338
+ ];
15339
+ MAX_SYMBOL_SUMMARY_CHARS = 300;
15340
+ MAX_SIDECAR_SYMBOLS = 200;
15341
+ TOKEN_RE2 = /[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*/g;
15342
+ RE_RESERVED = /[.*+?^${}()|[\]\\]/g;
15343
+ escapeRe2 = (s) => s.replace(RE_RESERVED, "\\$&");
15344
+ PATH_SHAPE_RE = /[\w.~-]+[/\\][\w.$~-]+/;
15345
+ CONTRACTION_RE = /([A-Za-z])'([A-Za-z])/g;
15346
+ QUOTED_LITERAL_RE = /"[^"]+"|'[^']+'|`[^`]+`/;
15347
+ }
15348
+ });
15349
+
15271
15350
  // ../../packages/shared/src/wire.ts
15272
15351
  function provenanceBand(source) {
15273
15352
  switch (source) {
@@ -15317,13 +15396,14 @@ function summarizeIngestResult(r) {
15317
15396
  ...r.patternReconciliation ? { patternReconciliation: r.patternReconciliation } : {}
15318
15397
  };
15319
15398
  }
15320
- var CLOUD_NODE_LABELS, CLOUD_EDGE_TYPES, INGEST_SOURCES, INGEST_EXTRACTION_SOURCES, MAX_NODES_PER_PAYLOAD, MAX_EDGES_PER_PAYLOAD, CastaliaIngestNodeSchema, RouteContextCountWireSchema, CastaliaIngestEdgeSchema, CastaliaIngestPayloadSchema;
15399
+ var CLOUD_NODE_LABELS, CLOUD_EDGE_TYPES, INGEST_SOURCES, INGEST_EXTRACTION_SOURCES, MAX_NODES_PER_PAYLOAD, MAX_EDGES_PER_PAYLOAD, CastaliaIngestNodeSchema, RouteContextCountWireSchema, CastaliaIngestEdgeSchema, SymbolSummarySidecarSchema, CastaliaIngestPayloadSchema;
15321
15400
  var init_wire = __esm({
15322
15401
  "../../packages/shared/src/wire.ts"() {
15323
15402
  "use strict";
15324
15403
  init_zod();
15325
15404
  init_castalia();
15326
15405
  init_edge_rules();
15406
+ init_symbol_intent();
15327
15407
  CLOUD_NODE_LABELS = [
15328
15408
  ...SEMANTIC_NODE_LABELS,
15329
15409
  ...CONTEXT_NODE_LABELS
@@ -15373,12 +15453,24 @@ var init_wire = __esm({
15373
15453
  perContext: external_exports.record(external_exports.string(), RouteContextCountWireSchema).optional()
15374
15454
  }).optional()
15375
15455
  });
15456
+ SymbolSummarySidecarSchema = external_exports.record(
15457
+ external_exports.string().min(1).max(160),
15458
+ external_exports.object({
15459
+ kind: external_exports.enum(SYMBOL_SUMMARY_KINDS),
15460
+ summary: external_exports.string().min(1).max(MAX_SYMBOL_SUMMARY_CHARS)
15461
+ })
15462
+ ).refine((m) => Object.keys(m).length <= MAX_SIDECAR_SYMBOLS, {
15463
+ message: `symbolSummaries sidecar exceeds ${MAX_SIDECAR_SYMBOLS} entries`
15464
+ });
15376
15465
  CastaliaIngestPayloadSchema = external_exports.object({
15377
15466
  /** Client-supplied UUID — one batch; pairs with the payload digest for replay. */
15378
15467
  runId: external_exports.string().uuid(),
15379
15468
  source: external_exports.enum(INGEST_SOURCES),
15380
15469
  nodes: external_exports.array(CastaliaIngestNodeSchema).max(1e3),
15381
- edges: external_exports.array(CastaliaIngestEdgeSchema).max(5e3)
15470
+ edges: external_exports.array(CastaliaIngestEdgeSchema).max(5e3),
15471
+ /** Optional symbol→intent-summary sidecar (PP-symbol-intent). Only symbols
15472
+ * present in this payload's node text; server re-vets no-verbatim. */
15473
+ symbolSummaries: SymbolSummarySidecarSchema.optional()
15382
15474
  });
15383
15475
  }
15384
15476
  });
@@ -15443,12 +15535,16 @@ __export(src_exports, {
15443
15535
  JUSTIFICATION_EDGES: () => JUSTIFICATION_EDGES,
15444
15536
  MAX_EDGES_PER_PAYLOAD: () => MAX_EDGES_PER_PAYLOAD,
15445
15537
  MAX_NODES_PER_PAYLOAD: () => MAX_NODES_PER_PAYLOAD,
15538
+ MAX_SIDECAR_SYMBOLS: () => MAX_SIDECAR_SYMBOLS,
15539
+ MAX_SYMBOL_SUMMARY_CHARS: () => MAX_SYMBOL_SUMMARY_CHARS,
15446
15540
  PAGERANK_EXCLUSIONS: () => PAGERANK_EXCLUSIONS,
15447
15541
  PROBLEM_RESOLUTION: () => PROBLEM_RESOLUTION,
15448
15542
  RESOLUTION_EDGES: () => RESOLUTION_EDGES,
15449
15543
  RouteContextCountWireSchema: () => RouteContextCountWireSchema,
15450
15544
  SEMANTIC_NODE_LABELS: () => SEMANTIC_NODE_LABELS,
15451
15545
  STRUCTURAL_EDGES: () => STRUCTURAL_EDGES,
15546
+ SYMBOL_SUMMARY_KINDS: () => SYMBOL_SUMMARY_KINDS,
15547
+ SymbolSummarySidecarSchema: () => SymbolSummarySidecarSchema,
15452
15548
  TAXONOMY_EDGES: () => TAXONOMY_EDGES,
15453
15549
  TRANSFER_EDGES: () => TRANSFER_EDGES,
15454
15550
  TRIAGE_EDGES: () => TRIAGE_EDGES,
@@ -15468,6 +15564,7 @@ __export(src_exports, {
15468
15564
  detectStance: () => detectStance,
15469
15565
  digest: () => digest,
15470
15566
  errorSignatureEqual: () => errorSignatureEqual,
15567
+ extractIdentifierTokens: () => extractIdentifierTokens,
15471
15568
  genericCanonicalId: () => genericCanonicalId,
15472
15569
  identityId: () => identityId,
15473
15570
  isCausalProtected: () => isCausalProtected,
@@ -15493,9 +15590,12 @@ __export(src_exports, {
15493
15590
  resolveCanonicalId: () => resolveCanonicalId,
15494
15591
  sha256: () => sha256,
15495
15592
  sha256Short: () => sha256Short,
15593
+ substituteSymbolSummaries: () => substituteSymbolSummaries,
15496
15594
  summarizeIngestResult: () => summarizeIngestResult,
15595
+ summaryRejectionReason: () => summaryRejectionReason,
15497
15596
  toCloudAttrs: () => toCloudAttrs,
15498
- validateCastaliaPayload: () => validateCastaliaPayload
15597
+ validateCastaliaPayload: () => validateCastaliaPayload,
15598
+ vetSidecarSummaries: () => vetSidecarSummaries
15499
15599
  });
15500
15600
  var init_src = __esm({
15501
15601
  "../../packages/shared/src/index.ts"() {
@@ -15504,6 +15604,7 @@ var init_src = __esm({
15504
15604
  init_identity();
15505
15605
  init_wire();
15506
15606
  init_edge_rules();
15607
+ init_symbol_intent();
15507
15608
  init_hash();
15508
15609
  init_error_signature();
15509
15610
  init_canonicalize();
@@ -17246,7 +17347,7 @@ function buildPackageIndex(store) {
17246
17347
  }
17247
17348
  return idx;
17248
17349
  }
17249
- function escapeRe2(s) {
17350
+ function escapeRe3(s) {
17250
17351
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17251
17352
  }
17252
17353
  function matchPackagesInText(text, index) {
@@ -17256,7 +17357,7 @@ function matchPackagesInText(text, index) {
17256
17357
  for (const [key, node2] of index) {
17257
17358
  if (key.length < MIN_NAME_LEN) continue;
17258
17359
  if (!hay.includes(key)) continue;
17259
- const esc3 = escapeRe2(key);
17360
+ const esc3 = escapeRe3(key);
17260
17361
  const bounded = new RegExp(`(?<![\\w@/.\\-])${esc3}(?![\\w])`);
17261
17362
  if (!bounded.test(hay)) continue;
17262
17363
  const verMatch = new RegExp(`${esc3}\\s*@?\\s*v?(\\d+(?:\\.\\d+){0,2})\\b`).exec(hay);
@@ -20507,6 +20608,20 @@ var init_oauth = __esm({
20507
20608
 
20508
20609
  // ../../packages/cloud-client/src/client.ts
20509
20610
  import { randomUUID } from "node:crypto";
20611
+ function sidecarForNodes(sidecar, nodes) {
20612
+ if (!sidecar) return void 0;
20613
+ const present = /* @__PURE__ */ new Set();
20614
+ for (const n of nodes) for (const tok of extractIdentifierTokens(n.description)) present.add(tok);
20615
+ const filtered = {};
20616
+ let count = 0;
20617
+ for (const [symbol2, entry] of Object.entries(sidecar)) {
20618
+ if (present.has(symbol2)) {
20619
+ filtered[symbol2] = entry;
20620
+ count++;
20621
+ }
20622
+ }
20623
+ return count > 0 ? filtered : void 0;
20624
+ }
20510
20625
  function toWirePayload(batch, runId) {
20511
20626
  const nodes = batch.nodes.map((n) => ({
20512
20627
  canonicalId: n.id,
@@ -20525,7 +20640,8 @@ function toWirePayload(batch, runId) {
20525
20640
  ...typeof e.confidence === "number" ? { confidence: clamp012(e.confidence) } : {}
20526
20641
  }
20527
20642
  }));
20528
- return { runId, source: "daemon", nodes, edges };
20643
+ const symbolSummaries = sidecarForNodes(batch.symbolSummaries, nodes);
20644
+ return { runId, source: "daemon", nodes, edges, ...symbolSummaries ? { symbolSummaries } : {} };
20529
20645
  }
20530
20646
  function clamp012(x) {
20531
20647
  return Math.max(0, Math.min(1, x));
@@ -23939,21 +24055,32 @@ function isDistinctiveIdentifier(name2) {
23939
24055
  if (/^[A-Z]/.test(name2) && name2.length >= 4) return true;
23940
24056
  return name2.length >= 8;
23941
24057
  }
23942
- function buildSymbolLexicon(store) {
24058
+ function buildSymbolLexicon(store, summariesByBodyHash2) {
23943
24059
  const lex = /* @__PURE__ */ new Map();
24060
+ const candidates = [];
23944
24061
  for (const [label, kind] of SYMBOL_LABELS) {
23945
24062
  for (const n of store.findNodesByLabel(label)) {
24063
+ const bodyHash = typeof n.attrs["bodyHash"] === "string" ? n.attrs["bodyHash"] : void 0;
24064
+ const summary = bodyHash ? summariesByBodyHash2?.get(bodyHash) : void 0;
23946
24065
  for (const name2 of [n.description, String(n.attrs["qname"] ?? "")]) {
23947
- if (name2 && isDistinctiveIdentifier(name2)) lex.set(name2, kind);
24066
+ if (name2 && isDistinctiveIdentifier(name2)) {
24067
+ if (!lex.has(name2)) lex.set(name2, { kind });
24068
+ if (summary) candidates.push([name2, summary]);
24069
+ }
23948
24070
  }
23949
24071
  }
23950
24072
  }
24073
+ for (const [name2, summary] of candidates) {
24074
+ if (summaryRejectionReason(summary, (tok) => lex.has(tok)) === null) {
24075
+ lex.get(name2).summary = summary;
24076
+ }
24077
+ }
23951
24078
  return lex;
23952
24079
  }
23953
24080
  function generalizeSymbols(text, lexicon, level = 1) {
23954
24081
  const present = [];
23955
24082
  const seen = /* @__PURE__ */ new Set();
23956
- for (const m of text.matchAll(TOKEN_RE2)) {
24083
+ for (const m of text.matchAll(TOKEN_RE3)) {
23957
24084
  const tok = m[0];
23958
24085
  if (!seen.has(tok) && lexicon.has(tok)) {
23959
24086
  seen.add(tok);
@@ -23963,17 +24090,19 @@ function generalizeSymbols(text, lexicon, level = 1) {
23963
24090
  present.sort((a, b) => b.length - a.length);
23964
24091
  let out2 = text;
23965
24092
  for (const key of present) {
23966
- const phrase = KIND_PHRASE[lexicon.get(key)][level === 1 ? "l1" : "l2"];
23967
- const re = new RegExp(`(?<![A-Za-z0-9_$.])${escapeRe3(key)}(?![A-Za-z0-9_$])`, "g");
24093
+ const entry = lexicon.get(key);
24094
+ const phrase = level === 1 && entry.summary ? entry.summary : KIND_PHRASE[entry.kind][level === 1 ? "l1" : "l2"];
24095
+ const re = new RegExp(`(?<![A-Za-z0-9_$.])${escapeRe4(key)}(?![A-Za-z0-9_$])`, "g");
23968
24096
  out2 = out2.replace(re, phrase);
23969
24097
  }
23970
24098
  return generalize(out2, { level }).text;
23971
24099
  }
23972
- var SYMBOL_LABELS, KIND_PHRASE, RE_RESERVED, escapeRe3, TOKEN_RE2;
24100
+ var SYMBOL_LABELS, KIND_PHRASE, RE_RESERVED2, escapeRe4, TOKEN_RE3;
23973
24101
  var init_generalize_graph = __esm({
23974
24102
  "src/generalize-graph.ts"() {
23975
24103
  "use strict";
23976
24104
  init_src8();
24105
+ init_src();
23977
24106
  SYMBOL_LABELS = [
23978
24107
  ["Function", "function"],
23979
24108
  ["Method", "method"],
@@ -23988,9 +24117,9 @@ var init_generalize_graph = __esm({
23988
24117
  interface: { l1: "an interface", l2: "an interface" },
23989
24118
  constant: { l1: "a constant", l2: "a value" }
23990
24119
  };
23991
- RE_RESERVED = /[.*+?^${}()|[\]\\]/g;
23992
- escapeRe3 = (s) => s.replace(RE_RESERVED, "\\$&");
23993
- TOKEN_RE2 = /[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*/g;
24120
+ RE_RESERVED2 = /[.*+?^${}()|[\]\\]/g;
24121
+ escapeRe4 = (s) => s.replace(RE_RESERVED2, "\\$&");
24122
+ TOKEN_RE3 = /[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*/g;
23994
24123
  }
23995
24124
  });
23996
24125
 
@@ -24095,7 +24224,7 @@ async function dualAugment(opts) {
24095
24224
  let seedProvenance = "local";
24096
24225
  const calls = [];
24097
24226
  try {
24098
- const lexicon = buildSymbolLexicon(store);
24227
+ const lexicon = buildSymbolLexicon(store, opts.summaries);
24099
24228
  const hardIds = [.../* @__PURE__ */ new Set([...seedNode ? [seedNode.id] : [], ...localHits.map((h) => h.id)])].filter((nid) => {
24100
24229
  const n = seedNode && nid === seedNode.id ? seedNode : store.getNode(nid);
24101
24230
  return !!n && isHardBridge(n);
@@ -25920,6 +26049,177 @@ var init_src10 = __esm({
25920
26049
  }
25921
26050
  });
25922
26051
 
26052
+ // src/symbol-summaries.ts
26053
+ import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
26054
+ import { join as join6 } from "node:path";
26055
+ function symbolSummariesPath(configDir) {
26056
+ return join6(configDir, "symbol-summaries.json");
26057
+ }
26058
+ function loadSymbolSummaryCache(configDir) {
26059
+ const p = symbolSummariesPath(configDir);
26060
+ if (existsSync7(p)) {
26061
+ try {
26062
+ const raw2 = JSON.parse(readFileSync5(p, "utf8"));
26063
+ if (raw2 && raw2.version === 1 && raw2.entries && typeof raw2.entries === "object") {
26064
+ return { version: 1, entries: raw2.entries };
26065
+ }
26066
+ } catch {
26067
+ }
26068
+ }
26069
+ return { version: 1, entries: {} };
26070
+ }
26071
+ function saveSymbolSummaryCache(configDir, cache) {
26072
+ writeFileSync6(symbolSummariesPath(configDir), JSON.stringify(cache, null, 2), "utf8");
26073
+ }
26074
+ function summariesByBodyHash(cache) {
26075
+ const m = /* @__PURE__ */ new Map();
26076
+ for (const [bodyHash, e] of Object.entries(cache.entries)) {
26077
+ if (!e.rejected && typeof e.summary === "string" && e.summary.trim()) m.set(bodyHash, e.summary);
26078
+ }
26079
+ return m;
26080
+ }
26081
+ function parseSummaryJson(text, expected) {
26082
+ const start2 = text.indexOf("[");
26083
+ const end = text.lastIndexOf("]");
26084
+ if (start2 < 0 || end <= start2) return new Array(expected).fill(null);
26085
+ let arr;
26086
+ try {
26087
+ arr = JSON.parse(text.slice(start2, end + 1));
26088
+ } catch {
26089
+ return new Array(expected).fill(null);
26090
+ }
26091
+ if (!Array.isArray(arr)) return new Array(expected).fill(null);
26092
+ const out2 = [];
26093
+ for (let i2 = 0; i2 < expected; i2++) {
26094
+ const v = arr[i2];
26095
+ out2.push(typeof v === "string" && v.trim() ? v.trim().slice(0, MAX_SYMBOL_SUMMARY_CHARS) : null);
26096
+ }
26097
+ return out2;
26098
+ }
26099
+ function haikuIntentSummarizer(apiKey) {
26100
+ if (!apiKey) return void 0;
26101
+ return async (symbols) => {
26102
+ try {
26103
+ const listing = symbols.map(
26104
+ (s, i2) => `### ${i2 + 1}. kind: ${s.kind}
26105
+ name: ${s.name}
26106
+ ${s.snippet ? `\`\`\`
26107
+ ${s.snippet}
26108
+ \`\`\`` : "(no source available \u2014 describe from the name)"}`
26109
+ ).join("\n\n");
26110
+ const resp = await fetch("https://api.anthropic.com/v1/messages", {
26111
+ method: "POST",
26112
+ headers: {
26113
+ "x-api-key": apiKey,
26114
+ "anthropic-version": "2023-06-01",
26115
+ "content-type": "application/json"
26116
+ },
26117
+ body: JSON.stringify({
26118
+ model: SUMMARY_MODEL,
26119
+ max_tokens: 2e3,
26120
+ messages: [{ role: "user", content: `${SUMMARY_PROMPT}
26121
+
26122
+ SYMBOLS:
26123
+ ${listing}` }]
26124
+ })
26125
+ });
26126
+ if (!resp.ok) return symbols.map(() => null);
26127
+ const json2 = await resp.json();
26128
+ return parseSummaryJson(json2.content?.find((c) => c.type === "text")?.text ?? "", symbols.length);
26129
+ } catch {
26130
+ return symbols.map(() => null);
26131
+ }
26132
+ };
26133
+ }
26134
+ function envIntentSummarizer() {
26135
+ if (process.env["ERRATA_SYMBOL_SUMMARIES"] !== "1") return void 0;
26136
+ return haikuIntentSummarizer(process.env["ANTHROPIC_API_KEY"] ?? null);
26137
+ }
26138
+ function readSnippet(workspaceRoot, attrs) {
26139
+ const relPath = attrs["relPath"];
26140
+ const startByte = attrs["startByte"];
26141
+ const endByte = attrs["endByte"];
26142
+ if (typeof relPath !== "string" || typeof startByte !== "number" || typeof endByte !== "number") {
26143
+ return void 0;
26144
+ }
26145
+ try {
26146
+ const buf = readFileSync5(join6(workspaceRoot, ...relPath.split("/")));
26147
+ const slice = buf.subarray(Math.max(0, startByte), Math.min(buf.length, endByte));
26148
+ const text = slice.toString("utf8");
26149
+ return text.length > MAX_SNIPPET_CHARS ? text.slice(0, MAX_SNIPPET_CHARS) : text;
26150
+ } catch {
26151
+ return void 0;
26152
+ }
26153
+ }
26154
+ async function runSymbolSummarySweep(store, workspaceRoot, configDir, summarizer, opts = {}) {
26155
+ const maxSymbols = opts.maxSymbols ?? DEFAULT_MAX_SYMBOLS_PER_SWEEP;
26156
+ const batchSize = Math.max(1, opts.batchSize ?? DEFAULT_BATCH_SIZE);
26157
+ const cache = loadSymbolSummaryCache(configDir);
26158
+ const candidates = [];
26159
+ const seen = /* @__PURE__ */ new Set();
26160
+ for (const [label, kind] of SUMMARY_LABELS) {
26161
+ for (const n of store.findNodesByLabel(label)) {
26162
+ const bodyHash = n.attrs["bodyHash"];
26163
+ if (typeof bodyHash !== "string" || !bodyHash || seen.has(bodyHash)) continue;
26164
+ if (cache.entries[bodyHash]) continue;
26165
+ const name2 = n.description;
26166
+ if (!name2 || !isDistinctiveIdentifier(name2)) continue;
26167
+ seen.add(bodyHash);
26168
+ const snippet = readSnippet(workspaceRoot, n.attrs);
26169
+ candidates.push({ bodyHash, name: name2, kind, ...snippet ? { snippet } : {} });
26170
+ }
26171
+ }
26172
+ const todo = candidates.slice(0, maxSymbols);
26173
+ const remaining = candidates.length - todo.length;
26174
+ if (todo.length === 0) return { generated: 0, rejected: 0, remaining };
26175
+ const lexicon = buildSymbolLexicon(store);
26176
+ const isKnown = (tok) => lexicon.has(tok);
26177
+ let generated = 0;
26178
+ let rejected = 0;
26179
+ for (let i2 = 0; i2 < todo.length; i2 += batchSize) {
26180
+ const batch = todo.slice(i2, i2 + batchSize);
26181
+ const phrases = await summarizer(batch);
26182
+ const now = Date.now();
26183
+ for (let j = 0; j < batch.length; j++) {
26184
+ const sym = batch[j];
26185
+ const phrase = phrases[j];
26186
+ if (phrase == null) continue;
26187
+ if (summaryRejectionReason(phrase, isKnown) === null) {
26188
+ cache.entries[sym.bodyHash] = { summary: phrase, model: SUMMARY_MODEL, createdAt: now };
26189
+ generated++;
26190
+ } else {
26191
+ cache.entries[sym.bodyHash] = { rejected: true, model: SUMMARY_MODEL, createdAt: now };
26192
+ rejected++;
26193
+ }
26194
+ }
26195
+ saveSymbolSummaryCache(configDir, cache);
26196
+ }
26197
+ return { generated, rejected, remaining };
26198
+ }
26199
+ var SUMMARY_LABELS, MAX_SNIPPET_CHARS, DEFAULT_MAX_SYMBOLS_PER_SWEEP, DEFAULT_BATCH_SIZE, SUMMARY_MODEL, SUMMARY_PROMPT;
26200
+ var init_symbol_summaries = __esm({
26201
+ "src/symbol-summaries.ts"() {
26202
+ "use strict";
26203
+ init_src();
26204
+ init_generalize_graph();
26205
+ SUMMARY_LABELS = [
26206
+ ["Function", "function"],
26207
+ ["Method", "method"],
26208
+ ["Class", "class"]
26209
+ ];
26210
+ MAX_SNIPPET_CHARS = 1500;
26211
+ DEFAULT_MAX_SYMBOLS_PER_SWEEP = 64;
26212
+ DEFAULT_BATCH_SIZE = 16;
26213
+ SUMMARY_MODEL = "claude-haiku-4-5-20251001";
26214
+ SUMMARY_PROMPT = `For each code symbol below, write ONE short English phrase stating its intent/role, shaped like "a <kind> that <does what>" (e.g. "a function that binds the server's listen port").
26215
+ HARD RULES \u2014 a violating phrase is discarded:
26216
+ - NEVER include any identifier, symbol name, variable, type name, file path, or module name from the code \u2014 describe meaning in plain English words only.
26217
+ - NEVER include quoted strings, literals, numbers copied from the code, or code syntax.
26218
+ - One phrase per symbol, under 200 characters, lowercase start, no trailing period.
26219
+ Output a JSON array of strings, one per symbol in the SAME ORDER (use null for a symbol you cannot describe). Output JSON only, no prose.`;
26220
+ }
26221
+ });
26222
+
25923
26223
  // ../../packages/indexer/src/simhash.ts
25924
26224
  function fnv1a64(s) {
25925
26225
  let h = FNV_OFFSET;
@@ -26143,9 +26443,9 @@ var init_identity2 = __esm({
26143
26443
  });
26144
26444
 
26145
26445
  // ../../packages/indexer/src/pipeline.ts
26146
- import { appendFileSync, readFileSync as readFileSync5, statSync } from "node:fs";
26446
+ import { appendFileSync, readFileSync as readFileSync6, statSync } from "node:fs";
26147
26447
  import { readdir } from "node:fs/promises";
26148
- import { extname, join as join6, relative as relative2, sep } from "node:path";
26448
+ import { extname, join as join7, relative as relative2, sep } from "node:path";
26149
26449
  import { createHash as createHash3 } from "node:crypto";
26150
26450
  import { execFileSync } from "node:child_process";
26151
26451
  function nowTs() {
@@ -26261,7 +26561,7 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
26261
26561
  for (const rel of [...changedRelPaths]) {
26262
26562
  let h;
26263
26563
  try {
26264
- h = createHash3("sha256").update(readFileSync5(join6(rootPath, rel))).digest("hex");
26564
+ h = createHash3("sha256").update(readFileSync6(join7(rootPath, rel))).digest("hex");
26265
26565
  } catch {
26266
26566
  continue;
26267
26567
  }
@@ -26308,13 +26608,13 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
26308
26608
  let parsedFiles = 0;
26309
26609
  for (const rel of changedRelPaths) {
26310
26610
  if (parsedFiles++ > 0) await new Promise((r2) => setImmediate(r2));
26311
- const abs = join6(rootPath, ...rel.split("/"));
26611
+ const abs = join7(rootPath, ...rel.split("/"));
26312
26612
  const ext = extname(abs).toLowerCase();
26313
26613
  const provider = providers.find((p) => p.fileExtensions.includes(ext));
26314
26614
  if (!provider) continue;
26315
26615
  let symbols;
26316
26616
  try {
26317
- symbols = provider.extractSymbols(readFileSync5(abs, "utf8"), abs);
26617
+ symbols = provider.extractSymbols(readFileSync6(abs, "utf8"), abs);
26318
26618
  } catch {
26319
26619
  continue;
26320
26620
  }
@@ -26614,7 +26914,7 @@ async function runIndexer(store, opts) {
26614
26914
  report.filesSkipped++;
26615
26915
  continue;
26616
26916
  }
26617
- source = readFileSync5(f.absPath, "utf8");
26917
+ source = readFileSync6(f.absPath, "utf8");
26618
26918
  } catch {
26619
26919
  report.filesSkipped++;
26620
26920
  continue;
@@ -26666,7 +26966,7 @@ async function runIndexer(store, opts) {
26666
26966
  const depth = fileNode.relPath.split("/").length;
26667
26967
  if (depth !== 3) continue;
26668
26968
  try {
26669
- const pkg = JSON.parse(readFileSync5(fileNode.absPath, "utf8"));
26969
+ const pkg = JSON.parse(readFileSync6(fileNode.absPath, "utf8"));
26670
26970
  if (!pkg.name) continue;
26671
26971
  const pkgDir = fileNode.relPath.replace(/\/package\.json$/, "");
26672
26972
  const candidates = [
@@ -27048,7 +27348,7 @@ async function scan(root, current, ignores, providers, out2) {
27048
27348
  for (const ent of entries) {
27049
27349
  if (ignores.has(ent.name)) continue;
27050
27350
  if (ent.name.startsWith(".") && ent.name !== ".") continue;
27051
- const abs = join6(current, ent.name);
27351
+ const abs = join7(current, ent.name);
27052
27352
  if (ent.isDirectory()) {
27053
27353
  await scan(root, abs, ignores, providers, out2);
27054
27354
  } else if (ent.isFile()) {
@@ -27094,7 +27394,7 @@ function gitListFiles(root, ignores, providers) {
27094
27394
  if (segs.some((s) => ignores.has(s) || s.startsWith(".") && s.length > 1)) {
27095
27395
  continue;
27096
27396
  }
27097
- const abs = join6(root, rel);
27397
+ const abs = join7(root, rel);
27098
27398
  let size;
27099
27399
  try {
27100
27400
  size = statSync(abs).size;
@@ -27117,7 +27417,7 @@ function upsertFile(store, id, f, workspaceId2) {
27117
27417
  const now = nowTs();
27118
27418
  let contentHash;
27119
27419
  try {
27120
- contentHash = createHash3("sha256").update(readFileSync5(f.absPath)).digest("hex");
27420
+ contentHash = createHash3("sha256").update(readFileSync6(f.absPath)).digest("hex");
27121
27421
  } catch {
27122
27422
  }
27123
27423
  const node2 = {
@@ -31479,8 +31779,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
31479
31779
 
31480
31780
  // ../../packages/indexer/src/languages/tree-sitter-loader.ts
31481
31781
  import { fileURLToPath } from "node:url";
31482
- import { dirname as dirname4, join as join7 } from "node:path";
31483
- import { existsSync as existsSync7, readdirSync as readdirSync2 } from "node:fs";
31782
+ import { dirname as dirname4, join as join8 } from "node:path";
31783
+ import { existsSync as existsSync8, readdirSync as readdirSync2 } from "node:fs";
31484
31784
  import { createRequire as createRequire2 } from "node:module";
31485
31785
  function entryDir() {
31486
31786
  try {
@@ -31494,27 +31794,27 @@ function entryDir() {
31494
31794
  }
31495
31795
  function findWasmDir() {
31496
31796
  const here = entryDir();
31497
- const seaWasm = join7(here, "resources", "wasm");
31498
- if (existsSync7(join7(seaWasm, "tree-sitter-typescript.wasm"))) return seaWasm;
31797
+ const seaWasm = join8(here, "resources", "wasm");
31798
+ if (existsSync8(join8(seaWasm, "tree-sitter-typescript.wasm"))) return seaWasm;
31499
31799
  let dir = here;
31500
31800
  for (let i2 = 0; i2 < 6; i2++) {
31501
- const flat = join7(
31801
+ const flat = join8(
31502
31802
  dir,
31503
31803
  "node_modules",
31504
31804
  "@vscode",
31505
31805
  "tree-sitter-wasm",
31506
31806
  "wasm"
31507
31807
  );
31508
- if (existsSync7(join7(flat, "tree-sitter-typescript.wasm"))) return flat;
31808
+ if (existsSync8(join8(flat, "tree-sitter-typescript.wasm"))) return flat;
31509
31809
  dir = dirname4(dir);
31510
31810
  }
31511
31811
  let root = here;
31512
31812
  for (let i2 = 0; i2 < 6; i2++) {
31513
- const pnpmDir = join7(root, "node_modules", ".pnpm");
31514
- if (existsSync7(pnpmDir)) {
31813
+ const pnpmDir = join8(root, "node_modules", ".pnpm");
31814
+ if (existsSync8(pnpmDir)) {
31515
31815
  for (const entry of readdirSync2(pnpmDir)) {
31516
31816
  if (entry.startsWith("@vscode+tree-sitter-wasm@")) {
31517
- const candidate = join7(
31817
+ const candidate = join8(
31518
31818
  pnpmDir,
31519
31819
  entry,
31520
31820
  "node_modules",
@@ -31522,7 +31822,7 @@ function findWasmDir() {
31522
31822
  "tree-sitter-wasm",
31523
31823
  "wasm"
31524
31824
  );
31525
- if (existsSync7(join7(candidate, "tree-sitter-typescript.wasm"))) {
31825
+ if (existsSync8(join8(candidate, "tree-sitter-typescript.wasm"))) {
31526
31826
  return candidate;
31527
31827
  }
31528
31828
  }
@@ -31536,27 +31836,27 @@ function findWasmDir() {
31536
31836
  }
31537
31837
  function findRuntimeDir() {
31538
31838
  const here = entryDir();
31539
- const seaRuntime = join7(here, "resources", "wasm");
31540
- if (existsSync7(join7(seaRuntime, "web-tree-sitter.wasm"))) return seaRuntime;
31839
+ const seaRuntime = join8(here, "resources", "wasm");
31840
+ if (existsSync8(join8(seaRuntime, "web-tree-sitter.wasm"))) return seaRuntime;
31541
31841
  let dir = here;
31542
31842
  for (let i2 = 0; i2 < 6; i2++) {
31543
- const flat = join7(dir, "node_modules", "web-tree-sitter");
31544
- if (existsSync7(join7(flat, "web-tree-sitter.wasm"))) return flat;
31843
+ const flat = join8(dir, "node_modules", "web-tree-sitter");
31844
+ if (existsSync8(join8(flat, "web-tree-sitter.wasm"))) return flat;
31545
31845
  dir = dirname4(dir);
31546
31846
  }
31547
31847
  let root = here;
31548
31848
  for (let i2 = 0; i2 < 6; i2++) {
31549
- const pnpmDir = join7(root, "node_modules", ".pnpm");
31550
- if (existsSync7(pnpmDir)) {
31849
+ const pnpmDir = join8(root, "node_modules", ".pnpm");
31850
+ if (existsSync8(pnpmDir)) {
31551
31851
  for (const entry of readdirSync2(pnpmDir)) {
31552
31852
  if (entry.startsWith("web-tree-sitter@")) {
31553
- const candidate = join7(
31853
+ const candidate = join8(
31554
31854
  pnpmDir,
31555
31855
  entry,
31556
31856
  "node_modules",
31557
31857
  "web-tree-sitter"
31558
31858
  );
31559
- if (existsSync7(join7(candidate, "web-tree-sitter.wasm"))) {
31859
+ if (existsSync8(join8(candidate, "web-tree-sitter.wasm"))) {
31560
31860
  return candidate;
31561
31861
  }
31562
31862
  }
@@ -31573,7 +31873,7 @@ async function loadWebTreeSitter() {
31573
31873
  void err2;
31574
31874
  }
31575
31875
  const here = entryDir();
31576
- const seaResourceBase = join7(here, "resources", "_resolve.js");
31876
+ const seaResourceBase = join8(here, "resources", "_resolve.js");
31577
31877
  const resourceRequire = createRequire2(seaResourceBase);
31578
31878
  return resourceRequire("web-tree-sitter");
31579
31879
  }
@@ -31588,9 +31888,9 @@ async function ensureTreeSitterReady() {
31588
31888
  await Parser2.init({
31589
31889
  locateFile: (name2) => {
31590
31890
  if (name2 === "tree-sitter.wasm" || name2 === "web-tree-sitter.wasm") {
31591
- return join7(runtime, name2);
31891
+ return join8(runtime, name2);
31592
31892
  }
31593
- return join7(grammars, name2);
31893
+ return join8(grammars, name2);
31594
31894
  }
31595
31895
  });
31596
31896
  })();
@@ -31602,7 +31902,7 @@ async function loadGrammar(name2) {
31602
31902
  if (cached2) return cached2;
31603
31903
  if (!languageClass) throw new Error("tree-sitter not initialized");
31604
31904
  const grammars = findWasmDir();
31605
- const lang = await languageClass.load(join7(grammars, `${name2}.wasm`));
31905
+ const lang = await languageClass.load(join8(grammars, `${name2}.wasm`));
31606
31906
  grammarCache.set(name2, lang);
31607
31907
  return lang;
31608
31908
  }
@@ -34504,7 +34804,7 @@ var init_src11 = __esm({
34504
34804
  // src/reconcile.ts
34505
34805
  import { execFileSync as execFileSync2 } from "node:child_process";
34506
34806
  import { readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
34507
- import { join as join8, relative as relative3, sep as sep2 } from "node:path";
34807
+ import { join as join9, relative as relative3, sep as sep2 } from "node:path";
34508
34808
  function gitSourceFiles(root) {
34509
34809
  let stdout;
34510
34810
  try {
@@ -34519,7 +34819,7 @@ function gitSourceFiles(root) {
34519
34819
  const out2 = [];
34520
34820
  for (const rel of stdout.split("\0")) {
34521
34821
  if (!rel || !SOURCE_RE.test(rel)) continue;
34522
- const abs = join8(root, rel);
34822
+ const abs = join9(root, rel);
34523
34823
  if (IGNORED.test(abs)) continue;
34524
34824
  out2.push(abs);
34525
34825
  }
@@ -34534,7 +34834,7 @@ function* walkSource(dir) {
34534
34834
  }
34535
34835
  for (const e of entries) {
34536
34836
  const name2 = String(e.name);
34537
- const full = join8(dir, name2);
34837
+ const full = join9(dir, name2);
34538
34838
  if (IGNORED.test(full)) continue;
34539
34839
  if (e.isDirectory()) yield* walkSource(full);
34540
34840
  else if (SOURCE_RE.test(name2)) yield full;
@@ -34612,7 +34912,7 @@ __export(mcp_exports, {
34612
34912
  runTool: () => runTool,
34613
34913
  searchGraph: () => searchGraph
34614
34914
  });
34615
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
34915
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
34616
34916
  import { resolve } from "node:path";
34617
34917
  function collectEnrichmentPulls(store, opts = {}) {
34618
34918
  const pending = store.findNodesByLabel("Solution").filter((s) => s.attrs["enrichmentPending"] === true).filter((s) => !opts.onlyUnsurfaced || s.attrs["enrichmentSurfaced"] !== true);
@@ -34725,10 +35025,10 @@ function showNode(store, node2, maxLines) {
34725
35025
  if (!relPath) return { found: false, reason: "no file owner for node" };
34726
35026
  const workspaceRoot = process.cwd();
34727
35027
  const absPath = resolve(workspaceRoot, relPath);
34728
- if (!existsSync8(absPath)) {
35028
+ if (!existsSync9(absPath)) {
34729
35029
  return { found: false, reason: `file not found on disk: ${absPath}` };
34730
35030
  }
34731
- const source = readFileSync6(absPath, "utf8");
35031
+ const source = readFileSync7(absPath, "utf8");
34732
35032
  const attrs = node2.attrs;
34733
35033
  let startByte = attrs["bodyStartByte"] ?? attrs["startByte"];
34734
35034
  let endByte = attrs["bodyEndByte"] ?? attrs["endByte"];
@@ -34941,7 +35241,20 @@ function buildToolContext() {
34941
35241
  async function runMcpServer(workspaceRoot) {
34942
35242
  const paths = workspacePaths(workspaceRoot);
34943
35243
  const store = openGraphStore({ path: paths.castalia });
34944
- const handle2 = createMcpHandler(store, buildToolContext());
35244
+ let summariesMemo = null;
35245
+ const symbolSummaries = () => {
35246
+ const now = Date.now();
35247
+ if (!summariesMemo || now - summariesMemo.at > 6e4) {
35248
+ let map2 = /* @__PURE__ */ new Map();
35249
+ try {
35250
+ map2 = summariesByBodyHash(loadSymbolSummaryCache(paths.configDir));
35251
+ } catch {
35252
+ }
35253
+ summariesMemo = { at: now, map: map2 };
35254
+ }
35255
+ return summariesMemo.map;
35256
+ };
35257
+ const handle2 = createMcpHandler(store, { ...buildToolContext(), symbolSummaries });
34945
35258
  process.stdin.setEncoding("utf8");
34946
35259
  let buffer = "";
34947
35260
  process.stdin.on("data", (chunk) => {
@@ -34980,6 +35293,7 @@ var init_mcp = __esm({
34980
35293
  init_src2();
34981
35294
  init_src10();
34982
35295
  init_paths();
35296
+ init_symbol_summaries();
34983
35297
  init_webui();
34984
35298
  init_reconcile();
34985
35299
  init_agent_signals();
@@ -35052,7 +35366,14 @@ var init_mcp = __esm({
35052
35366
  score: h.pageRank,
35053
35367
  hops: 0
35054
35368
  }));
35055
- const dual = await dualAugment({ store, cloud: ctx.cloud, localHits, queryText: query, limit });
35369
+ const dual = await dualAugment({
35370
+ store,
35371
+ cloud: ctx.cloud,
35372
+ localHits,
35373
+ queryText: query,
35374
+ limit,
35375
+ ...ctx.symbolSummaries ? { summaries: ctx.symbolSummaries() } : {}
35376
+ });
35056
35377
  if (dual.status === "merged") {
35057
35378
  return {
35058
35379
  query,
@@ -35217,7 +35538,8 @@ var init_mcp = __esm({
35217
35538
  cloud: ctx.cloud,
35218
35539
  localHits,
35219
35540
  seedNode,
35220
- limit: args2["limit"] != null ? Number(args2["limit"]) : 20
35541
+ limit: args2["limit"] != null ? Number(args2["limit"]) : 20,
35542
+ ...ctx.symbolSummaries ? { summaries: ctx.symbolSummaries() } : {}
35221
35543
  });
35222
35544
  if (dual.status === "merged") {
35223
35545
  return {
@@ -35732,7 +36054,14 @@ var init_mcp = __esm({
35732
36054
  score: h.score,
35733
36055
  hops: 0
35734
36056
  }));
35735
- const dual = await dualAugment({ store, cloud: ctx.cloud, localHits, seedNode: node2, limit });
36057
+ const dual = await dualAugment({
36058
+ store,
36059
+ cloud: ctx.cloud,
36060
+ localHits,
36061
+ seedNode: node2,
36062
+ limit,
36063
+ ...ctx.symbolSummaries ? { summaries: ctx.symbolSummaries() } : {}
36064
+ });
35736
36065
  if (dual.status === "merged") {
35737
36066
  return {
35738
36067
  seedId: id,
@@ -35923,7 +36252,7 @@ var init_mcp = __esm({
35923
36252
  inputSchema: { type: "object", properties: {} },
35924
36253
  handler: () => {
35925
36254
  const path2 = sharedStorePath();
35926
- if (!existsSync8(path2)) return { count: 0, pending: [] };
36255
+ if (!existsSync9(path2)) return { count: 0, pending: [] };
35927
36256
  const shared = openGraphStore({ path: path2 });
35928
36257
  try {
35929
36258
  const pending = pendingAbstractions(shared);
@@ -35954,7 +36283,7 @@ var init_mcp = __esm({
35954
36283
  },
35955
36284
  handler: (args2) => {
35956
36285
  const path2 = sharedStorePath();
35957
- if (!existsSync8(path2)) return { count: 0, routes: [] };
36286
+ if (!existsSync9(path2)) return { count: 0, routes: [] };
35958
36287
  const shared = openGraphStore({ path: path2 });
35959
36288
  try {
35960
36289
  const result = triageOf(shared, {
@@ -36339,8 +36668,8 @@ __export(vfile_exports, {
36339
36668
  resolvePath: () => resolvePath,
36340
36669
  segmentsOf: () => segmentsOf
36341
36670
  });
36342
- import { writeFileSync as writeFileSync6 } from "node:fs";
36343
- import { join as join9, resolve as resolve2, sep as sep3 } from "node:path";
36671
+ import { writeFileSync as writeFileSync7 } from "node:fs";
36672
+ import { join as join10, resolve as resolve2, sep as sep3 } from "node:path";
36344
36673
  function segmentsOf(rawPath) {
36345
36674
  let p = rawPath.replace(/\\/g, "/");
36346
36675
  p = p.replace(/^.*\.errata\/g\//, "").replace(/^\/?g\//, "").replace(/^\/+/, "");
@@ -36414,14 +36743,14 @@ async function renderVFile(rawPath, store, ctx = {}) {
36414
36743
  }
36415
36744
  async function materializeVFile(rawPath, workspaceRoot, store, ctx = {}) {
36416
36745
  const segs = segmentsOf(rawPath);
36417
- const gRoot = join9(workspaceRoot, ".errata", "g");
36746
+ const gRoot = join10(workspaceRoot, ".errata", "g");
36418
36747
  const abs = resolve2(gRoot, ...segs.length ? segs : ["index"]);
36419
36748
  if (abs !== gRoot && !abs.startsWith(gRoot + sep3)) {
36420
36749
  throw new Error(`refusing to materialize outside .errata/g: ${rawPath}`);
36421
36750
  }
36422
36751
  const text = await renderVFile(rawPath, store, ctx);
36423
36752
  ensureParent(abs);
36424
- writeFileSync6(abs, text, "utf8");
36753
+ writeFileSync7(abs, text, "utf8");
36425
36754
  return abs;
36426
36755
  }
36427
36756
  async function materializeOverview(workspaceRoot, store, ctx = {}) {
@@ -36506,25 +36835,25 @@ the tool surface; the path is the thin, composable channel.
36506
36835
 
36507
36836
  // src/outbox.ts
36508
36837
  import {
36509
- existsSync as existsSync9,
36838
+ existsSync as existsSync10,
36510
36839
  readdirSync as readdirSync4,
36511
- readFileSync as readFileSync7,
36840
+ readFileSync as readFileSync8,
36512
36841
  renameSync,
36513
36842
  unlinkSync,
36514
- writeFileSync as writeFileSync7
36843
+ writeFileSync as writeFileSync8
36515
36844
  } from "node:fs";
36516
- import { join as join10 } from "node:path";
36845
+ import { join as join11 } from "node:path";
36517
36846
  import { createHash as createHash11 } from "node:crypto";
36518
36847
  function enqueueOutbox(paths, payload) {
36519
36848
  ensureDir(paths.outbox);
36520
36849
  const id = createHash11("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
36521
- const file2 = join10(paths.outbox, `${Date.now()}-${id}.json`);
36522
- writeFileSync7(file2, JSON.stringify(payload, null, 2), "utf8");
36850
+ const file2 = join11(paths.outbox, `${Date.now()}-${id}.json`);
36851
+ writeFileSync8(file2, JSON.stringify(payload, null, 2), "utf8");
36523
36852
  return file2;
36524
36853
  }
36525
36854
  async function flushOutbox(paths, client, store) {
36526
36855
  ensureDir(paths.outbox);
36527
- const entries = existsSync9(paths.outbox) ? readdirSync4(paths.outbox) : [];
36856
+ const entries = existsSync10(paths.outbox) ? readdirSync4(paths.outbox) : [];
36528
36857
  let uploaded = 0;
36529
36858
  let failed = 0;
36530
36859
  const quarantine = (abs, reason) => {
@@ -36535,10 +36864,10 @@ async function flushOutbox(paths, client, store) {
36535
36864
  }
36536
36865
  };
36537
36866
  for (const f of entries.filter((e) => e.endsWith(".json")).sort()) {
36538
- const abs = join10(paths.outbox, f);
36867
+ const abs = join11(paths.outbox, f);
36539
36868
  let payload;
36540
36869
  try {
36541
- payload = JSON.parse(readFileSync7(abs, "utf8"));
36870
+ payload = JSON.parse(readFileSync8(abs, "utf8"));
36542
36871
  } catch {
36543
36872
  failed++;
36544
36873
  quarantine(abs, "unparseable JSON");
@@ -36566,7 +36895,7 @@ async function flushOutbox(paths, client, store) {
36566
36895
  }
36567
36896
  }
36568
36897
  }
36569
- const remainingFiles = existsSync9(paths.outbox) ? readdirSync4(paths.outbox).filter((e) => e.endsWith(".json")) : [];
36898
+ const remainingFiles = existsSync10(paths.outbox) ? readdirSync4(paths.outbox).filter((e) => e.endsWith(".json")) : [];
36570
36899
  return { uploaded, failed, remaining: remainingFiles.length };
36571
36900
  }
36572
36901
  var init_outbox = __esm({
@@ -36585,7 +36914,7 @@ __export(webui_exports, {
36585
36914
  findFileByPath: () => findFileByPath,
36586
36915
  recallForFile: () => recallForFile
36587
36916
  });
36588
- import { join as join11 } from "node:path";
36917
+ import { join as join12 } from "node:path";
36589
36918
  function fileUriToFsPath(raw2) {
36590
36919
  let p = raw2.replace(/^file:\/\//, "").replace(/\\/g, "/");
36591
36920
  if (/^\/[A-Za-z]:/.test(p)) p = p.slice(1);
@@ -36946,7 +37275,7 @@ function buildWebUi(deps) {
36946
37275
  } catch {
36947
37276
  return c.json({});
36948
37277
  }
36949
- const block = readManagedBlockWithHash(join11(deps.paths.root, "AGENTS.md"));
37278
+ const block = readManagedBlockWithHash(join12(deps.paths.root, "AGENTS.md"));
36950
37279
  const decision = decideContextInject({
36951
37280
  event: String(body2["hook_event_name"] ?? "UserPromptSubmit"),
36952
37281
  source: String(body2["source"] ?? ""),
@@ -37548,12 +37877,12 @@ var init_report_render = __esm({
37548
37877
 
37549
37878
  // src/cli.ts
37550
37879
  init_src5();
37551
- import { closeSync as closeSync2, existsSync as existsSync19, openSync as openSync2, readFileSync as readFileSync18, renameSync as renameSync3, statSync as statSync5 } from "node:fs";
37552
- import { join as join22 } from "node:path";
37880
+ import { closeSync as closeSync2, existsSync as existsSync20, openSync as openSync2, readFileSync as readFileSync19, renameSync as renameSync3, statSync as statSync5 } from "node:fs";
37881
+ import { join as join23 } from "node:path";
37553
37882
  import { spawn as spawn3 } from "node:child_process";
37554
37883
 
37555
37884
  // src/daemon.ts
37556
- import { existsSync as existsSync15, writeFileSync as writeFileSync12 } from "node:fs";
37885
+ import { existsSync as existsSync16, writeFileSync as writeFileSync13 } from "node:fs";
37557
37886
 
37558
37887
  // ../../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.8/node_modules/@hono/node-server/dist/index.mjs
37559
37888
  import { createServer as createServerHTTP } from "http";
@@ -38133,8 +38462,8 @@ init_config();
38133
38462
 
38134
38463
  // src/engine.ts
38135
38464
  import { execFileSync as execFileSync3 } from "node:child_process";
38136
- import { existsSync as existsSync14, statSync as statSync4, appendFileSync as appendFileSync2, readdirSync as readdirSync7, renameSync as renameSync2, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "node:fs";
38137
- import { join as join19, relative as relative6, sep as sep4 } from "node:path";
38465
+ import { existsSync as existsSync15, statSync as statSync4, appendFileSync as appendFileSync2, readdirSync as readdirSync7, renameSync as renameSync2, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "node:fs";
38466
+ import { join as join20, relative as relative6, sep as sep4 } from "node:path";
38138
38467
 
38139
38468
  // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
38140
38469
  import { stat as statcb } from "fs";
@@ -39996,8 +40325,8 @@ init_src10();
39996
40325
  init_review2();
39997
40326
 
39998
40327
  // src/turn.ts
39999
- import { closeSync, existsSync as existsSync10, fstatSync, openSync, readdirSync as readdirSync5, readSync, statSync as statSync3 } from "node:fs";
40000
- import { basename as basename3, dirname as dirname7, join as join14 } from "node:path";
40328
+ import { closeSync, existsSync as existsSync11, fstatSync, openSync, readdirSync as readdirSync5, readSync, statSync as statSync3 } from "node:fs";
40329
+ import { basename as basename3, dirname as dirname7, join as join15 } from "node:path";
40001
40330
  import { homedir as homedir3 } from "node:os";
40002
40331
  function readTail(path2, maxBytes) {
40003
40332
  let fd;
@@ -40106,11 +40435,11 @@ function turnsSince(turns, mark) {
40106
40435
  return at >= 0 ? turns.slice(at + 1) : turns;
40107
40436
  }
40108
40437
  function claudeProjectDir(cwd, home = homedir3()) {
40109
- return join14(home, ".claude", "projects", cwd.replace(/[\\/:.]/g, "-"));
40438
+ return join15(home, ".claude", "projects", cwd.replace(/[\\/:.]/g, "-"));
40110
40439
  }
40111
40440
  function recentTranscripts(cwd, opts = {}) {
40112
40441
  const dir = claudeProjectDir(cwd, opts.home ?? homedir3());
40113
- if (!existsSync10(dir)) return [];
40442
+ if (!existsSync11(dir)) return [];
40114
40443
  let names;
40115
40444
  try {
40116
40445
  names = readdirSync5(dir);
@@ -40121,7 +40450,7 @@ function recentTranscripts(cwd, opts = {}) {
40121
40450
  const refs = [];
40122
40451
  for (const name2 of names) {
40123
40452
  if (!name2.endsWith(".jsonl")) continue;
40124
- const path2 = join14(dir, name2);
40453
+ const path2 = join15(dir, name2);
40125
40454
  let mtimeMs;
40126
40455
  try {
40127
40456
  mtimeMs = statSync3(path2).mtimeMs;
@@ -40136,8 +40465,8 @@ function recentTranscripts(cwd, opts = {}) {
40136
40465
  }
40137
40466
  function subagentTranscripts(mainTranscriptPath, sessionId) {
40138
40467
  if (!mainTranscriptPath || !sessionId) return [];
40139
- const dir = join14(dirname7(mainTranscriptPath), sessionId, "subagents");
40140
- if (!existsSync10(dir)) return [];
40468
+ const dir = join15(dirname7(mainTranscriptPath), sessionId, "subagents");
40469
+ if (!existsSync11(dir)) return [];
40141
40470
  let names;
40142
40471
  try {
40143
40472
  names = readdirSync5(dir);
@@ -40147,7 +40476,7 @@ function subagentTranscripts(mainTranscriptPath, sessionId) {
40147
40476
  const refs = [];
40148
40477
  for (const name2 of names) {
40149
40478
  if (!name2.endsWith(".jsonl")) continue;
40150
- const path2 = join14(dir, name2);
40479
+ const path2 = join15(dir, name2);
40151
40480
  let mtimeMs;
40152
40481
  try {
40153
40482
  mtimeMs = statSync3(path2).mtimeMs;
@@ -40164,7 +40493,7 @@ function subagentTranscripts(mainTranscriptPath, sessionId) {
40164
40493
  init_src();
40165
40494
  init_src2();
40166
40495
  init_agent_signals();
40167
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "node:fs";
40496
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "node:fs";
40168
40497
  var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
40169
40498
  var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
40170
40499
  var HANDLE_RE = /\[([a-z0-9][\w-]{0,40})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
@@ -40414,13 +40743,13 @@ function writePrimingHandles(path2, nodes, now = Date.now()) {
40414
40743
  entries.sort((a, b) => (b[1].seenAt ?? 0) - (a[1].seenAt ?? 0));
40415
40744
  entries.length = HANDLE_MAP_MAX;
40416
40745
  }
40417
- writeFileSync8(path2, JSON.stringify(Object.fromEntries(entries)));
40746
+ writeFileSync9(path2, JSON.stringify(Object.fromEntries(entries)));
40418
40747
  } catch {
40419
40748
  }
40420
40749
  }
40421
40750
  function readPrimingHandles(path2) {
40422
40751
  try {
40423
- return JSON.parse(readFileSync8(path2, "utf8"));
40752
+ return JSON.parse(readFileSync9(path2, "utf8"));
40424
40753
  } catch {
40425
40754
  return {};
40426
40755
  }
@@ -40676,6 +41005,7 @@ ${conversation}` }]
40676
41005
  }
40677
41006
 
40678
41007
  // src/engine.ts
41008
+ init_symbol_summaries();
40679
41009
  init_reconcile();
40680
41010
 
40681
41011
  // src/episode.ts
@@ -40872,11 +41202,11 @@ init_outbox();
40872
41202
  init_src8();
40873
41203
  init_src();
40874
41204
  init_src2();
40875
- import { readFileSync as readFileSync9 } from "node:fs";
40876
- import { join as join15 } from "node:path";
41205
+ import { readFileSync as readFileSync10 } from "node:fs";
41206
+ import { join as join16 } from "node:path";
40877
41207
  function loadClaimIgnorePatterns(workspaceRoot) {
40878
41208
  try {
40879
- return readFileSync9(join15(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
41209
+ return readFileSync10(join16(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
40880
41210
  } catch {
40881
41211
  return [];
40882
41212
  }
@@ -41168,22 +41498,22 @@ async function pullCloudTriage(shared, cloud, profile, limit = 50) {
41168
41498
  }
41169
41499
 
41170
41500
  // src/git-sensor.ts
41171
- import { existsSync as existsSync11, readFileSync as readFileSync10, watch as fsWatch } from "node:fs";
41172
- import { join as join16 } from "node:path";
41501
+ import { existsSync as existsSync12, readFileSync as readFileSync11, watch as fsWatch } from "node:fs";
41502
+ import { join as join17 } from "node:path";
41173
41503
  function readFirstLine(path2) {
41174
41504
  try {
41175
- return readFileSync10(path2, "utf8").split(/\r?\n/, 1)[0].trim();
41505
+ return readFileSync11(path2, "utf8").split(/\r?\n/, 1)[0].trim();
41176
41506
  } catch {
41177
41507
  return null;
41178
41508
  }
41179
41509
  }
41180
41510
  function readGitRefState(gitDir) {
41181
- const head2 = readFirstLine(join16(gitDir, "HEAD"));
41511
+ const head2 = readFirstLine(join17(gitDir, "HEAD"));
41182
41512
  const m = head2 ? /^ref:\s*refs\/heads\/(.+)$/.exec(head2) : null;
41183
41513
  const branch = m ? m[1] : null;
41184
41514
  let sha2 = null;
41185
41515
  if (branch) {
41186
- sha2 = readFirstLine(join16(gitDir, "refs", "heads", branch));
41516
+ sha2 = readFirstLine(join17(gitDir, "refs", "heads", branch));
41187
41517
  if (!sha2) sha2 = shaFromPackedRefs(gitDir, `refs/heads/${branch}`);
41188
41518
  } else if (head2 && /^[0-9a-f]{7,40}$/i.test(head2)) {
41189
41519
  sha2 = head2;
@@ -41191,13 +41521,13 @@ function readGitRefState(gitDir) {
41191
41521
  return {
41192
41522
  branch,
41193
41523
  sha: sha2,
41194
- mergeHeadExists: existsSync11(join16(gitDir, "MERGE_HEAD")),
41195
- origHeadExists: existsSync11(join16(gitDir, "ORIG_HEAD"))
41524
+ mergeHeadExists: existsSync12(join17(gitDir, "MERGE_HEAD")),
41525
+ origHeadExists: existsSync12(join17(gitDir, "ORIG_HEAD"))
41196
41526
  };
41197
41527
  }
41198
41528
  function shaFromPackedRefs(gitDir, ref) {
41199
41529
  try {
41200
- for (const line of readFileSync10(join16(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
41530
+ for (const line of readFileSync11(join17(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
41201
41531
  const [sha2, name2] = line.split(/\s+/);
41202
41532
  if (name2 === ref && sha2) return sha2;
41203
41533
  }
@@ -41231,7 +41561,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
41231
41561
  const settle = () => {
41232
41562
  if (timer) clearTimeout(timer);
41233
41563
  timer = setTimeout(() => {
41234
- if (existsSync11(join16(gitDir, "index.lock"))) {
41564
+ if (existsSync12(join17(gitDir, "index.lock"))) {
41235
41565
  settle();
41236
41566
  return;
41237
41567
  }
@@ -41242,7 +41572,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
41242
41572
  }, debounceMs);
41243
41573
  };
41244
41574
  for (const sub of ["HEAD", "logs/HEAD", "MERGE_HEAD", "ORIG_HEAD"]) {
41245
- const p = join16(gitDir, sub);
41575
+ const p = join17(gitDir, sub);
41246
41576
  try {
41247
41577
  watchers.push(fsWatch(p, settle));
41248
41578
  } catch {
@@ -41467,21 +41797,21 @@ var TelemetryRecorder = class {
41467
41797
 
41468
41798
  // src/skills.ts
41469
41799
  import {
41470
- existsSync as existsSync12,
41800
+ existsSync as existsSync13,
41471
41801
  mkdirSync as mkdirSync5,
41472
- readFileSync as readFileSync11,
41802
+ readFileSync as readFileSync12,
41473
41803
  readdirSync as readdirSync6,
41474
41804
  unlinkSync as unlinkSync2,
41475
- writeFileSync as writeFileSync9
41805
+ writeFileSync as writeFileSync10
41476
41806
  } from "node:fs";
41477
- import { basename as basename4, join as join17 } from "node:path";
41807
+ import { basename as basename4, join as join18 } from "node:path";
41478
41808
  function skillFileName(id) {
41479
41809
  return `${id.replace(/[^A-Za-z0-9_.-]/g, "_")}.md`;
41480
41810
  }
41481
41811
  function readSkillManifest(manifestPath) {
41482
- if (!existsSync12(manifestPath)) return [];
41812
+ if (!existsSync13(manifestPath)) return [];
41483
41813
  try {
41484
- const parsed = JSON.parse(readFileSync11(manifestPath, "utf8"));
41814
+ const parsed = JSON.parse(readFileSync12(manifestPath, "utf8"));
41485
41815
  return (parsed.skills ?? []).map((s) => ({
41486
41816
  title: s.title ?? "",
41487
41817
  layer: s.layer ?? "technique",
@@ -41504,7 +41834,7 @@ async function syncSkills(paths, client, seed) {
41504
41834
  for (const s of res.skills) {
41505
41835
  const fileName = skillFileName(s.id);
41506
41836
  keep.add(fileName);
41507
- writeFileSync9(join17(paths.skillsDir, fileName), s.markdown, "utf8");
41837
+ writeFileSync10(join18(paths.skillsDir, fileName), s.markdown, "utf8");
41508
41838
  rows.push({
41509
41839
  id: s.id,
41510
41840
  title: s.title,
@@ -41518,13 +41848,13 @@ async function syncSkills(paths, client, seed) {
41518
41848
  if (!f.endsWith(".md")) continue;
41519
41849
  if (keep.has(basename4(f))) continue;
41520
41850
  try {
41521
- unlinkSync2(join17(paths.skillsDir, f));
41851
+ unlinkSync2(join18(paths.skillsDir, f));
41522
41852
  pruned++;
41523
41853
  } catch {
41524
41854
  }
41525
41855
  }
41526
41856
  rows.sort((a, b) => a.id.localeCompare(b.id));
41527
- writeFileSync9(
41857
+ writeFileSync10(
41528
41858
  paths.skillsManifest,
41529
41859
  JSON.stringify({ generatedAt: Date.now(), skills: rows }, null, 2),
41530
41860
  "utf8"
@@ -41661,30 +41991,30 @@ var CausalBuffer = class {
41661
41991
  // src/profile.ts
41662
41992
  init_src2();
41663
41993
  init_paths();
41664
- import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "node:fs";
41994
+ import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "node:fs";
41665
41995
  import { createHash as createHash12 } from "node:crypto";
41666
- import { join as join18 } from "node:path";
41996
+ import { join as join19 } from "node:path";
41667
41997
  function workspaceId(root) {
41668
41998
  return "wp_" + createHash12("sha256").update(root).digest("hex").slice(0, 12);
41669
41999
  }
41670
42000
  function loadProfile(root) {
41671
42001
  const p = workspacePaths(root);
41672
- if (!existsSync13(p.workspaceJson)) return null;
41673
- return JSON.parse(readFileSync12(p.workspaceJson, "utf8"));
42002
+ if (!existsSync14(p.workspaceJson)) return null;
42003
+ return JSON.parse(readFileSync13(p.workspaceJson, "utf8"));
41674
42004
  }
41675
42005
  function saveProfile(root, profile) {
41676
42006
  const p = workspacePaths(root);
41677
42007
  ensureDir(p.configDir);
41678
- writeFileSync10(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
42008
+ writeFileSync11(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
41679
42009
  }
41680
42010
  function autodetectProfile(root) {
41681
42011
  const id = workspaceId(root);
41682
42012
  const name2 = root.split(/[\\/]/).filter(Boolean).pop() ?? "workspace";
41683
42013
  const p = emptyProfile(id, name2);
41684
- const pkgPath = join18(root, "package.json");
41685
- if (existsSync13(pkgPath)) {
42014
+ const pkgPath = join19(root, "package.json");
42015
+ if (existsSync14(pkgPath)) {
41686
42016
  try {
41687
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
42017
+ const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
41688
42018
  p.languages.push("typescript", "javascript");
41689
42019
  const nodeVer = pkg.engines?.node ?? "node";
41690
42020
  p.stack.push(`node@${nodeVer}`);
@@ -41705,10 +42035,10 @@ function autodetectProfile(root) {
41705
42035
  } catch {
41706
42036
  }
41707
42037
  }
41708
- const pyproject = join18(root, "pyproject.toml");
41709
- if (existsSync13(pyproject)) {
42038
+ const pyproject = join19(root, "pyproject.toml");
42039
+ if (existsSync14(pyproject)) {
41710
42040
  try {
41711
- const txt = readFileSync12(pyproject, "utf8");
42041
+ const txt = readFileSync13(pyproject, "utf8");
41712
42042
  const py = /python\s*=\s*"([^"]+)"/.exec(txt)?.[1];
41713
42043
  p.languages.push("python");
41714
42044
  p.stack.push(`python@${py ?? "3"}`);
@@ -41719,16 +42049,16 @@ function autodetectProfile(root) {
41719
42049
  } catch {
41720
42050
  }
41721
42051
  }
41722
- const reqs = join18(root, "requirements.txt");
41723
- if (existsSync13(reqs)) {
42052
+ const reqs = join19(root, "requirements.txt");
42053
+ if (existsSync14(reqs)) {
41724
42054
  if (!p.languages.includes("python")) p.languages.push("python");
41725
42055
  if (!p.stack.includes("python@3")) p.stack.push("python@3");
41726
42056
  }
41727
- if (existsSync13(join18(root, "go.mod"))) {
42057
+ if (existsSync14(join19(root, "go.mod"))) {
41728
42058
  p.languages.push("go");
41729
42059
  p.stack.push("go");
41730
42060
  }
41731
- if (existsSync13(join18(root, "Cargo.toml"))) {
42061
+ if (existsSync14(join19(root, "Cargo.toml"))) {
41732
42062
  p.languages.push("rust");
41733
42063
  p.stack.push("rust");
41734
42064
  }
@@ -41886,7 +42216,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
41886
42216
  }
41887
42217
 
41888
42218
  // src/engine.ts
41889
- var DAEMON_VERSION = true ? "2.0.0-dev.35" : "2.0.0-alpha.0";
42219
+ var DAEMON_VERSION = true ? "2.0.0-dev.36" : "2.0.0-alpha.0";
41890
42220
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
41891
42221
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
41892
42222
  var GIT_OP_MUTE_MS = 4e3;
@@ -41896,7 +42226,7 @@ var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
41896
42226
  function appendIdentityAudit(path2, record2, line) {
41897
42227
  if (!record2.accepted && record2.score <= 0) return;
41898
42228
  try {
41899
- if (existsSync14(path2) && statSync4(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
42229
+ if (existsSync15(path2) && statSync4(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
41900
42230
  renameSync2(path2, `${path2}.1`);
41901
42231
  }
41902
42232
  appendFileSync2(path2, line);
@@ -41906,14 +42236,14 @@ function appendIdentityAudit(path2, record2, line) {
41906
42236
  var yieldToLoop = () => new Promise((r) => setImmediate(r));
41907
42237
  function loadTurnCursors(path2) {
41908
42238
  try {
41909
- return new Map(Object.entries(JSON.parse(readFileSync13(path2, "utf8"))));
42239
+ return new Map(Object.entries(JSON.parse(readFileSync14(path2, "utf8"))));
41910
42240
  } catch {
41911
42241
  return /* @__PURE__ */ new Map();
41912
42242
  }
41913
42243
  }
41914
42244
  function saveTurnCursors(path2, cursors) {
41915
42245
  try {
41916
- writeFileSync11(path2, JSON.stringify(Object.fromEntries(cursors)), "utf8");
42246
+ writeFileSync12(path2, JSON.stringify(Object.fromEntries(cursors)), "utf8");
41917
42247
  } catch {
41918
42248
  }
41919
42249
  }
@@ -41935,7 +42265,7 @@ function gitSourceWatchTargets(root) {
41935
42265
  ["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
41936
42266
  { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
41937
42267
  );
41938
- ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join19(root, d) + sep4));
42268
+ ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join20(root, d) + sep4));
41939
42269
  } catch {
41940
42270
  }
41941
42271
  const hasIgnoredChild = (dir) => ignoredDirs.some((ig) => ig.startsWith(dir + "/"));
@@ -41947,19 +42277,19 @@ function gitSourceWatchTargets(root) {
41947
42277
  if (!f.startsWith(prefix)) continue;
41948
42278
  const rest2 = f.slice(prefix.length);
41949
42279
  if (rest2.includes("/")) children.add(dir === "" ? rest2.slice(0, rest2.indexOf("/")) : dir + "/" + rest2.slice(0, rest2.indexOf("/")));
41950
- else targets.add(join19(root, f));
42280
+ else targets.add(join20(root, f));
41951
42281
  }
41952
42282
  for (const c of children) {
41953
- if (IGNORED_PATH.test(join19(root, c) + sep4)) continue;
42283
+ if (IGNORED_PATH.test(join20(root, c) + sep4)) continue;
41954
42284
  if (hasIgnoredChild(c)) addUnder(c);
41955
- else targets.add(join19(root, c));
42285
+ else targets.add(join20(root, c));
41956
42286
  }
41957
42287
  };
41958
42288
  addUnder("");
41959
42289
  if (targets.size > 0) return [...targets];
41960
42290
  } catch {
41961
42291
  }
41962
- return readdirSync7(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !IGNORED_PATH.test(join19(root, String(e.name)) + sep4)).map((e) => join19(root, String(e.name)));
42292
+ return readdirSync7(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !IGNORED_PATH.test(join20(root, String(e.name)) + sep4)).map((e) => join20(root, String(e.name)));
41963
42293
  }
41964
42294
  function createWorkspaceEngine(opts) {
41965
42295
  const paths = workspacePaths(opts.workspaceRoot);
@@ -42111,7 +42441,7 @@ function createWorkspaceEngine(opts) {
42111
42441
  const srcPaths = diff.changedPaths.filter((p) => /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(p));
42112
42442
  let episodeId2;
42113
42443
  if (srcPaths.length > 0) {
42114
- const abs = srcPaths.map((p) => join19(opts.workspaceRoot, p));
42444
+ const abs = srcPaths.map((p) => join20(opts.workspaceRoot, p));
42115
42445
  try {
42116
42446
  const r = await runReindexPass(
42117
42447
  `git-reindex:${profile.name} (${abs.length} files)`,
@@ -42142,8 +42472,8 @@ function createWorkspaceEngine(opts) {
42142
42472
  `[errata] git: ${ev.kind} ${ev.newSha.slice(0, 7)} by ${meta3.authorName} \u2014 ${diff.changedPaths.length} file(s), ${diff.renames.length} rename(s)`
42143
42473
  );
42144
42474
  };
42145
- const gitDir = join19(opts.workspaceRoot, ".git");
42146
- if (existsSync14(gitDir)) {
42475
+ const gitDir = join20(opts.workspaceRoot, ".git");
42476
+ if (existsSync15(gitDir)) {
42147
42477
  stopGit = startGitSensor(gitDir, (ev) => {
42148
42478
  void handleGitEvent(ev);
42149
42479
  });
@@ -42209,10 +42539,10 @@ function createWorkspaceEngine(opts) {
42209
42539
  ...pendingUpdate ? { pendingUpdate } : {}
42210
42540
  });
42211
42541
  doneRender?.();
42212
- const target = join19(opts.workspaceRoot, "AGENTS.md");
42542
+ const target = join20(opts.workspaceRoot, "AGENTS.md");
42213
42543
  writeManagedBlock(target, { body: body2 });
42214
42544
  if (elicit) {
42215
- writePrimingHandles(join19(paths.configDir, "priming-handles.json"), [
42545
+ writePrimingHandles(join20(paths.configDir, "priming-handles.json"), [
42216
42546
  ...snapshot.recentProblems.map((r) => r.node),
42217
42547
  // Resolved-band handles: the ✓ problem AND its Solution are citable
42218
42548
  // (a fix tag on an already-resolved problem no-ops idempotently; the
@@ -42382,7 +42712,7 @@ function createWorkspaceEngine(opts) {
42382
42712
  resultSummary: { exitCode: e.exitCode ?? 1, errorTokens: e.errorTokens }
42383
42713
  });
42384
42714
  };
42385
- const turnCursorPath = join19(paths.configDir, "turn-cursors.json");
42715
+ const turnCursorPath = join20(paths.configDir, "turn-cursors.json");
42386
42716
  const lastTurnUuid = loadTurnCursors(turnCursorPath);
42387
42717
  const sessionLastProblem = /* @__PURE__ */ new Map();
42388
42718
  const sessionThreads = /* @__PURE__ */ new Map();
@@ -42405,7 +42735,7 @@ function createWorkspaceEngine(opts) {
42405
42735
  const t = Date.now();
42406
42736
  let processedTurns = 0;
42407
42737
  const elicit = isEdgeElicitationEnabled();
42408
- const handleMap = elicit ? readPrimingHandles(join19(paths.configDir, "priming-handles.json")) : {};
42738
+ const handleMap = elicit ? readPrimingHandles(join20(paths.configDir, "priming-handles.json")) : {};
42409
42739
  const wsRoot = (opts.workspaceRoot ?? "").replace(/\\/g, "/");
42410
42740
  const toRel = (abs) => {
42411
42741
  const p = abs.replace(/\\/g, "/");
@@ -42922,7 +43252,21 @@ function createWorkspaceEngine(opts) {
42922
43252
  return report;
42923
43253
  },
42924
43254
  async nightly() {
42925
- return (await runNightly()).report;
43255
+ const report = (await runNightly()).report;
43256
+ const summarizer = opts.intentSummarizer ?? envIntentSummarizer();
43257
+ if (summarizer) {
43258
+ try {
43259
+ const s = await runSymbolSummarySweep(store, opts.workspaceRoot, paths.configDir, summarizer);
43260
+ if (s.generated > 0 || s.rejected > 0) {
43261
+ console.log(
43262
+ `[errata] symbol summaries: +${s.generated}${s.rejected > 0 ? ` (${s.rejected} rejected by no-verbatim check)` : ""}${s.remaining > 0 ? `, ${s.remaining} pending` : ""}`
43263
+ );
43264
+ }
43265
+ } catch (err2) {
43266
+ console.warn("[errata] symbol summary sweep failed:", err2 instanceof Error ? err2.message : err2);
43267
+ }
43268
+ }
43269
+ return report;
42926
43270
  },
42927
43271
  async embedSettled() {
42928
43272
  try {
@@ -43010,7 +43354,7 @@ function createWorkspaceEngine(opts) {
43010
43354
  console.log(
43011
43355
  "[errata] sync skipped \u2014 cloud sync consent is off (enable with `errata consent sync on`)"
43012
43356
  );
43013
- const pending = existsSync14(paths.outbox) ? readdirSync7(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
43357
+ const pending = existsSync15(paths.outbox) ? readdirSync7(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
43014
43358
  return { uploaded: 0, failed: 0, remaining: pending };
43015
43359
  }
43016
43360
  try {
@@ -43097,7 +43441,7 @@ async function startDaemon(opts) {
43097
43441
  reviewUrl: () => webUiUrl + "/review"
43098
43442
  });
43099
43443
  const writeLockFile = (url2) => {
43100
- writeFileSync12(
43444
+ writeFileSync13(
43101
43445
  engine.paths.daemonLock,
43102
43446
  JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
43103
43447
  "utf8"
@@ -43140,7 +43484,7 @@ async function startDaemon(opts) {
43140
43484
  );
43141
43485
  await engine.stop();
43142
43486
  try {
43143
- if (existsSync15(engine.paths.daemonLock)) {
43487
+ if (existsSync16(engine.paths.daemonLock)) {
43144
43488
  }
43145
43489
  } catch {
43146
43490
  }
@@ -43157,16 +43501,16 @@ async function listenServer(fetchFn, port) {
43157
43501
 
43158
43502
  // src/registry.ts
43159
43503
  init_paths();
43160
- import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync13 } from "node:fs";
43161
- import { join as join20 } from "node:path";
43504
+ import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync14 } from "node:fs";
43505
+ import { join as join21 } from "node:path";
43162
43506
  function registryPath() {
43163
- return process.env["ERRATA_REGISTRY_PATH"] ?? join20(globalDir(), "workspaces.json");
43507
+ return process.env["ERRATA_REGISTRY_PATH"] ?? join21(globalDir(), "workspaces.json");
43164
43508
  }
43165
43509
  function read() {
43166
43510
  const p = registryPath();
43167
- if (!existsSync16(p)) return { version: 1, workspaces: {} };
43511
+ if (!existsSync17(p)) return { version: 1, workspaces: {} };
43168
43512
  try {
43169
- const parsed = JSON.parse(readFileSync14(p, "utf8"));
43513
+ const parsed = JSON.parse(readFileSync15(p, "utf8"));
43170
43514
  return { version: 1, workspaces: parsed.workspaces ?? {} };
43171
43515
  } catch {
43172
43516
  return { version: 1, workspaces: {} };
@@ -43174,7 +43518,7 @@ function read() {
43174
43518
  }
43175
43519
  function write(reg) {
43176
43520
  ensureDir(globalDir());
43177
- writeFileSync13(registryPath(), JSON.stringify(reg, null, 2), "utf8");
43521
+ writeFileSync14(registryPath(), JSON.stringify(reg, null, 2), "utf8");
43178
43522
  }
43179
43523
  function registerWorkspace(profile, root, now = Date.now()) {
43180
43524
  const reg = read();
@@ -43191,7 +43535,7 @@ function pruneMissingWorkspaces() {
43191
43535
  const reg = read();
43192
43536
  const removed = [];
43193
43537
  for (const [id, entry] of Object.entries(reg.workspaces)) {
43194
- if (!existsSync16(entry.path)) {
43538
+ if (!existsSync17(entry.path)) {
43195
43539
  removed.push(entry);
43196
43540
  delete reg.workspaces[id];
43197
43541
  }
@@ -43200,13 +43544,13 @@ function pruneMissingWorkspaces() {
43200
43544
  return removed;
43201
43545
  }
43202
43546
  function workspaceStatus(entry) {
43203
- const missing = !existsSync16(entry.path);
43547
+ const missing = !existsSync17(entry.path);
43204
43548
  const lockPath = workspacePaths(entry.path).daemonLock;
43205
43549
  let running = false;
43206
43550
  let webUiUrl = null;
43207
- if (existsSync16(lockPath)) {
43551
+ if (existsSync17(lockPath)) {
43208
43552
  try {
43209
- const lock = JSON.parse(readFileSync14(lockPath, "utf8"));
43553
+ const lock = JSON.parse(readFileSync15(lockPath, "utf8"));
43210
43554
  if (lock.pid && lock.webUiUrl && pidAlive(lock.pid)) {
43211
43555
  running = true;
43212
43556
  webUiUrl = lock.webUiUrl;
@@ -43234,7 +43578,7 @@ function pidAlive(pid) {
43234
43578
  // src/multi.ts
43235
43579
  init_dist();
43236
43580
  init_src4();
43237
- import { readFileSync as readFileSync17, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
43581
+ import { readFileSync as readFileSync18, unlinkSync as unlinkSync3, writeFileSync as writeFileSync15 } from "node:fs";
43238
43582
 
43239
43583
  // src/principle-sync.ts
43240
43584
  init_src4();
@@ -43261,8 +43605,8 @@ async function syncPrinciples(store, cloud, opts) {
43261
43605
  init_reconcile();
43262
43606
 
43263
43607
  // src/lockfile-auto.ts
43264
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
43265
- import { join as join21 } from "node:path";
43608
+ import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
43609
+ import { join as join22 } from "node:path";
43266
43610
 
43267
43611
  // src/package-index.ts
43268
43612
  init_src();
@@ -43526,11 +43870,11 @@ function runLockfilePass(opts) {
43526
43870
  { file: "package-lock.json", parse: parsePackageLockJson }
43527
43871
  ];
43528
43872
  for (const c of candidates) {
43529
- const p = join21(opts.root, c.file);
43530
- if (!existsSync17(p)) continue;
43873
+ const p = join22(opts.root, c.file);
43874
+ if (!existsSync18(p)) continue;
43531
43875
  let sbom;
43532
43876
  try {
43533
- sbom = c.parse(readFileSync15(p, "utf8"));
43877
+ sbom = c.parse(readFileSync16(p, "utf8"));
43534
43878
  } catch {
43535
43879
  continue;
43536
43880
  }
@@ -43685,6 +44029,27 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
43685
44029
  }
43686
44030
  }
43687
44031
  if (nodes.length === 0 && anchorEdges.length === 0) return null;
44032
+ let symbolSummaries;
44033
+ if (opts.lexicon && opts.lexicon.size > 0 && nodes.length > 0) {
44034
+ const lexicon = opts.lexicon;
44035
+ const isKnown = (tok) => lexicon.has(tok);
44036
+ const sidecar = {};
44037
+ let count = 0;
44038
+ outer: for (const n of nodes) {
44039
+ if (!INSTANCE_LABELS.includes(n.label)) continue;
44040
+ for (const tok of extractIdentifierTokens(n.description)) {
44041
+ if (sidecar[tok]) continue;
44042
+ const entry = lexicon.get(tok);
44043
+ if (!entry?.summary) continue;
44044
+ const scrubbed = generalize(entry.summary, { level }).text.trim();
44045
+ if (!scrubbed || summaryRejectionReason(scrubbed, isKnown) !== null) continue;
44046
+ if (count >= MAX_SIDECAR_SYMBOLS) break outer;
44047
+ sidecar[tok] = { kind: entry.kind, summary: scrubbed };
44048
+ count++;
44049
+ }
44050
+ }
44051
+ if (count > 0) symbolSummaries = sidecar;
44052
+ }
43688
44053
  const edges = [];
43689
44054
  for (const id of seen) {
43690
44055
  for (const e of store.outEdges(id, [...INSTANCE_EDGES])) {
@@ -43699,12 +44064,17 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
43699
44064
  // co-locates the few Language/Package stubs with the first instance chunk,
43700
44065
  // maximizing in-payload endpoint resolution.
43701
44066
  nodes: [...contextNodes, ...nodes],
43702
- edges
44067
+ edges,
44068
+ // Spread-if-present: an absent sidecar keeps `base` — and therefore the
44069
+ // payloadDigest — byte-identical to the pre-sidecar batch (backward compat).
44070
+ ...symbolSummaries ? { symbolSummaries } : {}
43703
44071
  };
43704
44072
  return { ...base, payloadDigest: digest(base), anchorDigests };
43705
44073
  }
43706
44074
 
43707
44075
  // src/multi.ts
44076
+ init_generalize_graph();
44077
+ init_symbol_summaries();
43708
44078
  init_webui();
43709
44079
 
43710
44080
  // src/consolidate-worker-client.ts
@@ -43780,7 +44150,7 @@ var ConsolidateWorker = class {
43780
44150
  init_paths();
43781
44151
 
43782
44152
  // src/lock.ts
43783
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
44153
+ import { existsSync as existsSync19, readFileSync as readFileSync17 } from "node:fs";
43784
44154
  function isProcessAlive(pid) {
43785
44155
  if (!pid || pid <= 0) return false;
43786
44156
  try {
@@ -43791,9 +44161,9 @@ function isProcessAlive(pid) {
43791
44161
  }
43792
44162
  }
43793
44163
  function readDaemonLock(lockPath) {
43794
- if (!existsSync18(lockPath)) return null;
44164
+ if (!existsSync19(lockPath)) return null;
43795
44165
  try {
43796
- const lock = JSON.parse(readFileSync16(lockPath, "utf8"));
44166
+ const lock = JSON.parse(readFileSync17(lockPath, "utf8"));
43797
44167
  return typeof lock.pid === "number" ? lock : null;
43798
44168
  } catch {
43799
44169
  return null;
@@ -44041,7 +44411,7 @@ async function startMultiDaemon(opts = {}) {
44041
44411
  records.push(rec);
44042
44412
  app.route(`/ws/${rec.id}`, rec.webApp);
44043
44413
  try {
44044
- writeFileSync14(
44414
+ writeFileSync15(
44045
44415
  rec.engine.paths.daemonLock,
44046
44416
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${rec.id}`, startedAt: Date.now() }),
44047
44417
  "utf8"
@@ -44224,7 +44594,7 @@ async function startMultiDaemon(opts = {}) {
44224
44594
  baseUrl = `http://127.0.0.1:${port}`;
44225
44595
  try {
44226
44596
  ensureDir(globalDir());
44227
- writeFileSync14(
44597
+ writeFileSync15(
44228
44598
  lockPath,
44229
44599
  JSON.stringify({ pid: process.pid, webUiUrl: baseUrl, startedAt: Date.now() }),
44230
44600
  "utf8"
@@ -44233,7 +44603,7 @@ async function startMultiDaemon(opts = {}) {
44233
44603
  }
44234
44604
  for (const r of records) {
44235
44605
  try {
44236
- writeFileSync14(
44606
+ writeFileSync15(
44237
44607
  r.engine.paths.daemonLock,
44238
44608
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${r.id}`, startedAt: Date.now() }),
44239
44609
  "utf8"
@@ -44445,8 +44815,15 @@ async function startMultiDaemon(opts = {}) {
44445
44815
  const res = await client.ingest(context);
44446
44816
  uploaded += res.accepted;
44447
44817
  }
44818
+ let lexicon;
44819
+ try {
44820
+ const summaries = summariesByBodyHash(loadSymbolSummaryCache(r.engine.paths.configDir));
44821
+ if (summaries.size > 0) lexicon = buildSymbolLexicon(store, summaries);
44822
+ } catch {
44823
+ }
44448
44824
  const instances = buildInstanceIngest(store, r.engine.profile, DAEMON_VERSION, ignore, {
44449
- includePackages: cfg2.consent.contributePackages
44825
+ includePackages: cfg2.consent.contributePackages,
44826
+ ...lexicon ? { lexicon } : {}
44450
44827
  });
44451
44828
  if (instances) {
44452
44829
  const res = await client.ingest(instances);
@@ -44529,7 +44906,7 @@ async function startMultiDaemon(opts = {}) {
44529
44906
  },
44530
44907
  async stop() {
44531
44908
  try {
44532
- const cur = readFileSync17(lockPath, "utf8");
44909
+ const cur = readFileSync18(lockPath, "utf8");
44533
44910
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
44534
44911
  } catch {
44535
44912
  }
@@ -44983,21 +45360,21 @@ async function cmdInit() {
44983
45360
  if (!skipHooks) {
44984
45361
  console.log("");
44985
45362
  console.log("installing harness hooks...");
44986
- const { existsSync: existsSync20 } = await import("node:fs");
44987
- const { join: join23 } = await import("node:path");
45363
+ const { existsSync: existsSync21 } = await import("node:fs");
45364
+ const { join: join24 } = await import("node:path");
44988
45365
  try {
44989
45366
  await installClaudeHooks(port);
44990
45367
  } catch (err2) {
44991
45368
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
44992
45369
  }
44993
- if (existsSync20(join23(ROOT, ".cursor"))) {
45370
+ if (existsSync21(join24(ROOT, ".cursor"))) {
44994
45371
  try {
44995
45372
  await installCursorMcpConfig();
44996
45373
  } catch (err2) {
44997
45374
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
44998
45375
  }
44999
45376
  }
45000
- if (existsSync20(join23(ROOT, ".codex"))) {
45377
+ if (existsSync21(join24(ROOT, ".codex"))) {
45001
45378
  try {
45002
45379
  await installCodexHooks(port);
45003
45380
  } catch (err2) {
@@ -45114,8 +45491,8 @@ async function cmdStatus() {
45114
45491
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
45115
45492
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
45116
45493
  }
45117
- console.log(` graph db: ${existsSync19(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
45118
- console.log(` event log: ${existsSync19(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
45494
+ console.log(` graph db: ${existsSync20(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
45495
+ console.log(` event log: ${existsSync20(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
45119
45496
  const lockPath = globalDaemonLock();
45120
45497
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
45121
45498
  console.log(
@@ -45411,11 +45788,11 @@ async function cmdLogout() {
45411
45788
  }
45412
45789
  async function cmdReview() {
45413
45790
  const paths = workspacePaths(ROOT);
45414
- if (!existsSync19(paths.reviewQueue)) {
45791
+ if (!existsSync20(paths.reviewQueue)) {
45415
45792
  console.log("(review queue empty)");
45416
45793
  return;
45417
45794
  }
45418
- const queue = JSON.parse(readFileSync18(paths.reviewQueue, "utf8"));
45795
+ const queue = JSON.parse(readFileSync19(paths.reviewQueue, "utf8"));
45419
45796
  if (queue.length === 0) {
45420
45797
  console.log("(review queue empty)");
45421
45798
  return;
@@ -46045,7 +46422,7 @@ async function gatherRepo(store, ws) {
46045
46422
  };
46046
46423
  }
46047
46424
  async function gatherReportData(generatedAt) {
46048
- const { existsSync: existsSync20 } = await import("node:fs");
46425
+ const { existsSync: existsSync21 } = await import("node:fs");
46049
46426
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
46050
46427
  const cfg = loadConfig();
46051
46428
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -46053,7 +46430,7 @@ async function gatherReportData(generatedAt) {
46053
46430
  for (const ws of listWorkspaces()) {
46054
46431
  if (ws.missing) continue;
46055
46432
  const dbPath = workspacePaths(ws.path).castalia;
46056
- if (!existsSync20(dbPath)) continue;
46433
+ if (!existsSync21(dbPath)) continue;
46057
46434
  let store = null;
46058
46435
  try {
46059
46436
  store = openGraphStore2({ path: dbPath });
@@ -46084,7 +46461,7 @@ async function gatherReportData(generatedAt) {
46084
46461
  };
46085
46462
  }
46086
46463
  async function cmdReport(args2) {
46087
- const { mkdirSync: mkdirSync6, writeFileSync: writeFileSync15 } = await import("node:fs");
46464
+ const { mkdirSync: mkdirSync6, writeFileSync: writeFileSync16 } = await import("node:fs");
46088
46465
  const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
46089
46466
  const includeFutureVerbs = args2.includes("--future-verbs");
46090
46467
  const now = /* @__PURE__ */ new Date();
@@ -46097,8 +46474,8 @@ async function cmdReport(args2) {
46097
46474
  const outDir = workspacePaths(ROOT).configDir;
46098
46475
  mkdirSync6(outDir, { recursive: true });
46099
46476
  const files = renderReport2(data, { includeFutureVerbs });
46100
- for (const f of files) writeFileSync15(join22(outDir, f.name), f.html, "utf8");
46101
- const indexPath = join22(outDir, "report.html");
46477
+ for (const f of files) writeFileSync16(join23(outDir, f.name), f.html, "utf8");
46478
+ const indexPath = join23(outDir, "report.html");
46102
46479
  console.log(`report \u2192 ${indexPath}`);
46103
46480
  console.log(` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`);
46104
46481
  console.log(` open: file://${indexPath.replace(/\\/g, "/")}`);
@@ -46218,15 +46595,15 @@ function hookRelayCommand(port, path2) {
46218
46595
  return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 2 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} 2>NUL || echo {}"` : `curl -s --connect-timeout 1 --max-time 2 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} 2>/dev/null || echo '{}'`;
46219
46596
  }
46220
46597
  async function installClaudeHooks(port) {
46221
- const { mkdirSync: mkdirSync6, existsSync: existsSync20, readFileSync: readFileSync19, writeFileSync: writeFileSync15 } = await import("node:fs");
46222
- const { join: join23 } = await import("node:path");
46223
- const dir = join23(ROOT, ".claude");
46224
- if (!existsSync20(dir)) mkdirSync6(dir, { recursive: true });
46225
- const file2 = join23(dir, "settings.json");
46598
+ const { mkdirSync: mkdirSync6, existsSync: existsSync21, readFileSync: readFileSync20, writeFileSync: writeFileSync16 } = await import("node:fs");
46599
+ const { join: join24 } = await import("node:path");
46600
+ const dir = join24(ROOT, ".claude");
46601
+ if (!existsSync21(dir)) mkdirSync6(dir, { recursive: true });
46602
+ const file2 = join24(dir, "settings.json");
46226
46603
  let settings = {};
46227
- if (existsSync20(file2)) {
46604
+ if (existsSync21(file2)) {
46228
46605
  try {
46229
- settings = JSON.parse(readFileSync19(file2, "utf8"));
46606
+ settings = JSON.parse(readFileSync20(file2, "utf8"));
46230
46607
  } catch {
46231
46608
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
46232
46609
  process.exit(2);
@@ -46271,21 +46648,21 @@ async function installClaudeHooks(port) {
46271
46648
  dropErrata(list);
46272
46649
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
46273
46650
  }
46274
- writeFileSync15(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
46651
+ writeFileSync16(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
46275
46652
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
46276
46653
  await installClaudeMcpConfig();
46277
46654
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
46278
46655
  }
46279
46656
  async function installClaudeMcpConfig() {
46280
- const { mkdirSync: mkdirSync6, existsSync: existsSync20, readFileSync: readFileSync19, writeFileSync: writeFileSync15 } = await import("node:fs");
46281
- const { join: join23, dirname: dirname8 } = await import("node:path");
46282
- const file2 = join23(ROOT, ".mcp.json");
46657
+ const { mkdirSync: mkdirSync6, existsSync: existsSync21, readFileSync: readFileSync20, writeFileSync: writeFileSync16 } = await import("node:fs");
46658
+ const { join: join24, dirname: dirname8 } = await import("node:path");
46659
+ const file2 = join24(ROOT, ".mcp.json");
46283
46660
  const dir = dirname8(file2);
46284
- if (!existsSync20(dir)) mkdirSync6(dir, { recursive: true });
46661
+ if (!existsSync21(dir)) mkdirSync6(dir, { recursive: true });
46285
46662
  let cfg = {};
46286
- if (existsSync20(file2)) {
46663
+ if (existsSync21(file2)) {
46287
46664
  try {
46288
- cfg = JSON.parse(readFileSync19(file2, "utf8"));
46665
+ cfg = JSON.parse(readFileSync20(file2, "utf8"));
46289
46666
  } catch {
46290
46667
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
46291
46668
  process.exit(2);
@@ -46293,21 +46670,21 @@ async function installClaudeMcpConfig() {
46293
46670
  }
46294
46671
  cfg.mcpServers ??= {};
46295
46672
  cfg.mcpServers["errata"] = errataMcpInvocation();
46296
- writeFileSync15(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
46673
+ writeFileSync16(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
46297
46674
  console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
46298
46675
  console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
46299
46676
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
46300
46677
  }
46301
46678
  async function installCursorMcpConfig() {
46302
- const { mkdirSync: mkdirSync6, existsSync: existsSync20, readFileSync: readFileSync19, writeFileSync: writeFileSync15 } = await import("node:fs");
46303
- const { join: join23 } = await import("node:path");
46304
- const dir = join23(ROOT, ".cursor");
46305
- if (!existsSync20(dir)) mkdirSync6(dir, { recursive: true });
46306
- const file2 = join23(dir, "mcp.json");
46679
+ const { mkdirSync: mkdirSync6, existsSync: existsSync21, readFileSync: readFileSync20, writeFileSync: writeFileSync16 } = await import("node:fs");
46680
+ const { join: join24 } = await import("node:path");
46681
+ const dir = join24(ROOT, ".cursor");
46682
+ if (!existsSync21(dir)) mkdirSync6(dir, { recursive: true });
46683
+ const file2 = join24(dir, "mcp.json");
46307
46684
  let cfg = {};
46308
- if (existsSync20(file2)) {
46685
+ if (existsSync21(file2)) {
46309
46686
  try {
46310
- cfg = JSON.parse(readFileSync19(file2, "utf8"));
46687
+ cfg = JSON.parse(readFileSync20(file2, "utf8"));
46311
46688
  } catch {
46312
46689
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
46313
46690
  process.exit(2);
@@ -46315,7 +46692,7 @@ async function installCursorMcpConfig() {
46315
46692
  }
46316
46693
  cfg.mcpServers ??= {};
46317
46694
  cfg.mcpServers["errata"] = errataMcpInvocation();
46318
- writeFileSync15(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
46695
+ writeFileSync16(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
46319
46696
  console.log(`installed Cursor MCP server config \u2192 ${file2}`);
46320
46697
  console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
46321
46698
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
@@ -46323,16 +46700,16 @@ async function installCursorMcpConfig() {
46323
46700
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
46324
46701
  }
46325
46702
  async function installCodexHooks(port) {
46326
- const { mkdirSync: mkdirSync6, existsSync: existsSync20, readFileSync: readFileSync19, writeFileSync: writeFileSync15 } = await import("node:fs");
46327
- const { join: join23 } = await import("node:path");
46328
- const dir = join23(ROOT, ".codex");
46329
- if (!existsSync20(dir)) mkdirSync6(dir, { recursive: true });
46330
- const file2 = join23(dir, "config.toml");
46703
+ const { mkdirSync: mkdirSync6, existsSync: existsSync21, readFileSync: readFileSync20, writeFileSync: writeFileSync16 } = await import("node:fs");
46704
+ const { join: join24 } = await import("node:path");
46705
+ const dir = join24(ROOT, ".codex");
46706
+ if (!existsSync21(dir)) mkdirSync6(dir, { recursive: true });
46707
+ const file2 = join24(dir, "config.toml");
46331
46708
  const BEGIN = `# >>> errata hooks (errata-managed)`;
46332
46709
  const END = `# <<< errata hooks`;
46333
46710
  let existing = "";
46334
- if (existsSync20(file2)) {
46335
- existing = readFileSync19(file2, "utf8");
46711
+ if (existsSync21(file2)) {
46712
+ existing = readFileSync20(file2, "utf8");
46336
46713
  const beginIdx = existing.indexOf(BEGIN);
46337
46714
  const endIdx = existing.indexOf(END);
46338
46715
  if (beginIdx >= 0 && endIdx > beginIdx) {
@@ -46361,7 +46738,7 @@ ${END}
46361
46738
  const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
46362
46739
 
46363
46740
  ${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
46364
- writeFileSync15(file2, final, "utf8");
46741
+ writeFileSync16(file2, final, "utf8");
46365
46742
  console.log(`installed Codex hooks \u2192 ${file2}`);
46366
46743
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
46367
46744
  console.log("");
@@ -46624,7 +47001,7 @@ async function cmdDash(args2) {
46624
47001
  await yieldToLoop2();
46625
47002
  try {
46626
47003
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
46627
- const res = bleedRules(join22(r.root, ".claude", "rules"), items);
47004
+ const res = bleedRules(join23(r.root, ".claude", "rules"), items);
46628
47005
  if (res.written || res.pruned) {
46629
47006
  console.log(
46630
47007
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")