@c4a/context-cli 0.5.29-beta.29 → 0.5.33-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.js CHANGED
@@ -8905,11 +8905,35 @@ function validateCompile(value) {
8905
8905
  }
8906
8906
  return compile;
8907
8907
  }
8908
- async function loadConfig(ctxDir) {
8909
- const configPath = join(ctxDir, "config.yaml");
8910
- if (!existsSync(configPath)) {
8911
- throw new Error(`config.yaml not found at ${configPath}`);
8908
+ function validateRender(value) {
8909
+ if (value === undefined)
8910
+ return;
8911
+ if (!value || typeof value !== "object") {
8912
+ throw new Error(`config.yaml: render must be an object`);
8913
+ }
8914
+ const r = value;
8915
+ const render = {};
8916
+ if (r.obsidian_mode !== undefined) {
8917
+ if (typeof r.obsidian_mode !== "boolean") {
8918
+ throw new Error(`config.yaml: render.obsidian_mode must be boolean`);
8919
+ }
8920
+ render.obsidian_mode = r.obsidian_mode;
8912
8921
  }
8922
+ if (r.used_by !== undefined) {
8923
+ if (typeof r.used_by !== "boolean") {
8924
+ throw new Error(`config.yaml: render.used_by must be boolean`);
8925
+ }
8926
+ render.used_by = r.used_by;
8927
+ }
8928
+ return render;
8929
+ }
8930
+ function normalizeRenderConfig(value) {
8931
+ return {
8932
+ obsidian_mode: value?.obsidian_mode ?? DEFAULT_RENDER_CONFIG.obsidian_mode,
8933
+ used_by: value?.used_by ?? DEFAULT_RENDER_CONFIG.used_by
8934
+ };
8935
+ }
8936
+ async function parseConfigFile(configPath, options) {
8913
8937
  const raw = await readFile(configPath, "utf8");
8914
8938
  let parsed;
8915
8939
  try {
@@ -8918,7 +8942,16 @@ async function loadConfig(ctxDir) {
8918
8942
  const msg = err2 instanceof Error ? err2.message : String(err2);
8919
8943
  throw new Error(`config.yaml parse failed: ${msg}`);
8920
8944
  }
8921
- const doc = validateConfigRoot(parsed, configPath);
8945
+ if (parsed === null && !options.requireWorkspace)
8946
+ return {};
8947
+ return validateConfigRoot(parsed, configPath);
8948
+ }
8949
+ async function loadConfig(ctxDir) {
8950
+ const configPath = join(ctxDir, "config.yaml");
8951
+ if (!existsSync(configPath)) {
8952
+ throw new Error(`config.yaml not found at ${configPath}`);
8953
+ }
8954
+ const doc = await parseConfigFile(configPath, { requireWorkspace: true });
8922
8955
  const result = {
8923
8956
  workspace: validateWorkspace(doc)
8924
8957
  };
@@ -8939,12 +8972,28 @@ async function loadConfig(ctxDir) {
8939
8972
  if (compile !== undefined) {
8940
8973
  result.compile = compile;
8941
8974
  }
8975
+ const render = validateRender(doc.render);
8976
+ if (render !== undefined) {
8977
+ result.render = render;
8978
+ }
8942
8979
  return result;
8943
8980
  }
8944
- var import_yaml, VALID_TRUSTS;
8981
+ async function loadRenderConfig(ctxDir) {
8982
+ const configPath = join(ctxDir, "config.yaml");
8983
+ if (!existsSync(configPath)) {
8984
+ return { ...DEFAULT_RENDER_CONFIG };
8985
+ }
8986
+ const doc = await parseConfigFile(configPath, { requireWorkspace: false });
8987
+ return normalizeRenderConfig(validateRender(doc.render));
8988
+ }
8989
+ var import_yaml, VALID_TRUSTS, DEFAULT_RENDER_CONFIG;
8945
8990
  var init_config = __esm(() => {
8946
8991
  import_yaml = __toESM(require_dist(), 1);
8947
8992
  VALID_TRUSTS = ["private", "company", "public"];
8993
+ DEFAULT_RENDER_CONFIG = Object.freeze({
8994
+ obsidian_mode: false,
8995
+ used_by: true
8996
+ });
8948
8997
  });
8949
8998
 
8950
8999
  // src/lib/errors.ts
@@ -29389,7 +29438,7 @@ async function syncEvidenceManifestsForSource(ctxDir, source2, now = new Date) {
29389
29438
  if (await evidenceManifestModeForSource(ctxDir, source2) === "none")
29390
29439
  return;
29391
29440
  for (const snapshot of source2.snapshots) {
29392
- if (existsSync4(evidenceManifestPath(ctxDir, source2.id, snapshot.content_hash)))
29441
+ if (await readEvidenceManifest(ctxDir, source2.id, snapshot.content_hash))
29393
29442
  continue;
29394
29443
  const manifest = await buildEvidenceManifestForSnapshot({ ctxDir, source: source2, snapshot, now });
29395
29444
  if (manifest)
@@ -29425,23 +29474,35 @@ async function sourceSnapshotForManifest(input) {
29425
29474
  return null;
29426
29475
  const source2 = parsed.sources.map(asSourceEntry).find((entry) => entry?.id === input.sourceId);
29427
29476
  const wantedHash = normalizeStoredHash(input.snapshotHash);
29428
- const snapshot = source2?.snapshots.map(asSnapshotEntry).find((candidate) => candidate !== null && normalizeStoredHash(candidate.content_hash) === wantedHash);
29429
- return source2 !== undefined && snapshot !== undefined ? { source: source2, snapshot } : null;
29477
+ const matchingSnapshots = source2?.snapshots.map(asSnapshotEntry).filter((candidate) => candidate !== null && normalizeStoredHash(candidate.content_hash) === wantedHash) ?? [];
29478
+ const snapshot = selectLatestEvidenceSnapshot(matchingSnapshots);
29479
+ return source2 !== undefined && snapshot !== null ? { source: source2, snapshot } : null;
29430
29480
  }
29431
- async function rebuildEvidenceManifest(ctxDir, sourceId, snapshotHash) {
29432
- const resolved = await sourceSnapshotForManifest({ ctxDir, sourceId, snapshotHash });
29433
- if (resolved === null)
29434
- return null;
29481
+ async function rebuildEvidenceManifestFromSnapshot(ctxDir, source2, snapshot) {
29435
29482
  const manifest = await buildEvidenceManifestForSnapshot({
29436
29483
  ctxDir,
29437
- source: resolved.source,
29438
- snapshot: resolved.snapshot
29484
+ source: source2,
29485
+ snapshot
29439
29486
  });
29440
29487
  if (manifest === null)
29441
29488
  return null;
29442
29489
  await writeEvidenceManifest(ctxDir, manifest);
29443
29490
  return manifest;
29444
29491
  }
29492
+ function optionalSnapshotField(value) {
29493
+ return value === undefined || value.length === 0 ? undefined : value;
29494
+ }
29495
+ function cachedManifestMatchesSnapshot(ctxDir, manifest, snapshot) {
29496
+ if (optionalSnapshotField(manifest.snapshot_file) !== optionalSnapshotField(snapshot.file))
29497
+ return false;
29498
+ if (optionalSnapshotField(manifest.snapshot_dir) !== optionalSnapshotField(snapshot.dir))
29499
+ return false;
29500
+ if (manifest.captured_at !== snapshot.captured_at)
29501
+ return false;
29502
+ if (manifest.snapshot_file !== undefined && !existsSync4(join6(ctxDir, manifest.snapshot_file)))
29503
+ return false;
29504
+ return true;
29505
+ }
29445
29506
  async function readCachedEvidenceManifest(path3) {
29446
29507
  if (!existsSync4(path3))
29447
29508
  return null;
@@ -29452,6 +29513,7 @@ async function readCachedEvidenceManifest(path3) {
29452
29513
  }
29453
29514
  async function readEvidenceManifest(ctxDir, sourceId, snapshotHash) {
29454
29515
  const path3 = evidenceManifestPath(ctxDir, sourceId, snapshotHash);
29516
+ const currentSnapshot = await sourceSnapshotForManifest({ ctxDir, sourceId, snapshotHash });
29455
29517
  let parsed;
29456
29518
  try {
29457
29519
  parsed = await readCachedEvidenceManifest(path3);
@@ -29459,10 +29521,10 @@ async function readEvidenceManifest(ctxDir, sourceId, snapshotHash) {
29459
29521
  parsed = null;
29460
29522
  }
29461
29523
  if (parsed === null) {
29462
- return rebuildEvidenceManifest(ctxDir, sourceId, snapshotHash);
29524
+ return currentSnapshot === null ? null : rebuildEvidenceManifestFromSnapshot(ctxDir, currentSnapshot.source, currentSnapshot.snapshot);
29463
29525
  }
29464
29526
  if (parsed.source_id !== sourceId || normalizeStoredHash(parsed.snapshot_content_hash) !== normalizeStoredHash(snapshotHash)) {
29465
- const rebuilt = await rebuildEvidenceManifest(ctxDir, sourceId, snapshotHash);
29527
+ const rebuilt = currentSnapshot === null ? null : await rebuildEvidenceManifestFromSnapshot(ctxDir, currentSnapshot.source, currentSnapshot.snapshot);
29466
29528
  if (rebuilt !== null)
29467
29529
  return rebuilt;
29468
29530
  throw new ContextError(ExitCode.WorkspaceStateError, "evidence-manifest identity mismatch", {
@@ -29479,6 +29541,9 @@ async function readEvidenceManifest(ctxDir, sourceId, snapshotHash) {
29479
29541
  }]
29480
29542
  });
29481
29543
  }
29544
+ if (currentSnapshot !== null && !cachedManifestMatchesSnapshot(ctxDir, parsed, currentSnapshot.snapshot)) {
29545
+ return rebuildEvidenceManifestFromSnapshot(ctxDir, currentSnapshot.source, currentSnapshot.snapshot);
29546
+ }
29482
29547
  return parsed;
29483
29548
  }
29484
29549
  async function readLatestEvidenceManifest(ctxDir, source2) {
@@ -31763,7 +31828,9 @@ function splitFrontmatter2(markdown) {
31763
31828
  }
31764
31829
  return {
31765
31830
  frontmatter: parsed,
31766
- body: normalized.slice(bodyIndex)
31831
+ body: normalized.slice(bodyIndex),
31832
+ bodyLineOffset: normalized.slice(0, bodyIndex).split(`
31833
+ `).length - 1
31767
31834
  };
31768
31835
  }
31769
31836
  function toTemporalValue(value) {
@@ -31883,25 +31950,54 @@ function parseContainsList(lines, startIndex, rootSlug) {
31883
31950
  }
31884
31951
  return { entries, edges, nextIndex: index2 };
31885
31952
  }
31886
- function skipDerivedList(lines, startIndex, title) {
31887
- const heading2 = new RegExp(`^(#{2,6})\\s+${title}\\s*$`).exec(lines[startIndex] ?? "");
31953
+ function parseAutoBlockHeading(line, expectedLevel) {
31954
+ const heading2 = /^(#{2,6})\s+(.+?)\s*$/.exec(line);
31888
31955
  if (!heading2)
31889
- return startIndex;
31890
- const level = heading2[1]?.length ?? 2;
31891
- let index2 = startIndex + 1;
31956
+ return null;
31957
+ const level = heading2[1]?.length ?? 0;
31958
+ if (level !== expectedLevel)
31959
+ return null;
31960
+ const title = (heading2[2] ?? "").trim();
31961
+ const kind = AUTO_BLOCK_TITLES.get(title);
31962
+ return kind === undefined ? null : { level, title, kind };
31963
+ }
31964
+ function autoBlockOpenPattern(kind) {
31965
+ return new RegExp(`^<!--\\s+c4a:auto-block\\s+${kind}\\s+-->$`);
31966
+ }
31967
+ function consumeAutoBlock(lines, startIndex, expectedLevel, lineOffset = 0) {
31968
+ const heading2 = parseAutoBlockHeading(lines[startIndex] ?? "", expectedLevel);
31969
+ if (heading2 === null)
31970
+ return { matched: false, nextIndex: startIndex };
31971
+ const openIndex = skipBlankLines(lines, startIndex + 1);
31972
+ const opening = lines[openIndex]?.trim() ?? "";
31973
+ if (!autoBlockOpenPattern(heading2.kind).test(opening)) {
31974
+ return { matched: false, nextIndex: startIndex };
31975
+ }
31976
+ let index2 = openIndex + 1;
31892
31977
  while (index2 < lines.length) {
31893
31978
  const line = lines[index2] ?? "";
31894
- assertNoLegacySectionMarker(line);
31895
- if (isSectionOpenComment(line))
31896
- break;
31979
+ if (line.trim() === AUTO_BLOCK_CLOSE) {
31980
+ index2 += 1;
31981
+ while (index2 < lines.length && (lines[index2]?.trim() ?? "") === "")
31982
+ index2 += 1;
31983
+ return { matched: true, nextIndex: index2 };
31984
+ }
31897
31985
  const nextHeading = /^(#{1,6})\s+/.exec(line);
31898
- if (nextHeading && (nextHeading[1]?.length ?? 0) <= level)
31986
+ if (nextHeading && (nextHeading[1]?.length ?? 0) <= heading2.level) {
31899
31987
  break;
31988
+ }
31900
31989
  index2 += 1;
31901
31990
  }
31902
- while (index2 < lines.length && (lines[index2]?.trim() ?? "") === "")
31903
- index2 += 1;
31904
- return index2;
31991
+ return {
31992
+ matched: false,
31993
+ nextIndex: startIndex,
31994
+ issue: {
31995
+ code: "render-auto-block-unclosed",
31996
+ kind: heading2.kind,
31997
+ title: heading2.title,
31998
+ line: lineOffset + openIndex + 1
31999
+ }
32000
+ };
31905
32001
  }
31906
32002
  function assertNoLegacySectionMarker(line) {
31907
32003
  if (!isLegacySectionOpenComment(line))
@@ -31964,7 +32060,7 @@ function parseFrontmatterNode(lines, index2) {
31964
32060
  }
31965
32061
  return { node: node3, nextIndex: fmEnd + 1 };
31966
32062
  }
31967
- function parseNodeBlock(lines, startIndex, headingLevel) {
32063
+ function parseNodeBlock(lines, startIndex, headingLevel, lineOffset = 0) {
31968
32064
  const headingEnd = consumeExpectedHeading(lines, startIndex, headingLevel);
31969
32065
  const frontmatter = parseFrontmatterNode(lines, headingEnd);
31970
32066
  const node3 = frontmatter.node;
@@ -31973,6 +32069,7 @@ function parseNodeBlock(lines, startIndex, headingLevel) {
31973
32069
  const bodyLines = [];
31974
32070
  const containsList = [];
31975
32071
  const children = [];
32072
+ const renderAutoBlockIssues = [];
31976
32073
  const childLevel = headingLevel + 1;
31977
32074
  const childHeadingPattern = new RegExp(`^#{${childLevel}}\\s+.+`);
31978
32075
  while (index2 < lines.length) {
@@ -31987,7 +32084,7 @@ function parseNodeBlock(lines, startIndex, headingLevel) {
31987
32084
  if (childHeadingPattern.test(currentLine)) {
31988
32085
  const peek = peekIsFrontmatterAfterHeading(lines, index2);
31989
32086
  if (peek) {
31990
- const childResult = parseNodeBlock(lines, index2, childLevel);
32087
+ const childResult = parseNodeBlock(lines, index2, childLevel, lineOffset);
31991
32088
  children.push(childResult.parsed);
31992
32089
  index2 = childResult.nextIndex;
31993
32090
  continue;
@@ -32005,8 +32102,12 @@ function parseNodeBlock(lines, startIndex, headingLevel) {
32005
32102
  index2 = listParsed.nextIndex;
32006
32103
  continue;
32007
32104
  }
32008
- if (new RegExp(`^#{${headingLevel + 1}}\\s+Related\\s*$`).test(currentLine)) {
32009
- index2 = skipDerivedList(lines, index2, "Related");
32105
+ const autoBlock = consumeAutoBlock(lines, index2, headingLevel + 1, lineOffset);
32106
+ if (autoBlock.issue !== undefined) {
32107
+ renderAutoBlockIssues.push(autoBlock.issue);
32108
+ }
32109
+ if (autoBlock.matched) {
32110
+ index2 = autoBlock.nextIndex;
32010
32111
  continue;
32011
32112
  }
32012
32113
  if (isSectionGroupHeading(currentLine, headingLevel + 1)) {
@@ -32042,11 +32143,14 @@ function parseNodeBlock(lines, startIndex, headingLevel) {
32042
32143
  parsed: {
32043
32144
  node: node3,
32044
32145
  headingLevel,
32146
+ startLine: lineOffset + startIndex + 1,
32147
+ endLine: lineOffset + index2,
32045
32148
  body: body2,
32046
32149
  sections,
32047
32150
  children,
32048
32151
  containsEdges,
32049
- containsList
32152
+ containsList,
32153
+ ...renderAutoBlockIssues.length > 0 ? { renderAutoBlockIssues } : {}
32050
32154
  },
32051
32155
  nextIndex: index2
32052
32156
  };
@@ -32079,7 +32183,7 @@ function parsePlainSubHeadingEdges(body2, parentSlug, parentLevel) {
32079
32183
  if (level <= parentLevel)
32080
32184
  continue;
32081
32185
  const title = (match[2] ?? "").trim();
32082
- if (title.length === 0 || level === parentLevel + 1 && (title === "Contains" || title === "Related" || SECTION_GROUP_TITLES.has(title)))
32186
+ if (title.length === 0 || level === parentLevel + 1 && (AUTO_BLOCK_EXCLUDED_CONTAINS_TITLES.has(title) || SECTION_GROUP_TITLES.has(title)))
32083
32187
  continue;
32084
32188
  if (peekIsFrontmatterAfterHeading(lines, i))
32085
32189
  continue;
@@ -32106,7 +32210,7 @@ function parseNodeMarkdown(markdown) {
32106
32210
  return parsed;
32107
32211
  }
32108
32212
  function parseRootNodeFrontmatterFirst(normalized) {
32109
- const { frontmatter, body: body2 } = splitFrontmatter2(normalized);
32213
+ const { frontmatter, body: body2, bodyLineOffset } = splitFrontmatter2(normalized);
32110
32214
  const node3 = parseNodeRecord(frontmatter);
32111
32215
  const nodeIssues = validateKnowledgeNode(node3);
32112
32216
  if (nodeIssues.length > 0) {
@@ -32117,7 +32221,9 @@ function parseRootNodeFrontmatterFirst(normalized) {
32117
32221
  let index2 = 0;
32118
32222
  while (index2 < lines.length && (lines[index2]?.trim() ?? "") === "")
32119
32223
  index2 += 1;
32224
+ let rootStartLine = 1;
32120
32225
  if ((lines[index2] ?? "").startsWith("# ")) {
32226
+ rootStartLine = bodyLineOffset + index2 + 1;
32121
32227
  index2 += 1;
32122
32228
  while (index2 < lines.length && (lines[index2]?.trim() ?? "") === "")
32123
32229
  index2 += 1;
@@ -32126,12 +32232,13 @@ function parseRootNodeFrontmatterFirst(normalized) {
32126
32232
  const bodyLines = [];
32127
32233
  const containsList = [];
32128
32234
  const children = [];
32235
+ const renderAutoBlockIssues = [];
32129
32236
  const childLevel = 2;
32130
32237
  while (index2 < lines.length) {
32131
32238
  const currentLine = lines[index2] ?? "";
32132
32239
  assertNoLegacySectionMarker(currentLine);
32133
32240
  if (new RegExp(`^#{${childLevel}}\\s+.+`).test(currentLine) && peekIsFrontmatterAfterHeading(lines, index2)) {
32134
- const childResult = parseNodeBlock(lines, index2, childLevel);
32241
+ const childResult = parseNodeBlock(lines, index2, childLevel, bodyLineOffset);
32135
32242
  children.push(childResult.parsed);
32136
32243
  index2 = childResult.nextIndex;
32137
32244
  continue;
@@ -32148,8 +32255,12 @@ function parseRootNodeFrontmatterFirst(normalized) {
32148
32255
  index2 = listParsed.nextIndex;
32149
32256
  continue;
32150
32257
  }
32151
- if (/^##\s+Related\s*$/.test(currentLine)) {
32152
- index2 = skipDerivedList(lines, index2, "Related");
32258
+ const autoBlock = consumeAutoBlock(lines, index2, 2, bodyLineOffset);
32259
+ if (autoBlock.issue !== undefined) {
32260
+ renderAutoBlockIssues.push(autoBlock.issue);
32261
+ }
32262
+ if (autoBlock.matched) {
32263
+ index2 = autoBlock.nextIndex;
32153
32264
  continue;
32154
32265
  }
32155
32266
  if (isSectionGroupHeading(currentLine, 2)) {
@@ -32184,20 +32295,33 @@ function parseRootNodeFrontmatterFirst(normalized) {
32184
32295
  return {
32185
32296
  node: node3,
32186
32297
  headingLevel: 1,
32298
+ startLine: rootStartLine,
32299
+ endLine: bodyLineOffset + lines.length,
32187
32300
  body: bodyText,
32188
32301
  sections,
32189
32302
  children,
32190
32303
  containsEdges,
32191
- containsList
32304
+ containsList,
32305
+ ...renderAutoBlockIssues.length > 0 ? { renderAutoBlockIssues } : {}
32192
32306
  };
32193
32307
  }
32194
- var import_yaml10;
32308
+ var import_yaml10, AUTO_BLOCK_TITLES, AUTO_BLOCK_EXCLUDED_CONTAINS_TITLES, AUTO_BLOCK_CLOSE = "<!-- /c4a:auto-block -->";
32195
32309
  var init_nodeParser = __esm(() => {
32196
32310
  init_normalize();
32197
32311
  init_nodeDisplayGroups();
32198
32312
  init_nodeParserSections();
32199
32313
  init_knowledge();
32200
32314
  import_yaml10 = __toESM(require_dist(), 1);
32315
+ AUTO_BLOCK_TITLES = new Map([
32316
+ ["Depends On", "depends_on"],
32317
+ ["External Dependencies", "external_dependencies"],
32318
+ ["Used By", "used_by"],
32319
+ ["Related", "related"]
32320
+ ]);
32321
+ AUTO_BLOCK_EXCLUDED_CONTAINS_TITLES = new Set([
32322
+ "Contains",
32323
+ ...AUTO_BLOCK_TITLES.keys()
32324
+ ]);
32201
32325
  });
32202
32326
 
32203
32327
  // src/lib/nodeRenderer.ts
@@ -32347,8 +32471,67 @@ function renderContainsList(entries, headingLevel) {
32347
32471
  return lines.join(`
32348
32472
  `);
32349
32473
  }
32350
- function renderRelatedList(nodeSlug, sections, headingLevel, containsList = [], graphEdges = [], relatedLinks = new Map) {
32474
+ function normalizeRenderOptions(options) {
32475
+ return {
32476
+ nodeLinkMode: options?.nodeLinkMode ?? "md",
32477
+ usedBy: options?.usedBy ?? true
32478
+ };
32479
+ }
32480
+ function formatVersionLabel(value) {
32481
+ const label = String(value);
32482
+ return label.startsWith("v") ? label : `v${label}`;
32483
+ }
32484
+ function formatVersionAnnotation(entry) {
32485
+ if (entry.valid_from !== undefined && entry.valid_until !== undefined) {
32486
+ return ` — from ${formatVersionLabel(entry.valid_from)} before ${formatVersionLabel(entry.valid_until)}`;
32487
+ }
32488
+ if (entry.valid_from !== undefined) {
32489
+ return ` — since ${formatVersionLabel(entry.valid_from)}`;
32490
+ }
32491
+ if (entry.valid_until !== undefined) {
32492
+ return ` — before ${formatVersionLabel(entry.valid_until)}`;
32493
+ }
32494
+ return "";
32495
+ }
32496
+ function formatNodeLink(link2, mode) {
32497
+ if (mode === "wiki" && link2.wikiTarget !== undefined && link2.wikiTarget.length > 0) {
32498
+ return `[[${link2.wikiTarget}|${link2.title}]]`;
32499
+ }
32500
+ return `[${link2.title}](${link2.href})`;
32501
+ }
32502
+ function renderAutoBlock(headingTitle, kind, headingLevel, lines) {
32503
+ if (lines.length === 0)
32504
+ return "";
32505
+ const hashes = "#".repeat(headingLevel);
32506
+ return [
32507
+ `${hashes} ${headingTitle}`,
32508
+ "",
32509
+ `<!-- c4a:auto-block ${kind} -->`,
32510
+ ...lines,
32511
+ "<!-- /c4a:auto-block -->"
32512
+ ].join(`
32513
+ `);
32514
+ }
32515
+ function renderNodeRelationBlock(headingTitle, kind, entries, headingLevel, linkMode) {
32516
+ if (entries === undefined || entries.length === 0)
32517
+ return "";
32518
+ const lines = [...entries].sort((left, right) => left.slug.localeCompare(right.slug)).map((entry) => `- ${formatNodeLink(entry, linkMode)}${formatVersionAnnotation(entry)}`);
32519
+ return renderAutoBlock(headingTitle, kind, headingLevel, lines);
32520
+ }
32521
+ function renderExternalDependencyBlock(entries, headingLevel) {
32522
+ if (entries === undefined || entries.length === 0)
32523
+ return "";
32524
+ const lines = [...entries].sort((left, right) => left.package.localeCompare(right.package)).map((entry) => {
32525
+ const version2 = entry.version_constraint !== undefined ? ` \`${entry.version_constraint}\`` : "";
32526
+ return `- \`${entry.package}\`${version2}${formatVersionAnnotation(entry)}`;
32527
+ });
32528
+ return renderAutoBlock("External Dependencies", "external_dependencies", headingLevel, lines);
32529
+ }
32530
+ function renderRelatedList(nodeSlug, sections, headingLevel, containsList = [], graphEdges = [], relatedLinks = new Map, renderOptions, excludedRelatedSlugs = []) {
32531
+ const options = normalizeRenderOptions(renderOptions);
32351
32532
  const contained = new Set(containsList.map((entry) => entry.slug));
32533
+ for (const slug of excludedRelatedSlugs)
32534
+ contained.add(slug);
32352
32535
  for (const edge2 of graphEdges) {
32353
32536
  if (edge2.type !== "contains")
32354
32537
  continue;
@@ -32367,26 +32550,25 @@ function renderRelatedList(nodeSlug, sections, headingLevel, containsList = [],
32367
32550
  related.set(slug, [...related.get(slug) ?? [], section.id]);
32368
32551
  }
32369
32552
  }
32370
- for (const edge2 of graphEdges) {
32371
- if (edge2.type === "contains")
32372
- continue;
32373
- const target = edge2.from === nodeSlug ? edge2.to : null;
32374
- if (!target || contained.has(target))
32375
- continue;
32376
- const label = `align:${edge2.type}`;
32377
- related.set(target, [...related.get(target) ?? [], label]);
32378
- }
32379
32553
  if (related.size === 0)
32380
32554
  return "";
32381
- const hashes = "#".repeat(headingLevel);
32382
- const lines = [`${hashes} Related`, ""];
32555
+ const lines = [];
32383
32556
  for (const [slug] of [...related.entries()].sort(([left], [right]) => left.localeCompare(right))) {
32384
32557
  const link2 = relatedLinks.get(slug);
32385
- const renderedTarget = link2 ? `[${link2.title}](${link2.href})` : `\`${slug}\``;
32558
+ const renderedTarget = link2 ? formatNodeLink(link2, options.nodeLinkMode) : `\`${slug}\``;
32386
32559
  lines.push(`- ${renderedTarget}`);
32387
32560
  }
32388
- return lines.join(`
32389
- `);
32561
+ return renderAutoBlock("Related", "related", headingLevel, lines);
32562
+ }
32563
+ function renderRelationBlocks(input, headingLevel) {
32564
+ const options = normalizeRenderOptions(input.renderOptions);
32565
+ const blocks = [
32566
+ renderNodeRelationBlock("Depends On", "depends_on", input.dependsOn, headingLevel, options.nodeLinkMode),
32567
+ renderExternalDependencyBlock(input.externalDependencies, headingLevel),
32568
+ ...options.usedBy ? [renderNodeRelationBlock("Used By", "used_by", input.usedBy, headingLevel, options.nodeLinkMode)] : [],
32569
+ renderRelatedList(input.node.id, input.sections, headingLevel, input.containsList, input.graphEdges, input.relatedLinks, input.renderOptions, [...(input.dependsOn ?? []).map((entry) => entry.slug), ...(input.usedBy ?? []).map((entry) => entry.slug)])
32570
+ ];
32571
+ return blocks.filter((block) => block.length > 0);
32390
32572
  }
32391
32573
  function renderedBody(input) {
32392
32574
  if (input.body !== undefined && input.body.trim().length > 0) {
@@ -32412,13 +32594,13 @@ function renderNodeMarkdown(input) {
32412
32594
  parts.push("", renderChildNodeBlock({
32413
32595
  ...child,
32414
32596
  graphEdges: input.graphEdges ?? [],
32415
- ...input.relatedLinks !== undefined ? { relatedLinks: input.relatedLinks } : {}
32597
+ ...input.relatedLinks !== undefined ? { relatedLinks: input.relatedLinks } : {},
32598
+ ...input.renderOptions !== undefined ? { renderOptions: input.renderOptions } : {}
32416
32599
  }, 2));
32417
32600
  }
32418
32601
  }
32419
- const related = renderRelatedList(input.node.id, input.sections, 2, input.containsList, input.graphEdges, input.relatedLinks);
32420
- if (related.length > 0) {
32421
- parts.push("", related);
32602
+ for (const block of renderRelationBlocks(input, 2)) {
32603
+ parts.push("", block);
32422
32604
  }
32423
32605
  return `${parts.join(`
32424
32606
  `).replace(/\n{3,}/g, `
@@ -32445,13 +32627,13 @@ function renderChildNodeBlock(input, headingLevel) {
32445
32627
  parts.push("", renderChildNodeBlock({
32446
32628
  ...child,
32447
32629
  graphEdges: input.graphEdges ?? [],
32448
- ...input.relatedLinks !== undefined ? { relatedLinks: input.relatedLinks } : {}
32630
+ ...input.relatedLinks !== undefined ? { relatedLinks: input.relatedLinks } : {},
32631
+ ...input.renderOptions !== undefined ? { renderOptions: input.renderOptions } : {}
32449
32632
  }, headingLevel + 1));
32450
32633
  }
32451
32634
  }
32452
- const related = renderRelatedList(input.node.id, input.sections, headingLevel + 1, input.containsList, input.graphEdges, input.relatedLinks);
32453
- if (related.length > 0) {
32454
- parts.push("", related);
32635
+ for (const block of renderRelationBlocks(input, headingLevel + 1)) {
32636
+ parts.push("", block);
32455
32637
  }
32456
32638
  return parts.join(`
32457
32639
  `);
@@ -32488,15 +32670,15 @@ function flatSlugForNestedSlug(slug) {
32488
32670
  return pkg && leaf ? `${pkg}-${leaf}` : null;
32489
32671
  }
32490
32672
 
32491
- // src/mdrive/graphEdges.ts
32673
+ // src/mdrive/externalDeps.ts
32492
32674
  import { existsSync as existsSync9 } from "node:fs";
32493
32675
  import { readFile as readFile10, readdir as readdir2, rm as rm2 } from "node:fs/promises";
32494
32676
  import { join as join10, relative as relative3, resolve as resolve3 } from "node:path";
32495
32677
  function knowledgeRoot(ctxDir) {
32496
32678
  return join10(ctxDir, "knowledge");
32497
32679
  }
32498
- function graphEdgesPath(ctxDir) {
32499
- return join10(knowledgeRoot(ctxDir), "_edges.yaml");
32680
+ function externalDepsPath(ctxDir) {
32681
+ return join10(knowledgeRoot(ctxDir), "_external.yaml");
32500
32682
  }
32501
32683
  function toPosixPath(value) {
32502
32684
  return value.replace(/\\/g, "/");
@@ -32507,11 +32689,262 @@ function relativeToKnowledge(ctxDir, path3) {
32507
32689
  function isTemporalValue(value) {
32508
32690
  return typeof value === "string" || typeof value === "number";
32509
32691
  }
32692
+ function isExternalDep(value) {
32693
+ if (!value || typeof value !== "object" || Array.isArray(value))
32694
+ return false;
32695
+ const dep = value;
32696
+ return typeof dep.from === "string" && typeof dep.package === "string" && (dep.version_constraint === undefined || typeof dep.version_constraint === "string") && (dep.valid_from === undefined || isTemporalValue(dep.valid_from)) && (dep.valid_until === undefined || isTemporalValue(dep.valid_until));
32697
+ }
32698
+ function cleanExternalDep(dep, sourcePath) {
32699
+ return {
32700
+ from: dep.from,
32701
+ package: dep.package,
32702
+ ...dep.version_constraint !== undefined ? { version_constraint: dep.version_constraint } : {},
32703
+ ...dep.valid_from !== undefined ? { valid_from: dep.valid_from } : {},
32704
+ ...dep.valid_until !== undefined ? { valid_until: dep.valid_until } : {},
32705
+ ...sourcePath !== undefined ? { source_path: sourcePath } : {}
32706
+ };
32707
+ }
32708
+ function externalSortKey(dep) {
32709
+ return [
32710
+ dep.from,
32711
+ dep.package,
32712
+ dep.version_constraint ?? "",
32713
+ String(dep.valid_from ?? ""),
32714
+ String(dep.valid_until ?? "")
32715
+ ].join(":");
32716
+ }
32717
+ async function atomicWriteYaml(path3, value) {
32718
+ await atomicWriteFile(path3, import_yaml12.default.stringify(value));
32719
+ }
32720
+ function includeTarget(ctxDir, includePath) {
32721
+ if (includePath.startsWith("/") || includePath.length === 0) {
32722
+ return { filePath: includePath, error: "include path must be relative to knowledge/" };
32723
+ }
32724
+ const root2 = resolve3(knowledgeRoot(ctxDir));
32725
+ const target = resolve3(root2, includePath);
32726
+ if (target !== root2 && !target.startsWith(`${root2}/`)) {
32727
+ return { filePath: target, error: "include path must stay under knowledge/" };
32728
+ }
32729
+ return { filePath: target };
32730
+ }
32731
+ async function readExternalFile(ctxDir, filePath) {
32732
+ const parsed = import_yaml12.default.parse(await readFile10(filePath, "utf8"));
32733
+ const relativePath = relativeToKnowledge(ctxDir, filePath);
32734
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
32735
+ return { filePath, relativePath, includes: [], externals: [], rawExternals: [] };
32736
+ }
32737
+ const file = parsed;
32738
+ if (file.schema_version !== 1) {
32739
+ return { filePath, relativePath, includes: [], externals: [], rawExternals: [] };
32740
+ }
32741
+ const includes = Array.isArray(file.includes) ? file.includes.filter((item) => typeof item === "string") : [];
32742
+ const rawExternals = Array.isArray(file.externals) ? file.externals : [];
32743
+ const externals = rawExternals.filter(isExternalDep).map((dep) => cleanExternalDep(dep, relativePath));
32744
+ return { filePath, relativePath, includes, externals, rawExternals };
32745
+ }
32746
+ async function inspectExternalDeps(ctxDir) {
32747
+ const rootPath = externalDepsPath(ctxDir);
32748
+ const issues = [];
32749
+ const files = [];
32750
+ const visited = new Set;
32751
+ const stack = new Set;
32752
+ const referencedIncludes = new Set;
32753
+ const visit2 = async (filePath, viaInclude) => {
32754
+ const relativePath = relativeToKnowledge(ctxDir, filePath);
32755
+ if (stack.has(filePath)) {
32756
+ issues.push({
32757
+ code: "external-include-cycle",
32758
+ message: `external include cycle detected at "${relativePath}"`,
32759
+ path: viaInclude ?? relativePath
32760
+ });
32761
+ return;
32762
+ }
32763
+ if (visited.has(filePath))
32764
+ return;
32765
+ if (!existsSync9(filePath)) {
32766
+ issues.push({
32767
+ code: "external-include-missing",
32768
+ message: `external include "${viaInclude ?? relativePath}" does not exist`,
32769
+ path: viaInclude ?? relativePath
32770
+ });
32771
+ return;
32772
+ }
32773
+ visited.add(filePath);
32774
+ stack.add(filePath);
32775
+ const file = await readExternalFile(ctxDir, filePath);
32776
+ files.push(file);
32777
+ const includeSeenInFile = new Set;
32778
+ for (const includePath of file.includes) {
32779
+ if (includeSeenInFile.has(includePath)) {
32780
+ issues.push({
32781
+ code: "external-include-duplicate",
32782
+ message: `external include "${includePath}" is declared more than once`,
32783
+ path: file.relativePath
32784
+ });
32785
+ }
32786
+ includeSeenInFile.add(includePath);
32787
+ referencedIncludes.add(includePath);
32788
+ const target = includeTarget(ctxDir, includePath);
32789
+ if (target.error) {
32790
+ issues.push({
32791
+ code: "external-include-outside-knowledge",
32792
+ message: target.error,
32793
+ path: file.relativePath
32794
+ });
32795
+ continue;
32796
+ }
32797
+ await visit2(target.filePath, includePath);
32798
+ }
32799
+ stack.delete(filePath);
32800
+ };
32801
+ if (existsSync9(rootPath)) {
32802
+ await visit2(rootPath);
32803
+ }
32804
+ const entityRoot = join10(knowledgeRoot(ctxDir), "entity");
32805
+ if (existsSync9(entityRoot)) {
32806
+ const entries = await readdir2(entityRoot, { withFileTypes: true });
32807
+ for (const entry of entries) {
32808
+ if (!entry.isDirectory())
32809
+ continue;
32810
+ const packageExternalPath = join10(entityRoot, entry.name, "_external.yaml");
32811
+ const includePath = `entity/${entry.name}/_external.yaml`;
32812
+ if (existsSync9(packageExternalPath) && !referencedIncludes.has(includePath)) {
32813
+ issues.push({
32814
+ code: "external-include-unreachable",
32815
+ message: `external file "${includePath}" exists but is not included by knowledge/_external.yaml`,
32816
+ path: includePath
32817
+ });
32818
+ }
32819
+ }
32820
+ }
32821
+ return {
32822
+ files,
32823
+ externals: files.flatMap((file) => file.externals),
32824
+ issues
32825
+ };
32826
+ }
32827
+ async function loadExternalDeps(ctxDir) {
32828
+ return (await inspectExternalDeps(ctxDir)).externals;
32829
+ }
32830
+ async function readRootExternalFile(ctxDir) {
32831
+ const path3 = externalDepsPath(ctxDir);
32832
+ if (!existsSync9(path3))
32833
+ return { schema_version: 1, includes: [], externals: [] };
32834
+ const parsed = import_yaml12.default.parse(await readFile10(path3, "utf8"));
32835
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
32836
+ return { schema_version: 1, includes: [], externals: [] };
32837
+ const file = parsed;
32838
+ return {
32839
+ schema_version: 1,
32840
+ includes: Array.isArray(file.includes) ? file.includes.filter((item) => typeof item === "string") : [],
32841
+ externals: Array.isArray(file.externals) ? file.externals.filter(isExternalDep).map((dep) => cleanExternalDep(dep)) : []
32842
+ };
32843
+ }
32844
+ async function writeExternalFile(ctxDir, relativePath, deps, includes = []) {
32845
+ const path3 = join10(knowledgeRoot(ctxDir), relativePath);
32846
+ const file = {
32847
+ schema_version: 1,
32848
+ ...includes.length > 0 ? { includes: [...includes].sort() } : {},
32849
+ externals: [...deps].map((dep) => cleanExternalDep(dep)).sort((left, right) => externalSortKey(left).localeCompare(externalSortKey(right)))
32850
+ };
32851
+ await atomicWriteYaml(path3, file);
32852
+ }
32853
+ async function syncRootIncludes(ctxDir, packageIncludes) {
32854
+ const root2 = await readRootExternalFile(ctxDir);
32855
+ const rootIncludes = new Set((root2.includes ?? []).filter((include) => !/^entity\/[^/]+\/_external\.yaml$/u.test(include)));
32856
+ for (const include of packageIncludes)
32857
+ rootIncludes.add(include);
32858
+ await writeExternalFile(ctxDir, "_external.yaml", root2.externals, [...rootIncludes]);
32859
+ }
32860
+ function shouldKeepExistingDep(dep, options) {
32861
+ if (options.fromOwners !== undefined) {
32862
+ return !options.fromOwners.has(ownerSlugForPackageScopedPath(dep.from));
32863
+ }
32864
+ if (options.fromOwner !== undefined) {
32865
+ return dep.from !== options.fromOwner && !dep.from.startsWith(`${options.fromOwner}/`);
32866
+ }
32867
+ if (options.fromPrefix !== undefined)
32868
+ return !dep.from.startsWith(options.fromPrefix);
32869
+ return false;
32870
+ }
32871
+ async function replaceExternalDepsInternal(ctxDir, deps, options = {}) {
32872
+ const inspection = await inspectExternalDeps(ctxDir);
32873
+ const byPath = new Map;
32874
+ const includesByPath = new Map;
32875
+ for (const file of inspection.files) {
32876
+ const kept = file.externals.filter((dep) => shouldKeepExistingDep(dep, options));
32877
+ byPath.set(file.relativePath, kept);
32878
+ includesByPath.set(file.relativePath, file.includes);
32879
+ }
32880
+ for (const dep of deps) {
32881
+ const relativePath = externalDepRelativePath(dep);
32882
+ const bucket = byPath.get(relativePath) ?? [];
32883
+ byPath.set(relativePath, [...bucket, cleanExternalDep(dep)]);
32884
+ }
32885
+ const packageIncludes = new Set;
32886
+ for (const [relativePath, nextDeps] of byPath) {
32887
+ const includes = includesByPath.get(relativePath) ?? [];
32888
+ const isPackageExternalFile = /^entity\/[^/]+\/_external\.yaml$/u.test(relativePath);
32889
+ if (relativePath === "_external.yaml") {
32890
+ const root2 = await readRootExternalFile(ctxDir);
32891
+ await writeExternalFile(ctxDir, "_external.yaml", nextDeps, root2.includes ?? []);
32892
+ continue;
32893
+ }
32894
+ if (nextDeps.length === 0) {
32895
+ if (isPackageExternalFile && includes.length === 0) {
32896
+ await rm2(join10(knowledgeRoot(ctxDir), relativePath), { force: true });
32897
+ } else {
32898
+ await writeExternalFile(ctxDir, relativePath, [], includes);
32899
+ }
32900
+ continue;
32901
+ }
32902
+ if (isPackageExternalFile)
32903
+ packageIncludes.add(relativePath);
32904
+ await writeExternalFile(ctxDir, relativePath, nextDeps, includes);
32905
+ }
32906
+ for (const [relativePath, nextDeps] of byPath) {
32907
+ const includes = includesByPath.get(relativePath) ?? [];
32908
+ if (/^entity\/[^/]+\/_external\.yaml$/u.test(relativePath) && (nextDeps.length > 0 || includes.length > 0)) {
32909
+ packageIncludes.add(relativePath);
32910
+ }
32911
+ }
32912
+ await syncRootIncludes(ctxDir, packageIncludes);
32913
+ return deps.length;
32914
+ }
32915
+ async function replaceExternalDepsForOwners(ctxDir, deps, owners) {
32916
+ return replaceExternalDepsInternal(ctxDir, deps, { fromOwners: owners });
32917
+ }
32918
+ var import_yaml12;
32919
+ var init_externalDeps = __esm(() => {
32920
+ init_atomicWrite();
32921
+ import_yaml12 = __toESM(require_dist(), 1);
32922
+ });
32923
+
32924
+ // src/mdrive/graphEdges.ts
32925
+ import { existsSync as existsSync10 } from "node:fs";
32926
+ import { readFile as readFile11, readdir as readdir3, rm as rm3 } from "node:fs/promises";
32927
+ import { join as join11, relative as relative4, resolve as resolve4 } from "node:path";
32928
+ function knowledgeRoot2(ctxDir) {
32929
+ return join11(ctxDir, "knowledge");
32930
+ }
32931
+ function graphEdgesPath(ctxDir) {
32932
+ return join11(knowledgeRoot2(ctxDir), "_edges.yaml");
32933
+ }
32934
+ function toPosixPath2(value) {
32935
+ return value.replace(/\\/g, "/");
32936
+ }
32937
+ function relativeToKnowledge2(ctxDir, path3) {
32938
+ return toPosixPath2(relative4(knowledgeRoot2(ctxDir), path3));
32939
+ }
32940
+ function isTemporalValue2(value) {
32941
+ return typeof value === "string" || typeof value === "number";
32942
+ }
32510
32943
  function isStoredEdge(value) {
32511
32944
  if (!value || typeof value !== "object" || Array.isArray(value))
32512
32945
  return false;
32513
32946
  const edge2 = value;
32514
- return typeof edge2.type === "string" && edge2.type === EdgeType2.depends_on && typeof edge2.from === "string" && typeof edge2.to === "string" && typeof edge2.grounding === "string" && (edge2.note === undefined || typeof edge2.note === "string") && (edge2.valid_from === undefined || isTemporalValue(edge2.valid_from)) && (edge2.valid_until === undefined || isTemporalValue(edge2.valid_until));
32947
+ return typeof edge2.type === "string" && edge2.type === EdgeType2.depends_on && typeof edge2.from === "string" && typeof edge2.to === "string" && typeof edge2.grounding === "string" && (edge2.note === undefined || typeof edge2.note === "string") && (edge2.valid_from === undefined || isTemporalValue2(edge2.valid_from)) && (edge2.valid_until === undefined || isTemporalValue2(edge2.valid_until));
32515
32948
  }
32516
32949
  function cleanStoredEdge(edge2, sourcePath) {
32517
32950
  return {
@@ -32525,23 +32958,23 @@ function cleanStoredEdge(edge2, sourcePath) {
32525
32958
  ...sourcePath !== undefined ? { source_path: sourcePath } : {}
32526
32959
  };
32527
32960
  }
32528
- async function atomicWriteYaml(path3, value) {
32529
- await atomicWriteFile(path3, import_yaml12.default.stringify(value));
32961
+ async function atomicWriteYaml2(path3, value) {
32962
+ await atomicWriteFile(path3, import_yaml13.default.stringify(value));
32530
32963
  }
32531
- function includeTarget(ctxDir, includePath) {
32964
+ function includeTarget2(ctxDir, includePath) {
32532
32965
  if (includePath.startsWith("/") || includePath.length === 0) {
32533
32966
  return { filePath: includePath, error: "include path must be relative to knowledge/" };
32534
32967
  }
32535
- const root2 = resolve3(knowledgeRoot(ctxDir));
32536
- const target = resolve3(root2, includePath);
32968
+ const root2 = resolve4(knowledgeRoot2(ctxDir));
32969
+ const target = resolve4(root2, includePath);
32537
32970
  if (target !== root2 && !target.startsWith(`${root2}/`)) {
32538
32971
  return { filePath: target, error: "include path must stay under knowledge/" };
32539
32972
  }
32540
32973
  return { filePath: target };
32541
32974
  }
32542
32975
  async function readEdgeFile(ctxDir, filePath) {
32543
- const parsed = import_yaml12.default.parse(await readFile10(filePath, "utf8"));
32544
- const relativePath = relativeToKnowledge(ctxDir, filePath);
32976
+ const parsed = import_yaml13.default.parse(await readFile11(filePath, "utf8"));
32977
+ const relativePath = relativeToKnowledge2(ctxDir, filePath);
32545
32978
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
32546
32979
  return { filePath, relativePath, includes: [], edges: [], rawEdges: [] };
32547
32980
  }
@@ -32562,7 +32995,7 @@ async function inspectKnowledgeGraphEdges(ctxDir) {
32562
32995
  const stack = new Set;
32563
32996
  const referencedIncludes = new Set;
32564
32997
  const visit2 = async (filePath, viaInclude) => {
32565
- const relativePath = relativeToKnowledge(ctxDir, filePath);
32998
+ const relativePath = relativeToKnowledge2(ctxDir, filePath);
32566
32999
  if (stack.has(filePath)) {
32567
33000
  issues.push({
32568
33001
  code: "edge-include-cycle",
@@ -32573,7 +33006,7 @@ async function inspectKnowledgeGraphEdges(ctxDir) {
32573
33006
  }
32574
33007
  if (visited.has(filePath))
32575
33008
  return;
32576
- if (!existsSync9(filePath)) {
33009
+ if (!existsSync10(filePath)) {
32577
33010
  issues.push({
32578
33011
  code: "edge-include-missing",
32579
33012
  message: `edge include "${viaInclude ?? relativePath}" does not exist`,
@@ -32596,7 +33029,7 @@ async function inspectKnowledgeGraphEdges(ctxDir) {
32596
33029
  }
32597
33030
  includeSeenInFile.add(includePath);
32598
33031
  referencedIncludes.add(includePath);
32599
- const target = includeTarget(ctxDir, includePath);
33032
+ const target = includeTarget2(ctxDir, includePath);
32600
33033
  if (target.error) {
32601
33034
  issues.push({
32602
33035
  code: "edge-include-outside-knowledge",
@@ -32609,18 +33042,18 @@ async function inspectKnowledgeGraphEdges(ctxDir) {
32609
33042
  }
32610
33043
  stack.delete(filePath);
32611
33044
  };
32612
- if (existsSync9(rootPath)) {
33045
+ if (existsSync10(rootPath)) {
32613
33046
  await visit2(rootPath);
32614
33047
  }
32615
- const entityRoot = join10(knowledgeRoot(ctxDir), "entity");
32616
- if (existsSync9(entityRoot)) {
32617
- const entries = await readdir2(entityRoot, { withFileTypes: true });
33048
+ const entityRoot = join11(knowledgeRoot2(ctxDir), "entity");
33049
+ if (existsSync10(entityRoot)) {
33050
+ const entries = await readdir3(entityRoot, { withFileTypes: true });
32618
33051
  for (const entry of entries) {
32619
33052
  if (!entry.isDirectory())
32620
33053
  continue;
32621
- const packageEdgePath = join10(entityRoot, entry.name, "_edges.yaml");
33054
+ const packageEdgePath = join11(entityRoot, entry.name, "_edges.yaml");
32622
33055
  const includePath = `entity/${entry.name}/_edges.yaml`;
32623
- if (existsSync9(packageEdgePath) && !referencedIncludes.has(includePath)) {
33056
+ if (existsSync10(packageEdgePath) && !referencedIncludes.has(includePath)) {
32624
33057
  issues.push({
32625
33058
  code: "edge-include-unreachable",
32626
33059
  message: `edge file "${includePath}" exists but is not included by knowledge/_edges.yaml`,
@@ -32660,9 +33093,9 @@ function edgeSortKey(edge2) {
32660
33093
  }
32661
33094
  async function readRootGraphEdgesFile(ctxDir) {
32662
33095
  const path3 = graphEdgesPath(ctxDir);
32663
- if (!existsSync9(path3))
33096
+ if (!existsSync10(path3))
32664
33097
  return { schema_version: 1, includes: [], edges: [] };
32665
- const parsed = import_yaml12.default.parse(await readFile10(path3, "utf8"));
33098
+ const parsed = import_yaml13.default.parse(await readFile11(path3, "utf8"));
32666
33099
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
32667
33100
  return { schema_version: 1, includes: [], edges: [] };
32668
33101
  const file = parsed;
@@ -32673,15 +33106,15 @@ async function readRootGraphEdgesFile(ctxDir) {
32673
33106
  };
32674
33107
  }
32675
33108
  async function writeEdgeFile(ctxDir, relativePath, edges, includes = []) {
32676
- const path3 = join10(knowledgeRoot(ctxDir), relativePath);
33109
+ const path3 = join11(knowledgeRoot2(ctxDir), relativePath);
32677
33110
  const file = {
32678
33111
  schema_version: 1,
32679
33112
  ...includes.length > 0 ? { includes: [...includes].sort() } : {},
32680
33113
  edges: [...edges].map((edge2) => cleanStoredEdge(edge2)).sort((left, right) => edgeSortKey(left).localeCompare(edgeSortKey(right)))
32681
33114
  };
32682
- await atomicWriteYaml(path3, file);
33115
+ await atomicWriteYaml2(path3, file);
32683
33116
  }
32684
- async function syncRootIncludes(ctxDir, packageIncludes) {
33117
+ async function syncRootIncludes2(ctxDir, packageIncludes) {
32685
33118
  const root2 = await readRootGraphEdgesFile(ctxDir);
32686
33119
  const rootIncludes = new Set((root2.includes ?? []).filter((include) => !/^entity\/[^/]+\/_edges\.yaml$/u.test(include)));
32687
33120
  for (const include of packageIncludes)
@@ -32750,7 +33183,7 @@ async function replaceCodeGraphEdges(ctxDir, grounding, edges) {
32750
33183
  }
32751
33184
  if (nextEdges.length === 0) {
32752
33185
  if (isPackageEdgeFile && includes.length === 0) {
32753
- await rm2(join10(knowledgeRoot(ctxDir), relativePath), { force: true });
33186
+ await rm3(join11(knowledgeRoot2(ctxDir), relativePath), { force: true });
32754
33187
  } else {
32755
33188
  await writeEdgeFile(ctxDir, relativePath, [], includes);
32756
33189
  }
@@ -32766,7 +33199,7 @@ async function replaceCodeGraphEdges(ctxDir, grounding, edges) {
32766
33199
  packageIncludes.add(relativePath);
32767
33200
  }
32768
33201
  }
32769
- await syncRootIncludes(ctxDir, packageIncludes);
33202
+ await syncRootIncludes2(ctxDir, packageIncludes);
32770
33203
  return written;
32771
33204
  }
32772
33205
  async function writeKnowledgeGraphEdgeFiles(ctxDir, edges) {
@@ -32792,7 +33225,7 @@ async function writeKnowledgeGraphEdgeFiles(ctxDir, edges) {
32792
33225
  }
32793
33226
  if (bucket.length === 0) {
32794
33227
  if (isPackageEdgeFile && includes.length === 0) {
32795
- await rm2(join10(knowledgeRoot(ctxDir), relativePath), { force: true });
33228
+ await rm3(join11(knowledgeRoot2(ctxDir), relativePath), { force: true });
32796
33229
  } else {
32797
33230
  await writeEdgeFile(ctxDir, relativePath, [], includes);
32798
33231
  }
@@ -32808,7 +33241,7 @@ async function writeKnowledgeGraphEdgeFiles(ctxDir, edges) {
32808
33241
  packageIncludes.add(relativePath);
32809
33242
  }
32810
33243
  }
32811
- await syncRootIncludes(ctxDir, packageIncludes);
33244
+ await syncRootIncludes2(ctxDir, packageIncludes);
32812
33245
  }
32813
33246
  async function renameKnowledgeGraphEdgeNode(ctxDir, from, to) {
32814
33247
  const existing = await loadKnowledgeGraphEdges(ctxDir);
@@ -32847,31 +33280,31 @@ async function removeKnowledgeGraphEdgesForNodes(ctxDir, slugs) {
32847
33280
  }
32848
33281
  return removed;
32849
33282
  }
32850
- var import_yaml12;
33283
+ var import_yaml13;
32851
33284
  var init_graphEdges = __esm(() => {
32852
33285
  init_atomicWrite();
32853
33286
  init_knowledge();
32854
- import_yaml12 = __toESM(require_dist(), 1);
33287
+ import_yaml13 = __toESM(require_dist(), 1);
32855
33288
  });
32856
33289
 
32857
33290
  // src/mdrive/shared.ts
32858
- import { existsSync as existsSync10 } from "node:fs";
32859
- import { mkdir as mkdir5, readFile as readFile11, readdir as readdir3, rm as rm3 } from "node:fs/promises";
32860
- import { dirname as dirname6, join as join11, relative as relative4 } from "node:path";
32861
- function toPosixPath2(value) {
33291
+ import { existsSync as existsSync11 } from "node:fs";
33292
+ import { mkdir as mkdir5, readFile as readFile12, readdir as readdir4, rm as rm4 } from "node:fs/promises";
33293
+ import { dirname as dirname6, join as join12, relative as relative5 } from "node:path";
33294
+ function toPosixPath3(value) {
32862
33295
  return value.replace(/\\/g, "/");
32863
33296
  }
32864
- function knowledgeRoot2(ctxDir) {
32865
- return join11(ctxDir, "knowledge");
33297
+ function knowledgeRoot3(ctxDir) {
33298
+ return join12(ctxDir, "knowledge");
32866
33299
  }
32867
33300
  function knowledgeDir(ctxDir, type) {
32868
- return join11(knowledgeRoot2(ctxDir), type);
33301
+ return join12(knowledgeRoot3(ctxDir), type);
32869
33302
  }
32870
33303
  function knowledgeFilePath(ctxDir, type, slug) {
32871
- return join11(knowledgeDir(ctxDir, type), `${slug}.md`);
33304
+ return join12(knowledgeDir(ctxDir, type), `${slug}.md`);
32872
33305
  }
32873
33306
  function slugFromKnowledgeRelativePath(relativePath) {
32874
- const normalized = toPosixPath2(relativePath);
33307
+ const normalized = toPosixPath3(relativePath);
32875
33308
  const parts = normalized.split("/");
32876
33309
  if (parts.length === 2 && parts[1]?.endsWith(".md")) {
32877
33310
  return parts[1].slice(0, -3);
@@ -32882,7 +33315,7 @@ function slugFromKnowledgeRelativePath(relativePath) {
32882
33315
  return null;
32883
33316
  }
32884
33317
  function typeFromKnowledgeRelativePath(relativePath) {
32885
- const [type] = toPosixPath2(relativePath).split("/");
33318
+ const [type] = toPosixPath3(relativePath).split("/");
32886
33319
  return NODE_TYPES.includes(type) ? type : null;
32887
33320
  }
32888
33321
  function derivedContainsEdgeForFile(file) {
@@ -32908,30 +33341,115 @@ async function atomicWriteText(path3, content3) {
32908
33341
  await atomicWriteFile(path3, content3);
32909
33342
  }
32910
33343
  function ctxDirFromKnowledgeFilePath(filePath) {
32911
- const marker = `${toPosixPath2("/knowledge/")}`;
32912
- const normalized = toPosixPath2(filePath);
33344
+ const marker = `${toPosixPath3("/knowledge/")}`;
33345
+ const normalized = toPosixPath3(filePath);
32913
33346
  const index2 = normalized.lastIndexOf(marker);
32914
33347
  return index2 >= 0 ? normalized.slice(0, index2) : null;
32915
33348
  }
32916
33349
  async function buildRenderWorkspaceContext(ctxDir, files) {
32917
- const graphEdges = await loadKnowledgeGraphEdges(ctxDir);
33350
+ const [graphEdges, externalDeps, renderConfig] = await Promise.all([
33351
+ loadKnowledgeGraphEdges(ctxDir),
33352
+ loadExternalDeps(ctxDir),
33353
+ loadRenderConfig(ctxDir)
33354
+ ]);
32918
33355
  const workspaceFiles = files ?? await readWorkspaceNodeFiles(ctxDir);
32919
- const locatedNodes = flattenWorkspaceNodes(workspaceFiles);
33356
+ return buildRenderWorkspaceContextFromData({
33357
+ files: workspaceFiles,
33358
+ graphEdges,
33359
+ externalDeps,
33360
+ renderOptions: {
33361
+ nodeLinkMode: renderConfig.obsidian_mode ? "wiki" : "md",
33362
+ usedBy: renderConfig.used_by
33363
+ }
33364
+ });
33365
+ }
33366
+ function buildRenderWorkspaceContextFromData(input) {
33367
+ const locatedNodes = flattenWorkspaceNodes(input.files);
33368
+ const anchorsByFile = new Map(input.files.map((file) => [file.filePath, computeNodeAnchorsForFile(file.root)]));
32920
33369
  const relatedLinksByFile = new Map;
32921
- for (const file of workspaceFiles) {
33370
+ const relationEntriesByFile = new Map;
33371
+ for (const file of input.files) {
32922
33372
  const links = new Map;
32923
33373
  for (const target of locatedNodes) {
32924
- const href = target.filePath === file.filePath ? `#${slugify(target.parsed.node.title)}` : buildContainsHref(file.filePath, target.filePath);
32925
- links.set(target.parsed.node.id, { title: target.parsed.node.title, href });
33374
+ const targetAnchors = anchorsByFile.get(target.filePath) ?? new Map;
33375
+ links.set(target.parsed.node.id, {
33376
+ title: target.parsed.node.title,
33377
+ href: markdownHrefForLocatedNode(file.filePath, target, targetAnchors),
33378
+ wikiTarget: wikiTargetForLocatedNode(target, targetAnchors)
33379
+ });
32926
33380
  }
32927
33381
  relatedLinksByFile.set(file.filePath, links);
33382
+ relationEntriesByFile.set(file.filePath, buildRelationEntriesByNodeSlug(flattenNodeTree(file.root, file.filePath, file.relativePath), input.graphEdges, input.externalDeps, links));
32928
33383
  }
32929
33384
  return {
32930
- graphEdges,
32931
- workspaceContains: workspaceFiles.flatMap((file) => file.root.containsEdges),
32932
- relatedLinksByFile
33385
+ graphEdges: [...input.graphEdges],
33386
+ workspaceContains: input.files.flatMap((file) => file.root.containsEdges),
33387
+ relatedLinksByFile,
33388
+ relationEntriesByFile,
33389
+ renderOptions: input.renderOptions
32933
33390
  };
32934
33391
  }
33392
+ function computeNodeAnchorsForFile(root2) {
33393
+ const counts = new Map;
33394
+ const anchors = new Map;
33395
+ const claim = (title) => {
33396
+ const base = slugify(title);
33397
+ const seen = (counts.get(base) ?? 0) + 1;
33398
+ counts.set(base, seen);
33399
+ return seen === 1 ? base : `${base}-${seen}`;
33400
+ };
33401
+ const visit2 = (node3) => {
33402
+ anchors.set(node3.node.id, claim(node3.node.title));
33403
+ for (const child of node3.children)
33404
+ visit2(child);
33405
+ };
33406
+ visit2(root2);
33407
+ return anchors;
33408
+ }
33409
+ function markdownHrefForLocatedNode(fromFilePath, target, targetAnchors) {
33410
+ const anchor = targetAnchors.get(target.parsed.node.id);
33411
+ if (target.filePath === fromFilePath) {
33412
+ return anchor !== undefined ? `#${anchor}` : "";
33413
+ }
33414
+ const href = buildContainsHref(fromFilePath, target.filePath);
33415
+ return target.path.length > 0 && anchor !== undefined ? `${href}#${anchor}` : href;
33416
+ }
33417
+ function wikiTargetForLocatedNode(target, targetAnchors) {
33418
+ const base = toPosixPath3(target.relativePath).replace(/\.md$/u, "");
33419
+ const anchor = targetAnchors.get(target.parsed.node.id);
33420
+ return target.path.length > 0 && anchor !== undefined ? `${base}#${anchor}` : base;
33421
+ }
33422
+ function relationLink(links, slug) {
33423
+ return links.get(slug) ?? { title: slug, href: `${slug}.md`, wikiTarget: slug };
33424
+ }
33425
+ function toNodeRelationEntry(links, slug, edge2) {
33426
+ return {
33427
+ slug,
33428
+ ...relationLink(links, slug),
33429
+ ...edge2.valid_from !== undefined ? { valid_from: edge2.valid_from } : {},
33430
+ ...edge2.valid_until !== undefined ? { valid_until: edge2.valid_until } : {}
33431
+ };
33432
+ }
33433
+ function toExternalDependencyEntry(dep) {
33434
+ return {
33435
+ package: dep.package,
33436
+ ...dep.version_constraint !== undefined ? { version_constraint: dep.version_constraint } : {},
33437
+ ...dep.valid_from !== undefined ? { valid_from: dep.valid_from } : {},
33438
+ ...dep.valid_until !== undefined ? { valid_until: dep.valid_until } : {}
33439
+ };
33440
+ }
33441
+ function buildRelationEntriesByNodeSlug(nodesInFile, graphEdges, externalDeps, links) {
33442
+ const bySlug = new Map;
33443
+ for (const node3 of nodesInFile) {
33444
+ const slug = node3.parsed.node.id;
33445
+ bySlug.set(slug, {
33446
+ dependsOn: graphEdges.filter((edge2) => edge2.type === "depends_on" && edge2.from === slug).map((edge2) => toNodeRelationEntry(links, edge2.to, edge2)),
33447
+ usedBy: graphEdges.filter((edge2) => edge2.type === "depends_on" && edge2.to === slug).map((edge2) => toNodeRelationEntry(links, edge2.from, edge2)),
33448
+ externalDependencies: externalDeps.filter((dep) => dep.from === slug).map(toExternalDependencyEntry)
33449
+ });
33450
+ }
33451
+ return bySlug;
33452
+ }
32935
33453
  function collectTreeSlugs(root2, out2 = new Set) {
32936
33454
  out2.add(root2.node.id);
32937
33455
  for (const child of root2.children) {
@@ -32963,27 +33481,79 @@ function collectCurrentContainsEdges(root2) {
32963
33481
  visit2(root2);
32964
33482
  return edges;
32965
33483
  }
33484
+ function attachRelationInputs(input, filePath, context) {
33485
+ const relations = context.relationEntriesByFile.get(filePath)?.get(input.node.id);
33486
+ const relatedLinks = context.relatedLinksByFile.get(filePath);
33487
+ const output = {
33488
+ ...input,
33489
+ ...relations !== undefined ? {
33490
+ dependsOn: relations.dependsOn,
33491
+ usedBy: relations.usedBy,
33492
+ externalDependencies: relations.externalDependencies
33493
+ } : {},
33494
+ renderOptions: context.renderOptions
33495
+ };
33496
+ if (relatedLinks !== undefined) {
33497
+ output.relatedLinks = relatedLinks;
33498
+ }
33499
+ if (input.children !== undefined) {
33500
+ output.children = input.children.map((child) => attachRelationInputs(child, filePath, context));
33501
+ }
33502
+ return output;
33503
+ }
33504
+ function collectRenderInputSlugs(input, out2 = new Set) {
33505
+ out2.add(input.node.id);
33506
+ for (const child of input.children ?? []) {
33507
+ collectRenderInputSlugs(child, out2);
33508
+ }
33509
+ return out2;
33510
+ }
33511
+ function collectRenderInputChildContains(input) {
33512
+ const edges = [];
33513
+ const visit2 = (current) => {
33514
+ for (const child of current.children ?? []) {
33515
+ edges.push({
33516
+ type: "contains",
33517
+ from: current.node.id,
33518
+ to: child.node.id,
33519
+ grounding: "document"
33520
+ });
33521
+ visit2(child);
33522
+ }
33523
+ };
33524
+ visit2(input);
33525
+ return edges;
33526
+ }
33527
+ function renderNodeInputForFile(filePath, input, context) {
33528
+ const rootSlugs = collectRenderInputSlugs(input);
33529
+ const workspaceContains = context.workspaceContains.filter((edge2) => !rootSlugs.has(edge2.from));
33530
+ const rootContains = collectRenderInputChildContains(input);
33531
+ const decorated = attachRelationInputs({
33532
+ ...input,
33533
+ graphEdges: [...context.graphEdges, ...workspaceContains, ...rootContains, ...input.graphEdges ?? []]
33534
+ }, filePath, context);
33535
+ return renderNodeMarkdown(decorated);
33536
+ }
32966
33537
  async function renderRootForFile(filePath, root2, renderContext) {
32967
33538
  const ctxDir = ctxDirFromKnowledgeFilePath(filePath);
32968
- const context = renderContext ?? (ctxDir ? await buildRenderWorkspaceContext(ctxDir) : {
32969
- graphEdges: [],
32970
- workspaceContains: [],
32971
- relatedLinksByFile: new Map
32972
- });
33539
+ if (renderContext === undefined && ctxDir === null) {
33540
+ throw new ContextError(ExitCode.WorkspaceStateError, "renderRootForFile requires a knowledge file path or an explicit render context", { category: "schema-invalid" });
33541
+ }
33542
+ const context = renderContext ?? await buildRenderWorkspaceContext(ctxDir);
32973
33543
  const rootSlugs = collectTreeSlugs(root2);
32974
33544
  const workspaceContains = context.workspaceContains.filter((edge2) => !rootSlugs.has(edge2.from));
32975
33545
  const rootContains = collectCurrentContainsEdges(root2);
32976
- return renderNodeMarkdown({
33546
+ const decorated = attachRelationInputs({
32977
33547
  ...root2,
32978
- graphEdges: [...context.graphEdges, ...workspaceContains, ...rootContains],
32979
- relatedLinks: context.relatedLinksByFile.get(filePath)
32980
- });
33548
+ graphEdges: [...context.graphEdges, ...workspaceContains, ...rootContains]
33549
+ }, filePath, context);
33550
+ return renderNodeMarkdown(decorated);
32981
33551
  }
32982
33552
  async function atomicRewriteRootFile(filePath, root2, nextFilePath, renderContext) {
32983
33553
  const destination = nextFilePath ?? filePath;
32984
33554
  await atomicWriteText(destination, await renderRootForFile(destination, root2, renderContext));
32985
- if (destination !== filePath && existsSync10(filePath)) {
32986
- await rm3(filePath);
33555
+ if (destination !== filePath && existsSync11(filePath)) {
33556
+ await rm4(filePath);
32987
33557
  }
32988
33558
  }
32989
33559
  async function writeStandaloneNodeFile(ctxDir, root2, renderContext) {
@@ -32993,7 +33563,7 @@ async function writeStandaloneNodeFile(ctxDir, root2, renderContext) {
32993
33563
  return filePath;
32994
33564
  }
32995
33565
  function buildContainsHref(fromFilePath, toFilePath) {
32996
- return toPosixPath2(relative4(dirname6(fromFilePath), toFilePath));
33566
+ return toPosixPath3(relative5(dirname6(fromFilePath), toFilePath));
32997
33567
  }
32998
33568
  function buildContainsEntry(fromFilePath, targetFilePath, targetSlug, targetTitle) {
32999
33569
  return {
@@ -33006,25 +33576,25 @@ async function readWorkspaceNodeFiles(ctxDir) {
33006
33576
  const files = [];
33007
33577
  for (const type of NODE_TYPES) {
33008
33578
  const dir = knowledgeDir(ctxDir, type);
33009
- if (!existsSync10(dir))
33579
+ if (!existsSync11(dir))
33010
33580
  continue;
33011
- const entries = await readdir3(dir, { withFileTypes: true });
33581
+ const entries = await readdir4(dir, { withFileTypes: true });
33012
33582
  for (const entry of entries) {
33013
33583
  if (entry.isFile()) {
33014
33584
  if (!entry.name.endsWith(".md") || KNOWLEDGE_IGNORED_FILES.has(entry.name))
33015
33585
  continue;
33016
- files.push(await readWorkspaceNodeFile(ctxDir, join11(dir, entry.name)));
33586
+ files.push(await readWorkspaceNodeFile(ctxDir, join12(dir, entry.name)));
33017
33587
  continue;
33018
33588
  }
33019
33589
  if (!entry.isDirectory())
33020
33590
  continue;
33021
- const nestedDir = join11(dir, entry.name);
33022
- const nestedEntries = await readdir3(nestedDir, { withFileTypes: true });
33591
+ const nestedDir = join12(dir, entry.name);
33592
+ const nestedEntries = await readdir4(nestedDir, { withFileTypes: true });
33023
33593
  for (const nestedEntry of nestedEntries) {
33024
33594
  if (!nestedEntry.isFile() || !nestedEntry.name.endsWith(".md") || KNOWLEDGE_IGNORED_FILES.has(nestedEntry.name)) {
33025
33595
  continue;
33026
33596
  }
33027
- files.push(await readWorkspaceNodeFile(ctxDir, join11(nestedDir, nestedEntry.name)));
33597
+ files.push(await readWorkspaceNodeFile(ctxDir, join12(nestedDir, nestedEntry.name)));
33028
33598
  }
33029
33599
  }
33030
33600
  }
@@ -33036,8 +33606,8 @@ async function readWorkspaceNodeFiles(ctxDir) {
33036
33606
  return files;
33037
33607
  }
33038
33608
  async function readWorkspaceNodeFile(ctxDir, filePath) {
33039
- const relativePath = toPosixPath2(relative4(knowledgeRoot2(ctxDir), filePath));
33040
- const content3 = await readFile11(filePath, "utf8");
33609
+ const relativePath = toPosixPath3(relative5(knowledgeRoot3(ctxDir), filePath));
33610
+ const content3 = await readFile12(filePath, "utf8");
33041
33611
  let root2;
33042
33612
  try {
33043
33613
  root2 = parseNodeMarkdown(content3);
@@ -33195,15 +33765,17 @@ var init_shared = __esm(() => {
33195
33765
  init_nodeParser();
33196
33766
  init_nodeRenderer();
33197
33767
  init_normalize();
33768
+ init_config();
33198
33769
  init_exitCode();
33199
33770
  init_knowledge();
33771
+ init_externalDeps();
33200
33772
  init_graphEdges();
33201
33773
  KNOWLEDGE_IGNORED_FILES = new Set(["_index.md", "changelog.md"]);
33202
33774
  });
33203
33775
 
33204
33776
  // src/incremental/sectionFingerprints.ts
33205
- import { readFile as readFile12 } from "node:fs/promises";
33206
- import { join as join12 } from "node:path";
33777
+ import { readFile as readFile13 } from "node:fs/promises";
33778
+ import { join as join13 } from "node:path";
33207
33779
  function isRecord4(value) {
33208
33780
  return typeof value === "object" && value !== null && !Array.isArray(value);
33209
33781
  }
@@ -33270,7 +33842,7 @@ function isRawBlocksCache(value) {
33270
33842
  async function loadRawBlocks(path3) {
33271
33843
  let raw;
33272
33844
  try {
33273
- raw = await readFile12(path3, "utf8");
33845
+ raw = await readFile13(path3, "utf8");
33274
33846
  } catch (err2) {
33275
33847
  const code3 = err2.code;
33276
33848
  if (code3 === "ENOENT")
@@ -33350,7 +33922,7 @@ function normalizeHeadingTitle(value) {
33350
33922
  return normalizeMarkdown(value).trim().replace(/\s+/gu, " ");
33351
33923
  }
33352
33924
  async function anchorForBlock(ctxDir, snapshotFile, block) {
33353
- const raw = await readFile12(join12(ctxDir, snapshotFile), "utf8");
33925
+ const raw = await readFile13(join13(ctxDir, snapshotFile), "utf8");
33354
33926
  const anchors = extractAnchors(stripFrontmatter(raw));
33355
33927
  if (anchors.length === 0)
33356
33928
  return "document";
@@ -33449,7 +34021,7 @@ function fingerprintEntry(input) {
33449
34021
  async function loadSectionFingerprints(path3) {
33450
34022
  let raw;
33451
34023
  try {
33452
- raw = await readFile12(path3, "utf8");
34024
+ raw = await readFile13(path3, "utf8");
33453
34025
  } catch (err2) {
33454
34026
  const code3 = err2.code;
33455
34027
  if (code3 === "ENOENT")
@@ -33691,9 +34263,9 @@ var init_sectionFingerprints = __esm(() => {
33691
34263
  });
33692
34264
 
33693
34265
  // src/incremental/sourceDigests.ts
33694
- import { existsSync as existsSync11 } from "node:fs";
33695
- import { readFile as readFile13 } from "node:fs/promises";
33696
- import { join as join13 } from "node:path";
34266
+ import { existsSync as existsSync12 } from "node:fs";
34267
+ import { readFile as readFile14 } from "node:fs/promises";
34268
+ import { join as join14 } from "node:path";
33697
34269
  function sortJson4(value) {
33698
34270
  if (Array.isArray(value))
33699
34271
  return value.map(sortJson4);
@@ -33767,8 +34339,8 @@ async function buildSourceDigests(input) {
33767
34339
  rawBlocks.unknown_inputs.push(unknown2);
33768
34340
  continue;
33769
34341
  }
33770
- const rawPath = join13(input.ctxDir, snapshot.file);
33771
- if (!existsSync11(rawPath)) {
34342
+ const rawPath = join14(input.ctxDir, snapshot.file);
34343
+ if (!existsSync12(rawPath)) {
33772
34344
  const unknown2 = unknownInput(source2.id, "snapshot-file-not-found", snapshot.file);
33773
34345
  sourceDigests.unknown_inputs.push(unknown2);
33774
34346
  rawBlocks.unknown_inputs.push(unknown2);
@@ -33776,7 +34348,7 @@ async function buildSourceDigests(input) {
33776
34348
  }
33777
34349
  let raw;
33778
34350
  try {
33779
- raw = await readFile13(rawPath, "utf8");
34351
+ raw = await readFile14(rawPath, "utf8");
33780
34352
  } catch {
33781
34353
  const unknown2 = unknownInput(source2.id, "snapshot-file-read-failed", snapshot.file);
33782
34354
  sourceDigests.unknown_inputs.push(unknown2);
@@ -34007,257 +34579,6 @@ var init_nodeClassification = __esm(() => {
34007
34579
  ]);
34008
34580
  });
34009
34581
 
34010
- // src/mdrive/externalDeps.ts
34011
- import { existsSync as existsSync12 } from "node:fs";
34012
- import { readFile as readFile14, readdir as readdir4, rm as rm4 } from "node:fs/promises";
34013
- import { join as join14, relative as relative5, resolve as resolve4 } from "node:path";
34014
- function knowledgeRoot3(ctxDir) {
34015
- return join14(ctxDir, "knowledge");
34016
- }
34017
- function externalDepsPath(ctxDir) {
34018
- return join14(knowledgeRoot3(ctxDir), "_external.yaml");
34019
- }
34020
- function toPosixPath3(value) {
34021
- return value.replace(/\\/g, "/");
34022
- }
34023
- function relativeToKnowledge2(ctxDir, path3) {
34024
- return toPosixPath3(relative5(knowledgeRoot3(ctxDir), path3));
34025
- }
34026
- function isTemporalValue2(value) {
34027
- return typeof value === "string" || typeof value === "number";
34028
- }
34029
- function isExternalDep(value) {
34030
- if (!value || typeof value !== "object" || Array.isArray(value))
34031
- return false;
34032
- const dep = value;
34033
- return typeof dep.from === "string" && typeof dep.package === "string" && (dep.version_constraint === undefined || typeof dep.version_constraint === "string") && (dep.valid_from === undefined || isTemporalValue2(dep.valid_from)) && (dep.valid_until === undefined || isTemporalValue2(dep.valid_until));
34034
- }
34035
- function cleanExternalDep(dep, sourcePath) {
34036
- return {
34037
- from: dep.from,
34038
- package: dep.package,
34039
- ...dep.version_constraint !== undefined ? { version_constraint: dep.version_constraint } : {},
34040
- ...dep.valid_from !== undefined ? { valid_from: dep.valid_from } : {},
34041
- ...dep.valid_until !== undefined ? { valid_until: dep.valid_until } : {},
34042
- ...sourcePath !== undefined ? { source_path: sourcePath } : {}
34043
- };
34044
- }
34045
- function externalSortKey(dep) {
34046
- return [
34047
- dep.from,
34048
- dep.package,
34049
- dep.version_constraint ?? "",
34050
- String(dep.valid_from ?? ""),
34051
- String(dep.valid_until ?? "")
34052
- ].join(":");
34053
- }
34054
- async function atomicWriteYaml2(path3, value) {
34055
- await atomicWriteFile(path3, import_yaml13.default.stringify(value));
34056
- }
34057
- function includeTarget2(ctxDir, includePath) {
34058
- if (includePath.startsWith("/") || includePath.length === 0) {
34059
- return { filePath: includePath, error: "include path must be relative to knowledge/" };
34060
- }
34061
- const root2 = resolve4(knowledgeRoot3(ctxDir));
34062
- const target = resolve4(root2, includePath);
34063
- if (target !== root2 && !target.startsWith(`${root2}/`)) {
34064
- return { filePath: target, error: "include path must stay under knowledge/" };
34065
- }
34066
- return { filePath: target };
34067
- }
34068
- async function readExternalFile(ctxDir, filePath) {
34069
- const parsed = import_yaml13.default.parse(await readFile14(filePath, "utf8"));
34070
- const relativePath = relativeToKnowledge2(ctxDir, filePath);
34071
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
34072
- return { filePath, relativePath, includes: [], externals: [], rawExternals: [] };
34073
- }
34074
- const file = parsed;
34075
- if (file.schema_version !== 1) {
34076
- return { filePath, relativePath, includes: [], externals: [], rawExternals: [] };
34077
- }
34078
- const includes = Array.isArray(file.includes) ? file.includes.filter((item) => typeof item === "string") : [];
34079
- const rawExternals = Array.isArray(file.externals) ? file.externals : [];
34080
- const externals = rawExternals.filter(isExternalDep).map((dep) => cleanExternalDep(dep, relativePath));
34081
- return { filePath, relativePath, includes, externals, rawExternals };
34082
- }
34083
- async function inspectExternalDeps(ctxDir) {
34084
- const rootPath = externalDepsPath(ctxDir);
34085
- const issues = [];
34086
- const files = [];
34087
- const visited = new Set;
34088
- const stack = new Set;
34089
- const referencedIncludes = new Set;
34090
- const visit2 = async (filePath, viaInclude) => {
34091
- const relativePath = relativeToKnowledge2(ctxDir, filePath);
34092
- if (stack.has(filePath)) {
34093
- issues.push({
34094
- code: "external-include-cycle",
34095
- message: `external include cycle detected at "${relativePath}"`,
34096
- path: viaInclude ?? relativePath
34097
- });
34098
- return;
34099
- }
34100
- if (visited.has(filePath))
34101
- return;
34102
- if (!existsSync12(filePath)) {
34103
- issues.push({
34104
- code: "external-include-missing",
34105
- message: `external include "${viaInclude ?? relativePath}" does not exist`,
34106
- path: viaInclude ?? relativePath
34107
- });
34108
- return;
34109
- }
34110
- visited.add(filePath);
34111
- stack.add(filePath);
34112
- const file = await readExternalFile(ctxDir, filePath);
34113
- files.push(file);
34114
- const includeSeenInFile = new Set;
34115
- for (const includePath of file.includes) {
34116
- if (includeSeenInFile.has(includePath)) {
34117
- issues.push({
34118
- code: "external-include-duplicate",
34119
- message: `external include "${includePath}" is declared more than once`,
34120
- path: file.relativePath
34121
- });
34122
- }
34123
- includeSeenInFile.add(includePath);
34124
- referencedIncludes.add(includePath);
34125
- const target = includeTarget2(ctxDir, includePath);
34126
- if (target.error) {
34127
- issues.push({
34128
- code: "external-include-outside-knowledge",
34129
- message: target.error,
34130
- path: file.relativePath
34131
- });
34132
- continue;
34133
- }
34134
- await visit2(target.filePath, includePath);
34135
- }
34136
- stack.delete(filePath);
34137
- };
34138
- if (existsSync12(rootPath)) {
34139
- await visit2(rootPath);
34140
- }
34141
- const entityRoot = join14(knowledgeRoot3(ctxDir), "entity");
34142
- if (existsSync12(entityRoot)) {
34143
- const entries = await readdir4(entityRoot, { withFileTypes: true });
34144
- for (const entry of entries) {
34145
- if (!entry.isDirectory())
34146
- continue;
34147
- const packageExternalPath = join14(entityRoot, entry.name, "_external.yaml");
34148
- const includePath = `entity/${entry.name}/_external.yaml`;
34149
- if (existsSync12(packageExternalPath) && !referencedIncludes.has(includePath)) {
34150
- issues.push({
34151
- code: "external-include-unreachable",
34152
- message: `external file "${includePath}" exists but is not included by knowledge/_external.yaml`,
34153
- path: includePath
34154
- });
34155
- }
34156
- }
34157
- }
34158
- return {
34159
- files,
34160
- externals: files.flatMap((file) => file.externals),
34161
- issues
34162
- };
34163
- }
34164
- async function loadExternalDeps(ctxDir) {
34165
- return (await inspectExternalDeps(ctxDir)).externals;
34166
- }
34167
- async function readRootExternalFile(ctxDir) {
34168
- const path3 = externalDepsPath(ctxDir);
34169
- if (!existsSync12(path3))
34170
- return { schema_version: 1, includes: [], externals: [] };
34171
- const parsed = import_yaml13.default.parse(await readFile14(path3, "utf8"));
34172
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
34173
- return { schema_version: 1, includes: [], externals: [] };
34174
- const file = parsed;
34175
- return {
34176
- schema_version: 1,
34177
- includes: Array.isArray(file.includes) ? file.includes.filter((item) => typeof item === "string") : [],
34178
- externals: Array.isArray(file.externals) ? file.externals.filter(isExternalDep).map((dep) => cleanExternalDep(dep)) : []
34179
- };
34180
- }
34181
- async function writeExternalFile(ctxDir, relativePath, deps, includes = []) {
34182
- const path3 = join14(knowledgeRoot3(ctxDir), relativePath);
34183
- const file = {
34184
- schema_version: 1,
34185
- ...includes.length > 0 ? { includes: [...includes].sort() } : {},
34186
- externals: [...deps].map((dep) => cleanExternalDep(dep)).sort((left, right) => externalSortKey(left).localeCompare(externalSortKey(right)))
34187
- };
34188
- await atomicWriteYaml2(path3, file);
34189
- }
34190
- async function syncRootIncludes2(ctxDir, packageIncludes) {
34191
- const root2 = await readRootExternalFile(ctxDir);
34192
- const rootIncludes = new Set((root2.includes ?? []).filter((include) => !/^entity\/[^/]+\/_external\.yaml$/u.test(include)));
34193
- for (const include of packageIncludes)
34194
- rootIncludes.add(include);
34195
- await writeExternalFile(ctxDir, "_external.yaml", root2.externals, [...rootIncludes]);
34196
- }
34197
- function shouldKeepExistingDep(dep, options) {
34198
- if (options.fromOwners !== undefined) {
34199
- return !options.fromOwners.has(ownerSlugForPackageScopedPath(dep.from));
34200
- }
34201
- if (options.fromOwner !== undefined) {
34202
- return dep.from !== options.fromOwner && !dep.from.startsWith(`${options.fromOwner}/`);
34203
- }
34204
- if (options.fromPrefix !== undefined)
34205
- return !dep.from.startsWith(options.fromPrefix);
34206
- return false;
34207
- }
34208
- async function replaceExternalDepsInternal(ctxDir, deps, options = {}) {
34209
- const inspection = await inspectExternalDeps(ctxDir);
34210
- const byPath = new Map;
34211
- const includesByPath = new Map;
34212
- for (const file of inspection.files) {
34213
- const kept = file.externals.filter((dep) => shouldKeepExistingDep(dep, options));
34214
- byPath.set(file.relativePath, kept);
34215
- includesByPath.set(file.relativePath, file.includes);
34216
- }
34217
- for (const dep of deps) {
34218
- const relativePath = externalDepRelativePath(dep);
34219
- const bucket = byPath.get(relativePath) ?? [];
34220
- byPath.set(relativePath, [...bucket, cleanExternalDep(dep)]);
34221
- }
34222
- const packageIncludes = new Set;
34223
- for (const [relativePath, nextDeps] of byPath) {
34224
- const includes = includesByPath.get(relativePath) ?? [];
34225
- const isPackageExternalFile = /^entity\/[^/]+\/_external\.yaml$/u.test(relativePath);
34226
- if (relativePath === "_external.yaml") {
34227
- const root2 = await readRootExternalFile(ctxDir);
34228
- await writeExternalFile(ctxDir, "_external.yaml", nextDeps, root2.includes ?? []);
34229
- continue;
34230
- }
34231
- if (nextDeps.length === 0) {
34232
- if (isPackageExternalFile && includes.length === 0) {
34233
- await rm4(join14(knowledgeRoot3(ctxDir), relativePath), { force: true });
34234
- } else {
34235
- await writeExternalFile(ctxDir, relativePath, [], includes);
34236
- }
34237
- continue;
34238
- }
34239
- if (isPackageExternalFile)
34240
- packageIncludes.add(relativePath);
34241
- await writeExternalFile(ctxDir, relativePath, nextDeps, includes);
34242
- }
34243
- for (const [relativePath, nextDeps] of byPath) {
34244
- const includes = includesByPath.get(relativePath) ?? [];
34245
- if (/^entity\/[^/]+\/_external\.yaml$/u.test(relativePath) && (nextDeps.length > 0 || includes.length > 0)) {
34246
- packageIncludes.add(relativePath);
34247
- }
34248
- }
34249
- await syncRootIncludes2(ctxDir, packageIncludes);
34250
- return deps.length;
34251
- }
34252
- async function replaceExternalDepsForOwners(ctxDir, deps, owners) {
34253
- return replaceExternalDepsInternal(ctxDir, deps, { fromOwners: owners });
34254
- }
34255
- var import_yaml13;
34256
- var init_externalDeps = __esm(() => {
34257
- init_atomicWrite();
34258
- import_yaml13 = __toESM(require_dist(), 1);
34259
- });
34260
-
34261
34582
  // src/code/codeProjectionHelpers.ts
34262
34583
  function stringField2(row, fields) {
34263
34584
  for (const field of fields) {
@@ -37746,7 +38067,7 @@ function safeTimestamp(value) {
37746
38067
  return value.replace(/[:.]/g, "-");
37747
38068
  }
37748
38069
  function sourceArchiveRelPath(sourceId, droppedAt) {
37749
- return toPosixPath2(join23("archive", "sources", safePathSegment(sourceId), safeTimestamp(droppedAt)));
38070
+ return toPosixPath3(join23("archive", "sources", safePathSegment(sourceId), safeTimestamp(droppedAt)));
37750
38071
  }
37751
38072
  function archiveAbsPath(ctxDir, relPath) {
37752
38073
  return join23(ctxDir, relPath);
@@ -37769,7 +38090,7 @@ async function addArchiveEntry(input) {
37769
38090
  const kind = await pathKind(fromAbs);
37770
38091
  if (!kind)
37771
38092
  return;
37772
- const toRel = toPosixPath2(join23(input.archivePath, "raw", input.rel));
38093
+ const toRel = toPosixPath3(join23(input.archivePath, "raw", input.rel));
37773
38094
  const toAbs = join23(input.ctxDir, toRel);
37774
38095
  await mkdir12(dirname11(toAbs), { recursive: true });
37775
38096
  await cp(fromAbs, toAbs, { recursive: kind === "dir" });
@@ -37792,8 +38113,8 @@ async function createDropArchive(input) {
37792
38113
  }
37793
38114
  const knowledgeBefore = [];
37794
38115
  for (const filePath of input.touchedKnowledgeFiles) {
37795
- const rel = toPosixPath2(relative6(input.ctxDir, filePath));
37796
- const toRel = toPosixPath2(join23(archivePath, "knowledge", "before", rel));
38116
+ const rel = toPosixPath3(relative6(input.ctxDir, filePath));
38117
+ const toRel = toPosixPath3(join23(archivePath, "knowledge", "before", rel));
37797
38118
  const toAbs = join23(input.ctxDir, toRel);
37798
38119
  await mkdir12(dirname11(toAbs), { recursive: true });
37799
38120
  await cp(filePath, toAbs);
@@ -37873,7 +38194,7 @@ async function walkArchive(absPath, relPath = "") {
37873
38194
  return { files: 0, bytes: 0, rawFiles: 0, knowledgeFiles: 0 };
37874
38195
  const s = await stat(absPath);
37875
38196
  if (s.isFile()) {
37876
- const normalized = toPosixPath2(relPath);
38197
+ const normalized = toPosixPath3(relPath);
37877
38198
  return {
37878
38199
  files: 1,
37879
38200
  bytes: s.size,
@@ -37970,7 +38291,7 @@ async function summarizeRestoredSourceArchives(ctxDir) {
37970
38291
  for (const archiveDir of await readdir10(sourceRoot, { withFileTypes: true })) {
37971
38292
  if (!archiveDir.isDirectory())
37972
38293
  continue;
37973
- const archivePath = toPosixPath2(join23("archive", "sources", sourceDir.name, archiveDir.name));
38294
+ const archivePath = toPosixPath3(join23("archive", "sources", sourceDir.name, archiveDir.name));
37974
38295
  const manifest = await readDropArchiveManifest(join23(sourceRoot, archiveDir.name, "manifest.yaml"));
37975
38296
  if (!manifest || !restoredSourceIds.has(manifest.source_id))
37976
38297
  continue;
@@ -41407,11 +41728,11 @@ async function hydrateSourceOwnershipPreviews(ctxDir, ownership) {
41407
41728
  }));
41408
41729
  return changed ? { ...ownership, sources } : ownership;
41409
41730
  }
41410
- async function latestSnapshotHashes(ctxDir) {
41731
+ async function latestSnapshotsBySourceId(ctxDir) {
41411
41732
  const sources = await loadSources(ctxDir);
41412
41733
  return new Map(sources.sources.flatMap((source2) => {
41413
41734
  const latest = selectLatestSnapshot(source2.snapshots);
41414
- return latest === null ? [] : [[source2.id, latest.content_hash]];
41735
+ return latest === null ? [] : [[source2.id, latest]];
41415
41736
  }));
41416
41737
  }
41417
41738
  function compactSourceOwnership(ownership) {
@@ -41525,8 +41846,8 @@ async function readFreshCurrentSourceOwnership(ctxDir, input = {}) {
41525
41846
  const ownership = await readCurrentSourceOwnership(ctxDir, input);
41526
41847
  if (ownership === null)
41527
41848
  return null;
41528
- const currentHashes = await latestSnapshotHashes(ctxDir);
41529
- const stale = staleOwnershipSourceIds(ownership, currentHashes);
41849
+ const latestSnapshots = await latestSnapshotsBySourceId(ctxDir);
41850
+ const stale = staleOwnershipSourceIds(ctxDir, ownership, latestSnapshots);
41530
41851
  if (stale.length === 0)
41531
41852
  return ownership;
41532
41853
  const refreshed = await refreshStaleSourceOwnership(ctxDir, ownership);
@@ -41550,11 +41871,19 @@ function publishedSourceOwnershipSummary(record) {
41550
41871
  block_count: record.ownership.summary.blocks
41551
41872
  };
41552
41873
  }
41553
- function staleOwnershipSourceIds(ownership, currentHashes) {
41554
- return ownership.sources.filter((source2) => {
41555
- const currentHash = currentHashes.get(source2.source_id);
41556
- return source2.snapshot_hash === undefined || currentHash === undefined || source2.snapshot_hash !== currentHash;
41557
- }).map((source2) => source2.source_id);
41874
+ function staleOwnershipSourceIds(ctxDir, ownership, latestSnapshots) {
41875
+ return ownership.sources.filter((source2) => sourceOwnershipNeedsRefresh(ctxDir, source2, latestSnapshots.get(source2.source_id))).map((source2) => source2.source_id);
41876
+ }
41877
+ function sourceOwnershipNeedsRefresh(ctxDir, source2, latest) {
41878
+ if (source2.snapshot_hash === undefined || latest === undefined)
41879
+ return true;
41880
+ if (source2.snapshot_hash !== latest.content_hash)
41881
+ return true;
41882
+ if (latest.file !== undefined && source2.file !== latest.file)
41883
+ return true;
41884
+ if (!existsSync28(join31(ctxDir, source2.file)))
41885
+ return true;
41886
+ return source2.blocks.some((block) => !existsSync28(join31(ctxDir, block.file)));
41558
41887
  }
41559
41888
  function summarizeOwnershipSources(sources) {
41560
41889
  const blocks = sources.flatMap((source2) => source2.blocks);
@@ -41650,14 +41979,17 @@ function refreshedOwnershipBlock(input) {
41650
41979
  async function refreshStaleSourceOwnership(ctxDir, ownership) {
41651
41980
  const sourceFile = await loadSources(ctxDir);
41652
41981
  const sourceById = new Map(sourceFile.sources.map((source2) => [source2.id, source2]));
41653
- const currentHashes = await latestSnapshotHashes(ctxDir);
41982
+ const latestSnapshots = new Map(sourceFile.sources.flatMap((source2) => {
41983
+ const latest = selectLatestSnapshot(source2.snapshots);
41984
+ return latest === null ? [] : [[source2.id, latest]];
41985
+ }));
41654
41986
  const refreshedSources = [];
41655
41987
  let changed = false;
41656
41988
  for (const source2 of ownership.sources) {
41657
- const currentHash = currentHashes.get(source2.source_id);
41658
- if (currentHash === undefined || source2.snapshot_hash === undefined)
41989
+ const latest = latestSnapshots.get(source2.source_id);
41990
+ if (latest === undefined || source2.snapshot_hash === undefined)
41659
41991
  return null;
41660
- if (source2.snapshot_hash === currentHash) {
41992
+ if (!sourceOwnershipNeedsRefresh(ctxDir, source2, latest)) {
41661
41993
  refreshedSources.push(source2);
41662
41994
  continue;
41663
41995
  }
@@ -41668,7 +42000,7 @@ async function refreshStaleSourceOwnership(ctxDir, ownership) {
41668
42000
  const after = await readLatestEvidenceManifest(ctxDir, entry);
41669
42001
  if (before === null || after === null)
41670
42002
  return null;
41671
- const file = after.snapshot_file ?? source2.file;
42003
+ const file = after.snapshot_file ?? latest.file ?? source2.file;
41672
42004
  const body2 = await readEvidenceBody(ctxDir, file, entry);
41673
42005
  const blocks = [];
41674
42006
  for (const match of matchEvidenceBlocks(before, after)) {
@@ -41680,19 +42012,19 @@ async function refreshStaleSourceOwnership(ctxDir, ownership) {
41680
42012
  previous: previous3,
41681
42013
  after: match.after,
41682
42014
  file,
41683
- snapshotHash: currentHash,
42015
+ snapshotHash: latest.content_hash,
41684
42016
  ...textPreview !== undefined ? { textPreview } : {}
41685
42017
  }) : ownershipFromHeadingPeer({
41686
42018
  source: source2,
41687
42019
  block: match.after,
41688
42020
  file,
41689
- snapshotHash: currentHash,
42021
+ snapshotHash: latest.content_hash,
41690
42022
  ...textPreview !== undefined ? { textPreview } : {}
41691
42023
  }));
41692
42024
  }
41693
42025
  refreshedSources.push({
41694
42026
  source_id: source2.source_id,
41695
- snapshot_hash: currentHash,
42027
+ snapshot_hash: latest.content_hash,
41696
42028
  file,
41697
42029
  blocks
41698
42030
  });
@@ -42192,6 +42524,363 @@ var init_verifyPromotionCleanup = __esm(() => {
42192
42524
  NAVIGATION_LINK_LABEL_RE = /(?:related|references?|links?|docs?|documents?|navigation|相关|关联|参考|文档|链接|导航|入口|资料)/iu;
42193
42525
  });
42194
42526
 
42527
+ // src/mdrive/verifyWorkspaceReader.ts
42528
+ import { existsSync as existsSync29 } from "node:fs";
42529
+ import { readdir as readdir14, readFile as readFile36 } from "node:fs/promises";
42530
+ import { join as join33, relative as relative9 } from "node:path";
42531
+ function isIgnoredKnowledgeMarkdown(name) {
42532
+ return name === "_index.md" || name === "changelog.md";
42533
+ }
42534
+ async function pushUnsupportedNestedMarkdownIssues(root2, dir, issues, depth = 0) {
42535
+ if (depth > MAX_UNSUPPORTED_NESTED_SCAN_DEPTH) {
42536
+ issues.push({
42537
+ severity: "error",
42538
+ code: "file-layout-unsupported",
42539
+ message: "knowledge node files nested under this directory are too deep to scan safely",
42540
+ path: relative9(root2, dir).replace(/\\/g, "/")
42541
+ });
42542
+ return;
42543
+ }
42544
+ const entries = await readdir14(dir, { withFileTypes: true });
42545
+ for (const entry of entries) {
42546
+ const filePath = join33(dir, entry.name);
42547
+ if (entry.isSymbolicLink())
42548
+ continue;
42549
+ if (entry.isDirectory()) {
42550
+ await pushUnsupportedNestedMarkdownIssues(root2, filePath, issues, depth + 1);
42551
+ continue;
42552
+ }
42553
+ if (!entry.isFile() || !entry.name.endsWith(".md") || isIgnoredKnowledgeMarkdown(entry.name))
42554
+ continue;
42555
+ const relativePath = relative9(root2, filePath).replace(/\\/g, "/");
42556
+ issues.push({
42557
+ severity: "error",
42558
+ code: "file-layout-unsupported",
42559
+ message: "knowledge node files must live at knowledge/<type>/<slug>.md or knowledge/<type>/<package>/<slug>.md",
42560
+ path: relativePath
42561
+ });
42562
+ }
42563
+ }
42564
+ async function readParsedKnowledgeFile(root2, filePath) {
42565
+ const relativePath = relative9(root2, filePath).replace(/\\/g, "/");
42566
+ try {
42567
+ const rootNode = parseNodeMarkdown(await readFile36(filePath, "utf8"));
42568
+ const expectedSlug = slugFromKnowledgeRelativePath(relativePath);
42569
+ if (expectedSlug !== null && expectedSlug.includes("/") && rootNode.node.id === expectedSlug) {
42570
+ const parentSlug = expectedSlug.split("/")[0];
42571
+ if (parentSlug) {
42572
+ const edge2 = {
42573
+ type: "contains",
42574
+ from: parentSlug,
42575
+ to: expectedSlug,
42576
+ grounding: "directory",
42577
+ ...rootNode.node.valid_from !== undefined ? { valid_from: rootNode.node.valid_from } : {},
42578
+ ...rootNode.node.valid_until !== undefined ? { valid_until: rootNode.node.valid_until } : {}
42579
+ };
42580
+ rootNode.containsEdges = [edge2, ...rootNode.containsEdges];
42581
+ }
42582
+ }
42583
+ return { filePath, relativePath, root: rootNode };
42584
+ } catch (error) {
42585
+ return {
42586
+ severity: "error",
42587
+ code: "parse-error",
42588
+ message: error instanceof Error ? error.message : String(error),
42589
+ path: relativePath
42590
+ };
42591
+ }
42592
+ }
42593
+ function pushRenderAutoBlockIssues(node3, relativePath, issues) {
42594
+ for (const issue of node3.renderAutoBlockIssues ?? []) {
42595
+ issues.push({
42596
+ severity: "error",
42597
+ code: issue.code,
42598
+ message: `renderer auto-block "${issue.title}" is missing its closing sentinel`,
42599
+ path: relativePath,
42600
+ slug: node3.node.id,
42601
+ line: issue.line,
42602
+ next_action: "Run context compile --close after fixing the unclosed c4a:auto-block sentinel."
42603
+ });
42604
+ }
42605
+ for (const child of node3.children) {
42606
+ pushRenderAutoBlockIssues(child, relativePath, issues);
42607
+ }
42608
+ }
42609
+ async function collectParsedWorkspace(ctxDir) {
42610
+ const files = [];
42611
+ const issues = [];
42612
+ const root2 = knowledgeRoot3(ctxDir);
42613
+ if (existsSync29(root2)) {
42614
+ const entries = await readdir14(root2, { withFileTypes: true });
42615
+ for (const entry of entries) {
42616
+ if (entry.isDirectory() && entry.name === "concept") {
42617
+ issues.push({
42618
+ severity: "error",
42619
+ code: "concept-directory-not-allowed",
42620
+ message: "knowledge/concept is not supported; use knowledge/entity with the term tag for named concepts",
42621
+ path: "knowledge/concept"
42622
+ });
42623
+ }
42624
+ }
42625
+ }
42626
+ for (const type of NODE_TYPES) {
42627
+ const dir = join33(root2, type);
42628
+ if (!existsSync29(dir))
42629
+ continue;
42630
+ const entries = await readdir14(dir, { withFileTypes: true });
42631
+ for (const entry of entries) {
42632
+ if (entry.isFile()) {
42633
+ if (!entry.name.endsWith(".md") || isIgnoredKnowledgeMarkdown(entry.name))
42634
+ continue;
42635
+ const parsed = await readParsedKnowledgeFile(root2, join33(dir, entry.name));
42636
+ if ("root" in parsed) {
42637
+ files.push(parsed);
42638
+ pushRenderAutoBlockIssues(parsed.root, parsed.relativePath, issues);
42639
+ } else
42640
+ issues.push(parsed);
42641
+ continue;
42642
+ }
42643
+ if (!entry.isDirectory())
42644
+ continue;
42645
+ const nestedDir = join33(dir, entry.name);
42646
+ const nestedEntries = await readdir14(nestedDir, { withFileTypes: true });
42647
+ for (const nestedEntry of nestedEntries) {
42648
+ if (nestedEntry.isDirectory()) {
42649
+ await pushUnsupportedNestedMarkdownIssues(root2, join33(nestedDir, nestedEntry.name), issues);
42650
+ continue;
42651
+ }
42652
+ if (!nestedEntry.isFile() || !nestedEntry.name.endsWith(".md") || isIgnoredKnowledgeMarkdown(nestedEntry.name)) {
42653
+ continue;
42654
+ }
42655
+ const parsed = await readParsedKnowledgeFile(root2, join33(nestedDir, nestedEntry.name));
42656
+ if ("root" in parsed) {
42657
+ files.push(parsed);
42658
+ pushRenderAutoBlockIssues(parsed.root, parsed.relativePath, issues);
42659
+ } else
42660
+ issues.push(parsed);
42661
+ }
42662
+ }
42663
+ }
42664
+ return { files, issues };
42665
+ }
42666
+ function collectAllNodes(files) {
42667
+ return flattenWorkspaceNodes(files.map((file) => ({
42668
+ filePath: file.filePath,
42669
+ relativePath: file.relativePath,
42670
+ root: file.root
42671
+ })));
42672
+ }
42673
+ var MAX_UNSUPPORTED_NESTED_SCAN_DEPTH = 8;
42674
+ var init_verifyWorkspaceReader = __esm(() => {
42675
+ init_nodeParser();
42676
+ init_knowledge();
42677
+ init_shared();
42678
+ });
42679
+
42680
+ // src/mdrive/verifyRenderBlocks.ts
42681
+ import { readFile as readFile37 } from "node:fs/promises";
42682
+ import { dirname as dirname13, join as join34, resolve as resolve9 } from "node:path";
42683
+ function headingKind(line) {
42684
+ const match = /^(#{2,6})\s+(.+?)\s*$/u.exec(line);
42685
+ if (!match)
42686
+ return null;
42687
+ return AUTO_BLOCK_TITLES2.get((match[2] ?? "").trim()) ?? null;
42688
+ }
42689
+ function findBlockHeadingStart(lines, openIndex, kind) {
42690
+ let cursor = openIndex - 1;
42691
+ while (cursor >= 0 && (lines[cursor]?.trim() ?? "") === "")
42692
+ cursor -= 1;
42693
+ return cursor >= 0 && headingKind(lines[cursor] ?? "") === kind ? cursor : openIndex;
42694
+ }
42695
+ function extractAutoBlockSlices(markdown) {
42696
+ const lines = markdown.replace(/\r\n?/g, `
42697
+ `).split(`
42698
+ `);
42699
+ const occurrences = new Map;
42700
+ const blocks = [];
42701
+ for (let index2 = 0;index2 < lines.length; index2 += 1) {
42702
+ const open2 = AUTO_BLOCK_OPEN.exec((lines[index2] ?? "").trim());
42703
+ if (!open2)
42704
+ continue;
42705
+ const kind = open2[1];
42706
+ let closeIndex = index2 + 1;
42707
+ while (closeIndex < lines.length && (lines[closeIndex]?.trim() ?? "") !== AUTO_BLOCK_CLOSE2) {
42708
+ closeIndex += 1;
42709
+ }
42710
+ if (closeIndex >= lines.length)
42711
+ continue;
42712
+ const start2 = findBlockHeadingStart(lines, index2, kind);
42713
+ const occurrence = (occurrences.get(kind) ?? 0) + 1;
42714
+ occurrences.set(kind, occurrence);
42715
+ blocks.push({
42716
+ key: `${kind}:${occurrence}`,
42717
+ kind,
42718
+ line: start2 + 1,
42719
+ text: lines.slice(start2, closeIndex + 1).join(`
42720
+ `)
42721
+ });
42722
+ index2 = closeIndex;
42723
+ }
42724
+ return blocks;
42725
+ }
42726
+ function blockMap(blocks) {
42727
+ return new Map(blocks.map((block) => [block.key, block]));
42728
+ }
42729
+ function issuePath(file) {
42730
+ return file.relativePath;
42731
+ }
42732
+ function slugForLine(root2, line) {
42733
+ let match = root2.node.id;
42734
+ const visit2 = (node3) => {
42735
+ if (node3.startLine !== undefined && line < node3.startLine)
42736
+ return;
42737
+ if (node3.endLine !== undefined && line > node3.endLine)
42738
+ return;
42739
+ match = node3.node.id;
42740
+ for (const child of node3.children) {
42741
+ visit2(child);
42742
+ }
42743
+ };
42744
+ visit2(root2);
42745
+ return match;
42746
+ }
42747
+ function pushStaleIssue(file, current, expected, issues) {
42748
+ const block = current ?? expected;
42749
+ if (block === undefined)
42750
+ return;
42751
+ issues.push({
42752
+ severity: "error",
42753
+ code: "render-block-stale",
42754
+ message: `renderer auto-block ${block.key} is stale; rerun context compile --close to regenerate it`,
42755
+ path: issuePath(file),
42756
+ slug: slugForLine(file.root, block.line),
42757
+ line: block.line,
42758
+ next_action: "Run context compile --close to regenerate renderer-owned relation blocks."
42759
+ });
42760
+ }
42761
+ function isExternalHref(href) {
42762
+ return /^[a-z][a-z0-9+.-]*:/iu.test(href) || href.startsWith("//");
42763
+ }
42764
+ function targetKey(filePath, anchor) {
42765
+ const resolved = resolve9(filePath);
42766
+ return anchor !== undefined && anchor.length > 0 ? `${resolved}#${anchor}` : resolved;
42767
+ }
42768
+ function markdownHrefTarget(filePath, href) {
42769
+ if (isExternalHref(href))
42770
+ return null;
42771
+ const beforeQuery = href.split("?")[0] ?? "";
42772
+ const hashIndex = beforeQuery.indexOf("#");
42773
+ const pathPart = hashIndex >= 0 ? beforeQuery.slice(0, hashIndex) : beforeQuery;
42774
+ const anchor = hashIndex >= 0 ? beforeQuery.slice(hashIndex + 1) : undefined;
42775
+ const targetPath = pathPart.length === 0 ? filePath : resolve9(dirname13(filePath), pathPart);
42776
+ return targetKey(targetPath, anchor);
42777
+ }
42778
+ function wikiHrefTarget(ctxDir, target) {
42779
+ const hashIndex = target.indexOf("#");
42780
+ const rawPathPart = hashIndex >= 0 ? target.slice(0, hashIndex) : target;
42781
+ const anchor = hashIndex >= 0 ? target.slice(hashIndex + 1) : undefined;
42782
+ const pathPart = rawPathPart.replace(/^\/+/u, "");
42783
+ const markdownPath = pathPart.endsWith(".md") ? pathPart : `${pathPart}.md`;
42784
+ return targetKey(join34(knowledgeRoot3(ctxDir), markdownPath), anchor);
42785
+ }
42786
+ function validRenderNodeTargets(files) {
42787
+ const targets = new Set;
42788
+ for (const file of files) {
42789
+ targets.add(targetKey(file.filePath));
42790
+ const anchors = computeNodeAnchorsForFile(file.root);
42791
+ for (const anchor of anchors.values()) {
42792
+ targets.add(targetKey(file.filePath, anchor));
42793
+ }
42794
+ }
42795
+ return targets;
42796
+ }
42797
+ function pushBrokenHrefIssues(ctxDir, file, block, validTargets, issues) {
42798
+ if (block.kind === "external_dependencies")
42799
+ return;
42800
+ const lines = block.text.split(`
42801
+ `);
42802
+ for (let offset = 0;offset < lines.length; offset += 1) {
42803
+ const line = lines[offset] ?? "";
42804
+ for (const match of line.matchAll(MARKDOWN_LINK)) {
42805
+ const href = match[1] ?? "";
42806
+ const target = markdownHrefTarget(file.filePath, href);
42807
+ if (target === null || validTargets.has(target))
42808
+ continue;
42809
+ issues.push({
42810
+ severity: "error",
42811
+ code: "render-block-href-broken",
42812
+ message: `renderer auto-block link target "${href}" does not exist`,
42813
+ path: issuePath(file),
42814
+ slug: slugForLine(file.root, block.line + offset),
42815
+ line: block.line + offset,
42816
+ next_action: "Run context compile --close to regenerate relation block links after fixing the target Node."
42817
+ });
42818
+ }
42819
+ for (const match of line.matchAll(WIKI_LINK)) {
42820
+ const target = match[1] ?? "";
42821
+ const fileTarget = wikiHrefTarget(ctxDir, target);
42822
+ if (validTargets.has(fileTarget))
42823
+ continue;
42824
+ issues.push({
42825
+ severity: "error",
42826
+ code: "render-block-href-broken",
42827
+ message: `renderer auto-block wiki target "${target}" does not exist`,
42828
+ path: issuePath(file),
42829
+ slug: slugForLine(file.root, block.line + offset),
42830
+ line: block.line + offset,
42831
+ next_action: "Run context compile --close to regenerate relation block links after fixing the target Node."
42832
+ });
42833
+ }
42834
+ }
42835
+ }
42836
+ async function detectRenderBlockIssues(ctxDir, files, issues) {
42837
+ const renderContext = await buildRenderWorkspaceContext(ctxDir, files);
42838
+ const validTargets = validRenderNodeTargets(files);
42839
+ for (const file of files) {
42840
+ const current = extractAutoBlockSlices(await readFile37(file.filePath, "utf8"));
42841
+ const expected = extractAutoBlockSlices(await renderRootForFile(file.filePath, file.root, renderContext));
42842
+ const currentByKey = blockMap(current);
42843
+ const expectedByKey = blockMap(expected);
42844
+ const keys = new Set([...currentByKey.keys(), ...expectedByKey.keys()]);
42845
+ for (const key of [...keys].sort()) {
42846
+ const currentBlock = currentByKey.get(key);
42847
+ const expectedBlock = expectedByKey.get(key);
42848
+ if (currentBlock?.text === expectedBlock?.text)
42849
+ continue;
42850
+ pushStaleIssue(file, currentBlock, expectedBlock, issues);
42851
+ }
42852
+ for (const block of current) {
42853
+ pushBrokenHrefIssues(ctxDir, file, block, validTargets, issues);
42854
+ }
42855
+ }
42856
+ }
42857
+ async function assertNoUnclosedRenderAutoBlocks(ctxDir) {
42858
+ const { issues } = await collectParsedWorkspace(ctxDir);
42859
+ const unclosed = issues.filter((issue) => issue.code === "render-auto-block-unclosed");
42860
+ if (unclosed.length === 0)
42861
+ return;
42862
+ throw new ContextError(ExitCode.WorkspaceStateError, "unclosed renderer auto-block sentinel", {
42863
+ category: "schema-invalid",
42864
+ issues: unclosed
42865
+ });
42866
+ }
42867
+ var AUTO_BLOCK_TITLES2, AUTO_BLOCK_CLOSE2 = "<!-- /c4a:auto-block -->", AUTO_BLOCK_OPEN, MARKDOWN_LINK, WIKI_LINK;
42868
+ var init_verifyRenderBlocks = __esm(() => {
42869
+ init_errors();
42870
+ init_exitCode();
42871
+ init_shared();
42872
+ init_verifyWorkspaceReader();
42873
+ AUTO_BLOCK_TITLES2 = new Map([
42874
+ ["Depends On", "depends_on"],
42875
+ ["External Dependencies", "external_dependencies"],
42876
+ ["Used By", "used_by"],
42877
+ ["Related", "related"]
42878
+ ]);
42879
+ AUTO_BLOCK_OPEN = /^<!--\s+c4a:auto-block\s+(depends_on|external_dependencies|used_by|related)\s+-->$/u;
42880
+ MARKDOWN_LINK = /\[[^\]]+\]\(([^)]+)\)/gu;
42881
+ WIKI_LINK = /\[\[([^|\]]+)(?:\|[^\]]+)?\]\]/gu;
42882
+ });
42883
+
42195
42884
  // src/mdrive/verifyGraphRules.ts
42196
42885
  function edgeIssuePath(edge2) {
42197
42886
  return edge2.source_path ? `knowledge/${edge2.source_path}` : "knowledge/_edges.yaml";
@@ -42859,8 +43548,8 @@ var init_alignPlan = __esm(() => {
42859
43548
  });
42860
43549
 
42861
43550
  // src/evidence/ref.ts
42862
- import { readFile as readFile36 } from "node:fs/promises";
42863
- import { join as join33 } from "node:path";
43551
+ import { readFile as readFile38 } from "node:fs/promises";
43552
+ import { join as join35 } from "node:path";
42864
43553
  function createSourceRefResolutionCache() {
42865
43554
  return {
42866
43555
  manifests: new Map,
@@ -42952,7 +43641,7 @@ async function rawForManifestUncached(ctxDir, manifest) {
42952
43641
  if (!manifest.snapshot_file)
42953
43642
  return null;
42954
43643
  try {
42955
- const raw = await readFile36(join33(ctxDir, manifest.snapshot_file), "utf8");
43644
+ const raw = await readFile38(join35(ctxDir, manifest.snapshot_file), "utf8");
42956
43645
  if (manifest.source_type === "note")
42957
43646
  return readNoteMetadata(raw)?.body ?? stripCaptureFrontmatter(raw);
42958
43647
  return stripCaptureFrontmatter(raw);
@@ -43401,9 +44090,9 @@ var init_ref = __esm(() => {
43401
44090
  });
43402
44091
 
43403
44092
  // src/mdrive/verifyCodeSourceRefs.ts
43404
- import { existsSync as existsSync29 } from "node:fs";
43405
- import { readFile as readFile37 } from "node:fs/promises";
43406
- import { join as join34 } from "node:path";
44093
+ import { existsSync as existsSync30 } from "node:fs";
44094
+ import { readFile as readFile39 } from "node:fs/promises";
44095
+ import { join as join36 } from "node:path";
43407
44096
  function codeSourceRefNextAction(code3) {
43408
44097
  if (code3 === "code-source-ref-stale")
43409
44098
  return "Rerun context compile --code <slug> so generated Sections match the current raw code snapshot.";
@@ -43435,17 +44124,17 @@ function sourceSlug2(source2, snapshot) {
43435
44124
  }
43436
44125
  function snapshotDir2(ctxDir, source2, snapshot) {
43437
44126
  if (snapshot.dir)
43438
- return join34(ctxDir, snapshot.dir);
44127
+ return join36(ctxDir, snapshot.dir);
43439
44128
  const slug = sourceSlug2(source2, snapshot);
43440
44129
  const id2 = snapshot.snapshot_id;
43441
44130
  if (!slug || !id2)
43442
44131
  return null;
43443
- return join34(ctxDir, "raw", "aspect", "code", slug, id2);
44132
+ return join36(ctxDir, "raw", "aspect", "code", slug, id2);
43444
44133
  }
43445
44134
  async function readJsonl(path9) {
43446
- if (!existsSync29(path9))
44135
+ if (!existsSync30(path9))
43447
44136
  return [];
43448
- const text5 = await readFile37(path9, "utf8");
44137
+ const text5 = await readFile39(path9, "utf8");
43449
44138
  const rows = [];
43450
44139
  for (const line of text5.split(/\r?\n/u)) {
43451
44140
  if (line.trim().length === 0)
@@ -43478,11 +44167,11 @@ async function loadCodeSnapshotRecords(ctxDir, source2) {
43478
44167
  const dir = snapshotDir2(ctxDir, source2, snapshot);
43479
44168
  if (!dir)
43480
44169
  continue;
43481
- records.symbols.push(...annotateRows(await readJsonl(join34(dir, "symbols.jsonl")), snapshot));
43482
- records.digests.push(...annotateRows(await readJsonl(join34(dir, "digests.jsonl")), snapshot));
43483
- records.packages.push(...annotateRows(await readJsonl(join34(dir, "packages.jsonl")), snapshot));
43484
- records.sourceFiles.push(...annotateRows(await readJsonl(join34(dir, "source-files.jsonl")), snapshot));
43485
- records.edges.push(...annotateRows(await readJsonl(join34(dir, "edges.jsonl")), snapshot));
44170
+ records.symbols.push(...annotateRows(await readJsonl(join36(dir, "symbols.jsonl")), snapshot));
44171
+ records.digests.push(...annotateRows(await readJsonl(join36(dir, "digests.jsonl")), snapshot));
44172
+ records.packages.push(...annotateRows(await readJsonl(join36(dir, "packages.jsonl")), snapshot));
44173
+ records.sourceFiles.push(...annotateRows(await readJsonl(join36(dir, "source-files.jsonl")), snapshot));
44174
+ records.edges.push(...annotateRows(await readJsonl(join36(dir, "edges.jsonl")), snapshot));
43486
44175
  }
43487
44176
  return {
43488
44177
  symbols: records.symbols,
@@ -43930,141 +44619,6 @@ var init_verifySourceRules = __esm(() => {
43930
44619
  init_verifySourceRefs();
43931
44620
  });
43932
44621
 
43933
- // src/mdrive/verifyWorkspaceReader.ts
43934
- import { existsSync as existsSync30 } from "node:fs";
43935
- import { readdir as readdir14, readFile as readFile38 } from "node:fs/promises";
43936
- import { join as join35, relative as relative9 } from "node:path";
43937
- function isIgnoredKnowledgeMarkdown(name) {
43938
- return name === "_index.md" || name === "changelog.md";
43939
- }
43940
- async function pushUnsupportedNestedMarkdownIssues(root2, dir, issues, depth = 0) {
43941
- if (depth > MAX_UNSUPPORTED_NESTED_SCAN_DEPTH) {
43942
- issues.push({
43943
- severity: "error",
43944
- code: "file-layout-unsupported",
43945
- message: "knowledge node files nested under this directory are too deep to scan safely",
43946
- path: relative9(root2, dir).replace(/\\/g, "/")
43947
- });
43948
- return;
43949
- }
43950
- const entries = await readdir14(dir, { withFileTypes: true });
43951
- for (const entry of entries) {
43952
- const filePath = join35(dir, entry.name);
43953
- if (entry.isSymbolicLink())
43954
- continue;
43955
- if (entry.isDirectory()) {
43956
- await pushUnsupportedNestedMarkdownIssues(root2, filePath, issues, depth + 1);
43957
- continue;
43958
- }
43959
- if (!entry.isFile() || !entry.name.endsWith(".md") || isIgnoredKnowledgeMarkdown(entry.name))
43960
- continue;
43961
- const relativePath = relative9(root2, filePath).replace(/\\/g, "/");
43962
- issues.push({
43963
- severity: "error",
43964
- code: "file-layout-unsupported",
43965
- message: "knowledge node files must live at knowledge/<type>/<slug>.md or knowledge/<type>/<package>/<slug>.md",
43966
- path: relativePath
43967
- });
43968
- }
43969
- }
43970
- async function readParsedKnowledgeFile(root2, filePath) {
43971
- const relativePath = relative9(root2, filePath).replace(/\\/g, "/");
43972
- try {
43973
- const rootNode = parseNodeMarkdown(await readFile38(filePath, "utf8"));
43974
- const expectedSlug = slugFromKnowledgeRelativePath(relativePath);
43975
- if (expectedSlug !== null && expectedSlug.includes("/") && rootNode.node.id === expectedSlug) {
43976
- const parentSlug = expectedSlug.split("/")[0];
43977
- if (parentSlug) {
43978
- const edge2 = {
43979
- type: "contains",
43980
- from: parentSlug,
43981
- to: expectedSlug,
43982
- grounding: "directory",
43983
- ...rootNode.node.valid_from !== undefined ? { valid_from: rootNode.node.valid_from } : {},
43984
- ...rootNode.node.valid_until !== undefined ? { valid_until: rootNode.node.valid_until } : {}
43985
- };
43986
- rootNode.containsEdges = [edge2, ...rootNode.containsEdges];
43987
- }
43988
- }
43989
- return { filePath, relativePath, root: rootNode };
43990
- } catch (error) {
43991
- return {
43992
- severity: "error",
43993
- code: "parse-error",
43994
- message: error instanceof Error ? error.message : String(error),
43995
- path: relativePath
43996
- };
43997
- }
43998
- }
43999
- async function collectParsedWorkspace(ctxDir) {
44000
- const files = [];
44001
- const issues = [];
44002
- const root2 = knowledgeRoot2(ctxDir);
44003
- if (existsSync30(root2)) {
44004
- const entries = await readdir14(root2, { withFileTypes: true });
44005
- for (const entry of entries) {
44006
- if (entry.isDirectory() && entry.name === "concept") {
44007
- issues.push({
44008
- severity: "error",
44009
- code: "concept-directory-not-allowed",
44010
- message: "knowledge/concept is not supported; use knowledge/entity with the term tag for named concepts",
44011
- path: "knowledge/concept"
44012
- });
44013
- }
44014
- }
44015
- }
44016
- for (const type of NODE_TYPES) {
44017
- const dir = join35(root2, type);
44018
- if (!existsSync30(dir))
44019
- continue;
44020
- const entries = await readdir14(dir, { withFileTypes: true });
44021
- for (const entry of entries) {
44022
- if (entry.isFile()) {
44023
- if (!entry.name.endsWith(".md") || isIgnoredKnowledgeMarkdown(entry.name))
44024
- continue;
44025
- const parsed = await readParsedKnowledgeFile(root2, join35(dir, entry.name));
44026
- if ("root" in parsed)
44027
- files.push(parsed);
44028
- else
44029
- issues.push(parsed);
44030
- continue;
44031
- }
44032
- if (!entry.isDirectory())
44033
- continue;
44034
- const nestedDir = join35(dir, entry.name);
44035
- const nestedEntries = await readdir14(nestedDir, { withFileTypes: true });
44036
- for (const nestedEntry of nestedEntries) {
44037
- if (nestedEntry.isDirectory()) {
44038
- await pushUnsupportedNestedMarkdownIssues(root2, join35(nestedDir, nestedEntry.name), issues);
44039
- continue;
44040
- }
44041
- if (!nestedEntry.isFile() || !nestedEntry.name.endsWith(".md") || isIgnoredKnowledgeMarkdown(nestedEntry.name)) {
44042
- continue;
44043
- }
44044
- const parsed = await readParsedKnowledgeFile(root2, join35(nestedDir, nestedEntry.name));
44045
- if ("root" in parsed)
44046
- files.push(parsed);
44047
- else
44048
- issues.push(parsed);
44049
- }
44050
- }
44051
- }
44052
- return { files, issues };
44053
- }
44054
- function collectAllNodes(files) {
44055
- return flattenWorkspaceNodes(files.map((file) => ({
44056
- filePath: file.filePath,
44057
- relativePath: file.relativePath,
44058
- root: file.root
44059
- })));
44060
- }
44061
- var MAX_UNSUPPORTED_NESTED_SCAN_DEPTH = 8;
44062
- var init_verifyWorkspaceReader = __esm(() => {
44063
- init_nodeParser();
44064
- init_knowledge();
44065
- init_shared();
44066
- });
44067
-
44068
44622
  // src/mdrive/verify.ts
44069
44623
  function okFromIssues(issues) {
44070
44624
  return issues.every((issue) => issue.severity !== "error");
@@ -44137,6 +44691,7 @@ async function mdriveVerifyWorkspace(input) {
44137
44691
  }
44138
44692
  await detectDanglingGraphEdges(input.ctxDir, new Set(slugMap.keys()), issues);
44139
44693
  await detectExternalDepIssues(input.ctxDir, new Set(slugMap.keys()), issues);
44694
+ await detectRenderBlockIssues(input.ctxDir, files, issues);
44140
44695
  const currentOwnership = await readCurrentSourceOwnership(input.ctxDir);
44141
44696
  const allowedSourcelessSlugs = alignedNoWritePlaceholderSlugs(currentOwnership);
44142
44697
  for (const file of files) {
@@ -44225,6 +44780,7 @@ var init_verify = __esm(() => {
44225
44780
  init_verifyCoverage();
44226
44781
  init_verifyExternalRules();
44227
44782
  init_verifyPromotionCleanup();
44783
+ init_verifyRenderBlocks();
44228
44784
  init_verifyGraphRules();
44229
44785
  init_verifyNodeRules();
44230
44786
  init_verifySchemaRules();
@@ -44254,8 +44810,15 @@ var init_sectionDetail = __esm(() => {
44254
44810
  });
44255
44811
 
44256
44812
  // src/reconcile/sourceSupport.ts
44813
+ function orderedListMarkerSpans(text5) {
44814
+ return [...text5.matchAll(ORDERED_LIST_MARKER_RE)].map((match) => ({
44815
+ start: (match.index ?? 0) + match[0].indexOf(match[1]),
44816
+ end: (match.index ?? 0) + match[0].indexOf(match[1]) + match[1].length
44817
+ }));
44818
+ }
44257
44819
  function numbersIn(text5) {
44258
- return text5.match(/\b\d+(?:\.\d+)*\b/gu) ?? [];
44820
+ const markerSpans = orderedListMarkerSpans(text5);
44821
+ return [...text5.matchAll(NUMBER_RE)].filter((match) => !markerSpans.some((span) => (match.index ?? 0) >= span.start && (match.index ?? 0) + match[0].length <= span.end)).map((match) => match[0]);
44259
44822
  }
44260
44823
  function normalizedToken(value) {
44261
44824
  return normalizeMarkdown(value).trim().toLowerCase();
@@ -44745,7 +45308,7 @@ function weakSourceSupportGuidance(action, verdict) {
44745
45308
  }
44746
45309
  return " Key facts match, but this action rewrites or reanchors existing knowledge and requires direct support; choose source_ref/source_refs from the prepared evidence, split the claim, or ask the user before choosing a different final action.";
44747
45310
  }
44748
- var DEFAULT_THRESHOLDS, CJK_ONLY_RE, CJK_CHAR_RE, URL_RE2, TRAILING_URL_PUNCTUATION_RE, TRAILING_HARD_TERM_PUNCTUATION_RE, TRACKING_QUERY_PARAM_RE, EN_URL_REFERENCE_LABEL_RE, CJK_URL_REFERENCE_LABEL_RE, CONSTRAINT_PATTERNS, PLACEHOLDER_ANCHOR_PARTS;
45311
+ var NUMBER_RE, ORDERED_LIST_MARKER_RE, DEFAULT_THRESHOLDS, CJK_ONLY_RE, CJK_CHAR_RE, URL_RE2, TRAILING_URL_PUNCTUATION_RE, TRAILING_HARD_TERM_PUNCTUATION_RE, TRACKING_QUERY_PARAM_RE, EN_URL_REFERENCE_LABEL_RE, CJK_URL_REFERENCE_LABEL_RE, CONSTRAINT_PATTERNS, PLACEHOLDER_ANCHOR_PARTS;
44749
45312
  var init_sourceSupport = __esm(() => {
44750
45313
  init_normalize();
44751
45314
  init_sectionDetail();
@@ -44753,6 +45316,8 @@ var init_sourceSupport = __esm(() => {
44753
45316
  init_ref();
44754
45317
  init_rawBlocks();
44755
45318
  init_terms();
45319
+ NUMBER_RE = /\b\d+(?:\.\d+)*\b/gu;
45320
+ ORDERED_LIST_MARKER_RE = /(?:^|\n)[ \t]{0,3}(\d{1,3})(?:[.)]|[、.。])(?=[ \t]+)/gu;
44756
45321
  DEFAULT_THRESHOLDS = { supported: 0.75, weak: 0.5 };
44757
45322
  CJK_ONLY_RE = /^[\u4e00-\u9fff]+$/u;
44758
45323
  CJK_CHAR_RE = /[\u4e00-\u9fff]/gu;
@@ -45011,8 +45576,8 @@ var init_dropSemantic = __esm(() => {
45011
45576
  });
45012
45577
 
45013
45578
  // src/reconcile/evidence.ts
45014
- import { readFile as readFile39 } from "node:fs/promises";
45015
- import { join as join36 } from "node:path";
45579
+ import { readFile as readFile40 } from "node:fs/promises";
45580
+ import { join as join37 } from "node:path";
45016
45581
  function sourceRefAliasIndex(sourceRef) {
45017
45582
  const match = /^src-(\d+)#/u.exec(sourceRef.trim());
45018
45583
  return match ? Number(match[1]) - 1 : null;
@@ -45112,7 +45677,7 @@ async function evidenceSpansForSnapshotSourceRef(input) {
45112
45677
  if (combinedEvidenceBlockHash(manifestBlocks) !== sourceRefHash) {
45113
45678
  throw new Error(`${input.action} source_ref "${input.sourceRef}" does not match the cited evidence block hash`);
45114
45679
  }
45115
- const raw = await readFile39(join36(input.ctxDir, input.snapshot.file), "utf8");
45680
+ const raw = await readFile40(join37(input.ctxDir, input.snapshot.file), "utf8");
45116
45681
  const note = input.source.type === "note" ? readNoteMetadata(raw) : undefined;
45117
45682
  const body2 = note !== undefined ? note.body : stripCaptureFrontmatter(raw);
45118
45683
  const spans = extractRawBlocks(body2).flatMap((block) => {
@@ -45152,7 +45717,7 @@ async function evidenceSpansForSnapshotSourceRange(input) {
45152
45717
  if (rangeHash === null) {
45153
45718
  throw new Error(`${input.action} source_ref "${input.sourceRef}" does not overlap a reliable raw block`);
45154
45719
  }
45155
- const raw = await readFile39(join36(input.ctxDir, input.snapshot.file), "utf8");
45720
+ const raw = await readFile40(join37(input.ctxDir, input.snapshot.file), "utf8");
45156
45721
  const note = input.source.type === "note" ? readNoteMetadata(raw) : undefined;
45157
45722
  const body2 = note !== undefined ? note.body : stripCaptureFrontmatter(raw);
45158
45723
  const minLine = Math.min(...manifestBlocks.map((block) => block.line_start));
@@ -45347,13 +45912,13 @@ var init_evidence = __esm(() => {
45347
45912
  import { existsSync as existsSync31 } from "node:fs";
45348
45913
  import { execFile as execFile2 } from "node:child_process";
45349
45914
  import { promisify as promisify3 } from "node:util";
45350
- import { readFile as readFile40 } from "node:fs/promises";
45915
+ import { readFile as readFile41 } from "node:fs/promises";
45351
45916
  import { relative as relative10 } from "node:path";
45352
45917
  function changelogPath(ctxDir) {
45353
- return `${knowledgeRoot2(ctxDir)}/changelog.md`;
45918
+ return `${knowledgeRoot3(ctxDir)}/changelog.md`;
45354
45919
  }
45355
45920
  function indexPath(ctxDir) {
45356
- return `${knowledgeRoot2(ctxDir)}/_index.md`;
45921
+ return `${knowledgeRoot3(ctxDir)}/_index.md`;
45357
45922
  }
45358
45923
  async function mdriveWorkspaceStats(input) {
45359
45924
  const nodes = flattenWorkspaceNodes(await readWorkspaceNodeFiles(input.ctxDir));
@@ -45422,7 +45987,7 @@ async function mdriveWorkspaceRebuildIndex(input) {
45422
45987
  }
45423
45988
  async function mdriveWorkspaceAppendChangelog(input) {
45424
45989
  const path9 = changelogPath(input.ctxDir);
45425
- const existing = existsSync31(path9) ? await readFile40(path9, "utf8") : `# Knowledge Changelog
45990
+ const existing = existsSync31(path9) ? await readFile41(path9, "utf8") : `# Knowledge Changelog
45426
45991
 
45427
45992
  `;
45428
45993
  const trimmed = existing.endsWith(`
@@ -45433,7 +45998,7 @@ async function mdriveWorkspaceAppendChangelog(input) {
45433
45998
  }
45434
45999
  async function mdriveWorkspaceRebuildChangelog(input) {
45435
46000
  const workspaceRoot = workspaceRootFromCtxDir(input.ctxDir);
45436
- const relKnowledgePath = relative10(workspaceRoot, knowledgeRoot2(input.ctxDir));
46001
+ const relKnowledgePath = relative10(workspaceRoot, knowledgeRoot3(input.ctxDir));
45437
46002
  try {
45438
46003
  const { stdout } = await execFileAsync2("git", [
45439
46004
  "-C",
@@ -45461,16 +46026,11 @@ ${entries.join(`
45461
46026
  }
45462
46027
  }
45463
46028
  async function mdriveWorkspaceCompact(input) {
46029
+ await assertNoUnclosedRenderAutoBlocks(input.ctxDir);
45464
46030
  const files = await readWorkspaceNodeFiles(input.ctxDir);
45465
- const graphEdges = await loadKnowledgeGraphEdges(input.ctxDir);
45466
46031
  const renderContext = await buildRenderWorkspaceContext(input.ctxDir, files);
45467
46032
  for (const file of files) {
45468
- const relatedLinks = renderContext.relatedLinksByFile.get(file.filePath);
45469
- await atomicWriteText(file.filePath, renderNodeMarkdown({
45470
- ...file.root,
45471
- graphEdges,
45472
- ...relatedLinks !== undefined ? { relatedLinks } : {}
45473
- }));
46033
+ await atomicRewriteRootFile(file.filePath, file.root, undefined, renderContext);
45474
46034
  }
45475
46035
  }
45476
46036
  async function canonicalizeSourceRefsInNode(ctxDir, node3, cache) {
@@ -45508,38 +46068,31 @@ async function mdriveWorkspaceCanonicalizeSourceRefs(input) {
45508
46068
  }
45509
46069
  if (updates === 0)
45510
46070
  return 0;
45511
- const graphEdges = await loadKnowledgeGraphEdges(input.ctxDir);
45512
46071
  const renderContext = await buildRenderWorkspaceContext(input.ctxDir, files);
45513
46072
  for (const file of files) {
45514
46073
  if (!changed.has(file.filePath))
45515
46074
  continue;
45516
- const relatedLinks = renderContext.relatedLinksByFile.get(file.filePath);
45517
- await atomicWriteText(file.filePath, renderNodeMarkdown({
45518
- ...file.root,
45519
- graphEdges,
45520
- ...relatedLinks !== undefined ? { relatedLinks } : {}
45521
- }));
46075
+ await atomicRewriteRootFile(file.filePath, file.root, undefined, renderContext);
45522
46076
  }
45523
46077
  return updates;
45524
46078
  }
45525
46079
  var execFileAsync2;
45526
46080
  var init_workspace = __esm(() => {
45527
46081
  init_normalize();
45528
- init_nodeRenderer();
45529
46082
  init_workspaceLayout();
45530
46083
  init_ref();
45531
46084
  init_shared();
45532
- init_graphEdges();
46085
+ init_verifyRenderBlocks();
45533
46086
  execFileAsync2 = promisify3(execFile2);
45534
46087
  });
45535
46088
 
45536
46089
  // src/workflow/workflowOutputPaths.ts
45537
- import { join as join42 } from "node:path";
46090
+ import { join as join43 } from "node:path";
45538
46091
  function workflowOutputDir(ctxDir, kind) {
45539
- return join42(ctxDir, "output", kind);
46092
+ return join43(ctxDir, "output", kind);
45540
46093
  }
45541
46094
  function workflowOutputPath(ctxDir, kind, fileName) {
45542
- return join42(workflowOutputDir(ctxDir, kind), fileName);
46095
+ return join43(workflowOutputDir(ctxDir, kind), fileName);
45543
46096
  }
45544
46097
  function workflowOutputPathForFile(ctxDir, fileName) {
45545
46098
  return workflowOutputPath(ctxDir, workflowOutputKindForFile(fileName), fileName);
@@ -45586,13 +46139,13 @@ var init_dropPlanPayload = __esm(() => {
45586
46139
 
45587
46140
  // src/workflow/compileKnowledgeState.ts
45588
46141
  import { existsSync as existsSync36 } from "node:fs";
45589
- import { readFile as readFile48 } from "node:fs/promises";
45590
- import { join as join45 } from "node:path";
46142
+ import { readFile as readFile49 } from "node:fs/promises";
46143
+ import { join as join46 } from "node:path";
45591
46144
  async function readLastCompileAt(ctxDir) {
45592
- const path9 = join45(ctxDir, "knowledge", "changelog.md");
46145
+ const path9 = join46(ctxDir, "knowledge", "changelog.md");
45593
46146
  if (!existsSync36(path9))
45594
46147
  return null;
45595
- const lines = (await readFile48(path9, "utf8")).split(`
46148
+ const lines = (await readFile49(path9, "utf8")).split(`
45596
46149
  `).filter((line) => line.includes("[compile]"));
45597
46150
  for (let index2 = lines.length - 1;index2 >= 0; index2 -= 1) {
45598
46151
  const stamp = /@\s*([0-9]{4}-[0-9]{2}-[0-9]{2}(?:T[0-9:.+-]+Z?)?)/u.exec(lines[index2] ?? "")?.[1];
@@ -45909,7 +46462,7 @@ function ignoredLocatorChangeFromDiff(input) {
45909
46462
  }
45910
46463
 
45911
46464
  // src/workflow/compileChangesRaw.ts
45912
- import { readFile as readFile49 } from "node:fs/promises";
46465
+ import { readFile as readFile50 } from "node:fs/promises";
45913
46466
  function isRecord15(value) {
45914
46467
  return typeof value === "object" && value !== null && !Array.isArray(value);
45915
46468
  }
@@ -45923,7 +46476,7 @@ function isRawBlocksCache2(value) {
45923
46476
  return Object.values(value.snapshots).every((snapshot) => isRecord15(snapshot) && typeof snapshot.source_id === "string" && typeof snapshot.snapshot_file === "string" && typeof snapshot.content_hash === "string" && typeof snapshot.hash_id === "string" && typeof snapshot.structure_hash === "string" && Array.isArray(snapshot.blocks) && snapshot.blocks.every(isRawBlock2));
45924
46477
  }
45925
46478
  async function readJsonFile2(path9) {
45926
- return JSON.parse(await readFile49(path9, "utf8"));
46479
+ return JSON.parse(await readFile50(path9, "utf8"));
45927
46480
  }
45928
46481
  async function loadRawBlocks2(path9) {
45929
46482
  let parsed;
@@ -46506,10 +47059,10 @@ var init_node2 = __esm(() => {
46506
47059
  // src/workflow/coverage.ts
46507
47060
  import { createHash as createHash8 } from "node:crypto";
46508
47061
  import { existsSync as existsSync37 } from "node:fs";
46509
- import { mkdir as mkdir22, readFile as readFile50 } from "node:fs/promises";
46510
- import { dirname as dirname16, join as join46 } from "node:path";
47062
+ import { mkdir as mkdir22, readFile as readFile51 } from "node:fs/promises";
47063
+ import { dirname as dirname17, join as join47 } from "node:path";
46511
47064
  function coverageStatePath(ctxDir) {
46512
- return join46(ctxDir, "output", "workflows", "_coverage", "workspace", "coverage-state.json");
47065
+ return join47(ctxDir, "output", "workflows", "_coverage", "workspace", "coverage-state.json");
46513
47066
  }
46514
47067
  function shortHash3(value) {
46515
47068
  return createHash8("sha256").update(value).digest("hex").slice(0, 16);
@@ -46666,7 +47219,7 @@ async function readCoverageStateFile(ctxDir) {
46666
47219
  if (!existsSync37(path9))
46667
47220
  return emptyState();
46668
47221
  try {
46669
- const parsed = JSON.parse(await readFile50(path9, "utf8"));
47222
+ const parsed = JSON.parse(await readFile51(path9, "utf8"));
46670
47223
  if (parsed.schema_version !== COVERAGE_STATE_SCHEMA_VERSION || !Array.isArray(parsed.candidates)) {
46671
47224
  throw coverageStateInvalid("coverage state is invalid or uses an unsupported schema version");
46672
47225
  }
@@ -46727,7 +47280,7 @@ function mergeDispositions(existing, incoming) {
46727
47280
  }
46728
47281
  async function writeCoverageStateFile(ctxDir, state) {
46729
47282
  const path9 = coverageStatePath(ctxDir);
46730
- await mkdir22(dirname16(path9), { recursive: true });
47283
+ await mkdir22(dirname17(path9), { recursive: true });
46731
47284
  await atomicWriteFile(path9, `${JSON.stringify(state, null, 2)}
46732
47285
  `);
46733
47286
  }
@@ -47571,6 +48124,12 @@ async function computeCompileChanges(options) {
47571
48124
  }
47572
48125
  const alignNodes = alignResult.nodes;
47573
48126
  const sourcesFile = await loadSources(options.ctxDir);
48127
+ const inputSummary = await buildWorkspaceInputSummary({ ctxDir: options.ctxDir, workspaceRoot: paths.workspaceRoot, sourcesFile });
48128
+ const cacheRead = await readIncrementalCache({
48129
+ workspaceRoot,
48130
+ ...options.cacheHome !== undefined ? { cacheHome: options.cacheHome } : {},
48131
+ inputSummary
48132
+ });
47574
48133
  const sourcesById = new Map(sourcesFile.sources.map((source2) => [source2.id, source2]));
47575
48134
  const ignoredSources = await compileIgnoredSourceIds({ ctxDir: options.ctxDir, explicit: options.ignoreSourceIds ?? [] });
47576
48135
  const finalizedOwnership = await readCurrentSourceOwnership(options.ctxDir, {
@@ -47619,6 +48178,18 @@ async function computeCompileChanges(options) {
47619
48178
  await writeCompileState({ paths, output: output2, now });
47620
48179
  return output2;
47621
48180
  }
48181
+ const cacheNeedsRebuild = cacheRead.status !== "ready" && cacheRead.status !== "missing";
48182
+ if (options.writeState === false && cacheNeedsRebuild) {
48183
+ const unknown2 = globalUnknownNodes(alignNodes, sourceIdsByNode, cacheRead.reason);
48184
+ return createCompileChangesOutput({
48185
+ now,
48186
+ nodes: unknown2.nodes,
48187
+ sourcesChecked: 0,
48188
+ sourcesUnchanged: 0,
48189
+ sourcesContentChanged: 0,
48190
+ unknownInputs: unknown2.unknownInputs
48191
+ });
48192
+ }
47622
48193
  let rawBlocksResult = await loadRawBlocks2(paths.rawBlocks);
47623
48194
  let fingerprintResult = await loadSectionFingerprints(paths.sectionFingerprints);
47624
48195
  const knowledgeState = await readCompileKnowledgeState(options.ctxDir);
@@ -47638,7 +48209,7 @@ async function computeCompileChanges(options) {
47638
48209
  }
47639
48210
  let noPriorFingerprints = fingerprintResult.status === "unknown-input" && fingerprintResult.reason === "section-fingerprints-missing";
47640
48211
  const needsFingerprints = alignNodes.some((node3) => knowledgeState.state.sectionNodeSlugs.has(node3.slug));
47641
- if (options.writeState !== false && shouldSelfHealCache({ rawBlocksResult, fingerprintResult, needsFingerprints })) {
48212
+ if (options.writeState !== false && (cacheNeedsRebuild || shouldSelfHealCache({ rawBlocksResult, fingerprintResult, needsFingerprints }))) {
47642
48213
  try {
47643
48214
  await rebuildWorkspaceIncrementalCache({
47644
48215
  ctxDir: options.ctxDir,
@@ -48735,8 +49306,8 @@ var init_fullTextPaging = __esm(() => {
48735
49306
 
48736
49307
  // src/workflow/compileFinalizedContext.ts
48737
49308
  import { existsSync as existsSync44 } from "node:fs";
48738
- import { readFile as readFile55 } from "node:fs/promises";
48739
- import { join as join53 } from "node:path";
49309
+ import { readFile as readFile56 } from "node:fs/promises";
49310
+ import { join as join54 } from "node:path";
48740
49311
  function finalizedContextSourceIds(ownership, slug) {
48741
49312
  const evidenceSources = new Set;
48742
49313
  const contextSources = new Set;
@@ -48790,11 +49361,11 @@ function entryRole(block, slug) {
48790
49361
  return "unresolved";
48791
49362
  }
48792
49363
  async function rawBody(ctxDir, file, source2) {
48793
- const path9 = join53(ctxDir, file);
49364
+ const path9 = join54(ctxDir, file);
48794
49365
  if (!existsSync44(path9))
48795
49366
  return null;
48796
49367
  try {
48797
- const raw = (await readFile55(path9, "utf8")).replace(/\r\n/g, `
49368
+ const raw = (await readFile56(path9, "utf8")).replace(/\r\n/g, `
48798
49369
  `);
48799
49370
  if (source2?.type === "note") {
48800
49371
  const note = readNoteMetadata(raw);
@@ -49382,8 +49953,8 @@ var init_compileRuntimeHints = __esm(() => {
49382
49953
  });
49383
49954
 
49384
49955
  // src/workflow/compileContext.ts
49385
- import { readFile as readFile56 } from "node:fs/promises";
49386
- import { join as join54 } from "node:path";
49956
+ import { readFile as readFile57 } from "node:fs/promises";
49957
+ import { join as join55 } from "node:path";
49387
49958
  function compileAlignNodeFromKnowledge(node3) {
49388
49959
  return {
49389
49960
  slug: node3.id,
@@ -49586,7 +50157,7 @@ async function readChangedBlockSnippet(ctxDir, block, source2, aliasIndex2) {
49586
50157
  if (file.length === 0)
49587
50158
  return fallback;
49588
50159
  try {
49589
- const raw = await readFile56(join54(ctxDir, file), "utf8");
50160
+ const raw = await readFile57(join55(ctxDir, file), "utf8");
49590
50161
  let note;
49591
50162
  if (source2?.type === "note") {
49592
50163
  try {
@@ -51028,8 +51599,8 @@ var init_locatedNodeSources = __esm(() => {
51028
51599
 
51029
51600
  // src/reconcile/prepareCompileArchiveCandidates.ts
51030
51601
  import { existsSync as existsSync45 } from "node:fs";
51031
- import { readdir as readdir21, readFile as readFile57 } from "node:fs/promises";
51032
- import { join as join55 } from "node:path";
51602
+ import { readdir as readdir21, readFile as readFile58 } from "node:fs/promises";
51603
+ import { join as join56 } from "node:path";
51033
51604
  function sourceIdForSection3(nodeSources, sourceRef) {
51034
51605
  const index2 = sourceRefAliasIndex(sourceRef);
51035
51606
  if (index2 === null || index2 < 0)
@@ -51044,7 +51615,7 @@ async function readDropArchiveManifest2(path9) {
51044
51615
  if (!existsSync45(path9))
51045
51616
  return null;
51046
51617
  try {
51047
- const parsed = import_yaml29.default.parse(await readFile57(path9, "utf8"));
51618
+ const parsed = import_yaml29.default.parse(await readFile58(path9, "utf8"));
51048
51619
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && parsed.kind === "source-drop" && typeof parsed.source_id === "string") {
51049
51620
  return parsed;
51050
51621
  }
@@ -51086,31 +51657,31 @@ async function restoredArchiveCandidatesForNode(ctxDir, targetNode) {
51086
51657
  const restoredSourceIds = new Set(sourcesFile.sources.filter((source2) => source2.status === "active" && typeof source2.restored_at === "string").map((source2) => source2.id));
51087
51658
  if (restoredSourceIds.size === 0)
51088
51659
  return [];
51089
- const sourcesRoot = join55(ctxDir, "archive", "sources");
51660
+ const sourcesRoot = join56(ctxDir, "archive", "sources");
51090
51661
  if (!existsSync45(sourcesRoot))
51091
51662
  return [];
51092
51663
  const out2 = [];
51093
51664
  for (const sourceDir of await readdir21(sourcesRoot, { withFileTypes: true })) {
51094
51665
  if (!sourceDir.isDirectory())
51095
51666
  continue;
51096
- const sourceRoot = join55(sourcesRoot, sourceDir.name);
51667
+ const sourceRoot = join56(sourcesRoot, sourceDir.name);
51097
51668
  for (const archiveDir of await readdir21(sourceRoot, { withFileTypes: true })) {
51098
51669
  if (!archiveDir.isDirectory())
51099
51670
  continue;
51100
51671
  const archivePath = `archive/sources/${sourceDir.name}/${archiveDir.name}`;
51101
- const manifest = await readDropArchiveManifest2(join55(ctxDir, archivePath, "manifest.yaml"));
51672
+ const manifest = await readDropArchiveManifest2(join56(ctxDir, archivePath, "manifest.yaml"));
51102
51673
  if (!manifest || !restoredSourceIds.has(manifest.source_id))
51103
51674
  continue;
51104
51675
  const knowledgeBefore = Array.isArray(manifest.knowledge_before) ? manifest.knowledge_before : [];
51105
51676
  for (const relPath of knowledgeBefore) {
51106
51677
  if (!isSafeRelativePath(relPath))
51107
51678
  continue;
51108
- const absPath = join55(ctxDir, relPath);
51679
+ const absPath = join56(ctxDir, relPath);
51109
51680
  if (!existsSync45(absPath))
51110
51681
  continue;
51111
51682
  let parsed;
51112
51683
  try {
51113
- parsed = parseNodeMarkdown(await readFile57(absPath, "utf8"));
51684
+ parsed = parseNodeMarkdown(await readFile58(absPath, "utf8"));
51114
51685
  } catch {
51115
51686
  continue;
51116
51687
  }
@@ -51291,7 +51862,7 @@ function defaultDecisionForCompileDraftAction(input) {
51291
51862
 
51292
51863
  // src/mdrive/section.ts
51293
51864
  import { existsSync as existsSync46 } from "node:fs";
51294
- import { join as join56 } from "node:path";
51865
+ import { join as join57 } from "node:path";
51295
51866
  function locateNode(ctxDir, nodeSlug) {
51296
51867
  return readWorkspaceNodeFiles(ctxDir).then((files) => findLocatedNode(flattenWorkspaceNodes(files), nodeSlug));
51297
51868
  }
@@ -51519,7 +52090,7 @@ async function resolveSourceRefCandidates(input) {
51519
52090
  const latest = await getLatestSnapshot(input.ctxDir, sourceId);
51520
52091
  if (!latest?.file)
51521
52092
  continue;
51522
- const fullPath = join56(input.ctxDir, latest.file);
52093
+ const fullPath = join57(input.ctxDir, latest.file);
51523
52094
  if (existsSync46(fullPath))
51524
52095
  candidateFiles.push({ sourceId, path: fullPath });
51525
52096
  }
@@ -52623,9 +53194,11 @@ var init_agentHintRegistry = __esm(() => {
52623
53194
  "align-entity-tag-term-mixed-with-a-b",
52624
53195
  "align-finalize-node-without-citation-evidence",
52625
53196
  "align-finalize-patch-invalid",
53197
+ "align-block-preview-truncated",
52626
53198
  "align-ownership-patch-base-digest-stale",
52627
53199
  "align-ownership-patch-invalid",
52628
53200
  "align-segments-required",
53201
+ "align-segments-source-mapping",
52629
53202
  "align-scope-name-entity-warning",
52630
53203
  "align-structure-decision-invalid",
52631
53204
  "align-workflow-review-pending",
@@ -53123,14 +53696,14 @@ var init_agentHints = __esm(() => {
53123
53696
  // src/workflow/outputArchive.ts
53124
53697
  import { existsSync as existsSync47 } from "node:fs";
53125
53698
  import { copyFile as copyFile3, mkdir as mkdir24, readdir as readdir22, realpath as realpath2, rename as rename5, unlink as unlink4 } from "node:fs/promises";
53126
- import { dirname as dirname20, isAbsolute as isAbsolute6, join as join57, relative as relative13, resolve as resolve12 } from "node:path";
53699
+ import { dirname as dirname21, isAbsolute as isAbsolute6, join as join58, relative as relative14, resolve as resolve13 } from "node:path";
53127
53700
  function archiveStamp(now) {
53128
53701
  return now.toISOString().replace(/[:.]/g, "-");
53129
53702
  }
53130
53703
  async function uniqueArchiveDir(ctxDir, category, now) {
53131
- const archiveRoot = join57(ctxDir, "output", "archive");
53704
+ const archiveRoot = join58(ctxDir, "output", "archive");
53132
53705
  await mkdir24(archiveRoot, { recursive: true });
53133
- const base = join57(archiveRoot, `${archiveStamp(now)}-${category}`);
53706
+ const base = join58(archiveRoot, `${archiveStamp(now)}-${category}`);
53134
53707
  if (!existsSync47(base))
53135
53708
  return base;
53136
53709
  for (let index2 = 2;; index2 += 1) {
@@ -53147,7 +53720,7 @@ function normalizeOutputRelPath(name) {
53147
53720
  return normalized;
53148
53721
  }
53149
53722
  async function listOutputFiles(outputDir, relDir = "") {
53150
- const dir = join57(outputDir, relDir);
53723
+ const dir = join58(outputDir, relDir);
53151
53724
  const entries = await readdir22(dir, { withFileTypes: true }).catch(() => []);
53152
53725
  const out2 = [];
53153
53726
  for (const entry of entries) {
@@ -53172,7 +53745,7 @@ function patternMatches(pattern, relPath) {
53172
53745
  async function listMatchingOutputFiles(ctxDir, patterns) {
53173
53746
  if (patterns.length === 0)
53174
53747
  return [];
53175
- const outputDir = join57(ctxDir, "output");
53748
+ const outputDir = join58(ctxDir, "output");
53176
53749
  return (await listOutputFiles(outputDir)).filter((relPath) => patterns.some((pattern) => patternMatches(pattern, relPath)));
53177
53750
  }
53178
53751
  function uniqueNames(names) {
@@ -53180,7 +53753,7 @@ function uniqueNames(names) {
53180
53753
  }
53181
53754
  async function uniqueExternalCopies(ctxDir, copies) {
53182
53755
  const out2 = new Map;
53183
- const realCtxDir = await realpath2(ctxDir).catch(() => resolve12(ctxDir));
53756
+ const realCtxDir = await realpath2(ctxDir).catch(() => resolve13(ctxDir));
53184
53757
  for (const copy of copies) {
53185
53758
  const to = normalizeOutputRelPath(copy.to);
53186
53759
  if (to.length === 0 || !existsSync47(copy.from))
@@ -53189,7 +53762,7 @@ async function uniqueExternalCopies(ctxDir, copies) {
53189
53762
  throw new Error(`archive copyExternal.from must be an absolute path: ${copy.from}`);
53190
53763
  }
53191
53764
  const realFrom = await realpath2(copy.from);
53192
- const rel = relative13(realCtxDir, realFrom);
53765
+ const rel = relative14(realCtxDir, realFrom);
53193
53766
  if (rel === "" || rel.startsWith("..") || isAbsolute6(rel)) {
53194
53767
  throw new Error(`archive copyExternal.from must stay inside the workspace: ${copy.from}`);
53195
53768
  }
@@ -53206,31 +53779,31 @@ async function safeMove(src, dest) {
53206
53779
  }
53207
53780
  }
53208
53781
  async function archiveOutputArtifacts(options) {
53209
- const outputDir = join57(options.ctxDir, "output");
53782
+ const outputDir = join58(options.ctxDir, "output");
53210
53783
  const move = uniqueNames([
53211
53784
  ...options.move ?? [],
53212
53785
  ...await listMatchingOutputFiles(options.ctxDir, options.movePatterns ?? [])
53213
- ]).filter((name) => existsSync47(join57(outputDir, name)));
53786
+ ]).filter((name) => existsSync47(join58(outputDir, name)));
53214
53787
  const copy = uniqueNames([
53215
53788
  ...options.copy ?? [],
53216
53789
  ...await listMatchingOutputFiles(options.ctxDir, options.copyPatterns ?? [])
53217
- ]).filter((name) => existsSync47(join57(outputDir, name)) && !move.includes(name));
53790
+ ]).filter((name) => existsSync47(join58(outputDir, name)) && !move.includes(name));
53218
53791
  const copyExternal = (await uniqueExternalCopies(options.ctxDir, options.copyExternal ?? [])).filter((entry) => !move.includes(entry.to) && !copy.includes(entry.to));
53219
53792
  if (move.length === 0 && copy.length === 0 && copyExternal.length === 0)
53220
53793
  return null;
53221
53794
  const archiveDir = await uniqueArchiveDir(options.ctxDir, options.category, options.now ?? new Date);
53222
53795
  await mkdir24(archiveDir, { recursive: true });
53223
53796
  for (const name of copy) {
53224
- await mkdir24(dirname20(join57(archiveDir, name)), { recursive: true });
53225
- await copyFile3(join57(outputDir, name), join57(archiveDir, name));
53797
+ await mkdir24(dirname21(join58(archiveDir, name)), { recursive: true });
53798
+ await copyFile3(join58(outputDir, name), join58(archiveDir, name));
53226
53799
  }
53227
53800
  for (const entry of copyExternal) {
53228
- await mkdir24(dirname20(join57(archiveDir, entry.to)), { recursive: true });
53229
- await copyFile3(entry.from, join57(archiveDir, entry.to));
53801
+ await mkdir24(dirname21(join58(archiveDir, entry.to)), { recursive: true });
53802
+ await copyFile3(entry.from, join58(archiveDir, entry.to));
53230
53803
  }
53231
53804
  for (const name of move) {
53232
- await mkdir24(dirname20(join57(archiveDir, name)), { recursive: true });
53233
- await safeMove(join57(outputDir, name), join57(archiveDir, name));
53805
+ await mkdir24(dirname21(join58(archiveDir, name)), { recursive: true });
53806
+ await safeMove(join58(outputDir, name), join58(archiveDir, name));
53234
53807
  }
53235
53808
  return { archiveDir, moved: move, copied: [...copy, ...copyExternal.map((entry) => entry.to)] };
53236
53809
  }
@@ -53238,9 +53811,9 @@ var init_outputArchive = () => {};
53238
53811
 
53239
53812
  // src/workflow/compileCloseFinalizedNodes.ts
53240
53813
  import { existsSync as existsSync48 } from "node:fs";
53241
- import { join as join58 } from "node:path";
53814
+ import { join as join59 } from "node:path";
53242
53815
  function finalizedNodePath(ctxDir, node3) {
53243
- return join58(ctxDir, "knowledge", node3.type, `${node3.slug}.md`);
53816
+ return join59(ctxDir, "knowledge", node3.type, `${node3.slug}.md`);
53244
53817
  }
53245
53818
  function noWriteContextSources(node3) {
53246
53819
  return [...new Set([
@@ -53337,8 +53910,8 @@ var init_compileCloseFinalizedNodes = __esm(() => {
53337
53910
 
53338
53911
  // src/workflow/compileCloseDebt.ts
53339
53912
  import { existsSync as existsSync49 } from "node:fs";
53340
- import { readFile as readFile58, readdir as readdir23 } from "node:fs/promises";
53341
- import { join as join59 } from "node:path";
53913
+ import { readFile as readFile59, readdir as readdir23 } from "node:fs/promises";
53914
+ import { join as join60 } from "node:path";
53342
53915
  function coverageDebtItems(status) {
53343
53916
  return status.candidates.filter((candidate) => candidate.status === "unresolved" && candidate.coverage_type !== "context").map((candidate) => ({
53344
53917
  debt_type: "coverage",
@@ -53419,23 +53992,23 @@ async function orphanKnowledgeDebtItems(ctxDir) {
53419
53992
  return out2;
53420
53993
  }
53421
53994
  async function readWorkflowPayloadFiles(ctxDir, payloadBaseName) {
53422
- const root2 = join59(ctxDir, "output", "workflows");
53995
+ const root2 = join60(ctxDir, "output", "workflows");
53423
53996
  if (!existsSync49(root2))
53424
53997
  return [];
53425
53998
  const out2 = [];
53426
53999
  for (const workflowEntry of await readdir23(root2, { withFileTypes: true })) {
53427
54000
  if (!workflowEntry.isDirectory())
53428
54001
  continue;
53429
- const workflowDir = join59(root2, workflowEntry.name);
54002
+ const workflowDir = join60(root2, workflowEntry.name);
53430
54003
  for (const scopeEntry of await readdir23(workflowDir, { withFileTypes: true }).catch(() => [])) {
53431
54004
  if (!scopeEntry.isDirectory())
53432
54005
  continue;
53433
- const scopeDir = join59(workflowDir, scopeEntry.name);
54006
+ const scopeDir = join60(workflowDir, scopeEntry.name);
53434
54007
  for (const format of ["json", "yaml"]) {
53435
- const path9 = join59(scopeDir, `${payloadBaseName}.${format}`);
54008
+ const path9 = join60(scopeDir, `${payloadBaseName}.${format}`);
53436
54009
  if (!existsSync49(path9))
53437
54010
  continue;
53438
- const body2 = await readFile58(path9, "utf8");
54011
+ const body2 = await readFile59(path9, "utf8");
53439
54012
  out2.push(format === "json" ? JSON.parse(body2) : import_yaml30.default.parse(body2));
53440
54013
  }
53441
54014
  }
@@ -53460,7 +54033,7 @@ var init_compileCloseDebt = __esm(() => {
53460
54033
  });
53461
54034
 
53462
54035
  // src/workflow/compileCloseDraftScratch.ts
53463
- import { readFile as readFile59 } from "node:fs/promises";
54036
+ import { readFile as readFile60 } from "node:fs/promises";
53464
54037
  async function hasOnlyNoopCompileDraft(input) {
53465
54038
  if (input.draftPaths.length === 0)
53466
54039
  return false;
@@ -53474,7 +54047,7 @@ async function hasOnlyNoopDraftScratch(input) {
53474
54047
  const current = await mdriveNodeShow({ ctxDir: input.ctxDir, slug: input.node }).catch(() => null);
53475
54048
  const sections = new Map((current?.sections ?? []).map((section) => [section.id, section]));
53476
54049
  for (const draftPath of input.draftPaths) {
53477
- const draft = normalizeCompileDraftInput(import_yaml31.default.parse(await readFile59(draftPath, "utf8")));
54050
+ const draft = normalizeCompileDraftInput(import_yaml31.default.parse(await readFile60(draftPath, "utf8")));
53478
54051
  if (!isRecord21(draft) || !Array.isArray(draft.actions))
53479
54052
  return false;
53480
54053
  for (const action of draft.actions) {
@@ -53531,28 +54104,28 @@ var init_compileCloseDraftScratch = __esm(() => {
53531
54104
 
53532
54105
  // src/workflow/compileClose.ts
53533
54106
  import { existsSync as existsSync50 } from "node:fs";
53534
- import { readFile as readFile60, readdir as readdir24, rm as rm17 } from "node:fs/promises";
53535
- import { join as join60 } from "node:path";
54107
+ import { readFile as readFile61, readdir as readdir24, rm as rm17 } from "node:fs/promises";
54108
+ import { join as join61 } from "node:path";
53536
54109
  function knowledgePath(ctxDir, name) {
53537
- return join60(ctxDir, "knowledge", name);
54110
+ return join61(ctxDir, "knowledge", name);
53538
54111
  }
53539
54112
  async function snapshotFile(path9) {
53540
- return existsSync50(path9) ? { path: path9, content: await readFile60(path9, "utf8") } : { path: path9 };
54113
+ return existsSync50(path9) ? { path: path9, content: await readFile61(path9, "utf8") } : { path: path9 };
53541
54114
  }
53542
54115
  async function snapshotCompileCloseFiles(ctxDir) {
53543
54116
  const paths = new Set([
53544
54117
  knowledgePath(ctxDir, "_index.md"),
53545
54118
  knowledgePath(ctxDir, "changelog.md"),
53546
- join60(ctxDir, "knowledge", "_edges.yaml")
54119
+ join61(ctxDir, "knowledge", "_edges.yaml")
53547
54120
  ]);
53548
54121
  for (const type of NODE_TYPES) {
53549
- const dir = join60(ctxDir, "knowledge", type);
54122
+ const dir = join61(ctxDir, "knowledge", type);
53550
54123
  if (!existsSync50(dir))
53551
54124
  continue;
53552
54125
  const entries = await readdir24(dir, { withFileTypes: true });
53553
54126
  for (const entry of entries) {
53554
54127
  if (entry.isFile() && entry.name.endsWith(".md")) {
53555
- paths.add(join60(dir, entry.name));
54128
+ paths.add(join61(dir, entry.name));
53556
54129
  }
53557
54130
  }
53558
54131
  }
@@ -53573,7 +54146,7 @@ async function readLastCompileAt2(ctxDir) {
53573
54146
  const changelog = knowledgePath(ctxDir, "changelog.md");
53574
54147
  if (!existsSync50(changelog))
53575
54148
  return null;
53576
- const lines = (await readFile60(changelog, "utf8")).split(`
54149
+ const lines = (await readFile61(changelog, "utf8")).split(`
53577
54150
  `).filter((line) => line.includes("[compile]"));
53578
54151
  for (let index2 = lines.length - 1;index2 >= 0; index2 -= 1) {
53579
54152
  const stamp = /@\s*([0-9]{4}-[0-9]{2}-[0-9]{2}(?:T[0-9:.+-]+Z?)?)/u.exec(lines[index2] ?? "")?.[1];
@@ -53617,7 +54190,7 @@ async function compileDraftScratchEntries(ctxDir) {
53617
54190
  continue;
53618
54191
  const match = /^compile\.(.+)\.draft\.(?:json|ya?ml)$/u.exec(entry.name);
53619
54192
  if (match?.[1])
53620
- out2.push({ slug: match[1], path: join60(outputDir, entry.name) });
54193
+ out2.push({ slug: match[1], path: join61(outputDir, entry.name) });
53621
54194
  }
53622
54195
  return out2;
53623
54196
  }
@@ -53932,6 +54505,7 @@ async function compileCloseInner(options) {
53932
54505
  pendingCompileNodes: beforeCache.state.pending_compile.nodes ?? []
53933
54506
  });
53934
54507
  await assertNoUnownedSourceOwnership(options.ctxDir);
54508
+ await assertNoUnclosedRenderAutoBlocks(options.ctxDir);
53935
54509
  const coverageStatus = await readAndPruneCoverageWorkspaceStatus(options.ctxDir);
53936
54510
  const retractedNodes = await retractRemovedFinalizedNodes(options.ctxDir);
53937
54511
  const createdContainerDomains = await ensureFinalizedContainerDomains(options.ctxDir, now);
@@ -54055,6 +54629,7 @@ var init_compileClose = __esm(() => {
54055
54629
  init_temporal();
54056
54630
  init_workspaceLayout();
54057
54631
  init_verify();
54632
+ init_verifyRenderBlocks();
54058
54633
  init_ledger();
54059
54634
  init_semanticLedgerGc();
54060
54635
  init_workspace();
@@ -56008,7 +56583,7 @@ var init_prepareRefresh = __esm(() => {
56008
56583
  });
56009
56584
 
56010
56585
  // src/reconcile/prepare.ts
56011
- import { readFile as readFile61 } from "node:fs/promises";
56586
+ import { readFile as readFile62 } from "node:fs/promises";
56012
56587
  async function ensureRetrievalIndex2(input) {
56013
56588
  const read = await readRetrievalIndex(input);
56014
56589
  if (read.status === "ready" && read.index)
@@ -56042,7 +56617,7 @@ async function prepareRetrieval(input) {
56042
56617
  async function readJsonOrYaml(path9) {
56043
56618
  let raw;
56044
56619
  try {
56045
- raw = await readFile61(path9, "utf8");
56620
+ raw = await readFile62(path9, "utf8");
56046
56621
  } catch (error) {
56047
56622
  if (error.code === "ENOENT") {
56048
56623
  throw new ContextError(ExitCode.UserError, `file not found: ${path9}`, {
@@ -56163,7 +56738,7 @@ var init_prepare = __esm(() => {
56163
56738
 
56164
56739
  // src/cli.ts
56165
56740
  import { existsSync as existsSync54, readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
56166
- import { dirname as dirname27, join as join69 } from "node:path";
56741
+ import { dirname as dirname28, join as join70 } from "node:path";
56167
56742
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "node:url";
56168
56743
 
56169
56744
  // ../../node_modules/.bun/commander@11.1.0/node_modules/commander/esm.mjs
@@ -57080,6 +57655,7 @@ function verifyIssueAgentView(issue) {
57080
57655
  location: semanticIssueLocation(issue),
57081
57656
  ...issue.slug !== undefined ? { node_slug: issue.slug } : {},
57082
57657
  ...issue.sectionId !== undefined ? { section_id: issue.sectionId } : {},
57658
+ ...issue.line !== undefined ? { line: issue.line } : {},
57083
57659
  ...issue.next_action !== undefined ? { next_action: issue.next_action } : {}
57084
57660
  };
57085
57661
  }
@@ -62132,7 +62708,7 @@ init_exitCode();
62132
62708
  var import_yaml25 = __toESM(require_dist(), 1);
62133
62709
  import { createInterface as createInterface2 } from "node:readline/promises";
62134
62710
  import { mkdir as mkdir21, writeFile as writeFile16 } from "node:fs/promises";
62135
- import { dirname as dirname15, resolve as resolve10 } from "node:path";
62711
+ import { dirname as dirname16, resolve as resolve11 } from "node:path";
62136
62712
 
62137
62713
  // src/workflow/dropApply.ts
62138
62714
  init_atomicWrite();
@@ -62143,8 +62719,8 @@ init_graphEdges();
62143
62719
  init_verify();
62144
62720
  init_ledger();
62145
62721
  import { existsSync as existsSync32 } from "node:fs";
62146
- import { cp as cp2, mkdir as mkdir18, readFile as readFile41, rm as rm11 } from "node:fs/promises";
62147
- import { dirname as dirname13, join as join39, relative as relative12 } from "node:path";
62722
+ import { cp as cp2, mkdir as mkdir18, readFile as readFile42, rm as rm11 } from "node:fs/promises";
62723
+ import { dirname as dirname14, join as join40, relative as relative12 } from "node:path";
62148
62724
 
62149
62725
  // src/reconcile/sectionValidation.ts
62150
62726
  init_sectionMount();
@@ -62226,7 +62802,7 @@ init_workspace();
62226
62802
  init_exitCode();
62227
62803
  var import_yaml21 = __toESM(require_dist(), 1);
62228
62804
  import { writeFile as writeFile14 } from "node:fs/promises";
62229
- import { join as join37 } from "node:path";
62805
+ import { join as join38 } from "node:path";
62230
62806
  function nowIso4(now = new Date) {
62231
62807
  return now.toISOString();
62232
62808
  }
@@ -62295,7 +62871,7 @@ async function markSourceDropped(input) {
62295
62871
  ...input.archivePath !== undefined ? { archive_path: input.archivePath } : {}
62296
62872
  } : s)
62297
62873
  };
62298
- await writeFile14(join37(ctxDir, "raw", "_sources.yaml"), import_yaml21.default.stringify(next), "utf8");
62874
+ await writeFile14(join38(ctxDir, "raw", "_sources.yaml"), import_yaml21.default.stringify(next), "utf8");
62299
62875
  await mdriveWorkspaceAppendChangelog({
62300
62876
  ctxDir,
62301
62877
  entry: `[drop] source=${sourceId} reason=${reason} affected-nodes=${affectedNodes} affected-sections=${affectedSections} @ ${droppedAt}`
@@ -62308,7 +62884,7 @@ init_sectionFingerprints();
62308
62884
  init_errors();
62309
62885
  init_sourceRef();
62310
62886
  var import_yaml22 = __toESM(require_dist(), 1);
62311
- import { isAbsolute as isAbsolute5, join as join38, relative as relative11, resolve as resolve9 } from "node:path";
62887
+ import { isAbsolute as isAbsolute5, join as join39, relative as relative11, resolve as resolve10 } from "node:path";
62312
62888
  init_sources();
62313
62889
  init_shared();
62314
62890
  init_graphEdges();
@@ -62360,16 +62936,16 @@ function normalizeSnapshotPath(input) {
62360
62936
  const cwd = input.cwd ?? process.cwd();
62361
62937
  const candidates = new Set;
62362
62938
  const addAbs = (path9) => {
62363
- const rel = toPosixPath2(relative11(ctxDir, path9));
62939
+ const rel = toPosixPath3(relative11(ctxDir, path9));
62364
62940
  if (!rel.startsWith("../") && rel !== ".." && !isAbsolute5(rel)) {
62365
62941
  candidates.add(rel);
62366
62942
  }
62367
62943
  };
62368
- addAbs(isAbsolute5(target) ? target : resolve9(cwd, target));
62944
+ addAbs(isAbsolute5(target) ? target : resolve10(cwd, target));
62369
62945
  if (!isAbsolute5(target)) {
62370
- addAbs(resolve9(ctxDir, target));
62946
+ addAbs(resolve10(ctxDir, target));
62371
62947
  if (target.startsWith(".context/")) {
62372
- addAbs(resolve9(join38(ctxDir, ".."), target));
62948
+ addAbs(resolve10(join39(ctxDir, ".."), target));
62373
62949
  }
62374
62950
  }
62375
62951
  for (const rel of candidates) {
@@ -62948,7 +63524,7 @@ function buildDropScopeByItem(plan, decisions) {
62948
63524
  async function snapshotTextFile(path9) {
62949
63525
  if (!existsSync32(path9))
62950
63526
  return { path: path9, existed: false };
62951
- return { path: path9, existed: true, content: await readFile41(path9, "utf8") };
63527
+ return { path: path9, existed: true, content: await readFile42(path9, "utf8") };
62952
63528
  }
62953
63529
  async function restoreTextFile(snapshot) {
62954
63530
  if (!snapshot.existed) {
@@ -62959,21 +63535,21 @@ async function restoreTextFile(snapshot) {
62959
63535
  }
62960
63536
  async function restoreRawEntries(input) {
62961
63537
  for (const entry of input.entries) {
62962
- const fromAbs = join39(input.ctxDir, entry.to);
62963
- const toAbs = join39(input.ctxDir, entry.from);
63538
+ const fromAbs = join40(input.ctxDir, entry.to);
63539
+ const toAbs = join40(input.ctxDir, entry.from);
62964
63540
  if (!existsSync32(fromAbs))
62965
63541
  continue;
62966
- await mkdir18(dirname13(toAbs), { recursive: true });
63542
+ await mkdir18(dirname14(toAbs), { recursive: true });
62967
63543
  await cp2(fromAbs, toAbs, { recursive: entry.kind === "dir", force: true });
62968
63544
  }
62969
63545
  }
62970
63546
  async function restoreKnowledgeBefore(input) {
62971
63547
  for (const filePath of input.touchedKnowledgeFiles) {
62972
63548
  const rel = relative12(input.ctxDir, filePath);
62973
- const archivedPath = join39(input.ctxDir, input.archivePath, "knowledge", "before", rel);
63549
+ const archivedPath = join40(input.ctxDir, input.archivePath, "knowledge", "before", rel);
62974
63550
  if (!existsSync32(archivedPath))
62975
63551
  continue;
62976
- await mkdir18(dirname13(filePath), { recursive: true });
63552
+ await mkdir18(dirname14(filePath), { recursive: true });
62977
63553
  await cp2(archivedPath, filePath, { force: true });
62978
63554
  }
62979
63555
  }
@@ -62987,11 +63563,11 @@ async function restoreDropApplyFailure(input) {
62987
63563
  await restoreTextFile(input.sourcesBefore);
62988
63564
  await restoreTextFile(input.changelogBefore);
62989
63565
  await restoreTextFile(input.ledgerBefore);
62990
- await rm11(join39(input.ctxDir, input.archivePath), { recursive: true, force: true });
63566
+ await rm11(join40(input.ctxDir, input.archivePath), { recursive: true, force: true });
62991
63567
  }
62992
63568
  async function commitDropPlanWrites(input) {
62993
- const sourcesBefore = await snapshotTextFile(join39(input.ctxDir, "raw", "_sources.yaml"));
62994
- const changelogBefore = await snapshotTextFile(join39(input.ctxDir, "knowledge", "changelog.md"));
63569
+ const sourcesBefore = await snapshotTextFile(join40(input.ctxDir, "raw", "_sources.yaml"));
63570
+ const changelogBefore = await snapshotTextFile(join40(input.ctxDir, "knowledge", "changelog.md"));
62995
63571
  const ledgerBefore = await snapshotTextFile(semanticLedgerPath2(input.ctxDir));
62996
63572
  const plannedArchivePath = sourceArchiveRelPath(input.source.id, input.droppedAt);
62997
63573
  let archive;
@@ -63181,7 +63757,7 @@ async function applyDropPlan(input) {
63181
63757
  removeDroppedContextSources({ files, sourceId: plan.source_id, deletedFiles, touchedFiles });
63182
63758
  markGraphEdgeNeighborsForArchivedNodes({ plan, nodes, archivedSlugs, deletedFiles, touchedFiles });
63183
63759
  const targetAfterByItem = collectDropTargetSnapshots(nodes, semanticDecisions);
63184
- const graphEdgesPath2 = join39(input.ctxDir, "knowledge", "_edges.yaml");
63760
+ const graphEdgesPath2 = join40(input.ctxDir, "knowledge", "_edges.yaml");
63185
63761
  const touchedKnowledgeFiles = new Set([...touchedFiles, ...deletedFiles]);
63186
63762
  if (archivedSlugs.size > 0 && existsSync32(graphEdgesPath2)) {
63187
63763
  touchedKnowledgeFiles.add(graphEdgesPath2);
@@ -63219,8 +63795,8 @@ init_verify();
63219
63795
  init_workspace();
63220
63796
  init_exitCode();
63221
63797
  import { existsSync as existsSync34 } from "node:fs";
63222
- import { cp as cp3, mkdir as mkdir19, readFile as readFile45, readdir as readdir15, rm as rm12 } from "node:fs/promises";
63223
- import { dirname as dirname14, join as join41 } from "node:path";
63798
+ import { cp as cp3, mkdir as mkdir19, readFile as readFile46, readdir as readdir15, rm as rm12 } from "node:fs/promises";
63799
+ import { dirname as dirname15, join as join42 } from "node:path";
63224
63800
 
63225
63801
  // src/code/codeProjectionFreshness.ts
63226
63802
  init_errors();
@@ -63233,11 +63809,13 @@ init_verify();
63233
63809
  init_exitCode();
63234
63810
 
63235
63811
  // src/code/codeProjection.ts
63812
+ init_config();
63236
63813
  init_sourceRef();
63237
63814
  init_externalDeps();
63238
63815
  init_graphEdges();
63239
63816
  init_shared();
63240
- import { readFile as readFile44 } from "node:fs/promises";
63817
+ import { readFile as readFile45 } from "node:fs/promises";
63818
+ import { relative as relative13 } from "node:path";
63241
63819
 
63242
63820
  // src/code/codeProjectionBuild.ts
63243
63821
  init_cliFeedback();
@@ -63254,8 +63832,8 @@ init_sources();
63254
63832
  init_exitCode();
63255
63833
  var import_yaml23 = __toESM(require_dist(), 1);
63256
63834
  import { existsSync as existsSync33 } from "node:fs";
63257
- import { readFile as readFile42 } from "node:fs/promises";
63258
- import { join as join40 } from "node:path";
63835
+ import { readFile as readFile43 } from "node:fs/promises";
63836
+ import { join as join41 } from "node:path";
63259
63837
 
63260
63838
  // src/code/codeSnapshotVersioning.ts
63261
63839
  init_src();
@@ -63466,15 +64044,15 @@ function snapshotSemanticId3(snapshot) {
63466
64044
  }
63467
64045
  function snapshotDir3(ctxDir, source2, snapshot) {
63468
64046
  if (snapshot.dir)
63469
- return join40(ctxDir, snapshot.dir);
64047
+ return join41(ctxDir, snapshot.dir);
63470
64048
  const slug = sourceSlugFor2(source2, snapshot);
63471
64049
  const id2 = snapshot.snapshot_id;
63472
64050
  if (!slug || !id2)
63473
64051
  return null;
63474
- return join40(ctxDir, "raw", "aspect", "code", slug, id2);
64052
+ return join41(ctxDir, "raw", "aspect", "code", slug, id2);
63475
64053
  }
63476
64054
  async function readStructuredFile(path9) {
63477
- const text5 = await readFile42(path9, "utf8");
64055
+ const text5 = await readFile43(path9, "utf8");
63478
64056
  const parsed = import_yaml23.default.parse(text5);
63479
64057
  return isRecord12(parsed) ? parsed : {};
63480
64058
  }
@@ -63484,7 +64062,7 @@ async function readStructuredFileIfExists(path9) {
63484
64062
  return readStructuredFile(path9);
63485
64063
  }
63486
64064
  async function readJsonl2(path9) {
63487
- const text5 = await readFile42(path9, "utf8");
64065
+ const text5 = await readFile43(path9, "utf8");
63488
64066
  const rows = [];
63489
64067
  for (const line of text5.split(/\r?\n/u)) {
63490
64068
  if (line.trim().length === 0)
@@ -63510,15 +64088,15 @@ async function loadOneSnapshot(ctxDir, source2, snapshot) {
63510
64088
  source_id: source2.id
63511
64089
  });
63512
64090
  }
63513
- const files = Object.fromEntries(BETA9_CODE_SNAPSHOT_FILES.flatMap((fileName) => existsSync33(join40(dir, fileName)) ? [[fileName, true]] : []));
63514
- const sourceFile = await readStructuredFileIfExists(join40(dir, "source.yaml"));
63515
- const manifest = await readStructuredFileIfExists(join40(dir, "manifest.json"));
63516
- const meta = await readStructuredFileIfExists(join40(dir, "_meta.yaml"));
63517
- const digests = await readJsonlIfExists(join40(dir, "digests.jsonl"));
63518
- const sourceFiles = await readJsonlIfExists(join40(dir, "source-files.jsonl"));
63519
- const packages = await readJsonlIfExists(join40(dir, "packages.jsonl"));
63520
- const symbols = await readJsonlIfExists(join40(dir, "symbols.jsonl"));
63521
- const edges = await readJsonlIfExists(join40(dir, "edges.jsonl"));
64091
+ const files = Object.fromEntries(BETA9_CODE_SNAPSHOT_FILES.flatMap((fileName) => existsSync33(join41(dir, fileName)) ? [[fileName, true]] : []));
64092
+ const sourceFile = await readStructuredFileIfExists(join41(dir, "source.yaml"));
64093
+ const manifest = await readStructuredFileIfExists(join41(dir, "manifest.json"));
64094
+ const meta = await readStructuredFileIfExists(join41(dir, "_meta.yaml"));
64095
+ const digests = await readJsonlIfExists(join41(dir, "digests.jsonl"));
64096
+ const sourceFiles = await readJsonlIfExists(join41(dir, "source-files.jsonl"));
64097
+ const packages = await readJsonlIfExists(join41(dir, "packages.jsonl"));
64098
+ const symbols = await readJsonlIfExists(join41(dir, "symbols.jsonl"));
64099
+ const edges = await readJsonlIfExists(join41(dir, "edges.jsonl"));
63522
64100
  validateCodeSnapshotSchema({
63523
64101
  files,
63524
64102
  ...sourceFile !== undefined ? { source: sourceFile } : {},
@@ -63621,7 +64199,7 @@ init_sourceRef();
63621
64199
  init_sources();
63622
64200
  init_knowledge();
63623
64201
  init_exitCode();
63624
- import { readFile as readFile43 } from "node:fs/promises";
64202
+ import { readFile as readFile44 } from "node:fs/promises";
63625
64203
  var SECTION_ORDER = new Map(SECTION_RENDER_ORDER.map((kind, index2) => [kind, index2]));
63626
64204
  function visibleVersion(value) {
63627
64205
  return value === undefined ? undefined : `v${String(value).replace(/^v/u, "")}`;
@@ -63784,7 +64362,7 @@ function assertNoGeneratedDrift(draft, sections) {
63784
64362
  }
63785
64363
  async function renderedNodePlan(draft) {
63786
64364
  const sections = finalizeNodeSections(draft);
63787
- const current = draft.existingPath ? await readFile43(draft.existingPath, "utf8").catch(() => "") : "";
64365
+ const current = draft.existingPath ? await readFile44(draft.existingPath, "utf8").catch(() => "") : "";
63788
64366
  assertNoGeneratedDrift(draft, sections);
63789
64367
  const input = {
63790
64368
  node: draft.node,
@@ -64285,21 +64863,120 @@ async function buildCodeProjectionDraftState(ctxDir, sourceSlug3) {
64285
64863
  async function buildCodeProjectionPlan(ctxDir, sourceSlug3) {
64286
64864
  return renderCodeProjectionPlanFromState(await buildCodeProjectionDraftState(ctxDir, sourceSlug3));
64287
64865
  }
64866
+ function parsedRootFromRenderInput(input, headingLevel = 1) {
64867
+ return {
64868
+ node: input.node,
64869
+ headingLevel,
64870
+ body: input.body ?? "",
64871
+ sections: [...input.sections],
64872
+ children: (input.children ?? []).map((child) => parsedRootFromRenderInput(child, headingLevel + 1)),
64873
+ containsEdges: [],
64874
+ containsList: []
64875
+ };
64876
+ }
64877
+ function projectedWorkspaceFileFromInput(ctxDir, filePath, input) {
64878
+ const relativePath = toPosixPath3(relative13(knowledgeRoot3(ctxDir), filePath));
64879
+ const root2 = parsedRootFromRenderInput(input);
64880
+ const expectedSlug = slugFromKnowledgeRelativePath(relativePath);
64881
+ if (expectedSlug !== null && expectedSlug.includes("/") && root2.node.id === expectedSlug) {
64882
+ const parentSlug = expectedSlug.split("/")[0];
64883
+ root2.containsEdges = [{
64884
+ type: "contains",
64885
+ from: parentSlug,
64886
+ to: expectedSlug,
64887
+ grounding: "directory",
64888
+ ...root2.node.valid_from !== undefined ? { valid_from: root2.node.valid_from } : {},
64889
+ ...root2.node.valid_until !== undefined ? { valid_until: root2.node.valid_until } : {}
64890
+ }];
64891
+ }
64892
+ return { filePath, relativePath, root: root2 };
64893
+ }
64894
+ async function projectedWorkspaceFiles(ctxDir, rendered) {
64895
+ const byPath = new Map((await readWorkspaceNodeFiles(ctxDir)).map((file) => [file.filePath, file]));
64896
+ for (const item of rendered.values()) {
64897
+ const filePath = knowledgeFilePath(ctxDir, item.input.node.type, item.input.node.id);
64898
+ byPath.set(filePath, projectedWorkspaceFileFromInput(ctxDir, filePath, item.input));
64899
+ }
64900
+ return [...byPath.values()];
64901
+ }
64902
+ async function projectedRenderContext(plan) {
64903
+ const [files, currentEdges, currentExternals, renderConfig] = await Promise.all([
64904
+ projectedWorkspaceFiles(plan.state.ctxDir, plan.rendered),
64905
+ loadKnowledgeGraphEdges(plan.state.ctxDir),
64906
+ loadExternalDeps(plan.state.ctxDir),
64907
+ loadRenderConfig(plan.state.ctxDir)
64908
+ ]);
64909
+ const codeGroundings = new Set(plan.source_slugs.map((slug) => `code:${slug}`));
64910
+ const graphEdges = [
64911
+ ...currentEdges.filter((edge2) => !codeGroundings.has(edge2.grounding ?? "")),
64912
+ ...plan.state.edges
64913
+ ];
64914
+ const externalOwners = new Set;
64915
+ for (const node3 of plan.nodes)
64916
+ externalOwners.add(ownerSlugForPackageScopedPath(node3.slug));
64917
+ for (const dep of plan.state.externals)
64918
+ externalOwners.add(ownerSlugForPackageScopedPath(dep.from));
64919
+ const externalDeps = [
64920
+ ...currentExternals.filter((dep) => !externalOwners.has(ownerSlugForPackageScopedPath(dep.from))),
64921
+ ...plan.state.externals
64922
+ ];
64923
+ return buildRenderWorkspaceContextFromData({
64924
+ files,
64925
+ graphEdges,
64926
+ externalDeps,
64927
+ renderOptions: {
64928
+ nodeLinkMode: renderConfig.obsidian_mode ? "wiki" : "md",
64929
+ usedBy: renderConfig.used_by
64930
+ }
64931
+ });
64932
+ }
64288
64933
  async function renderCodeProjectionPlanFromState(state) {
64289
64934
  const rendered = new Map;
64290
- const nodes = [];
64935
+ const pending = [];
64291
64936
  for (const draft of [...state.nodeDrafts.values()].sort((left, right) => left.node.id.localeCompare(right.node.id))) {
64292
64937
  const result = await renderedNodePlan(draft);
64293
- rendered.set(draft.node.id, { input: result.input, rendered: result.rendered, diff: result.diff });
64294
- const codeSections = result.input.sections.filter((section) => parseCodeSourceRef(section.source_ref) !== null);
64295
- nodes.push({
64938
+ const filePath = knowledgeFilePath(state.ctxDir, result.input.node.type, result.input.node.id);
64939
+ pending.push({
64296
64940
  slug: draft.node.id,
64297
- title: draft.node.title,
64298
- section_count: result.input.sections.length,
64941
+ input: result.input,
64942
+ current: await readFile45(filePath, "utf8").catch(() => ""),
64943
+ filePath
64944
+ });
64945
+ rendered.set(draft.node.id, { input: result.input, rendered: result.rendered });
64946
+ }
64947
+ const initialPlan = {
64948
+ action: "code-align",
64949
+ sources: [...new Set([...state.nodeDrafts.values()].map((draft) => draft.source.source.id))].sort(),
64950
+ source_slugs: [...new Set([...state.nodeDrafts.values()].map((draft) => draft.source.sourceSlug))].sort(),
64951
+ nodes: pending.map((item) => ({
64952
+ slug: item.input.node.id,
64953
+ title: item.input.node.title,
64954
+ section_count: item.input.sections.length,
64955
+ code_section_count: item.input.sections.filter((section) => parseCodeSourceRef(section.source_ref) !== null).length,
64956
+ diff: "unchanged",
64957
+ ...item.input.node.valid_from !== undefined ? { valid_from: item.input.node.valid_from } : {},
64958
+ ...item.input.node.valid_until !== undefined ? { valid_until: item.input.node.valid_until } : {}
64959
+ })),
64960
+ edge_count: state.edges.length,
64961
+ external_count: state.externals.length,
64962
+ warnings: state.warnings,
64963
+ rendered,
64964
+ state
64965
+ };
64966
+ const renderContext = await projectedRenderContext(initialPlan);
64967
+ const nodes = [];
64968
+ for (const item of pending) {
64969
+ const renderedText = renderNodeInputForFile(item.filePath, item.input, renderContext);
64970
+ rendered.set(item.slug, { input: item.input, rendered: renderedText });
64971
+ const codeSections = item.input.sections.filter((section) => parseCodeSourceRef(section.source_ref) !== null);
64972
+ nodes.push({
64973
+ slug: item.input.node.id,
64974
+ title: item.input.node.title,
64975
+ section_count: item.input.sections.length,
64299
64976
  code_section_count: codeSections.length,
64300
- diff: result.diff,
64301
- ...draft.node.valid_from !== undefined ? { valid_from: draft.node.valid_from } : {},
64302
- ...draft.node.valid_until !== undefined ? { valid_until: draft.node.valid_until } : {}
64977
+ diff: item.current.length === 0 ? "create" : item.current === renderedText ? "unchanged" : "update",
64978
+ ...item.input.node.valid_from !== undefined ? { valid_from: item.input.node.valid_from } : {},
64979
+ ...item.input.node.valid_until !== undefined ? { valid_until: item.input.node.valid_until } : {}
64303
64980
  });
64304
64981
  }
64305
64982
  return {
@@ -64322,7 +64999,7 @@ async function writeProjectedNodes(plan, ctxDir) {
64322
64999
  for (const [slug, rendered] of plan.rendered) {
64323
65000
  const filePath = knowledgeFilePath(ctxDir, rendered.input.node.type, slug);
64324
65001
  await ensureKnowledgeTypeDir(ctxDir, rendered.input.node.type);
64325
- const current = await readFile44(filePath, "utf8").catch(() => "");
65002
+ const current = await readFile45(filePath, "utf8").catch(() => "");
64326
65003
  if (current === rendered.rendered)
64327
65004
  continue;
64328
65005
  await atomicWriteText(filePath, rendered.rendered);
@@ -64789,35 +65466,35 @@ function applyCodeDropToNode(input) {
64789
65466
  async function snapshotTextFile2(path9) {
64790
65467
  if (!existsSync34(path9))
64791
65468
  return { path: path9, existed: false };
64792
- return { path: path9, existed: true, content: await readFile45(path9, "utf8") };
65469
+ return { path: path9, existed: true, content: await readFile46(path9, "utf8") };
64793
65470
  }
64794
65471
  async function restoreTextFile2(snapshot) {
64795
65472
  if (!snapshot.existed) {
64796
65473
  await rm12(snapshot.path, { force: true });
64797
65474
  return;
64798
65475
  }
64799
- await mkdir19(dirname14(snapshot.path), { recursive: true });
65476
+ await mkdir19(dirname15(snapshot.path), { recursive: true });
64800
65477
  await atomicWriteFile(snapshot.path, snapshot.content ?? "");
64801
65478
  }
64802
65479
  async function restoreRawEntries2(ctxDir, entries) {
64803
65480
  for (const entry of entries) {
64804
- const fromAbs = join41(ctxDir, entry.to);
64805
- const toAbs = join41(ctxDir, entry.from);
65481
+ const fromAbs = join42(ctxDir, entry.to);
65482
+ const toAbs = join42(ctxDir, entry.from);
64806
65483
  if (!existsSync34(fromAbs))
64807
65484
  continue;
64808
- await mkdir19(dirname14(toAbs), { recursive: true });
65485
+ await mkdir19(dirname15(toAbs), { recursive: true });
64809
65486
  await cp3(fromAbs, toAbs, { recursive: entry.kind === "dir", force: true });
64810
65487
  }
64811
65488
  }
64812
65489
  async function collectPotentialSideEffectFiles(ctxDir) {
64813
65490
  const paths = new Set([
64814
- join41(ctxDir, "raw", "_sources.yaml"),
64815
- join41(ctxDir, "knowledge", "changelog.md"),
64816
- join41(ctxDir, "knowledge", "_index.md"),
64817
- join41(ctxDir, "knowledge", "_edges.yaml"),
64818
- join41(ctxDir, "knowledge", "_external.yaml")
65491
+ join42(ctxDir, "raw", "_sources.yaml"),
65492
+ join42(ctxDir, "knowledge", "changelog.md"),
65493
+ join42(ctxDir, "knowledge", "_index.md"),
65494
+ join42(ctxDir, "knowledge", "_edges.yaml"),
65495
+ join42(ctxDir, "knowledge", "_external.yaml")
64819
65496
  ]);
64820
- const entityRoot = join41(ctxDir, "knowledge", "entity");
65497
+ const entityRoot = join42(ctxDir, "knowledge", "entity");
64821
65498
  let entries = [];
64822
65499
  try {
64823
65500
  entries = await readdir15(entityRoot, { withFileTypes: true });
@@ -64829,8 +65506,8 @@ async function collectPotentialSideEffectFiles(ctxDir) {
64829
65506
  for (const entry of entries) {
64830
65507
  if (!entry.isDirectory())
64831
65508
  continue;
64832
- paths.add(join41(entityRoot, entry.name, "_edges.yaml"));
64833
- paths.add(join41(entityRoot, entry.name, "_external.yaml"));
65509
+ paths.add(join42(entityRoot, entry.name, "_edges.yaml"));
65510
+ paths.add(join42(entityRoot, entry.name, "_external.yaml"));
64834
65511
  }
64835
65512
  return paths;
64836
65513
  }
@@ -64838,7 +65515,7 @@ async function restoreCodeDropFailure(input) {
64838
65515
  for (const snapshot of input.snapshots)
64839
65516
  await restoreTextFile2(snapshot);
64840
65517
  await restoreRawEntries2(input.ctxDir, input.rawEntries);
64841
- await rm12(join41(input.ctxDir, input.archivePath), { recursive: true, force: true });
65518
+ await rm12(join42(input.ctxDir, input.archivePath), { recursive: true, force: true });
64842
65519
  }
64843
65520
  async function applyCodeSourceDropPlan(input) {
64844
65521
  const plan = parseDropPlan(input.plan);
@@ -64886,7 +65563,7 @@ async function applyCodeSourceDropPlan(input) {
64886
65563
  const sideEffectFiles = await collectPotentialSideEffectFiles(input.ctxDir);
64887
65564
  const snapshotPaths = new Set([...touchedFiles, ...deletedFiles, ...sideEffectFiles]);
64888
65565
  const snapshots = await Promise.all([...snapshotPaths].map((path9) => snapshotTextFile2(path9)));
64889
- const knowledgeRoot4 = join41(input.ctxDir, "knowledge");
65566
+ const knowledgeRoot4 = join42(input.ctxDir, "knowledge");
64890
65567
  const touchedKnowledgeFiles = [...snapshotPaths].filter((path9) => path9.startsWith(knowledgeRoot4) && existsSync34(path9));
64891
65568
  const plannedArchivePath = sourceArchiveRelPath(source2.id, droppedAt);
64892
65569
  let archive;
@@ -64899,6 +65576,8 @@ async function applyCodeSourceDropPlan(input) {
64899
65576
  touchedKnowledgeFiles,
64900
65577
  plan
64901
65578
  });
65579
+ await replaceCodeGraphEdges(input.ctxDir, `code:${codeSourceSlug(source2)}`, []);
65580
+ await replaceExternalDepsForOwners(input.ctxDir, [], owners);
64902
65581
  const renderContext = touchedFiles.size > 0 ? await buildRenderWorkspaceContext(input.ctxDir, files.filter((file) => !deletedFiles.has(file.filePath))) : undefined;
64903
65582
  for (const filePath of touchedFiles) {
64904
65583
  if (deletedFiles.has(filePath))
@@ -64909,8 +65588,6 @@ async function applyCodeSourceDropPlan(input) {
64909
65588
  }
64910
65589
  for (const filePath of deletedFiles)
64911
65590
  await rm12(filePath, { force: true });
64912
- await replaceCodeGraphEdges(input.ctxDir, `code:${codeSourceSlug(source2)}`, []);
64913
- await replaceExternalDepsForOwners(input.ctxDir, [], owners);
64914
65591
  await mdriveWorkspaceRebuildIndex({ ctxDir: input.ctxDir });
64915
65592
  await removeActiveRawEntries(input.ctxDir, archive.rawEntries);
64916
65593
  const dropped = await markSourceDropped({
@@ -64967,8 +65644,8 @@ init_exitCode();
64967
65644
  init_workflowPayloadStore();
64968
65645
  var import_yaml24 = __toESM(require_dist(), 1);
64969
65646
  import { createHash as createHash7 } from "node:crypto";
64970
- import { mkdir as mkdir20, readdir as readdir16, readFile as readFile46, rm as rm13, writeFile as writeFile15 } from "node:fs/promises";
64971
- import { join as join43 } from "node:path";
65647
+ import { mkdir as mkdir20, readdir as readdir16, readFile as readFile47, rm as rm13, writeFile as writeFile15 } from "node:fs/promises";
65648
+ import { join as join44 } from "node:path";
64972
65649
  var IMMUTABLE_REVIEW_SCHEMA_VERSION = "reconcile.review-artifact.v1";
64973
65650
  function shortHash2(value) {
64974
65651
  return createHash7("sha256").update(value).digest("hex").slice(0, 16);
@@ -64977,7 +65654,7 @@ function deriveReviewId(input) {
64977
65654
  return `rv_${shortHash2(`${input.scopeId}\x00${input.prepareDigest}\x00${input.decisionsDigest}`)}`;
64978
65655
  }
64979
65656
  function reviewArtifactPath(input) {
64980
- return join43(workflowScopePath(input.ctxDir, input.workflowId, input.scopeId), `review.${input.reviewId}.yaml`);
65657
+ return join44(workflowScopePath(input.ctxDir, input.workflowId, input.scopeId), `review.${input.reviewId}.yaml`);
64981
65658
  }
64982
65659
  function isRecord13(value) {
64983
65660
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -65032,12 +65709,12 @@ async function removeSupersededReviewArtifacts(scopeDir, keepFileName) {
65032
65709
  return;
65033
65710
  throw err2;
65034
65711
  }
65035
- await Promise.all(entries.filter((entry) => /^review\.rv_[a-f0-9]{16}\.ya?ml$/u.test(entry) && entry !== keepFileName).map((entry) => rm13(join43(scopeDir, entry), { force: true })));
65712
+ await Promise.all(entries.filter((entry) => /^review\.rv_[a-f0-9]{16}\.ya?ml$/u.test(entry) && entry !== keepFileName).map((entry) => rm13(join44(scopeDir, entry), { force: true })));
65036
65713
  }
65037
65714
  async function readArtifactFile(path9) {
65038
65715
  let parsed;
65039
65716
  try {
65040
- parsed = import_yaml24.default.parse(await readFile46(path9, "utf8"));
65717
+ parsed = import_yaml24.default.parse(await readFile47(path9, "utf8"));
65041
65718
  } catch {
65042
65719
  return null;
65043
65720
  }
@@ -65056,7 +65733,7 @@ async function readReadyReviewArtifacts(input) {
65056
65733
  }
65057
65734
  const out2 = [];
65058
65735
  for (const entry of entries.filter((name) => /^review\.rv_[a-f0-9]{16}\.ya?ml$/u.test(name)).sort()) {
65059
- const path9 = join43(dir, entry);
65736
+ const path9 = join44(dir, entry);
65060
65737
  const artifact = await readArtifactFile(path9);
65061
65738
  if (artifact !== null && artifact.ready_to_apply === true)
65062
65739
  out2.push({ artifact, path: path9 });
@@ -65091,7 +65768,7 @@ function dropPlanOutputPath(ctxDir, plan, format) {
65091
65768
  return workflowOutputPath(ctxDir, "drop", `drop.${safeFilePart(plan.source_id)}.plan.${format === "yaml" ? "yaml" : "json"}`);
65092
65769
  }
65093
65770
  async function saveTextFile(path9, body2) {
65094
- await mkdir21(dirname15(path9), { recursive: true });
65771
+ await mkdir21(dirname16(path9), { recursive: true });
65095
65772
  await writeFile16(path9, body2, "utf8");
65096
65773
  }
65097
65774
  function assertDropPlanSaveFlags(flags2) {
@@ -65108,7 +65785,7 @@ async function saveDropPlanOutputs(input) {
65108
65785
  await saveTextFile(path9, input.output);
65109
65786
  saved.push(path9);
65110
65787
  } else if (typeof input.saveOutput === "string") {
65111
- const path9 = resolve10(input.saveOutput);
65788
+ const path9 = resolve11(input.saveOutput);
65112
65789
  await saveTextFile(path9, input.output);
65113
65790
  saved.push(path9);
65114
65791
  }
@@ -65513,8 +66190,8 @@ init_temporal();
65513
66190
  init_knowledge();
65514
66191
  init_archive();
65515
66192
  import { existsSync as existsSync39 } from "node:fs";
65516
- import { readdir as readdir18, readFile as readFile52, stat as stat3 } from "node:fs/promises";
65517
- import { dirname as dirname18, join as join48, resolve as resolve11 } from "node:path";
66193
+ import { readdir as readdir18, readFile as readFile53, stat as stat3 } from "node:fs/promises";
66194
+ import { dirname as dirname19, join as join49, resolve as resolve12 } from "node:path";
65518
66195
 
65519
66196
  // src/reconcile/status.ts
65520
66197
  init_workspaceCache();
@@ -65527,8 +66204,8 @@ init_noteStatus();
65527
66204
  init_ledger();
65528
66205
  var import_yaml26 = __toESM(require_dist(), 1);
65529
66206
  import { existsSync as existsSync35 } from "node:fs";
65530
- import { readdir as readdir17, readFile as readFile47 } from "node:fs/promises";
65531
- import { join as join44 } from "node:path";
66207
+ import { readdir as readdir17, readFile as readFile48 } from "node:fs/promises";
66208
+ import { join as join45 } from "node:path";
65532
66209
 
65533
66210
  // src/reconcile/staleDecisions.ts
65534
66211
  init_sectionFingerprints();
@@ -65583,11 +66260,11 @@ async function findStaleSemanticDecisions(ctxDir) {
65583
66260
  latestByTarget.set(`${record.target.node}\x00${record.target.section_id}`, record);
65584
66261
  }
65585
66262
  const stale = [];
65586
- for (const [targetKey, record] of latestByTarget) {
66263
+ for (const [targetKey2, record] of latestByTarget) {
65587
66264
  const target = record.target;
65588
66265
  if (!target?.node || !target.section_id)
65589
66266
  continue;
65590
- const currentTargetHash = currentTargetHashes.get(targetKey) ?? null;
66267
+ const currentTargetHash = currentTargetHashes.get(targetKey2) ?? null;
65591
66268
  if (currentTargetHash === null) {
65592
66269
  continue;
65593
66270
  }
@@ -65602,7 +66279,7 @@ async function findStaleSemanticDecisions(ctxDir) {
65602
66279
  });
65603
66280
  if (reuse.kind !== "stale")
65604
66281
  continue;
65605
- if (await isDeterministicHeadingHousekeeping(ctxDir, record, currentSections.get(targetKey)))
66282
+ if (await isDeterministicHeadingHousekeeping(ctxDir, record, currentSections.get(targetKey2)))
65606
66283
  continue;
65607
66284
  stale.push({
65608
66285
  id: record.id,
@@ -65621,10 +66298,10 @@ function isRecord14(value) {
65621
66298
  return typeof value === "object" && value !== null && !Array.isArray(value);
65622
66299
  }
65623
66300
  async function parseLastCompile(ctxDir) {
65624
- const path9 = join44(ctxDir, "knowledge", "changelog.md");
66301
+ const path9 = join45(ctxDir, "knowledge", "changelog.md");
65625
66302
  if (!existsSync35(path9))
65626
66303
  return null;
65627
- const lines = (await readFile47(path9, "utf8")).split(`
66304
+ const lines = (await readFile48(path9, "utf8")).split(`
65628
66305
  `).filter((line) => line.includes("[compile]"));
65629
66306
  for (let index2 = lines.length - 1;index2 >= 0; index2 -= 1) {
65630
66307
  const stamp = /@\s*([0-9]{4}-[0-9]{2}-[0-9]{2}(?:T[0-9:.+-]+Z?)?)/.exec(lines[index2] ?? "")?.[1];
@@ -65644,13 +66321,13 @@ async function refreshedSourcesPendingCompile(ctxDir) {
65644
66321
  }).map((source2) => source2.id).sort();
65645
66322
  }
65646
66323
  async function pendingQuestionFiles(ctxDir) {
65647
- const outputDir = join44(ctxDir, "output");
66324
+ const outputDir = join45(ctxDir, "output");
65648
66325
  if (!existsSync35(outputDir))
65649
66326
  return { count: 0, files: [] };
65650
66327
  const files = [];
65651
66328
  let count = 0;
65652
66329
  const visit2 = async (relDir) => {
65653
- const absDir = join44(outputDir, relDir);
66330
+ const absDir = join45(outputDir, relDir);
65654
66331
  for (const entry of await readdir17(absDir, { withFileTypes: true })) {
65655
66332
  const relPath = relDir.length > 0 ? `${relDir}/${entry.name}` : entry.name;
65656
66333
  if (entry.isDirectory()) {
@@ -65662,7 +66339,7 @@ async function pendingQuestionFiles(ctxDir) {
65662
66339
  if (relDir.length === 0 || !entry.isFile() || !SEMANTIC_REVIEW_OUTPUT_FILE_RE.test(entry.name))
65663
66340
  continue;
65664
66341
  const rel = `output/${relPath}`;
65665
- const raw = await readFile47(join44(outputDir, relPath), "utf8");
66342
+ const raw = await readFile48(join45(outputDir, relPath), "utf8");
65666
66343
  let parsed;
65667
66344
  try {
65668
66345
  parsed = entry.name.endsWith(".json") ? JSON.parse(raw) : import_yaml26.default.parse(raw);
@@ -65940,11 +66617,11 @@ init_errors();
65940
66617
  init_exitCode();
65941
66618
  var import_yaml27 = __toESM(require_dist(), 1);
65942
66619
  import { existsSync as existsSync38 } from "node:fs";
65943
- import { mkdir as mkdir23, readFile as readFile51 } from "node:fs/promises";
65944
- import { dirname as dirname17, join as join47 } from "node:path";
66620
+ import { mkdir as mkdir23, readFile as readFile52 } from "node:fs/promises";
66621
+ import { dirname as dirname18, join as join48 } from "node:path";
65945
66622
  var ALIGN_DECISION_LEDGER_SCHEMA_VERSION = "decisions.align.v1";
65946
66623
  function alignDecisionLedgerPath(ctxDir) {
65947
- return join47(ctxDir, "decisions", "align.yaml");
66624
+ return join48(ctxDir, "decisions", "align.yaml");
65948
66625
  }
65949
66626
  function isRecord16(value) {
65950
66627
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -66006,7 +66683,7 @@ async function readAlignDecisionLedger(ctxDir) {
66006
66683
  const path9 = alignDecisionLedgerPath(ctxDir);
66007
66684
  if (!existsSync38(path9))
66008
66685
  return { schema_version: ALIGN_DECISION_LEDGER_SCHEMA_VERSION, decisions: [] };
66009
- return parseLedger(import_yaml27.default.parse(await readFile51(path9, "utf8")));
66686
+ return parseLedger(import_yaml27.default.parse(await readFile52(path9, "utf8")));
66010
66687
  }
66011
66688
  function recordSupersedeKey(record) {
66012
66689
  return `${record.scope}\x00${record.node_slug}`;
@@ -66056,7 +66733,7 @@ async function appendAlignDecisionLedger(input) {
66056
66733
  ]
66057
66734
  };
66058
66735
  const path9 = alignDecisionLedgerPath(input.ctxDir);
66059
- await mkdir23(dirname17(path9), { recursive: true });
66736
+ await mkdir23(dirname18(path9), { recursive: true });
66060
66737
  await atomicWriteFile(path9, import_yaml27.default.stringify(next));
66061
66738
  return next;
66062
66739
  }
@@ -66116,7 +66793,7 @@ async function collectKnowledgeFiles(dir, nested) {
66116
66793
  for (const e of entries) {
66117
66794
  if (e.name === "_index.md")
66118
66795
  continue;
66119
- const full = join48(dir, e.name);
66796
+ const full = join49(dir, e.name);
66120
66797
  if (e.isFile() && e.name.endsWith(".md")) {
66121
66798
  files.push(full);
66122
66799
  } else if (e.isDirectory() && nested) {
@@ -66132,7 +66809,7 @@ async function hasLikelySourceCode(root2, depth = 0) {
66132
66809
  for (const entry of entries) {
66133
66810
  if (entry.name.startsWith(".") && entry.name !== ".github")
66134
66811
  continue;
66135
- const full = join48(root2, entry.name);
66812
+ const full = join49(root2, entry.name);
66136
66813
  if (entry.isFile() && SOURCE_CODE_EXT_RE.test(entry.name))
66137
66814
  return true;
66138
66815
  if (entry.isDirectory() && !SOURCE_SCAN_SKIP_DIRS.has(entry.name)) {
@@ -66163,9 +66840,9 @@ function isKnowledgeType(v) {
66163
66840
  return KNOWLEDGE_TYPES.includes(v);
66164
66841
  }
66165
66842
  async function parseLastCompileTime(ctxDir) {
66166
- const path9 = join48(ctxDir, "knowledge", "changelog.md");
66843
+ const path9 = join49(ctxDir, "knowledge", "changelog.md");
66167
66844
  if (existsSync39(path9)) {
66168
- const body2 = await readFile52(path9, "utf8");
66845
+ const body2 = await readFile53(path9, "utf8");
66169
66846
  const compileLines = body2.split(`
66170
66847
  `).filter((line) => line.includes("[compile]"));
66171
66848
  for (let index2 = compileLines.length - 1;index2 >= 0; index2 -= 1) {
@@ -66228,13 +66905,13 @@ function latestCaptureFromSources(file) {
66228
66905
  return { capture, drop, hasActiveRaw, hasAspectCode, totalSnapshots };
66229
66906
  }
66230
66907
  function detectGitRepo(cwd, ctxDir) {
66231
- const roots = [resolve11(cwd), workspaceLocationFromCtxDir(ctxDir).workspaceRoot];
66908
+ const roots = [resolve12(cwd), workspaceLocationFromCtxDir(ctxDir).workspaceRoot];
66232
66909
  for (const start2 of roots) {
66233
66910
  let dir = start2;
66234
66911
  while (true) {
66235
- if (existsSync39(join48(dir, ".git")))
66912
+ if (existsSync39(join49(dir, ".git")))
66236
66913
  return true;
66237
- const parent = dirname18(dir);
66914
+ const parent = dirname19(dir);
66238
66915
  if (parent === dir)
66239
66916
  break;
66240
66917
  dir = parent;
@@ -66261,9 +66938,9 @@ async function collectWorkspaceStats(input) {
66261
66938
  let latestKnowledgeUpdated = null;
66262
66939
  let totalSections = 0;
66263
66940
  for (const root2 of KNOWLEDGE_ROOTS) {
66264
- const files = await collectKnowledgeFiles(join48(ctxDir, "knowledge", root2.dir), root2.nested);
66941
+ const files = await collectKnowledgeFiles(join49(ctxDir, "knowledge", root2.dir), root2.nested);
66265
66942
  for (const path9 of files) {
66266
- const text5 = await readFile52(path9, "utf8");
66943
+ const text5 = await readFile53(path9, "utf8");
66267
66944
  const typeField = parseFrontmatterField(text5, "type");
66268
66945
  if (!typeField || !isKnowledgeType(typeField))
66269
66946
  continue;
@@ -66757,12 +67434,12 @@ init_cliFeedback();
66757
67434
  import { existsSync as existsSync42 } from "node:fs";
66758
67435
  import { readdir as readdir19, rm as rm15 } from "node:fs/promises";
66759
67436
  import { homedir as homedir2 } from "node:os";
66760
- import { join as join51 } from "node:path";
67437
+ import { join as join52 } from "node:path";
66761
67438
 
66762
67439
  // src/commands/doctorWorkspace.ts
66763
67440
  import { existsSync as existsSync41 } from "node:fs";
66764
- import { readFile as readFile54 } from "node:fs/promises";
66765
- import { join as join50 } from "node:path";
67441
+ import { readFile as readFile55 } from "node:fs/promises";
67442
+ import { join as join51 } from "node:path";
66766
67443
  init_cliFeedback();
66767
67444
  init_verify();
66768
67445
 
@@ -66776,8 +67453,8 @@ init_sourceSupport();
66776
67453
  init_evidence();
66777
67454
  init_ledger();
66778
67455
  import { existsSync as existsSync40 } from "node:fs";
66779
- import { readFile as readFile53 } from "node:fs/promises";
66780
- import { join as join49 } from "node:path";
67456
+ import { readFile as readFile54 } from "node:fs/promises";
67457
+ import { join as join50 } from "node:path";
66781
67458
  function numbersIn2(text5) {
66782
67459
  return text5.match(/\b\d+(?:\.\d+)*\b/gu) ?? [];
66783
67460
  }
@@ -66816,10 +67493,10 @@ async function latestSourceRefText(input) {
66816
67493
  const snapshot = findSnapshotByHashPrefix(source2.snapshots, parsedSource.snapshotHash) ?? selectLatestSnapshot(source2.snapshots);
66817
67494
  if (!snapshot?.file)
66818
67495
  return null;
66819
- const fullPath = join49(input.ctxDir, snapshot.file);
67496
+ const fullPath = join50(input.ctxDir, snapshot.file);
66820
67497
  if (!existsSync40(fullPath))
66821
67498
  return null;
66822
- const raw = await readFile53(fullPath, "utf8");
67499
+ const raw = await readFile54(fullPath, "utf8");
66823
67500
  if (!sourceRefRangeOverlapsEvidenceBlock(raw, parsed.lineStart, parsed.lineEnd))
66824
67501
  return null;
66825
67502
  return { sourceId, text: sourceRefRangeText(raw, parsed.lineStart, parsed.lineEnd) };
@@ -66927,7 +67604,7 @@ init_cache();
66927
67604
  init_workspaceLayout();
66928
67605
  import { constants as constants3 } from "node:fs";
66929
67606
  import { access as access2, stat as stat4 } from "node:fs/promises";
66930
- import { dirname as dirname19 } from "node:path";
67607
+ import { dirname as dirname20 } from "node:path";
66931
67608
  async function assertRetrievalCacheWritable(ctxDir) {
66932
67609
  const workspaceRoot = workspaceRootFromCtxDir(ctxDir);
66933
67610
  const paths = await getIncrementalCachePaths({ workspaceRoot });
@@ -66953,7 +67630,7 @@ async function nearestExistingPath(target) {
66953
67630
  const code3 = typeof err2 === "object" && err2 !== null && "code" in err2 ? String(err2.code) : "";
66954
67631
  if (code3 !== "ENOENT")
66955
67632
  return current;
66956
- const parent = dirname19(current);
67633
+ const parent = dirname20(current);
66957
67634
  if (parent === current)
66958
67635
  return current;
66959
67636
  current = parent;
@@ -67315,11 +67992,11 @@ function notApplicableIssue2(path9, message) {
67315
67992
  };
67316
67993
  }
67317
67994
  async function hasCompileEntry(ctxDir) {
67318
- const changelogPath2 = join50(ctxDir, "knowledge", "changelog.md");
67995
+ const changelogPath2 = join51(ctxDir, "knowledge", "changelog.md");
67319
67996
  if (!existsSync41(changelogPath2))
67320
67997
  return false;
67321
67998
  try {
67322
- return (await readFile54(changelogPath2, "utf8")).split(`
67999
+ return (await readFile55(changelogPath2, "utf8")).split(`
67323
68000
  `).some((line) => line.includes("[compile]"));
67324
68001
  } catch {
67325
68002
  return false;
@@ -67699,19 +68376,19 @@ async function cleanClaudePluginCache(opts = {}) {
67699
68376
  for (const mp of marketplaces) {
67700
68377
  if (!mp.isDirectory())
67701
68378
  continue;
67702
- const mpDir = join51(cacheRoot, mp.name);
68379
+ const mpDir = join52(cacheRoot, mp.name);
67703
68380
  const plugins = await readdir19(mpDir, { withFileTypes: true });
67704
68381
  for (const pl of plugins) {
67705
68382
  if (!pl.isDirectory())
67706
68383
  continue;
67707
- const plDir = join51(mpDir, pl.name);
68384
+ const plDir = join52(mpDir, pl.name);
67708
68385
  const versions = await readdir19(plDir, { withFileTypes: true });
67709
68386
  for (const ver of versions) {
67710
68387
  if (!ver.isDirectory())
67711
68388
  continue;
67712
68389
  scanned += 1;
67713
- const verDir = join51(plDir, ver.name);
67714
- const markerPath = join51(verDir, ORPHAN_MARKER);
68390
+ const verDir = join52(plDir, ver.name);
68391
+ const markerPath = join52(verDir, ORPHAN_MARKER);
67715
68392
  if (!existsSync42(markerPath))
67716
68393
  continue;
67717
68394
  const label = `${mp.name}/${pl.name}/${ver.name}`;
@@ -67743,7 +68420,7 @@ function resolveClaudePluginCacheRoot(opts) {
67743
68420
  if (explicitRoot)
67744
68421
  return explicitRoot;
67745
68422
  const home = opts.home ?? process.env[CLAUDE_PLUGIN_CACHE_HOME_ENV] ?? homedir2();
67746
- return join51(home, ".claude", "plugins", "cache");
68423
+ return join52(home, ".claude", "plugins", "cache");
67747
68424
  }
67748
68425
  async function isEmptyDir(dir) {
67749
68426
  try {
@@ -67775,7 +68452,7 @@ init_errors();
67775
68452
  init_cliFeedback();
67776
68453
  import { existsSync as existsSync43 } from "node:fs";
67777
68454
  import { readdir as readdir20, rm as rm16 } from "node:fs/promises";
67778
- import { join as join52 } from "node:path";
68455
+ import { join as join53 } from "node:path";
67779
68456
  init_cache();
67780
68457
  init_workspaceCache();
67781
68458
  init_cache2();
@@ -67930,7 +68607,7 @@ async function countFiles2(dir) {
67930
68607
  return 0;
67931
68608
  let count = 0;
67932
68609
  for (const entry of await readdir20(dir, { withFileTypes: true })) {
67933
- const full = join52(dir, entry.name);
68610
+ const full = join53(dir, entry.name);
67934
68611
  count += entry.isDirectory() ? await countFiles2(full) : 1;
67935
68612
  }
67936
68613
  return count;
@@ -67946,7 +68623,7 @@ async function cleanAllRetrievalCache() {
67946
68623
  }
67947
68624
  async function inspectAllRetrievalCache() {
67948
68625
  const cacheHome = resolveCacheHome();
67949
- const cacheRoot = join52(cacheHome, "retrieval");
68626
+ const cacheRoot = join53(cacheHome, "retrieval");
67950
68627
  const projectIds = existsSync43(cacheRoot) ? (await readdir20(cacheRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort() : [];
67951
68628
  const files = await countFiles2(cacheRoot);
67952
68629
  return { cacheRoot, projects: projectIds.length, files, projectIds };
@@ -70561,8 +71238,8 @@ function weakSupportWarning(input) {
70561
71238
  item_id: input.decision.item_id,
70562
71239
  severity: "warning",
70563
71240
  code: "weak-source-support",
70564
- message: `proposed keep_separate is weakly supported by its cited raw text: ${formatSupportDiagnostic(input.diagnostic)}.`,
70565
- next_action: "Review the cited evidence if the content looks surprising; missing hard facts still remain errors."
71241
+ message: `proposed keep_separate has low lexical source_support but no missing hard facts: ${formatSupportDiagnostic(input.diagnostic)}.`,
71242
+ next_action: "Review the cited evidence if the content looks surprising; semantic judge/user/delegated acceptance may proceed, but missing hard facts remain errors."
70566
71243
  };
70567
71244
  }
70568
71245
  function supportConfirmationQuestion(input) {
@@ -70652,6 +71329,9 @@ function questionPrompt(decision) {
70652
71329
  return "Please confirm how this knowledge should be handled.";
70653
71330
  return "Please confirm the proposed semantic reconciliation.";
70654
71331
  }
71332
+ function judgeAcceptsLowLexicalSupport(input) {
71333
+ return input.decision.action === "keep_separate" && input.decision.judge_support_verdict === "supported" && input.changedSourceRef === false && input.diagnostic !== null && input.diagnostic.missingHardTerms.length === 0 && (input.diagnostic.verdict === "weak" || input.diagnostic.verdict === "unsupported");
71334
+ }
70655
71335
  function questionFromDecision(decision, index2, summary) {
70656
71336
  return {
70657
71337
  question_id: `q-${String(index2 + 1).padStart(3, "0")}`,
@@ -70957,11 +71637,16 @@ function reviewSemanticDecisions(input) {
70957
71637
  for (const prior of priorResults) {
70958
71638
  const diagnostic = supportDiagnosticFromItem({ decision: prior.decision, item: prior.item });
70959
71639
  const changedSourceRef = sourceRefChangedFromPreparedSupport({ decision: prior.decision, item: prior.item });
71640
+ const judgeAcceptedLowLexical = judgeAcceptsLowLexicalSupport({
71641
+ decision: prior.decision,
71642
+ diagnostic,
71643
+ changedSourceRef
71644
+ });
70960
71645
  const weakAllowed = allowsWeakSourceSupport({
70961
71646
  decision: prior.decision,
70962
71647
  allowDelegatedDecisions: input.allowDelegatedDecisions
70963
- });
70964
- if (diagnostic?.verdict === "weak" && prior.decision.action === "keep_separate" && !changedSourceRef && weakAllowed) {
71648
+ }) || judgeAcceptedLowLexical;
71649
+ if (diagnostic !== null && (diagnostic.verdict === "weak" || judgeAcceptedLowLexical) && prior.decision.action === "keep_separate" && !changedSourceRef && weakAllowed) {
70965
71650
  issues.push(weakSupportWarning({
70966
71651
  decision: prior.decision,
70967
71652
  diagnostic
@@ -71138,7 +71823,7 @@ init_ledger();
71138
71823
  import { existsSync as existsSync51 } from "node:fs";
71139
71824
  import { cp as cp4, mkdir as mkdir25, mkdtemp as mkdtemp2, rm as rm18 } from "node:fs/promises";
71140
71825
  import { tmpdir as tmpdir2 } from "node:os";
71141
- import { dirname as dirname21, join as join61 } from "node:path";
71826
+ import { dirname as dirname22, join as join62 } from "node:path";
71142
71827
 
71143
71828
  // src/reconcile/applyPreflight.ts
71144
71829
  init_sources();
@@ -71461,11 +72146,11 @@ async function propagateOmitCoverageSkips(input) {
71461
72146
  }
71462
72147
  }
71463
72148
  async function createApplyRollbackSnapshot(ctxDir) {
71464
- const tmpDir = await mkdtemp2(join61(tmpdir2(), "c4a-reconcile-apply-"));
71465
- const knowledgePath2 = join61(ctxDir, "knowledge");
72149
+ const tmpDir = await mkdtemp2(join62(tmpdir2(), "c4a-reconcile-apply-"));
72150
+ const knowledgePath2 = join62(ctxDir, "knowledge");
71466
72151
  const ledgerPath = semanticLedgerPath2(ctxDir);
71467
- const knowledgeBackup = join61(tmpDir, "knowledge");
71468
- const ledgerBackup = join61(tmpDir, "semantic.yaml");
72152
+ const knowledgeBackup = join62(tmpDir, "knowledge");
72153
+ const ledgerBackup = join62(tmpDir, "semantic.yaml");
71469
72154
  const knowledgeExisted = existsSync51(knowledgePath2);
71470
72155
  const ledgerExisted = existsSync51(ledgerPath);
71471
72156
  if (knowledgeExisted)
@@ -71475,7 +72160,7 @@ async function createApplyRollbackSnapshot(ctxDir) {
71475
72160
  return { ctxDir, tmpDir, knowledgeExisted, ledgerExisted, knowledgeBackup, ledgerBackup };
71476
72161
  }
71477
72162
  async function restoreApplyRollbackSnapshot(snapshot) {
71478
- const knowledgePath2 = join61(snapshot.ctxDir, "knowledge");
72163
+ const knowledgePath2 = join62(snapshot.ctxDir, "knowledge");
71479
72164
  if (snapshot.knowledgeExisted) {
71480
72165
  await rm18(knowledgePath2, { recursive: true, force: true });
71481
72166
  await cp4(snapshot.knowledgeBackup, knowledgePath2, { recursive: true });
@@ -71484,7 +72169,7 @@ async function restoreApplyRollbackSnapshot(snapshot) {
71484
72169
  }
71485
72170
  const ledgerPath = semanticLedgerPath2(snapshot.ctxDir);
71486
72171
  if (snapshot.ledgerExisted) {
71487
- await mkdir25(dirname21(ledgerPath), { recursive: true });
72172
+ await mkdir25(dirname22(ledgerPath), { recursive: true });
71488
72173
  await cp4(snapshot.ledgerBackup, ledgerPath);
71489
72174
  } else {
71490
72175
  await rm18(ledgerPath, { force: true });
@@ -71865,7 +72550,7 @@ init_compile();
71865
72550
  init_cliFeedback();
71866
72551
  init_errors();
71867
72552
  var import_yaml32 = __toESM(require_dist(), 1);
71868
- import { resolve as resolve13 } from "node:path";
72553
+ import { resolve as resolve14 } from "node:path";
71869
72554
  init_structuredInput();
71870
72555
  init_exitCode();
71871
72556
  function isRecord28(value) {
@@ -71946,7 +72631,7 @@ function saveOutputPath(value, flag) {
71946
72631
  flag
71947
72632
  });
71948
72633
  }
71949
- return resolve13(value);
72634
+ return resolve14(value);
71950
72635
  }
71951
72636
  function requireWorkspace2(ctxDir, command) {
71952
72637
  if (!ctxDir) {
@@ -72338,7 +73023,7 @@ init_exitCode();
72338
73023
  init_currentWorkflow();
72339
73024
  init_workflowOutputPaths();
72340
73025
  import { mkdir as mkdir26, writeFile as writeFile17 } from "node:fs/promises";
72341
- import { dirname as dirname22 } from "node:path";
73026
+ import { dirname as dirname23 } from "node:path";
72342
73027
  function familyForReconcileMode(mode) {
72343
73028
  if (mode === "compile")
72344
73029
  return "compile";
@@ -72442,14 +73127,14 @@ function reviewOutputFile(input) {
72442
73127
  return workflowOutputPath(input.ctxDir, input.context.mode, `${input.context.mode}.review.${ext}`);
72443
73128
  }
72444
73129
  async function saveOutput(path9, body2) {
72445
- await mkdir26(dirname22(path9), { recursive: true });
73130
+ await mkdir26(dirname23(path9), { recursive: true });
72446
73131
  await writeFile17(path9, body2, "utf8");
72447
73132
  }
72448
73133
 
72449
73134
  // src/commands/workflowCommandUtils.ts
72450
73135
  var import_yaml33 = __toESM(require_dist(), 1);
72451
- import { mkdir as mkdir27, readFile as readFile62, writeFile as writeFile18 } from "node:fs/promises";
72452
- import { dirname as dirname23 } from "node:path";
73136
+ import { mkdir as mkdir27, readFile as readFile63, writeFile as writeFile18 } from "node:fs/promises";
73137
+ import { dirname as dirname24 } from "node:path";
72453
73138
  init_errors();
72454
73139
  init_cliFeedback();
72455
73140
  init_structuredInput();
@@ -72473,7 +73158,7 @@ function writeSchemaOutput(value, format) {
72473
73158
  async function readStructuredInput2(path9) {
72474
73159
  let raw;
72475
73160
  try {
72476
- raw = path9 === "-" ? await readStdinText() : await readFile62(path9, "utf8");
73161
+ raw = path9 === "-" ? await readStdinText() : await readFile63(path9, "utf8");
72477
73162
  } catch (error) {
72478
73163
  if (error.code === "ENOENT") {
72479
73164
  throw new ContextError(ExitCode.UserError, `file not found: ${path9}`, {
@@ -72488,7 +73173,7 @@ async function readStructuredInput2(path9) {
72488
73173
  async function writeWorkflowPayload(input) {
72489
73174
  assertWorkflowOutputFileName(input.fileName);
72490
73175
  const outputPath = workflowOutputPathForFile(input.ctxDir, input.fileName);
72491
- await mkdir27(dirname23(outputPath), { recursive: true });
73176
+ await mkdir27(dirname24(outputPath), { recursive: true });
72492
73177
  const body2 = input.format === "json" ? `${JSON.stringify(input.payload, null, 2)}
72493
73178
  ` : import_yaml33.default.stringify(input.payload);
72494
73179
  await writeFile18(outputPath, body2, "utf8");
@@ -72953,7 +73638,7 @@ function renderReconcileApplyFeedbackBody(result) {
72953
73638
  init_errors();
72954
73639
  init_nodeRenderer();
72955
73640
  var import_yaml34 = __toESM(require_dist(), 1);
72956
- import { readFile as readFile65 } from "node:fs/promises";
73641
+ import { readFile as readFile66 } from "node:fs/promises";
72957
73642
  init_knowledge();
72958
73643
  init_exitCode();
72959
73644
  init_edge2();
@@ -72962,8 +73647,8 @@ init_edge2();
72962
73647
  init_knowledge();
72963
73648
  init_nodeParser();
72964
73649
  import { existsSync as existsSync52 } from "node:fs";
72965
- import { readdir as readdir25, readFile as readFile63 } from "node:fs/promises";
72966
- import { join as join62 } from "node:path";
73650
+ import { readdir as readdir25, readFile as readFile64 } from "node:fs/promises";
73651
+ import { join as join63 } from "node:path";
72967
73652
  function collectGlossaryFromParsedTree(parsed, glossary, allowedTypes) {
72968
73653
  const type = parsed.node.type;
72969
73654
  if (allowedTypes.has(type)) {
@@ -72986,8 +73671,8 @@ function normalizeGlossaryTerm(value) {
72986
73671
  return value.normalize("NFC").trim().toLowerCase();
72987
73672
  }
72988
73673
  function resolveKnowledgeRoot(path9) {
72989
- if (existsSync52(join62(path9, "knowledge"))) {
72990
- return join62(path9, "knowledge");
73674
+ if (existsSync52(join63(path9, "knowledge"))) {
73675
+ return join63(path9, "knowledge");
72991
73676
  }
72992
73677
  return path9;
72993
73678
  }
@@ -72995,7 +73680,7 @@ async function readGlossaryFiles(dir) {
72995
73680
  if (!existsSync52(dir))
72996
73681
  return [];
72997
73682
  const entries = await readdir25(dir, { withFileTypes: true });
72998
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md") && entry.name !== "_index.md" && entry.name !== "changelog.md").map((entry) => join62(dir, entry.name));
73683
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md") && entry.name !== "_index.md" && entry.name !== "changelog.md").map((entry) => join63(dir, entry.name));
72999
73684
  }
73000
73685
  async function buildGlossary(path9, options = {}) {
73001
73686
  const knowledgeRoot4 = resolveKnowledgeRoot(path9);
@@ -73003,9 +73688,9 @@ async function buildGlossary(path9, options = {}) {
73003
73688
  const allowedTypes = new Set(targets);
73004
73689
  const glossary = [];
73005
73690
  for (const type of targets) {
73006
- const files = await readGlossaryFiles(join62(knowledgeRoot4, type));
73691
+ const files = await readGlossaryFiles(join63(knowledgeRoot4, type));
73007
73692
  for (const file of files) {
73008
- const parsed = parseNodeMarkdown(await readFile63(file, "utf8"));
73693
+ const parsed = parseNodeMarkdown(await readFile64(file, "utf8"));
73009
73694
  if (parsed.node.type !== type)
73010
73695
  continue;
73011
73696
  collectGlossaryFromParsedTree(parsed, glossary, allowedTypes);
@@ -73071,7 +73756,7 @@ init_query();
73071
73756
  // src/commands/mdriveSection.ts
73072
73757
  init_errors();
73073
73758
  init_cliFeedback();
73074
- import { readFile as readFile64 } from "node:fs/promises";
73759
+ import { readFile as readFile65 } from "node:fs/promises";
73075
73760
  init_knowledge();
73076
73761
  init_exitCode();
73077
73762
  init_section();
@@ -73084,7 +73769,7 @@ async function readSourceRefText(options) {
73084
73769
  category: ErrorCategory.UserInputInvalid
73085
73770
  });
73086
73771
  }
73087
- return hasText ? String(options.text) : await readFile64(String(options.textFile), "utf8");
73772
+ return hasText ? String(options.text) : await readFile65(String(options.textFile), "utf8");
73088
73773
  }
73089
73774
  function registerMdriveSectionCommands(mdrive, deps) {
73090
73775
  const section = mdrive.command("section").description("Section primitives");
@@ -73189,7 +73874,7 @@ function optionalChoice(value, choices, label) {
73189
73874
  }
73190
73875
  async function readStructuredInput3(path9, label = "--input") {
73191
73876
  const value = requireString2(path9, label);
73192
- const raw = value === "-" ? await readStdinText() : await readFile65(value, "utf8");
73877
+ const raw = value === "-" ? await readStdinText() : await readFile66(value, "utf8");
73193
73878
  return import_yaml34.default.parse(raw);
73194
73879
  }
73195
73880
  function asRecord2(input, label) {
@@ -73618,8 +74303,8 @@ init_cliFeedback();
73618
74303
  init_errors();
73619
74304
  var import_yaml35 = __toESM(require_dist(), 1);
73620
74305
  import { existsSync as existsSync53 } from "node:fs";
73621
- import { lstat as lstat2, mkdir as mkdir28, readFile as readFile66, readdir as readdir26, realpath as realpath3, rename as rename6, writeFile as writeFile19 } from "node:fs/promises";
73622
- import { dirname as dirname24, isAbsolute as isAbsolute7, join as join63, relative as relative14 } from "node:path";
74306
+ import { lstat as lstat2, mkdir as mkdir28, readFile as readFile67, readdir as readdir26, realpath as realpath3, rename as rename6, writeFile as writeFile19 } from "node:fs/promises";
74307
+ import { dirname as dirname25, isAbsolute as isAbsolute7, join as join64, relative as relative15 } from "node:path";
73623
74308
  init_workspaceLayout();
73624
74309
  init_section();
73625
74310
  init_exitCode();
@@ -73675,7 +74360,7 @@ function normalizeWorkspacePath(input) {
73675
74360
  async function assertRealPathInside(ctxDir, targetPath) {
73676
74361
  const rootReal = await realpath3(ctxDir);
73677
74362
  const targetReal = await realpath3(targetPath);
73678
- const rel = relative14(rootReal, targetReal);
74363
+ const rel = relative15(rootReal, targetReal);
73679
74364
  if (rel === "" || !rel.startsWith("..") && !isAbsolute7(rel))
73680
74365
  return;
73681
74366
  throw new ContextError(ExitCode.UserError, "workspace path resolves outside the context data root", {
@@ -73683,7 +74368,7 @@ async function assertRealPathInside(ctxDir, targetPath) {
73683
74368
  });
73684
74369
  }
73685
74370
  function resolveWorkspacePath(ctxDir, relPath) {
73686
- return relPath === "." ? ctxDir : join63(ctxDir, relPath);
74371
+ return relPath === "." ? ctxDir : join64(ctxDir, relPath);
73687
74372
  }
73688
74373
  function pathKind2(stats) {
73689
74374
  if (stats.isFile())
@@ -73791,7 +74476,7 @@ async function searchWorkspace(input) {
73791
74476
  if (hits.length >= input.maxResults || entry.kind !== "file")
73792
74477
  continue;
73793
74478
  const abs = resolveWorkspacePath(input.ctxDir, entry.path);
73794
- const content3 = await readFile66(abs, "utf8");
74479
+ const content3 = await readFile67(abs, "utf8");
73795
74480
  if (!isProbablyText(content3))
73796
74481
  continue;
73797
74482
  const lines = content3.replace(/\r\n/g, `
@@ -73844,7 +74529,7 @@ async function writeWorkspaceFile(input) {
73844
74529
  path: input.relPath
73845
74530
  });
73846
74531
  }
73847
- await mkdir28(dirname24(destination), { recursive: true });
74532
+ await mkdir28(dirname25(destination), { recursive: true });
73848
74533
  const tmpPath = `${destination}.workspace-tmp-${process.pid}-${Date.now()}`;
73849
74534
  await writeFile19(tmpPath, input.content, "utf8");
73850
74535
  await assertRealPathInside(input.ctxDir, tmpPath);
@@ -73873,11 +74558,11 @@ async function readResolveRefText(ctxDir, options) {
73873
74558
  return String(options.text);
73874
74559
  const file = String(options.textFile);
73875
74560
  try {
73876
- return await readFile66(file, "utf8");
74561
+ return await readFile67(file, "utf8");
73877
74562
  } catch (error) {
73878
74563
  if (error.code !== "ENOENT" || isAbsolute7(file))
73879
74564
  throw error;
73880
- return await readFile66(resolveWorkspacePath(ctxDir, normalizeWorkspacePath(file)), "utf8");
74565
+ return await readFile67(resolveWorkspacePath(ctxDir, normalizeWorkspacePath(file)), "utf8");
73881
74566
  }
73882
74567
  }
73883
74568
  function registerWorkspaceCommands(program2) {
@@ -73901,7 +74586,7 @@ function registerWorkspaceCommands(program2) {
73901
74586
  path: relPath
73902
74587
  });
73903
74588
  }
73904
- const content3 = await readFile66(absPath, "utf8");
74589
+ const content3 = await readFile67(absPath, "utf8");
73905
74590
  const format = assertChoice3(options.format, READ_FORMATS, "--format");
73906
74591
  if (format === "json") {
73907
74592
  process.stdout.write(`${JSON.stringify({
@@ -74450,6 +75135,7 @@ function compactAlignSegments(record, value) {
74450
75135
  }
74451
75136
  var LARGE_WINDOW_BLOCK_THRESHOLD = 12;
74452
75137
  var WINDOW_PREVIEW_LIMIT = 1;
75138
+ var FULL_TEXT_METADATA_TOKEN_BUFFER = 100;
74453
75139
  var SOURCE_WINDOW_SELECTOR_RE = /^(src-[1-9]\d*):([1-9]\d*)$/u;
74454
75140
  var ALIGN_BLOCKS_SELECTION_POLICY = {
74455
75141
  id: "align-blocks-v1",
@@ -74484,6 +75170,16 @@ function blockLineRange(block) {
74484
75170
  return;
74485
75171
  return `L${block.line_start}-L${block.line_end}`;
74486
75172
  }
75173
+ function normalizedPreviewBasis(value) {
75174
+ return value.replace(/\s+/g, " ").trim();
75175
+ }
75176
+ function blockBodyText(block) {
75177
+ return typeof block.body_text === "string" && block.body_text.length > 0 ? block.body_text : undefined;
75178
+ }
75179
+ function fullTextTokenEstimate(block, bodyText) {
75180
+ const storedEstimate = typeof block.token_estimate === "number" && Number.isFinite(block.token_estimate) ? Math.max(0, block.token_estimate) : 0;
75181
+ return Math.max(storedEstimate, estimateTokenCount(bodyText));
75182
+ }
74487
75183
  function alignBlockRows(value) {
74488
75184
  if (!isRecord29(value))
74489
75185
  return [];
@@ -74492,6 +75188,8 @@ function alignBlockRows(value) {
74492
75188
  const locator = isRecord29(block.stable_locator_hint) ? block.stable_locator_hint : {};
74493
75189
  const ordinal = typeof locator.ordinal_in_source === "number" ? locator.ordinal_in_source : index2 + 1;
74494
75190
  const textPreview = previewText2(block.text_preview, 220);
75191
+ const bodyText = blockBodyText(block);
75192
+ const textPreviewTruncated = bodyText !== undefined && textPreview !== undefined ? normalizedPreviewBasis(bodyText).length > textPreview.length : undefined;
74495
75193
  const signals = blockSignalLabels(block, textPreview);
74496
75194
  const lineRange2 = blockLineRange(block);
74497
75195
  return {
@@ -74507,7 +75205,13 @@ function alignBlockRows(value) {
74507
75205
  ordinal_in_source: ordinal,
74508
75206
  signal_labels: signals,
74509
75207
  signal_score: signals.length,
74510
- ...textPreview !== undefined ? { text_preview: textPreview } : {}
75208
+ ...textPreview !== undefined ? { text_preview: textPreview } : {},
75209
+ ...bodyText !== undefined ? {
75210
+ body_text: bodyText,
75211
+ full_text_available: true,
75212
+ full_text_token_estimate: fullTextTokenEstimate(block, bodyText),
75213
+ text_preview_truncated: textPreviewTruncated === true
75214
+ } : {}
74511
75215
  };
74512
75216
  }));
74513
75217
  }
@@ -74640,7 +75344,7 @@ function blockDetailContext(blocks) {
74640
75344
  ...sameHeadingPath2 ? { heading_path: first.heading_path } : {}
74641
75345
  };
74642
75346
  }
74643
- function blockDetailRow(block, context) {
75347
+ function blockDetailRow(block, context, options) {
74644
75348
  return {
74645
75349
  ...context.source === undefined ? { source_alias: block.source_alias, source_id: block.source_id } : {},
74646
75350
  block_id: block.block_id,
@@ -74652,11 +75356,25 @@ function blockDetailRow(block, context) {
74652
75356
  block_locator_id: block.block_locator_id,
74653
75357
  block_ordinal: block.ordinal_in_source,
74654
75358
  ...block.signal_labels.length > 0 ? { signal_labels: block.signal_labels, signal_score: block.signal_score } : {},
74655
- ...block.text_preview !== undefined ? { text_preview: block.text_preview } : {}
75359
+ ...block.text_preview !== undefined ? { text_preview: block.text_preview } : {},
75360
+ ...block.full_text_available === true ? {
75361
+ full_text_available: true,
75362
+ ...block.full_text_token_estimate !== undefined ? { full_text_token_estimate: block.full_text_token_estimate } : {},
75363
+ text_preview_truncated: block.text_preview_truncated === true
75364
+ } : {},
75365
+ ...options.includeFullText && block.body_text !== undefined ? {
75366
+ body_text: block.body_text,
75367
+ full_text_included: true
75368
+ } : {}
74656
75369
  };
74657
75370
  }
74658
- function blockDetailItems(blocks, context) {
74659
- return blocks.map((block) => ({ block, row: blockDetailRow(block, context) }));
75371
+ function blockDetailItems(blocks, context, options) {
75372
+ const singleBlock = blocks.length === 1 ? blocks[0] : undefined;
75373
+ const includeFullText = singleBlock?.body_text !== undefined && (singleBlock.full_text_token_estimate ?? blockTokenEstimate(singleBlock)) + FULL_TEXT_METADATA_TOKEN_BUFFER <= options.tokenBudget;
75374
+ return blocks.map((block) => ({
75375
+ block,
75376
+ row: blockDetailRow(block, context, { includeFullText: includeFullText && block === singleBlock })
75377
+ }));
74660
75378
  }
74661
75379
  function blockTokenEstimate(block) {
74662
75380
  return typeof block.token_estimate === "number" && Number.isFinite(block.token_estimate) ? Math.max(0, block.token_estimate) : 0;
@@ -74683,6 +75401,27 @@ function alignBlocksHowToExplore(input) {
74683
75401
  ]
74684
75402
  });
74685
75403
  }
75404
+ function alignBlockPreviewHints(input) {
75405
+ const truncated = input.items.filter((item) => item.text_preview_truncated === true && typeof item.body_text !== "string");
75406
+ const first = truncated[0];
75407
+ if (first === undefined)
75408
+ return;
75409
+ const ordinal = typeof first.block_ordinal === "number" ? first.block_ordinal : undefined;
75410
+ const estimate = typeof first.full_text_token_estimate === "number" ? first.full_text_token_estimate : input.tokenBudget;
75411
+ return [{
75412
+ code: "align-block-preview-truncated",
75413
+ severity: "warning",
75414
+ message: `${truncated.length} shown block preview(s) are truncated while full block text is available.`,
75415
+ next_action: "Narrow to a single block with --range <ordinal>:<ordinal> so the blocks view can include body_text before drafting decisions.",
75416
+ ...ordinal !== undefined ? {
75417
+ command: alignBlocksCommand(input.record, {
75418
+ ...input.options,
75419
+ range: `${ordinal}:${ordinal}`,
75420
+ tokenBudget: String(Math.max(input.tokenBudget, estimate + FULL_TEXT_METADATA_TOKEN_BUFFER))
75421
+ })
75422
+ } : {}
75423
+ }];
75424
+ }
74686
75425
  function compactAlignSegmentBlocks(record, value, options) {
74687
75426
  const windows = alignWindowRows(value);
74688
75427
  const selectedWindow = typeof options.windowId === "string" ? resolveWindowSelector(windows, options.windowId) : undefined;
@@ -74699,8 +75438,8 @@ function compactAlignSegmentBlocks(record, value, options) {
74699
75438
  const range = parseOrdinalRange(options.range);
74700
75439
  const blocks = alignBlockRows(value).filter((block) => (options.sourceId === undefined || block.source_id === options.sourceId) && (windowBlockIds === undefined || typeof block.block_id === "string" && windowBlockIds.has(block.block_id)) && headingMatches(block.heading_path, options.heading) && (range === undefined || block.ordinal_in_source >= range.start && block.ordinal_in_source <= range.end));
74701
75440
  const detailContext = blockDetailContext(blocks);
74702
- const detailItems = blockDetailItems(blocks, detailContext);
74703
75441
  const tokenBudget = parseTokenBudgetOption(options.tokenBudget);
75442
+ const detailItems = blockDetailItems(blocks, detailContext, { tokenBudget });
74704
75443
  const window2 = buildTokenBudgetWindow({
74705
75444
  entries: detailItems.map((item) => ({
74706
75445
  item: item.row,
@@ -74712,6 +75451,7 @@ function compactAlignSegmentBlocks(record, value, options) {
74712
75451
  howToExplore: alignBlocksHowToExplore({ record, blocks, options, tokenBudget }),
74713
75452
  previewItem: (row) => row
74714
75453
  });
75454
+ const agentHints = alignBlockPreviewHints({ record, items: window2.items, options, tokenBudget });
74715
75455
  return {
74716
75456
  mode: "detail",
74717
75457
  filters: compactFilters(options),
@@ -74731,7 +75471,8 @@ function compactAlignSegmentBlocks(record, value, options) {
74731
75471
  counts: {
74732
75472
  matched_blocks: blocks.length,
74733
75473
  shown_blocks: window2.items.length
74734
- }
75474
+ },
75475
+ ...agentHints !== undefined ? { agent_hints: agentHints } : {}
74735
75476
  };
74736
75477
  }
74737
75478
  function compactAlignSegmentSourceMapping(value, options) {
@@ -77127,6 +77868,13 @@ function formatDraftPlanHeadline(result) {
77127
77868
  function shellQuote2(value) {
77128
77869
  return `'${value.replace(/'/g, "'\\''")}'`;
77129
77870
  }
77871
+ function formatDraftPatchHeadline(result) {
77872
+ const counts = actionCounts(result);
77873
+ return `${counts.total} actions parsed; draft patched; re-prepare required`;
77874
+ }
77875
+ function compileDraftPrepareCommand(slug) {
77876
+ return `context compile --draft ${shellQuote2(slug)} --plan --prepare --view summary --format json`;
77877
+ }
77130
77878
  function draftPlanBody(input) {
77131
77879
  const { ctxDir, slug, saved, hints, draftDigest, result } = input;
77132
77880
  const draftPath = workflowOutputPath(ctxDir, "compile", `compile.${slug}.draft.yaml`);
@@ -77164,7 +77912,7 @@ function draftPlanBody(input) {
77164
77912
  lines.push("- continue: `/context:compile`");
77165
77913
  if (draftDigest !== undefined) {
77166
77914
  lines.push(`- revise: \`context compile --draft-patch ${shellQuote2(slug)} --input - --plan\``);
77167
- lines.push(`- prepare saved draft: \`context compile --draft ${shellQuote2(slug)} --plan --prepare --view summary --format json\``);
77915
+ lines.push(`- prepare saved draft: \`${compileDraftPrepareCommand(slug)}\``);
77168
77916
  lines.push(`- primitive: \`context reconcile prepare --mode compile --node ${shellQuote2(slug)} --view summary --format json\``);
77169
77917
  } else if (saved) {
77170
77918
  lines.push(`- saved draft: \`${draftPath}\``);
@@ -77478,7 +78226,7 @@ init_generationPolicy();
77478
78226
  var import_yaml38 = __toESM(require_dist(), 1);
77479
78227
  import { createHash as createHash9 } from "node:crypto";
77480
78228
  import { mkdir as mkdir29, writeFile as writeFile20 } from "node:fs/promises";
77481
- import { dirname as dirname25 } from "node:path";
78229
+ import { dirname as dirname26 } from "node:path";
77482
78230
 
77483
78231
  // src/workflow/alignCandidates.ts
77484
78232
  init_rawBlocks();
@@ -77779,7 +78527,7 @@ async function writeAlignSegments(options) {
77779
78527
  const payload = await buildAlignSegments(options);
77780
78528
  if (options.dryRun !== true) {
77781
78529
  const outPath = options.outputPath ?? workflowOutputPath(options.ctxDir, "align", "align.segments.yaml");
77782
- await mkdir29(dirname25(outPath), { recursive: true });
78530
+ await mkdir29(dirname26(outPath), { recursive: true });
77783
78531
  await writeFile20(outPath, import_yaml38.default.stringify(payload), "utf8");
77784
78532
  }
77785
78533
  return payload;
@@ -79320,6 +80068,12 @@ var ALIGN_WORKFLOW_FORBIDDEN_FIELDS = [
79320
80068
  ];
79321
80069
  var termTags = TERM_TAG_VALUES;
79322
80070
  var entityTags = [...ENTITY_TAG_A_VALUES, ...ENTITY_TAG_B_VALUES, ...termTags];
80071
+ var entityTagGroups = {
80072
+ tag_a: ENTITY_TAG_A_VALUES,
80073
+ tag_b: ENTITY_TAG_B_VALUES,
80074
+ standalone: termTags,
80075
+ allowed_shapes: ["one tag_a", "one tag_b", "one tag_a plus one tag_b", "standalone only"]
80076
+ };
79323
80077
  var TERM_ENTITY_NOTES = [
79324
80078
  "Node type order: action first only when scale plus process evidence both pass; otherwise concrete/term entity; otherwise child-bearing domain; otherwise no Node.",
79325
80079
  "Classify the Node by evidence referent, not by source title or heading. Source title/heading is ordinary evidence and does not automatically become node.title, aliases[], or slug.",
@@ -79569,6 +80323,7 @@ var SCHEMAS = {
79569
80323
  consumer: "CLI ledger reducer",
79570
80324
  role: "One transactional batch of candidate ledger operations.",
79571
80325
  enums: enums(),
80326
+ entity_tag_groups: entityTagGroups,
79572
80327
  required: ["batch_id", "ops"],
79573
80328
  forbidden_fields: ALIGN_WORKFLOW_FORBIDDEN_FIELDS,
79574
80329
  example: {
@@ -79659,6 +80414,7 @@ var SCHEMAS = {
79659
80414
  consumer: "next Discovery batch / CLI aggregate / LLM resolution",
79660
80415
  role: "Current rolling candidate snapshot, not the operation stream.",
79661
80416
  enums: enums(),
80417
+ entity_tag_groups: entityTagGroups,
79662
80418
  required: ["schema_version", "last_batch_id", "candidates", "block_dispositions"],
79663
80419
  forbidden_fields: ALIGN_WORKFLOW_FORBIDDEN_FIELDS,
79664
80420
  example: {
@@ -79749,6 +80505,7 @@ var SCHEMAS = {
79749
80505
  consumer: "CLI finalize",
79750
80506
  role: "Final Node structure, depends_on edges, planned Sections, and block ownership.",
79751
80507
  enums: enums(),
80508
+ entity_tag_groups: entityTagGroups,
79752
80509
  mount_matrix: sectionMountMatrix(),
79753
80510
  required: ["schema_version", "nodes", "sections", "edges", "block_ownership"],
79754
80511
  forbidden_fields: ALIGN_WORKFLOW_FORBIDDEN_FIELDS,
@@ -80778,7 +81535,7 @@ init_sourceOwnership();
80778
81535
  init_workflowOutputPaths();
80779
81536
  var import_yaml39 = __toESM(require_dist(), 1);
80780
81537
  import { mkdir as mkdir30, writeFile as writeFile21 } from "node:fs/promises";
80781
- import { dirname as dirname26 } from "node:path";
81538
+ import { dirname as dirname27 } from "node:path";
80782
81539
  function lineRange2(block) {
80783
81540
  return `L${block.line_start}-L${block.line_end}`;
80784
81541
  }
@@ -80838,7 +81595,7 @@ function renderMarkdown2(input) {
80838
81595
  async function renderStructureDecisionProposal(input) {
80839
81596
  const path9 = workflowOutputPath(input.ctxDir, "align", "align.propose.md");
80840
81597
  const debugPath = workflowOutputPath(input.ctxDir, "align", "align.propose.yaml");
80841
- await mkdir30(dirname26(path9), { recursive: true });
81598
+ await mkdir30(dirname27(path9), { recursive: true });
80842
81599
  await writeFile21(path9, renderMarkdown2(input), "utf8");
80843
81600
  await writeFile21(debugPath, import_yaml39.default.stringify({
80844
81601
  schema_version: input.decision.schema_version,
@@ -83314,7 +84071,7 @@ async function runDraftPatch(input) {
83314
84071
  scopeId: nodeRunIdForSlug(input.slug),
83315
84072
  draft: normalizedDraft
83316
84073
  });
83317
- const revisionCommand = compileDraftRevisionHints(result.slug, nextPayload.digest)[0].command ?? `context compile --draft-patch ${result.slug} --input - --plan`;
84074
+ const prepareCommand = compileDraftPrepareCommand(result.slug);
83318
84075
  const outputHints = [...patch.agent_hints ?? [], ...result.agent_hints ?? []];
83319
84076
  if (compileChangesFormat(input.options.format) === "json") {
83320
84077
  writeJson3({
@@ -83331,7 +84088,7 @@ async function runDraftPatch(input) {
83331
84088
  },
83332
84089
  action_counts: draftActionCounts(normalizedDraft),
83333
84090
  agent_hints: outputHints,
83334
- next_command: revisionCommand
84091
+ next_command: prepareCommand
83335
84092
  });
83336
84093
  return;
83337
84094
  }
@@ -83339,7 +84096,7 @@ async function runDraftPatch(input) {
83339
84096
  symbol: outputHints.some((hint) => hint.severity === "warning" || hint.severity === "error") ? "⚠" : "✓",
83340
84097
  action: "patched",
83341
84098
  subject: input.slug,
83342
- headline: formatDraftPlanHeadline(result),
84099
+ headline: formatDraftPatchHeadline(result),
83343
84100
  body: [
83344
84101
  workflowSummaryLine(current),
83345
84102
  ...draftPlanBody({
@@ -83351,7 +84108,7 @@ async function runDraftPatch(input) {
83351
84108
  result
83352
84109
  })
83353
84110
  ],
83354
- next: revisionCommand
84111
+ next: prepareCommand
83355
84112
  }));
83356
84113
  }
83357
84114
  async function draftFromSavedSession(input) {
@@ -84124,7 +84881,7 @@ function registerWorkflowCommands(program2) {
84124
84881
  init_cliFeedback();
84125
84882
  init_errors();
84126
84883
  import { lstat as lstat3, readdir as readdir27 } from "node:fs/promises";
84127
- import { isAbsolute as isAbsolute8, join as join64, relative as relative15, resolve as resolve14 } from "node:path";
84884
+ import { isAbsolute as isAbsolute8, join as join65, relative as relative16, resolve as resolve15 } from "node:path";
84128
84885
 
84129
84886
  // src/lib/pathFreeAgentHintWorkflowInventory.ts
84130
84887
  var AGENT_HINT_EMITTER_WORKFLOW_INVENTORY = [
@@ -84881,6 +85638,13 @@ var AGENT_HINT_EMITTER_INVENTORY = [
84881
85638
  handles: ["edge_owner", "node_slug", "issue_code"],
84882
85639
  notes: "Graph verify next actions route to compile close, compile --code, or edge repair."
84883
85640
  },
85641
+ {
85642
+ source: "src/mdrive/verifyRenderBlocks.ts",
85643
+ family: "mdrive render-block verify",
85644
+ policy: "semantic",
85645
+ handles: ["node_slug", "issue_code", "line"],
85646
+ notes: "Render-block verify next actions route to compile close or target Node repair without storage probing."
85647
+ },
84884
85648
  {
84885
85649
  source: "src/mdrive/verifySourceRules.ts",
84886
85650
  family: "mdrive source verify",
@@ -84895,6 +85659,13 @@ var AGENT_HINT_EMITTER_INVENTORY = [
84895
85659
  handles: ["issue_code", "next_action"],
84896
85660
  notes: "Verify issue type exposes next_action as a semantic repair field."
84897
85661
  },
85662
+ {
85663
+ source: "src/mdrive/verifyWorkspaceReader.ts",
85664
+ family: "mdrive workspace reader verify",
85665
+ policy: "semantic",
85666
+ handles: ["node_slug", "issue_code", "line"],
85667
+ notes: "Reader-surfaced render-block issues use semantic Node and line handles."
85668
+ },
84898
85669
  {
84899
85670
  source: "src/reconcile/prepare.ts",
84900
85671
  family: "reconcile prepare",
@@ -84974,9 +85745,9 @@ function writeJson4(value) {
84974
85745
  `);
84975
85746
  }
84976
85747
  function workspacePath(ctxDir, relPath, label) {
84977
- const base = resolve14(ctxDir);
84978
- const path9 = resolve14(base, relPath);
84979
- const pathRelativeToBase = relative15(base, path9);
85748
+ const base = resolve15(ctxDir);
85749
+ const path9 = resolve15(base, relPath);
85750
+ const pathRelativeToBase = relative16(base, path9);
84980
85751
  if (pathRelativeToBase.startsWith("..") || isAbsolute8(pathRelativeToBase)) {
84981
85752
  throw new ContextError(ExitCode.UserError, "debug path resolved outside the context workspace", {
84982
85753
  category: ErrorCategory.UserInputInvalid,
@@ -85112,11 +85883,11 @@ async function debugSourceRawPath(ctx, options) {
85112
85883
  function workspaceStorageRoots(ctxDir) {
85113
85884
  return {
85114
85885
  ctxDir,
85115
- raw: join64(ctxDir, "raw"),
85116
- knowledge: join64(ctxDir, "knowledge"),
85117
- output: join64(ctxDir, "output"),
85118
- archive: join64(ctxDir, "archive"),
85119
- decisions: join64(ctxDir, "decisions"),
85886
+ raw: join65(ctxDir, "raw"),
85887
+ knowledge: join65(ctxDir, "knowledge"),
85888
+ output: join65(ctxDir, "output"),
85889
+ archive: join65(ctxDir, "archive"),
85890
+ decisions: join65(ctxDir, "decisions"),
85120
85891
  user_cache: workspaceUserCacheRoot(ctxDir)
85121
85892
  };
85122
85893
  }
@@ -85207,7 +85978,7 @@ async function knowledgeNodeTypeDirs(ctxDir) {
85207
85978
  async function inspectNodeObject(ctxDir, slug) {
85208
85979
  const typeDirs = await knowledgeNodeTypeDirs(ctxDir);
85209
85980
  const candidates = await Promise.all(typeDirs.map((type) => {
85210
- const relPath = join64("knowledge", type, `${slug}.md`);
85981
+ const relPath = join65("knowledge", type, `${slug}.md`);
85211
85982
  return inspectWorkspacePath(ctxDir, relPath, relPath);
85212
85983
  }));
85213
85984
  return {
@@ -85223,7 +85994,7 @@ function safeArchiveSourceSegment(value) {
85223
85994
  async function inspectArchiveObject(ctx, archiveId) {
85224
85995
  const ctxDir = requireContext2(ctx, "debug storage inspect");
85225
85996
  const source2 = (ctx.sources?.sources ?? []).find((entry) => entry.id === archiveId);
85226
- const directRel = join64("archive", "sources", safeArchiveSourceSegment(archiveId));
85997
+ const directRel = join65("archive", "sources", safeArchiveSourceSegment(archiveId));
85227
85998
  const paths = [
85228
85999
  ...source2?.archive_path !== undefined ? [await inspectWorkspacePath(ctxDir, source2.archive_path, "source.archive_path")] : [],
85229
86000
  await inspectWorkspacePath(ctxDir, directRel, directRel)
@@ -85271,7 +86042,7 @@ async function walkForFileName(root2, fileName, out2 = []) {
85271
86042
  throw error;
85272
86043
  }
85273
86044
  for (const entry of entries) {
85274
- const child = join64(root2, entry.name);
86045
+ const child = join65(root2, entry.name);
85275
86046
  if (entry.isDirectory()) {
85276
86047
  await walkForFileName(child, fileName, out2);
85277
86048
  } else if (entry.isFile() && entry.name === fileName) {
@@ -85281,7 +86052,7 @@ async function walkForFileName(root2, fileName, out2 = []) {
85281
86052
  return out2;
85282
86053
  }
85283
86054
  async function inspectReviewObject(ctxDir, reviewId) {
85284
- const matches = await walkForFileName(join64(ctxDir, "output", "workflows"), `review.${reviewId}.yaml`);
86055
+ const matches = await walkForFileName(join65(ctxDir, "output", "workflows"), `review.${reviewId}.yaml`);
85285
86056
  return {
85286
86057
  object: `review:${reviewId}`,
85287
86058
  kind: "review",
@@ -85291,7 +86062,7 @@ async function inspectReviewObject(ctxDir, reviewId) {
85291
86062
  }
85292
86063
  async function inspectWorkflowObject(ctxDir, workflowId) {
85293
86064
  const current = await readCurrentWorkflow(ctxDir);
85294
- const workflowRoot = workspacePath(ctxDir, join64("output", "workflows", safePathSegment2(workflowId)), "workflow output root");
86065
+ const workflowRoot = workspacePath(ctxDir, join65("output", "workflows", safePathSegment2(workflowId)), "workflow output root");
85295
86066
  const currentMatches = current.status === "ready" && current.state.workflow_id === workflowId;
85296
86067
  return {
85297
86068
  object: `workflow:${workflowId}`,
@@ -85445,10 +86216,10 @@ async function buildDebugSnapshot(ctx) {
85445
86216
  manifest: cache.manifest
85446
86217
  },
85447
86218
  counts: {
85448
- raw_entries: await countDirectEntries(join64(ctxDir, "raw")),
85449
- knowledge_entries: await countDirectEntries(join64(ctxDir, "knowledge")),
85450
- output_entries: await countDirectEntries(join64(ctxDir, "output")),
85451
- archive_entries: await countDirectEntries(join64(ctxDir, "archive"))
86219
+ raw_entries: await countDirectEntries(join65(ctxDir, "raw")),
86220
+ knowledge_entries: await countDirectEntries(join65(ctxDir, "knowledge")),
86221
+ output_entries: await countDirectEntries(join65(ctxDir, "output")),
86222
+ archive_entries: await countDirectEntries(join65(ctxDir, "archive"))
85452
86223
  }
85453
86224
  };
85454
86225
  }
@@ -85522,11 +86293,11 @@ init_errors();
85522
86293
  init_cliFeedback();
85523
86294
  init_config();
85524
86295
  init_errors();
85525
- import { join as join68 } from "node:path";
86296
+ import { join as join69 } from "node:path";
85526
86297
 
85527
86298
  // src/build/llms.ts
85528
86299
  import { writeFile as writeFile22 } from "node:fs/promises";
85529
- import { join as join66 } from "node:path";
86300
+ import { join as join67 } from "node:path";
85530
86301
 
85531
86302
  // src/build/renderKnowledge.ts
85532
86303
  init_nodeRenderer();
@@ -85609,7 +86380,7 @@ init_nodeRenderer();
85609
86380
  init_exitCode();
85610
86381
  init_knowledge();
85611
86382
  import { mkdir as mkdir31 } from "node:fs/promises";
85612
- import { join as join65 } from "node:path";
86383
+ import { join as join66 } from "node:path";
85613
86384
  var MAX_TIMESTAMP_COLLISION_ATTEMPTS = 60;
85614
86385
  var SUMMARY_MAX_LENGTH = 160;
85615
86386
  var NODE_TYPE_ORDER = [NodeType2.domain, NodeType2.entity, NodeType2.action];
@@ -85636,11 +86407,11 @@ function isFsCode(error, code3) {
85636
86407
  return typeof error === "object" && error !== null && "code" in error && error.code === code3;
85637
86408
  }
85638
86409
  async function createTimestampedPackageDir(outputRoot, packageName2, now) {
85639
- const packageRoot = join65(outputRoot, packageName2);
86410
+ const packageRoot = join66(outputRoot, packageName2);
85640
86411
  await mkdir31(packageRoot, { recursive: true });
85641
86412
  for (let offsetSeconds = 0;offsetSeconds < MAX_TIMESTAMP_COLLISION_ATTEMPTS; offsetSeconds += 1) {
85642
86413
  const candidateTime = new Date(now.getTime() + offsetSeconds * 1000);
85643
- const candidate = join65(packageRoot, formatBuildTimestamp(candidateTime));
86414
+ const candidate = join66(packageRoot, formatBuildTimestamp(candidateTime));
85644
86415
  try {
85645
86416
  await mkdir31(candidate);
85646
86417
  return candidate;
@@ -85716,10 +86487,10 @@ function renderLlmsIndex(input) {
85716
86487
  }
85717
86488
  async function writeLlmsPackage(input) {
85718
86489
  const packageDir = await createTimestampedPackageDir(input.outputRoot, "llms-pkg", input.now);
85719
- await writeFile22(join66(packageDir, "llms.txt"), renderLlmsIndex(input), "utf8");
86490
+ await writeFile22(join67(packageDir, "llms.txt"), renderLlmsIndex(input), "utf8");
85720
86491
  await Promise.all(input.nodes.map(async (node3) => {
85721
86492
  const parsed = node3.located.parsed.node;
85722
- await writeFile22(join66(packageDir, buildNodeFileName(parsed)), renderExportNodeMarkdown({ node: parsed, sections: node3.activeSections }), "utf8");
86493
+ await writeFile22(join67(packageDir, buildNodeFileName(parsed)), renderExportNodeMarkdown({ node: parsed, sections: node3.activeSections }), "utf8");
85723
86494
  }));
85724
86495
  return {
85725
86496
  packageDir,
@@ -85729,7 +86500,7 @@ async function writeLlmsPackage(input) {
85729
86500
 
85730
86501
  // src/build/skillsPack.ts
85731
86502
  import { mkdir as mkdir32, writeFile as writeFile23 } from "node:fs/promises";
85732
- import { join as join67 } from "node:path";
86503
+ import { join as join68 } from "node:path";
85733
86504
  var KNOWLEDGE_QUERY_PROCEDURE = [
85734
86505
  "1. For a named topic, read the matching wiki file directly from `wikis/`.",
85735
86506
  "2. For a broad question, grep `wikis/` with relevant keywords, then read the matching wiki files.",
@@ -85790,23 +86561,23 @@ function renderKnowledgeQuerySkill() {
85790
86561
  }
85791
86562
  async function writeSkillsPack(input) {
85792
86563
  const packageDir = await createTimestampedPackageDir(input.outputRoot, "skills-pkg", input.now);
85793
- const guidesDir = join67(packageDir, "guides");
85794
- const skillsDir = join67(packageDir, "skills");
85795
- const wikisDir = join67(packageDir, "wikis");
86564
+ const guidesDir = join68(packageDir, "guides");
86565
+ const skillsDir = join68(packageDir, "skills");
86566
+ const wikisDir = join68(packageDir, "wikis");
85796
86567
  await Promise.all([
85797
86568
  mkdir32(guidesDir, { recursive: true }),
85798
86569
  mkdir32(skillsDir, { recursive: true }),
85799
86570
  mkdir32(wikisDir, { recursive: true }),
85800
- mkdir32(join67(packageDir, "rules"), { recursive: true }),
85801
- mkdir32(join67(packageDir, "integrations"), { recursive: true })
86571
+ mkdir32(join68(packageDir, "rules"), { recursive: true }),
86572
+ mkdir32(join68(packageDir, "integrations"), { recursive: true })
85802
86573
  ]);
85803
86574
  await Promise.all([
85804
- writeFile23(join67(guidesDir, "AGENTS.md"), renderAgentsGuide2(input), "utf8"),
85805
- writeFile23(join67(skillsDir, "knowledge-query.md"), renderKnowledgeQuerySkill(), "utf8")
86575
+ writeFile23(join68(guidesDir, "AGENTS.md"), renderAgentsGuide2(input), "utf8"),
86576
+ writeFile23(join68(skillsDir, "knowledge-query.md"), renderKnowledgeQuerySkill(), "utf8")
85806
86577
  ]);
85807
86578
  await Promise.all(input.nodes.map(async (node3) => {
85808
86579
  const parsed = node3.located.parsed.node;
85809
- await writeFile23(join67(wikisDir, buildNodeFileName(parsed)), renderExportNodeMarkdown({ node: parsed, sections: node3.activeSections }), "utf8");
86580
+ await writeFile23(join68(wikisDir, buildNodeFileName(parsed)), renderExportNodeMarkdown({ node: parsed, sections: node3.activeSections }), "utf8");
85810
86581
  }));
85811
86582
  return {
85812
86583
  packageDir,
@@ -85888,7 +86659,7 @@ async function collectExportNodes(ctxDir) {
85888
86659
  return {
85889
86660
  config,
85890
86661
  nodes,
85891
- outputRoot: join68(ctxDir, "output")
86662
+ outputRoot: join69(ctxDir, "output")
85892
86663
  };
85893
86664
  }
85894
86665
  async function buildKnowledgePackage(input) {
@@ -86049,7 +86820,7 @@ function cmdVerifyWorkspace(opts = {}) {
86049
86820
  subject: "knowledge",
86050
86821
  headline: `${result.issues.length} issues (${errors3} error, ${warnings} warning)`,
86051
86822
  body: [
86052
- ...result.issues.map((issue2) => `[${issue2.severity}] ${issue2.code}${issue2.slug ? ` node=${issue2.slug}` : ""}${issue2.sectionId ? ` section=${issue2.sectionId}` : ""}: ${issue2.message}`),
86823
+ ...result.issues.map((issue2) => `[${issue2.severity}] ${issue2.code}${issue2.slug ? ` node=${issue2.slug}` : ""}${issue2.sectionId ? ` section=${issue2.sectionId}` : ""}${issue2.line ? ` line=${issue2.line}` : ""}: ${issue2.message}`),
86053
86824
  ...verifyHintLines(result)
86054
86825
  ]
86055
86826
  }));
@@ -86676,14 +87447,14 @@ function inferErrorCategory(message) {
86676
87447
  }
86677
87448
  function readPackageVersion() {
86678
87449
  try {
86679
- let dir = dirname27(fileURLToPath5(import.meta.url));
87450
+ let dir = dirname28(fileURLToPath5(import.meta.url));
86680
87451
  for (let i = 0;i < 8; i++) {
86681
- const pkg = join69(dir, "package.json");
87452
+ const pkg = join70(dir, "package.json");
86682
87453
  if (existsSync54(pkg)) {
86683
87454
  const parsed = JSON.parse(readFileSync3(pkg, "utf8"));
86684
87455
  return parsed.version ?? "unknown";
86685
87456
  }
86686
- const parent = dirname27(dir);
87457
+ const parent = dirname28(dir);
86687
87458
  if (parent === dir)
86688
87459
  break;
86689
87460
  dir = parent;