dsh-code-index 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -162,6 +162,66 @@ var IMPORT_QUERIES = {
162
162
  (preproc_include) @import
163
163
  `
164
164
  };
165
+ var CALL_QUERIES = {
166
+ typescript: `
167
+ (call_expression function: (identifier) @call)
168
+ (call_expression function: (member_expression property: (property_identifier) @call))
169
+ (new_expression constructor: (identifier) @call)
170
+ `,
171
+ javascript: `
172
+ (call_expression function: (identifier) @call)
173
+ (call_expression function: (member_expression property: (property_identifier) @call))
174
+ (new_expression constructor: (identifier) @call)
175
+ `,
176
+ python: `
177
+ (call function: (identifier) @call)
178
+ (call function: (attribute attribute: (identifier) @call))
179
+ `,
180
+ go: `
181
+ (call_expression function: (identifier) @call)
182
+ (call_expression function: (selector_expression field: (field_identifier) @call))
183
+ `,
184
+ rust: `
185
+ (call_expression function: (identifier) @call)
186
+ (call_expression function: (field_expression field: (field_identifier) @call))
187
+ (call_expression function: (scoped_identifier name: (identifier) @call))
188
+ `,
189
+ java: `
190
+ (method_invocation name: (identifier) @call)
191
+ (object_creation_expression type: (type_identifier) @call)
192
+ `,
193
+ cpp: `
194
+ (call_expression function: (identifier) @call)
195
+ (call_expression function: (field_expression field: (field_identifier) @call))
196
+ (call_expression function: (qualified_identifier name: (identifier) @call))
197
+ `,
198
+ c: `
199
+ (call_expression function: (identifier) @call)
200
+ (call_expression function: (field_expression field: (field_identifier) @call))
201
+ `
202
+ };
203
+ var CALLABLE_NODES = {
204
+ typescript: /* @__PURE__ */ new Set([
205
+ "function_declaration",
206
+ "generator_function_declaration",
207
+ "method_definition",
208
+ "function_expression",
209
+ "arrow_function"
210
+ ]),
211
+ javascript: /* @__PURE__ */ new Set([
212
+ "function_declaration",
213
+ "generator_function_declaration",
214
+ "method_definition",
215
+ "function_expression",
216
+ "arrow_function"
217
+ ]),
218
+ python: /* @__PURE__ */ new Set(["function_definition"]),
219
+ go: /* @__PURE__ */ new Set(["function_declaration", "method_declaration", "func_literal"]),
220
+ rust: /* @__PURE__ */ new Set(["function_item", "function_signature_item"]),
221
+ java: /* @__PURE__ */ new Set(["method_declaration", "constructor_declaration"]),
222
+ cpp: /* @__PURE__ */ new Set(["function_definition", "lambda_expression"]),
223
+ c: /* @__PURE__ */ new Set(["function_definition"])
224
+ };
165
225
  var parserPromise = null;
166
226
  async function getParser() {
167
227
  if (!parserPromise) {
@@ -184,6 +244,7 @@ function getLanguage(id) {
184
244
  const lang = await Language.load(bytes);
185
245
  new Query(lang, QUERIES[id]).delete();
186
246
  new Query(lang, IMPORT_QUERIES[id]).delete();
247
+ new Query(lang, CALL_QUERIES[id]).delete();
187
248
  return lang;
188
249
  });
189
250
  languageCache.set(id, entry);
@@ -232,6 +293,24 @@ async function extractAll(code, id) {
232
293
  } finally {
233
294
  importQuery.delete();
234
295
  }
296
+ const calls = [];
297
+ const callQuery = new Query(lang, CALL_QUERIES[id]);
298
+ try {
299
+ const seen2 = /* @__PURE__ */ new Set();
300
+ for (const cap of callQuery.captures(tree.rootNode)) {
301
+ const name2 = cap.node.text.trim();
302
+ if (!name2) continue;
303
+ const line = cap.node.startPosition.row + 1;
304
+ const from = enclosingCallable(id, cap.node);
305
+ const key = `${from}|${name2}|${line}`;
306
+ if (seen2.has(key)) continue;
307
+ seen2.add(key);
308
+ calls.push({ name: name2, line, from });
309
+ }
310
+ } finally {
311
+ callQuery.delete();
312
+ }
313
+ calls.sort((a, b) => a.line - b.line || a.name.localeCompare(b.name));
235
314
  symbols.sort((a, b) => a.line - b.line);
236
315
  const seen = /* @__PURE__ */ new Set();
237
316
  const deduped = symbols.filter((s) => {
@@ -240,7 +319,7 @@ async function extractAll(code, id) {
240
319
  seen.add(key);
241
320
  return true;
242
321
  });
243
- return { symbols: deduped, imports };
322
+ return { symbols: deduped, imports, calls };
244
323
  } finally {
245
324
  tree.delete();
246
325
  }
@@ -282,6 +361,19 @@ function cppNameOf(node) {
282
361
  }
283
362
  return "";
284
363
  }
364
+ function enclosingCallable(id, node) {
365
+ const callable = CALLABLE_NODES[id];
366
+ for (let parent = node.parent; parent; parent = parent.parent) {
367
+ if (!callable.has(parent.type)) continue;
368
+ const named = nameOf(id, parent);
369
+ if (named) return named;
370
+ const declaration = parent.parent;
371
+ const field = declaration?.type === "variable_declarator" ? "name" : null;
372
+ if (field) return declaration?.childForFieldName?.(field)?.text.trim() ?? "";
373
+ return "";
374
+ }
375
+ return "";
376
+ }
285
377
  function specifierOf(id, node) {
286
378
  switch (node.type) {
287
379
  case "import_statement":
@@ -617,13 +709,14 @@ async function buildIndex(root, options = {}, previous = null) {
617
709
  } catch {
618
710
  return null;
619
711
  }
620
- const { symbols, imports } = await extractAll(code, lang);
712
+ const { symbols, imports, calls } = await extractAll(code, lang);
621
713
  return {
622
714
  path: f.rel,
623
715
  lang,
624
716
  mtimeMs: f.mtimeMs,
625
717
  symbols: symbols.map((s) => ({ ...s, file: f.rel })),
626
- imports
718
+ imports,
719
+ calls
627
720
  };
628
721
  })
629
722
  );
@@ -669,12 +762,22 @@ function indexesEqual(left, right) {
669
762
  return false;
670
763
  }
671
764
  if (file.symbols.length !== other.symbols.length) return false;
765
+ if (!callsEqual(file.calls, other.calls)) return false;
672
766
  return file.symbols.every((symbol, symbolIndex) => {
673
767
  const candidate = other.symbols[symbolIndex];
674
768
  return candidate !== void 0 && symbol.name === candidate.name && symbol.kind === candidate.kind && symbol.file === candidate.file && symbol.line === candidate.line && symbol.endLine === candidate.endLine && symbol.exported === candidate.exported && symbol.signature === candidate.signature;
675
769
  });
676
770
  });
677
771
  }
772
+ function callsEqual(left, right) {
773
+ const a = left ?? [];
774
+ const b = right ?? [];
775
+ if (a.length !== b.length) return false;
776
+ return a.every((call, index) => {
777
+ const other = b[index];
778
+ return other !== void 0 && call.name === other.name && call.line === other.line && call.from === other.from;
779
+ });
780
+ }
678
781
  async function findRepoRoot(startDir, maxLevels = 12) {
679
782
  let dir = path4.resolve(startDir);
680
783
  for (let level = 0; level < maxLevels; level++) {
@@ -695,6 +798,56 @@ function symbolCount(index) {
695
798
  return index.files.reduce((n, f) => n + f.symbols.length, 0);
696
799
  }
697
800
 
801
+ // src/refgraph.ts
802
+ function buildSymbolTable(index) {
803
+ const table = /* @__PURE__ */ new Map();
804
+ for (const file of index.files) {
805
+ for (const symbol of file.symbols) {
806
+ const list = table.get(symbol.name);
807
+ if (list) list.push(symbol);
808
+ else table.set(symbol.name, [symbol]);
809
+ }
810
+ }
811
+ for (const list of table.values()) {
812
+ list.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
813
+ }
814
+ return table;
815
+ }
816
+ function callerCounts(index) {
817
+ const counts = /* @__PURE__ */ new Map();
818
+ for (const file of index.files) {
819
+ for (const call of file.calls ?? []) {
820
+ counts.set(call.name, (counts.get(call.name) ?? 0) + 1);
821
+ }
822
+ }
823
+ return counts;
824
+ }
825
+ function symbolRefs(index, symbol, limit = 200) {
826
+ const table = buildSymbolTable(index);
827
+ const defs = table.get(symbol) ?? [];
828
+ const callers = [];
829
+ const calleeFirstLine = /* @__PURE__ */ new Map();
830
+ for (const file of index.files) {
831
+ for (const call of file.calls ?? []) {
832
+ if (call.name === symbol) {
833
+ callers.push({ file: file.path, line: call.line, from: call.from });
834
+ }
835
+ if (call.from === symbol && !calleeFirstLine.has(call.name)) {
836
+ calleeFirstLine.set(call.name, call.line);
837
+ }
838
+ }
839
+ }
840
+ callers.sort(
841
+ (a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.from.localeCompare(b.from)
842
+ );
843
+ const callees = [...calleeFirstLine.entries()].map(([name2, line]) => ({ name: name2, line, targets: table.get(name2) ?? [] })).sort((a, b) => a.line - b.line || a.name.localeCompare(b.name));
844
+ return {
845
+ defs,
846
+ callers: callers.slice(0, limit),
847
+ callees: callees.slice(0, limit)
848
+ };
849
+ }
850
+
698
851
  // src/search.ts
699
852
  function matchScore(name2, query) {
700
853
  const q = query.toLowerCase();
@@ -727,8 +880,9 @@ function searchSymbols(index, filter, limit = 50) {
727
880
  hits.push({ ...sym, score });
728
881
  }
729
882
  }
883
+ const refs = callerCounts(index);
730
884
  hits.sort((a, b) => {
731
- const ab = Number(b.exported) - Number(a.exported) || b.score - a.score || a.name.localeCompare(b.name) || a.file.localeCompare(b.file);
885
+ const ab = Number(b.exported) - Number(a.exported) || b.score - a.score || (refs.get(b.name) ?? 0) - (refs.get(a.name) ?? 0) || a.name.localeCompare(b.name) || a.file.localeCompare(b.file);
732
886
  return ab;
733
887
  });
734
888
  return hits.slice(0, limit);
@@ -888,6 +1042,76 @@ function renderRepoMap(entries, options = {}) {
888
1042
  return totalSymbols > 0 ? text : "";
889
1043
  }
890
1044
 
1045
+ // src/health.ts
1046
+ function buildModuleGraph(index) {
1047
+ const nodes = index.files.map((file) => file.path).sort((a, b) => a.localeCompare(b));
1048
+ const fileSet = new Set(nodes);
1049
+ const edges = /* @__PURE__ */ new Map();
1050
+ for (const file of index.files) {
1051
+ const targets = /* @__PURE__ */ new Set();
1052
+ for (const spec of file.imports ?? []) {
1053
+ const target = resolveImport(spec, file.path, fileSet);
1054
+ if (target && target !== file.path) targets.add(target);
1055
+ }
1056
+ edges.set(file.path, targets);
1057
+ }
1058
+ return { nodes, edges };
1059
+ }
1060
+ function findCycles(index) {
1061
+ const { nodes, edges } = buildModuleGraph(index);
1062
+ const discovery = /* @__PURE__ */ new Map();
1063
+ const low = /* @__PURE__ */ new Map();
1064
+ const onStack = /* @__PURE__ */ new Set();
1065
+ const stack = [];
1066
+ const components = [];
1067
+ let counter = 0;
1068
+ const visit = (node) => {
1069
+ discovery.set(node, counter);
1070
+ low.set(node, counter);
1071
+ counter++;
1072
+ stack.push(node);
1073
+ onStack.add(node);
1074
+ for (const next of edges.get(node) ?? []) {
1075
+ if (!discovery.has(next)) {
1076
+ visit(next);
1077
+ low.set(node, Math.min(low.get(node), low.get(next)));
1078
+ } else if (onStack.has(next)) {
1079
+ low.set(node, Math.min(low.get(node), discovery.get(next)));
1080
+ }
1081
+ }
1082
+ if (low.get(node) === discovery.get(node)) {
1083
+ const component = [];
1084
+ for (; ; ) {
1085
+ const member = stack.pop();
1086
+ onStack.delete(member);
1087
+ component.push(member);
1088
+ if (member === node) break;
1089
+ }
1090
+ if (component.length > 1) components.push(component.sort());
1091
+ }
1092
+ };
1093
+ for (const node of nodes) {
1094
+ if (!discovery.has(node)) visit(node);
1095
+ }
1096
+ components.sort((a, b) => a[0].localeCompare(b[0]));
1097
+ return components;
1098
+ }
1099
+ var ENTRY_RE = /(^|\/)(index|main|cli|app|server|mod|lib|__init__)\.[A-Za-z0-9]+$|(^|\/)(bin|cmd|scripts)\/|\.config\.[A-Za-z0-9]+$/;
1100
+ var TEST_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx?$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/;
1101
+ function findOrphanModules(index) {
1102
+ const { nodes, edges } = buildModuleGraph(index);
1103
+ const incoming = /* @__PURE__ */ new Map();
1104
+ for (const targets of edges.values()) {
1105
+ for (const target of targets) incoming.set(target, (incoming.get(target) ?? 0) + 1);
1106
+ }
1107
+ const withSymbols = new Set(
1108
+ index.files.filter((file) => file.symbols.length > 0).map((file) => file.path)
1109
+ );
1110
+ return nodes.filter(
1111
+ (path7) => withSymbols.has(path7) && (incoming.get(path7) ?? 0) === 0 && (edges.get(path7)?.size ?? 0) === 0 && !ENTRY_RE.test(path7) && !TEST_RE.test(path7)
1112
+ );
1113
+ }
1114
+
891
1115
  // src/tools.ts
892
1116
  import { defineTool } from "@deepseek-ai/dsh-tools";
893
1117
  import path6 from "path";
@@ -898,7 +1122,8 @@ var DEFAULTS = {
898
1122
  mapTopFiles: 24,
899
1123
  mapMaxChars: 3200,
900
1124
  mapTtlMs: 6e4,
901
- autoInject: true
1125
+ autoInject: true,
1126
+ codeHealth: false
902
1127
  };
903
1128
  var state = { current: { ...DEFAULTS } };
904
1129
  function applyConfig(partial) {
@@ -987,6 +1212,38 @@ async function resolveRoot(arg, exec) {
987
1212
  if (!root) throw new Error(`no git repository found from ${path6.resolve(base)}`);
988
1213
  return root;
989
1214
  }
1215
+ function hitsToSearchMeta(hits) {
1216
+ const byFile = /* @__PURE__ */ new Map();
1217
+ for (const raw of hits) {
1218
+ const hit = raw;
1219
+ if (!hit.file || typeof hit.line !== "number") continue;
1220
+ const label = `${hit.kind ?? ""} ${hit.signature || hit.name || ""}`.trim();
1221
+ const list = byFile.get(hit.file) ?? [];
1222
+ list.push({ lineNumber: hit.line, line: label });
1223
+ byFile.set(hit.file, list);
1224
+ }
1225
+ return {
1226
+ shape: "matches",
1227
+ files: [...byFile.entries()].map(([path7, matches]) => ({ path: path7, matches })),
1228
+ truncated: false,
1229
+ total: hits.length
1230
+ };
1231
+ }
1232
+ function searchCardFromMeta(meta) {
1233
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
1234
+ const record = meta;
1235
+ if (record.shape !== "matches" || !Array.isArray(record.files)) return void 0;
1236
+ const files = record.files.filter(
1237
+ (entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry)
1238
+ );
1239
+ return {
1240
+ card: "search",
1241
+ shape: "matches",
1242
+ files,
1243
+ truncated: record.truncated === true,
1244
+ total: typeof record.total === "number" ? record.total : 0
1245
+ };
1246
+ }
990
1247
  var tools = [
991
1248
  defineTool({
992
1249
  name: "code_index",
@@ -1059,8 +1316,16 @@ var tools = [
1059
1316
  render: (_args, value) => value.map((v) => ({
1060
1317
  type: "text",
1061
1318
  text: renderHit(v)
1062
- }))
1319
+ })),
1320
+ presentationMeta: (_args, value) => hitsToSearchMeta(value)
1063
1321
  },
1322
+ presentCall: (args) => ({
1323
+ card: "generic",
1324
+ title: args.kind ? `List ${args.kind} symbols` : "List symbols",
1325
+ kind: "search",
1326
+ rawInput: { query: args.query, file: args.file, kind: args.kind }
1327
+ }),
1328
+ presentResult: (_args, result) => searchCardFromMeta(result.meta),
1064
1329
  async execute(args, exec) {
1065
1330
  const root = await resolveRoot(args.repoRoot, exec);
1066
1331
  const index = await getIndex(root);
@@ -1095,8 +1360,16 @@ var tools = [
1095
1360
  render: (_args, value) => value.map((v) => ({
1096
1361
  type: "text",
1097
1362
  text: renderHit(v)
1098
- }))
1363
+ })),
1364
+ presentationMeta: (_args, value) => hitsToSearchMeta(value)
1099
1365
  },
1366
+ presentCall: (args) => ({
1367
+ card: "generic",
1368
+ title: `Search "${args.query}"`,
1369
+ kind: "search",
1370
+ rawInput: { query: args.query }
1371
+ }),
1372
+ presentResult: (_args, result) => searchCardFromMeta(result.meta),
1100
1373
  async execute(args, exec) {
1101
1374
  const root = await resolveRoot(args.repoRoot, exec);
1102
1375
  const index = await getIndex(root);
@@ -1132,9 +1405,141 @@ var tools = [
1132
1405
  return `code_index: ${error.message ?? String(error)}`;
1133
1406
  }
1134
1407
  }
1408
+ }),
1409
+ defineTool({
1410
+ name: "code_refs",
1411
+ description: "Trace a symbol through the call graph: who calls it (callers) and what it calls (callees), resolved to in-repo definitions with file:line. Complements code_search \u2014 search finds a definition, code_refs shows how it is used. Resolution is name-based, so like-named symbols are reported as candidate definitions.",
1412
+ parameters: {
1413
+ symbol: {
1414
+ type: "string",
1415
+ required: true,
1416
+ description: "Symbol name to trace (case-sensitive)."
1417
+ },
1418
+ direction: {
1419
+ type: "string",
1420
+ enum: ["callers", "callees", "both"],
1421
+ description: 'Which side of the call graph to return (default "both").'
1422
+ },
1423
+ limit: {
1424
+ type: "number",
1425
+ description: "Max rows per side (default 50)."
1426
+ },
1427
+ repoRoot: {
1428
+ type: "string",
1429
+ description: "Optional absolute repo path; defaults to the session workspace root."
1430
+ }
1431
+ },
1432
+ output: {
1433
+ schema: { type: "string" },
1434
+ render: (_args, value) => [{ type: "text", text: value }]
1435
+ },
1436
+ presentCall: (args) => ({
1437
+ card: "generic",
1438
+ title: `Refs ${args.symbol}`,
1439
+ kind: "search",
1440
+ rawInput: { symbol: args.symbol, direction: args.direction }
1441
+ }),
1442
+ async execute(args, exec) {
1443
+ try {
1444
+ const root = await resolveRoot(args.repoRoot, exec);
1445
+ const index = await getIndex(root);
1446
+ const refs = symbolRefs(index, args.symbol, args.limit ?? 50);
1447
+ const direction = args.direction ?? "both";
1448
+ const lines = [];
1449
+ if (refs.defs.length > 0) {
1450
+ lines.push("definitions:");
1451
+ for (const def of refs.defs) {
1452
+ lines.push(` ${def.kind} ${def.signature || def.name} \u2014 ${def.file}:${def.line}`);
1453
+ }
1454
+ } else {
1455
+ lines.push(`no definition of "${args.symbol}" in this repo`);
1456
+ }
1457
+ if (direction === "callers" || direction === "both") {
1458
+ lines.push("", `callers (${refs.callers.length}):`);
1459
+ if (refs.callers.length === 0) lines.push(" (none)");
1460
+ for (const caller of refs.callers) {
1461
+ lines.push(` ${caller.file}:${caller.line}${caller.from ? ` in ${caller.from}` : ""}`);
1462
+ }
1463
+ }
1464
+ if (direction === "callees" || direction === "both") {
1465
+ lines.push("", `callees (${refs.callees.length}):`);
1466
+ if (refs.callees.length === 0) lines.push(" (none)");
1467
+ for (const callee of refs.callees) {
1468
+ const target = callee.targets[0];
1469
+ lines.push(
1470
+ ` ${callee.name} :${callee.line}${target ? ` \u2192 ${target.file}:${target.line}` : " (external)"}`
1471
+ );
1472
+ }
1473
+ }
1474
+ return lines.join("\n");
1475
+ } catch (error) {
1476
+ return `code_refs: ${error.message ?? String(error)}`;
1477
+ }
1478
+ }
1479
+ }),
1480
+ defineTool({
1481
+ name: "code_health",
1482
+ description: "Report structural health from the import graph: circular dependencies (import cycles) and orphan modules (symbol-bearing files that nothing imports and that import nothing, excluding entry points and tests).",
1483
+ parameters: {
1484
+ limit: {
1485
+ type: "number",
1486
+ description: "Max cycles and orphan rows to list (default 50)."
1487
+ },
1488
+ repoRoot: {
1489
+ type: "string",
1490
+ description: "Optional absolute repo path; defaults to the session workspace root."
1491
+ }
1492
+ },
1493
+ output: {
1494
+ schema: { type: "string" },
1495
+ render: (_args, value) => [{ type: "text", text: value }]
1496
+ },
1497
+ presentCall: () => ({ card: "generic", title: "Code health", kind: "read" }),
1498
+ async execute(args, exec) {
1499
+ try {
1500
+ const root = await resolveRoot(args.repoRoot, exec);
1501
+ const index = await getIndex(root);
1502
+ const limit = args.limit ?? 50;
1503
+ const cycles = findCycles(index);
1504
+ const orphans = findOrphanModules(index);
1505
+ const lines = [`repo health \u2014 ${index.files.length} files`];
1506
+ lines.push("", `circular dependencies (${cycles.length}):`);
1507
+ if (cycles.length === 0) lines.push(" (none)");
1508
+ for (const cycle of cycles.slice(0, limit)) lines.push(` ${cycle.join(" \u2192 ")}`);
1509
+ lines.push("", `orphan modules (${orphans.length}):`);
1510
+ if (orphans.length === 0) lines.push(" (none)");
1511
+ for (const orphan of orphans.slice(0, limit)) lines.push(` ${orphan}`);
1512
+ return lines.join("\n");
1513
+ } catch (error) {
1514
+ return `code_health: ${error.message ?? String(error)}`;
1515
+ }
1516
+ }
1135
1517
  })
1136
1518
  ];
1137
1519
 
1520
+ // src/settings.ts
1521
+ import Schema from "@deepseek-ai/schemastery";
1522
+ var SETTINGS_NAMESPACE = "code-index";
1523
+ var settingsSchema = Schema.object({
1524
+ excludeDirs: Schema.array(Schema.string()).default([]),
1525
+ mapTopFiles: Schema.number().default(24),
1526
+ mapMaxChars: Schema.number().default(3200),
1527
+ mapTtlMs: Schema.number().default(6e4),
1528
+ autoInject: Schema.boolean().default(true),
1529
+ codeHealth: Schema.boolean().default(false)
1530
+ });
1531
+ function registerSettings(getService, pluginConfig, onResolved) {
1532
+ const service = getService("settings");
1533
+ if (!service) return;
1534
+ const scope = service.register(SETTINGS_NAMESPACE, settingsSchema, {
1535
+ base: pluginConfig ?? {}
1536
+ });
1537
+ onResolved(scope.get());
1538
+ scope.watch((next) => {
1539
+ onResolved(next);
1540
+ });
1541
+ }
1542
+
1138
1543
  // src/index.ts
1139
1544
  var name = "dsh-code-index";
1140
1545
  var inject = ["tools", "systemPrompt"];
@@ -1144,7 +1549,17 @@ function apply(ctx, pluginConfig) {
1144
1549
  ctx.effect(() => {
1145
1550
  const disposers = [];
1146
1551
  console.log("[dsh-code-index] plugin loaded");
1552
+ if (ctx.inject) {
1553
+ ctx.inject(["settings"], (settingsCtx) => {
1554
+ registerSettings(
1555
+ (name2) => settingsCtx.get(name2),
1556
+ pluginConfig,
1557
+ (resolved) => applyConfig(resolved)
1558
+ );
1559
+ });
1560
+ }
1147
1561
  for (const tool of tools) {
1562
+ if (tool.name === "code_health" && !getConfig().codeHealth) continue;
1148
1563
  disposers.push(ctx.tools.register(tool));
1149
1564
  console.log(`[dsh-code-index] registered tool: ${tool.name}`);
1150
1565
  }
@@ -1210,9 +1625,14 @@ export {
1210
1625
  apply,
1211
1626
  buildIndex,
1212
1627
  buildIndexWithCache,
1628
+ buildModuleGraph,
1629
+ buildSymbolTable,
1630
+ callerCounts,
1213
1631
  defaultCachePath,
1214
1632
  extractAll,
1215
1633
  extractSymbols,
1634
+ findCycles,
1635
+ findOrphanModules,
1216
1636
  findRepoRoot,
1217
1637
  inject,
1218
1638
  languageForFile,
@@ -1227,6 +1647,7 @@ export {
1227
1647
  scoreFile,
1228
1648
  searchSymbols,
1229
1649
  symbolCount,
1650
+ symbolRefs,
1230
1651
  tools
1231
1652
  };
1232
1653
  //# sourceMappingURL=index.js.map