project-librarian 0.4.0 → 0.4.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.
@@ -2,6 +2,7 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const args_1 = require("./args");
5
+ const agent_surfaces_1 = require("./agent-surfaces");
5
6
  const hooks_1 = require("./hooks");
6
7
  const install_skill_1 = require("./install-skill");
7
8
  const modes_1 = require("./modes");
@@ -39,10 +40,12 @@ Options:
39
40
  --refresh-index Update the managed auto-discovered wiki index block.
40
41
  --capture-inbox Append a candidate note with --title, --content, and optional --category.
41
42
  --glossary-init Create and route the optional glossary page.
43
+ --agents <list> With init/update, write only selected agent surfaces: codex, claude, cursor, gemini, or all. Existing project skill/setup surfaces are preserved by default.
42
44
  --prune-check Report active pages with stale or unresolved signals.
43
45
  --review-migration Sync unit coverage and compatible inbox statuses into migration review files.
44
46
  --no-git-config Install hook files without changing git core.hooksPath.
45
47
  --code-index Build the disposable .project-wiki code evidence index.
48
+ --code-index-health Inspect code evidence cache compatibility and print rebuild guidance without writing.
46
49
  --acknowledge-small-repo With --code-index, proceed below the small-repo scale gate after its cost warning.
47
50
  --incremental With --code-index, require an existing compatible index and update only changes.
48
51
  --code-index-full With --code-index, force a full rebuild even when incremental update is possible.
@@ -68,6 +71,20 @@ Commands:
68
71
  function exitAfterStdoutDrain(code) {
69
72
  process.stdout.write("", () => process.exit(code));
70
73
  }
74
+ function activeCodeEvidenceCliModes() {
75
+ const modes = [
76
+ { active: args_1.codeQueryMode, drainStdout: true, run: (codeIndexModule) => codeIndexModule.runCodeQueryMode() },
77
+ { active: args_1.codeReportMode, drainStdout: true, run: (codeIndexModule) => codeIndexModule.runCodeReportMode() },
78
+ { active: args_1.codeStatusMode, drainStdout: true, run: (codeIndexModule) => codeIndexModule.runCodeStatusMode() },
79
+ { active: args_1.codeFilesMode, drainStdout: true, run: (codeIndexModule) => codeIndexModule.runCodeFilesMode() },
80
+ { active: args_1.codeImpactMode, drainStdout: true, run: (codeIndexModule) => codeIndexModule.runCodeImpactMode() },
81
+ { active: args_1.codeContextPackMode, drainStdout: true, run: (codeIndexModule) => codeIndexModule.runCodeContextPackMode() },
82
+ { active: args_1.codeSearchSymbolMode, drainStdout: true, run: (codeIndexModule) => codeIndexModule.runCodeSearchSymbolMode() },
83
+ { active: args_1.codeIndexHealthMode, drainStdout: true, run: (codeIndexModule) => codeIndexModule.runCodeIndexHealthMode() },
84
+ { active: args_1.codeIndexMode, drainStdout: false, run: (codeIndexModule) => codeIndexModule.runCodeIndexMode() },
85
+ ];
86
+ return modes.filter((mode) => mode.active);
87
+ }
71
88
  if (args_1.helpMode) {
72
89
  printUsage();
73
90
  process.exit(0);
@@ -92,6 +109,11 @@ if (args_1.missingValueOptions.length > 0) {
92
109
  printUsage();
93
110
  process.exit(1);
94
111
  }
112
+ if (args_1.invalidAgentTargets.length > 0) {
113
+ console.error(`invalid --agents entr${args_1.invalidAgentTargets.length === 1 ? "y" : "ies"}: ${args_1.invalidAgentTargets.join(", ")}; expected codex, claude, cursor, gemini, or all`);
114
+ printUsage();
115
+ process.exit(1);
116
+ }
95
117
  if (args_1.command === "update" && args_1.migrateMode) {
96
118
  console.error("update cannot be combined with --migrate or --adopt-existing; use project-librarian --migrate for migration.");
97
119
  process.exit(1);
@@ -147,50 +169,20 @@ else {
147
169
  runInitCommand();
148
170
  }
149
171
  function runInitCommand() {
150
- const activeCodeModes = [args_1.codeQueryMode, args_1.codeReportMode, args_1.codeStatusMode, args_1.codeFilesMode, args_1.codeImpactMode, args_1.codeContextPackMode, args_1.codeSearchSymbolMode, args_1.codeIndexMode].filter(Boolean).length;
151
- if (activeCodeModes > 1) {
152
- console.error("Use one code evidence mode at a time: --code-index, --code-query, --code-report, --code-status, --code-files, --code-impact, --code-context-pack, or --code-search-symbol.");
172
+ const activeCodeModes = activeCodeEvidenceCliModes();
173
+ if (activeCodeModes.length > 1) {
174
+ console.error("Use one code evidence mode at a time: --code-index, --code-index-health, --code-query, --code-report, --code-status, --code-files, --code-impact, --code-context-pack, or --code-search-symbol.");
153
175
  process.exit(1);
154
176
  }
155
- if (args_1.codeQueryMode) {
156
- codeIndex().runCodeQueryMode();
157
- exitAfterStdoutDrain(0);
158
- return;
159
- }
160
- if (args_1.codeReportMode) {
161
- codeIndex().runCodeReportMode();
162
- exitAfterStdoutDrain(0);
163
- return;
164
- }
165
- if (args_1.codeStatusMode) {
166
- codeIndex().runCodeStatusMode();
167
- exitAfterStdoutDrain(0);
168
- return;
169
- }
170
- if (args_1.codeFilesMode) {
171
- codeIndex().runCodeFilesMode();
172
- exitAfterStdoutDrain(0);
173
- return;
174
- }
175
- if (args_1.codeImpactMode) {
176
- codeIndex().runCodeImpactMode();
177
- exitAfterStdoutDrain(0);
178
- return;
179
- }
180
- if (args_1.codeContextPackMode) {
181
- codeIndex().runCodeContextPackMode();
182
- exitAfterStdoutDrain(0);
183
- return;
184
- }
185
- if (args_1.codeSearchSymbolMode) {
186
- codeIndex().runCodeSearchSymbolMode();
187
- exitAfterStdoutDrain(0);
177
+ const activeCodeMode = activeCodeModes[0];
178
+ if (activeCodeMode) {
179
+ activeCodeMode.run(codeIndex());
180
+ if (activeCodeMode.drainStdout)
181
+ exitAfterStdoutDrain(0);
182
+ else
183
+ process.exit(0);
188
184
  return;
189
185
  }
190
- if (args_1.codeIndexMode) {
191
- codeIndex().runCodeIndexMode();
192
- process.exit(0);
193
- }
194
186
  if (args_1.wikiImpactMode) {
195
187
  (0, modes_1.runWikiImpactMode)();
196
188
  exitAfterStdoutDrain(0);
@@ -258,6 +250,20 @@ function runInitCommand() {
258
250
  (0, modes_1.runLintMode)();
259
251
  process.exit(0);
260
252
  }
253
+ if (args_1.refreshIndexMode && !args_1.migrateMode && !args_1.glossaryMode && !args_1.captureInboxMode) {
254
+ runRefreshIndexOnlyMode();
255
+ process.exit(0);
256
+ }
257
+ const selectedAgentSurfaces = args_1.agentTargets.length > 0
258
+ ? args_1.agentTargets
259
+ : args_1.migrateMode
260
+ ? Array.from(agent_surfaces_1.allAgentSurfaces)
261
+ : (0, agent_surfaces_1.resolveBootstrapAgentSurfaces)(args_1.agentTargets, workspace_1.exists, workspace_1.read);
262
+ const shouldWriteSurface = (surface) => (0, agent_surfaces_1.includesAgentSurface)(selectedAgentSurfaces, surface);
263
+ const writeCodexSurface = shouldWriteSurface("codex");
264
+ const writeClaudeSurface = shouldWriteSurface("claude");
265
+ const writeCursorSurface = shouldWriteSurface("cursor");
266
+ const writeGeminiSurface = shouldWriteSurface("gemini");
261
267
  const migrationState = args_1.migrateMode ? (0, migration_1.prepareMigrationMode)() : null;
262
268
  const results = [];
263
269
  if (migrationState)
@@ -267,11 +273,16 @@ function runInitCommand() {
267
273
  (0, workspace_1.mkdirp)("wiki/inbox");
268
274
  (0, workspace_1.mkdirp)("wiki/meta");
269
275
  (0, workspace_1.mkdirp)("wiki/sources");
270
- (0, workspace_1.mkdirp)(".codex/hooks");
271
- (0, workspace_1.mkdirp)(".claude/hooks");
272
- (0, workspace_1.mkdirp)(".cursor/hooks");
273
- (0, workspace_1.mkdirp)(".cursor/rules");
274
- (0, workspace_1.mkdirp)(".gemini/hooks");
276
+ if (writeCodexSurface)
277
+ (0, workspace_1.mkdirp)(".codex/hooks");
278
+ if (writeClaudeSurface)
279
+ (0, workspace_1.mkdirp)(".claude/hooks");
280
+ if (writeCursorSurface) {
281
+ (0, workspace_1.mkdirp)(".cursor/hooks");
282
+ (0, workspace_1.mkdirp)(".cursor/rules");
283
+ }
284
+ if (writeGeminiSurface)
285
+ (0, workspace_1.mkdirp)(".gemini/hooks");
275
286
  (0, workspace_1.mkdirp)(".githooks");
276
287
  // B1 fallback: sync the CURRENT startup.md TL;DR into the managed AGENTS.md block
277
288
  // so non-interactive `codex exec` (which does not run SessionStart hooks) still
@@ -282,23 +293,34 @@ function runInitCommand() {
282
293
  const startupForSync = (0, workspace_1.exists)("wiki/startup.md") ? (0, workspace_1.read)("wiki/startup.md") : templates_1.startup;
283
294
  const startupTldrForAgents = (0, templates_1.extractStartupTldr)(startupForSync);
284
295
  results.push(["AGENTS.md", (0, workspace_1.upsertMarkedSection)("AGENTS.md", "<!-- PROJECT-WIKI-FIRST:START -->", "<!-- PROJECT-WIKI-FIRST:END -->", (0, templates_1.agentsSection)(startupTldrForAgents))]);
285
- results.push(["CLAUDE.md", (0, workspace_1.upsertMarkedSection)("CLAUDE.md", "<!-- PROJECT-WIKI-CLAUDE:START -->", "<!-- PROJECT-WIKI-CLAUDE:END -->", templates_1.claudeSection)]);
286
- results.push(["GEMINI.md", (0, workspace_1.upsertMarkedSection)("GEMINI.md", "<!-- PROJECT-WIKI-GEMINI:START -->", "<!-- PROJECT-WIKI-GEMINI:END -->", templates_1.geminiSection)]);
287
- results.push([".cursor/rules/project-librarian.mdc", (0, workspace_1.writeManaged)(".cursor/rules/project-librarian.mdc", templates_1.cursorRule)]);
296
+ if (writeClaudeSurface)
297
+ results.push(["CLAUDE.md", (0, workspace_1.upsertMarkedSection)("CLAUDE.md", "<!-- PROJECT-WIKI-CLAUDE:START -->", "<!-- PROJECT-WIKI-CLAUDE:END -->", templates_1.claudeSection)]);
298
+ if (writeGeminiSurface)
299
+ results.push(["GEMINI.md", (0, workspace_1.upsertMarkedSection)("GEMINI.md", "<!-- PROJECT-WIKI-GEMINI:START -->", "<!-- PROJECT-WIKI-GEMINI:END -->", templates_1.geminiSection)]);
300
+ if (writeCursorSurface)
301
+ results.push([".cursor/rules/project-librarian.mdc", (0, workspace_1.writeManaged)(".cursor/rules/project-librarian.mdc", templates_1.cursorRule)]);
288
302
  results.push(["wiki/AGENTS.md", (0, workspace_1.upsertMarkedSection)("wiki/AGENTS.md", "<!-- PROJECT-WIKI-INTERNAL:START -->", "<!-- PROJECT-WIKI-INTERNAL:END -->", templates_1.wikiAgentsSection)]);
289
303
  results.push([".githooks/prepare-commit-msg", (0, workspace_1.writeManaged)(".githooks/prepare-commit-msg", hooks_1.gitPrepareCommitMsgHook)]);
290
304
  (0, workspace_1.makeExecutable)(".githooks/prepare-commit-msg");
291
305
  results.push([".githooks/wiki-commit-trailers.js", (0, workspace_1.writeManaged)(".githooks/wiki-commit-trailers.js", hooks_1.gitWikiCommitTrailersScript)]);
292
306
  (0, workspace_1.makeExecutable)(".githooks/wiki-commit-trailers.js");
293
307
  results.push(["git core.hooksPath", (0, hooks_1.upsertGitHooksPath)()]);
294
- results.push([".codex/hooks.json", (0, hooks_1.upsertHookConfig)()]);
295
- results.push([".codex/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".codex/hooks/wiki-session-start.js", hooks_1.hookScript)]);
296
- results.push([".claude/settings.json", (0, hooks_1.upsertClaudeHookConfig)()]);
297
- results.push([".claude/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".claude/hooks/wiki-session-start.js", hooks_1.hookScript)]);
298
- results.push([".cursor/hooks.json", (0, hooks_1.upsertCursorHookConfig)()]);
299
- results.push([".cursor/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".cursor/hooks/wiki-session-start.js", hooks_1.cursorHookScript)]);
300
- results.push([".gemini/settings.json", (0, hooks_1.upsertGeminiHookConfig)()]);
301
- results.push([".gemini/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".gemini/hooks/wiki-session-start.js", hooks_1.hookScript)]);
308
+ if (writeCodexSurface) {
309
+ results.push([".codex/hooks.json", (0, hooks_1.upsertHookConfig)()]);
310
+ results.push([".codex/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".codex/hooks/wiki-session-start.js", hooks_1.hookScript)]);
311
+ }
312
+ if (writeClaudeSurface) {
313
+ results.push([".claude/settings.json", (0, hooks_1.upsertClaudeHookConfig)()]);
314
+ results.push([".claude/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".claude/hooks/wiki-session-start.js", hooks_1.hookScript)]);
315
+ }
316
+ if (writeCursorSurface) {
317
+ results.push([".cursor/hooks.json", (0, hooks_1.upsertCursorHookConfig)()]);
318
+ results.push([".cursor/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".cursor/hooks/wiki-session-start.js", hooks_1.cursorHookScript)]);
319
+ }
320
+ if (writeGeminiSurface) {
321
+ results.push([".gemini/settings.json", (0, hooks_1.upsertGeminiHookConfig)()]);
322
+ results.push([".gemini/hooks/wiki-session-start.js", (0, workspace_1.writeManaged)(".gemini/hooks/wiki-session-start.js", hooks_1.hookScript)]);
323
+ }
302
324
  // Bootstrap-managed MCP registration (preservation-first, idempotent). Claude
303
325
  // Code reads `.mcp.json`, Cursor reads `.cursor/mcp.json`, and Gemini reads
304
326
  // `mcpServers` inside `.gemini/settings.json`. Codex only supports user-level MCP
@@ -307,16 +329,24 @@ function runInitCommand() {
307
329
  // Registration is scale-gated (2026-06-12 decision): below the measured
308
330
  // file-count threshold with no existing .project-wiki index, the rows report the
309
331
  // skip reason instead of writing config; an existing index registers regardless.
310
- const mcpGate = (0, hooks_1.mcpRegistrationGate)();
311
- if (mcpGate.register) {
312
- results.push([".mcp.json", (0, hooks_1.upsertClaudeMcpConfig)()]);
313
- results.push([".cursor/mcp.json", (0, hooks_1.upsertCursorMcpConfig)()]);
314
- results.push([".gemini/settings.json mcpServers", (0, hooks_1.upsertGeminiMcpConfig)()]);
315
- }
316
- else {
317
- results.push([".mcp.json", mcpGate.reason]);
318
- results.push([".cursor/mcp.json", mcpGate.reason]);
319
- results.push([".gemini/settings.json mcpServers", mcpGate.reason]);
332
+ if (writeClaudeSurface || writeCursorSurface || writeGeminiSurface) {
333
+ const mcpGate = (0, hooks_1.mcpRegistrationGate)();
334
+ if (mcpGate.register) {
335
+ if (writeClaudeSurface)
336
+ results.push([".mcp.json", (0, hooks_1.upsertClaudeMcpConfig)()]);
337
+ if (writeCursorSurface)
338
+ results.push([".cursor/mcp.json", (0, hooks_1.upsertCursorMcpConfig)()]);
339
+ if (writeGeminiSurface)
340
+ results.push([".gemini/settings.json mcpServers", (0, hooks_1.upsertGeminiMcpConfig)()]);
341
+ }
342
+ else {
343
+ if (writeClaudeSurface)
344
+ results.push([".mcp.json", mcpGate.reason]);
345
+ if (writeCursorSurface)
346
+ results.push([".cursor/mcp.json", mcpGate.reason]);
347
+ if (writeGeminiSurface)
348
+ results.push([".gemini/settings.json mcpServers", mcpGate.reason]);
349
+ }
320
350
  }
321
351
  // Routers accumulate user-maintained project state after bootstrap, so they are
322
352
  // starter files: templates are written only when the file is absent, never rebuilt.
@@ -366,3 +396,12 @@ function runInitCommand() {
366
396
  console.log(`${String(status).padEnd(7)} ${relativePath}`);
367
397
  }
368
398
  }
399
+ function runRefreshIndexOnlyMode() {
400
+ const results = [];
401
+ results.push(["wiki/index.md", (0, workspace_1.writeStarter)("wiki/index.md", templates_1.index)]);
402
+ results.push(["wiki/index.md auto-discovered pages", (0, workspace_1.upsertMarkedSection)("wiki/index.md", "<!-- PROJECT-WIKI-AUTO-INDEX:START -->", "<!-- PROJECT-WIKI-AUTO-INDEX:END -->", (0, modes_1.buildRefreshIndexBlock)())]);
403
+ console.log("Project Librarian refresh-index complete.");
404
+ for (const [relativePath, status] of results) {
405
+ console.log(`${String(status).padEnd(7)} ${relativePath}`);
406
+ }
407
+ }
@@ -37,9 +37,9 @@ exports.runInstallSkillMode = runInstallSkillMode;
37
37
  const fs = __importStar(require("node:fs"));
38
38
  const os = __importStar(require("node:os"));
39
39
  const path = __importStar(require("node:path"));
40
+ const agent_surfaces_1 = require("./agent-surfaces");
40
41
  const args_1 = require("./args");
41
42
  const skillName = "project-librarian";
42
- const allAgentTargets = ["codex", "claude", "cursor", "gemini"];
43
43
  const packageFiles = [
44
44
  "SKILL.md",
45
45
  "dist",
@@ -65,10 +65,10 @@ function installAgents() {
65
65
  const agents = new Set();
66
66
  for (const part of parts) {
67
67
  if (part === "all") {
68
- for (const agent of allAgentTargets)
68
+ for (const agent of agent_surfaces_1.allAgentSurfaces)
69
69
  agents.add(agent);
70
70
  }
71
- else if (allAgentTargets.includes(part)) {
71
+ else if (agent_surfaces_1.allAgentSurfaces.includes(part)) {
72
72
  agents.add(part);
73
73
  }
74
74
  else {
@@ -58,6 +58,7 @@ const fs = __importStar(require("node:fs"));
58
58
  const path = __importStar(require("node:path"));
59
59
  const code_index_1 = require("./code-index");
60
60
  const code_index_file_policy_1 = require("./code-index-file-policy");
61
+ const schema_1 = require("./code-index/schema");
61
62
  const workspace_1 = require("./workspace");
62
63
  // Pinned MCP protocol version. This is the spec revision this server is written
63
64
  // against; one constant so the supported version is auditable in a single place.
@@ -366,8 +367,8 @@ function ownershipAnswer(filePath) {
366
367
  lines.push(`Workspace owner: ${info.owner} (source: ${info.owner_source})${info.codeowners ? `; codeowners ${info.codeowners}` : ""}.`);
367
368
  return lines.join("\n");
368
369
  }
369
- function impactAnswer(database, term) {
370
- const impact = (0, code_index_1.codeImpact)(database, term);
370
+ function impactAnswer(database, term, context) {
371
+ const impact = (0, code_index_1.codeImpact)(database, term, { staleness: context.staleness });
371
372
  const matches = (impact.matches ?? {});
372
373
  const edges = (impact.edges ?? {});
373
374
  const owners = asRows(impact.impacted_owners);
@@ -483,7 +484,7 @@ const TOOLS = [
483
484
  properties: { term: { type: "string", description: "Path, symbol, route, module, or concept to build a compact code context pack for." } },
484
485
  required: ["term"],
485
486
  },
486
- run: (database, args) => (0, code_index_1.codeContextPack)(database, requireStringArg(args, "term")),
487
+ run: (database, args, context) => (0, code_index_1.codeContextPack)(database, requireStringArg(args, "term"), { staleness: context.staleness }),
487
488
  },
488
489
  {
489
490
  name: "code_impact",
@@ -493,7 +494,7 @@ const TOOLS = [
493
494
  properties: { term: { type: "string", description: "File path, symbol name, route, or module to trace." } },
494
495
  required: ["term"],
495
496
  },
496
- run: (database, args) => impactAnswer(database, requireStringArg(args, "term")),
497
+ run: (database, args, context) => impactAnswer(database, requireStringArg(args, "term"), context),
497
498
  },
498
499
  {
499
500
  name: "code_ownership",
@@ -537,6 +538,59 @@ const TOOLS = [
537
538
  },
538
539
  ];
539
540
  const TOOLS_BY_NAME = new Map(TOOLS.map((tool) => [tool.name, tool]));
541
+ let cachedStaleness = null;
542
+ function statFingerprint(relativePath) {
543
+ const absolutePath = path.join(workspace_1.root, relativePath);
544
+ try {
545
+ const stat = fs.statSync(absolutePath);
546
+ if (!stat.isFile())
547
+ return `${relativePath}:not-file`;
548
+ return [
549
+ relativePath,
550
+ stat.size,
551
+ stat.mtimeMs,
552
+ stat.ctimeMs,
553
+ ].join(":");
554
+ }
555
+ catch {
556
+ return `${relativePath}:missing`;
557
+ }
558
+ }
559
+ function codeEvidenceFreshnessCacheKey(opened) {
560
+ try {
561
+ const database = opened.database;
562
+ const scopes = (0, schema_1.indexedScopes)(database);
563
+ const parserMode = (0, schema_1.indexedParserMode)(database);
564
+ const schemaVersion = (0, schema_1.readMetaValue)(database, "schema_version");
565
+ const updatedAt = (0, schema_1.readMetaValue)(database, "updated_at");
566
+ const currentFiles = (0, code_index_file_policy_1.discoverCodeFiles)(scopes.length > 0 ? scopes : ["."]);
567
+ const currentFingerprint = currentFiles.map(statFingerprint).join("\n");
568
+ const indexedFingerprint = database.prepare("SELECT path, hash, mtime_ms, size FROM files ORDER BY path")
569
+ .all()
570
+ .map((row) => `${String(row.path)}:${String(row.hash)}:${Number(row.mtime_ms)}:${Number(row.size)}`)
571
+ .join("\n");
572
+ return [
573
+ opened.relativePath,
574
+ schemaVersion,
575
+ updatedAt,
576
+ parserMode,
577
+ scopes.join("\0"),
578
+ currentFingerprint,
579
+ indexedFingerprint,
580
+ ].join("\n---\n");
581
+ }
582
+ catch {
583
+ return null;
584
+ }
585
+ }
586
+ function codeIndexStalenessForToolCall(opened) {
587
+ const cacheKey = codeEvidenceFreshnessCacheKey(opened);
588
+ if (cacheKey && cachedStaleness?.cacheKey === cacheKey)
589
+ return cachedStaleness.staleness;
590
+ const staleness = (0, code_index_1.codeIndexStaleness)(opened.database);
591
+ cachedStaleness = cacheKey ? { cacheKey, staleness } : null;
592
+ return staleness;
593
+ }
540
594
  // ---------------------------------------------------------------------------
541
595
  // tools/call dispatch
542
596
  // ---------------------------------------------------------------------------
@@ -559,10 +613,10 @@ function callTool(name, rawArgs) {
559
613
  throw error;
560
614
  }
561
615
  try {
562
- const staleness = (0, code_index_1.codeIndexStaleness)(opened.database);
616
+ const staleness = codeIndexStalenessForToolCall(opened);
563
617
  const body = name === "code_status"
564
618
  ? statusAnswer(opened.database, opened.relativePath, staleness)
565
- : TOOLS_BY_NAME.get(name).run(opened.database, args);
619
+ : TOOLS_BY_NAME.get(name).run(opened.database, args, { staleness });
566
620
  return toolResultContent(finalizeAnswer(body, staleness));
567
621
  }
568
622
  catch (error) {
package/dist/migration.js CHANGED
@@ -37,6 +37,7 @@ exports.generatedMigrationInboxFiles = void 0;
37
37
  exports.classifyMarkdown = classifyMarkdown;
38
38
  exports.formOnlyMigrationDocumentReason = formOnlyMigrationDocumentReason;
39
39
  exports.extractMigrationUnits = extractMigrationUnits;
40
+ exports.loadMigrationUnitContext = loadMigrationUnitContext;
40
41
  exports.collectMigrationCoverageDiagnostics = collectMigrationCoverageDiagnostics;
41
42
  exports.collectMigrationUnitMapDiagnostics = collectMigrationUnitMapDiagnostics;
42
43
  exports.collectMigrationSplitPlanDiagnostics = collectMigrationSplitPlanDiagnostics;
@@ -321,6 +322,9 @@ function expectedMigrationUnits() {
321
322
  .flatMap((legacyRoot) => (0, wiki_files_1.walkMarkdownFiles)((0, workspace_1.abs)(legacyRoot), [], (0, workspace_1.abs)(legacyRoot)))
322
323
  .flatMap((file) => extractMigrationUnits(file.basePath, (0, workspace_1.read)(file.path)));
323
324
  }
325
+ function loadMigrationUnitContext() {
326
+ return { units: expectedMigrationUnits() };
327
+ }
324
328
  function isMigrationConfidence(value) {
325
329
  return ["high", "medium", "low"].includes(value);
326
330
  }
@@ -353,8 +357,8 @@ function isReviewedCoverageRetarget(cells) {
353
357
  return note.includes("reviewed low-confidence content; retargeted")
354
358
  || reason.includes("reviewed source context; taxonomy target assigned");
355
359
  }
356
- function collectMigrationCoverageDiagnostics() {
357
- const units = expectedMigrationUnits();
360
+ function collectMigrationCoverageDiagnostics(context = loadMigrationUnitContext()) {
361
+ const units = context.units;
358
362
  if (units.length === 0)
359
363
  return [];
360
364
  if (!(0, workspace_1.exists)("wiki/migration/coverage.md")) {
@@ -417,8 +421,8 @@ function collectMigrationCoverageDiagnostics() {
417
421
  }
418
422
  return diagnostics.sort((a, b) => a.file.localeCompare(b.file) || a.code.localeCompare(b.code) || a.message.localeCompare(b.message));
419
423
  }
420
- function collectMigrationUnitMapDiagnostics() {
421
- const units = expectedMigrationUnits();
424
+ function collectMigrationUnitMapDiagnostics(context = loadMigrationUnitContext()) {
425
+ const units = context.units;
422
426
  if (units.length === 0)
423
427
  return [];
424
428
  const file = "wiki/migration/unit-map.md";
@@ -485,8 +489,8 @@ function collectMigrationUnitMapDiagnostics() {
485
489
  }
486
490
  return diagnostics.sort((a, b) => a.file.localeCompare(b.file) || a.code.localeCompare(b.code) || a.message.localeCompare(b.message));
487
491
  }
488
- function collectMigrationSplitPlanDiagnostics() {
489
- const units = expectedMigrationUnits();
492
+ function collectMigrationSplitPlanDiagnostics(context = loadMigrationUnitContext()) {
493
+ const units = context.units;
490
494
  if (units.length === 0)
491
495
  return [];
492
496
  const file = "wiki/migration/split-plan.md";