trace-mcp 1.21.2 → 1.22.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
@@ -10,10 +10,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
10
  if (typeof require !== "undefined") return require.apply(this, arguments);
11
11
  throw Error('Dynamic require of "' + x + '" is not supported');
12
12
  });
13
- var __glob = (map) => (path65) => {
14
- var fn2 = map[path65];
13
+ var __glob = (map) => (path66) => {
14
+ var fn2 = map[path66];
15
15
  if (fn2) return fn2();
16
- throw new Error("Module not found in bundle: " + path65);
16
+ throw new Error("Module not found in bundle: " + path66);
17
17
  };
18
18
  var __esm = (fn2, res) => function __init() {
19
19
  return fn2 && (res = (0, fn2[__getOwnPropNames(fn2)[0]])(fn2 = 0)), res;
@@ -149,9 +149,9 @@ var require_backend_impl = __commonJS({
149
149
  if (!backend) {
150
150
  throw new Error(`no available backend found. ERR: ${errors.map((e) => `[${e.name}] ${e.err}`).join(", ")}`);
151
151
  }
152
- for (const { name, err: err13 } of errors) {
152
+ for (const { name, err: err14 } of errors) {
153
153
  if (backendHints.includes(name)) {
154
- console.warn(`removing requested execution provider "${name}" from session options because it is not available: ${err13}`);
154
+ console.warn(`removing requested execution provider "${name}" from session options because it is not available: ${err14}`);
155
155
  }
156
156
  }
157
157
  const filteredEps = eps.filter((i) => availableBackendNames.has(typeof i === "string" ? i : i.name));
@@ -2641,18 +2641,18 @@ var require_filesystem = __commonJS({
2641
2641
  var LDD_PATH = "/usr/bin/ldd";
2642
2642
  var SELF_PATH = "/proc/self/exe";
2643
2643
  var MAX_LENGTH = 2048;
2644
- var readFileSync9 = (path65) => {
2645
- const fd = fs56.openSync(path65, "r");
2644
+ var readFileSync10 = (path66) => {
2645
+ const fd = fs56.openSync(path66, "r");
2646
2646
  const buffer = Buffer.alloc(MAX_LENGTH);
2647
2647
  const bytesRead = fs56.readSync(fd, buffer, 0, MAX_LENGTH, 0);
2648
2648
  fs56.close(fd, () => {
2649
2649
  });
2650
2650
  return buffer.subarray(0, bytesRead);
2651
2651
  };
2652
- var readFile = (path65) => new Promise((resolve3, reject) => {
2653
- fs56.open(path65, "r", (err13, fd) => {
2654
- if (err13) {
2655
- reject(err13);
2652
+ var readFile = (path66) => new Promise((resolve3, reject) => {
2653
+ fs56.open(path66, "r", (err14, fd) => {
2654
+ if (err14) {
2655
+ reject(err14);
2656
2656
  } else {
2657
2657
  const buffer = Buffer.alloc(MAX_LENGTH);
2658
2658
  fs56.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
@@ -2666,7 +2666,7 @@ var require_filesystem = __commonJS({
2666
2666
  module.exports = {
2667
2667
  LDD_PATH,
2668
2668
  SELF_PATH,
2669
- readFileSync: readFileSync9,
2669
+ readFileSync: readFileSync10,
2670
2670
  readFile
2671
2671
  };
2672
2672
  }
@@ -2715,7 +2715,7 @@ var require_detect_libc = __commonJS({
2715
2715
  "use strict";
2716
2716
  var childProcess = __require("child_process");
2717
2717
  var { isLinux, getReport } = require_process();
2718
- var { LDD_PATH, SELF_PATH, readFile, readFileSync: readFileSync9 } = require_filesystem();
2718
+ var { LDD_PATH, SELF_PATH, readFile, readFileSync: readFileSync10 } = require_filesystem();
2719
2719
  var { interpreterPath } = require_elf();
2720
2720
  var cachedFamilyInterpreter;
2721
2721
  var cachedFamilyFilesystem;
@@ -2725,8 +2725,8 @@ var require_detect_libc = __commonJS({
2725
2725
  var safeCommand = () => {
2726
2726
  if (!commandOut) {
2727
2727
  return new Promise((resolve3) => {
2728
- childProcess.exec(command, (err13, out) => {
2729
- commandOut = err13 ? " " : out;
2728
+ childProcess.exec(command, (err14, out) => {
2729
+ commandOut = err14 ? " " : out;
2730
2730
  resolve3(commandOut);
2731
2731
  });
2732
2732
  });
@@ -2769,11 +2769,11 @@ var require_detect_libc = __commonJS({
2769
2769
  }
2770
2770
  return null;
2771
2771
  };
2772
- var familyFromInterpreterPath = (path65) => {
2773
- if (path65) {
2774
- if (path65.includes("/ld-musl-")) {
2772
+ var familyFromInterpreterPath = (path66) => {
2773
+ if (path66) {
2774
+ if (path66.includes("/ld-musl-")) {
2775
2775
  return MUSL;
2776
- } else if (path65.includes("/ld-linux-")) {
2776
+ } else if (path66.includes("/ld-linux-")) {
2777
2777
  return GLIBC;
2778
2778
  }
2779
2779
  }
@@ -2807,7 +2807,7 @@ var require_detect_libc = __commonJS({
2807
2807
  }
2808
2808
  cachedFamilyFilesystem = null;
2809
2809
  try {
2810
- const lddContent = readFileSync9(LDD_PATH);
2810
+ const lddContent = readFileSync10(LDD_PATH);
2811
2811
  cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
2812
2812
  } catch (e) {
2813
2813
  }
@@ -2820,8 +2820,8 @@ var require_detect_libc = __commonJS({
2820
2820
  cachedFamilyInterpreter = null;
2821
2821
  try {
2822
2822
  const selfContent = await readFile(SELF_PATH);
2823
- const path65 = interpreterPath(selfContent);
2824
- cachedFamilyInterpreter = familyFromInterpreterPath(path65);
2823
+ const path66 = interpreterPath(selfContent);
2824
+ cachedFamilyInterpreter = familyFromInterpreterPath(path66);
2825
2825
  } catch (e) {
2826
2826
  }
2827
2827
  return cachedFamilyInterpreter;
@@ -2832,9 +2832,9 @@ var require_detect_libc = __commonJS({
2832
2832
  }
2833
2833
  cachedFamilyInterpreter = null;
2834
2834
  try {
2835
- const selfContent = readFileSync9(SELF_PATH);
2836
- const path65 = interpreterPath(selfContent);
2837
- cachedFamilyInterpreter = familyFromInterpreterPath(path65);
2835
+ const selfContent = readFileSync10(SELF_PATH);
2836
+ const path66 = interpreterPath(selfContent);
2837
+ cachedFamilyInterpreter = familyFromInterpreterPath(path66);
2838
2838
  } catch (e) {
2839
2839
  }
2840
2840
  return cachedFamilyInterpreter;
@@ -2896,7 +2896,7 @@ var require_detect_libc = __commonJS({
2896
2896
  }
2897
2897
  cachedVersionFilesystem = null;
2898
2898
  try {
2899
- const lddContent = readFileSync9(LDD_PATH);
2899
+ const lddContent = readFileSync10(LDD_PATH);
2900
2900
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
2901
2901
  if (versionMatch) {
2902
2902
  cachedVersionFilesystem = versionMatch[1];
@@ -4553,21 +4553,21 @@ var require_sharp = __commonJS({
4553
4553
  `@img/sharp-${runtimePlatform}/sharp.node`,
4554
4554
  "@img/sharp-wasm32/sharp.node"
4555
4555
  ];
4556
- var path65;
4556
+ var path66;
4557
4557
  var sharp2;
4558
4558
  var errors = [];
4559
- for (path65 of paths) {
4559
+ for (path66 of paths) {
4560
4560
  try {
4561
- sharp2 = __require(path65);
4561
+ sharp2 = __require(path66);
4562
4562
  break;
4563
- } catch (err13) {
4564
- errors.push(err13);
4563
+ } catch (err14) {
4564
+ errors.push(err14);
4565
4565
  }
4566
4566
  }
4567
- if (sharp2 && path65.startsWith("@img/sharp-linux-x64") && !sharp2._isUsingX64V2()) {
4568
- const err13 = new Error("Prebuilt binaries for linux-x64 require v2 microarchitecture");
4569
- err13.code = "Unsupported CPU";
4570
- errors.push(err13);
4567
+ if (sharp2 && path66.startsWith("@img/sharp-linux-x64") && !sharp2._isUsingX64V2()) {
4568
+ const err14 = new Error("Prebuilt binaries for linux-x64 require v2 microarchitecture");
4569
+ err14.code = "Unsupported CPU";
4570
+ errors.push(err14);
4571
4571
  sharp2 = null;
4572
4572
  }
4573
4573
  if (sharp2) {
@@ -4575,12 +4575,12 @@ var require_sharp = __commonJS({
4575
4575
  } else {
4576
4576
  const [isLinux, isMacOs, isWindows] = ["linux", "darwin", "win32"].map((os8) => runtimePlatform.startsWith(os8));
4577
4577
  const help = [`Could not load the "sharp" module using the ${runtimePlatform} runtime`];
4578
- errors.forEach((err13) => {
4579
- if (err13.code !== "MODULE_NOT_FOUND") {
4580
- help.push(`${err13.code}: ${err13.message}`);
4578
+ errors.forEach((err14) => {
4579
+ if (err14.code !== "MODULE_NOT_FOUND") {
4580
+ help.push(`${err14.code}: ${err14.message}`);
4581
4581
  }
4582
4582
  });
4583
- const messages = errors.map((err13) => err13.message).join(" ");
4583
+ const messages = errors.map((err14) => err14.message).join(" ");
4584
4584
  help.push("Possible solutions:");
4585
4585
  if (isUnsupportedNodeRuntime()) {
4586
4586
  const { found, expected } = isUnsupportedNodeRuntime();
@@ -4633,7 +4633,7 @@ var require_sharp = __commonJS({
4633
4633
  " brew update && brew upgrade vips"
4634
4634
  );
4635
4635
  }
4636
- if (errors.some((err13) => err13.code === "ERR_DLOPEN_DISABLED")) {
4636
+ if (errors.some((err14) => err14.code === "ERR_DLOPEN_DISABLED")) {
4637
4637
  help.push("- Run Node.js without using the --no-addons flag");
4638
4638
  }
4639
4639
  if (isWindows && /The specified procedure could not be found/.test(messages)) {
@@ -5387,18 +5387,18 @@ var require_input = __commonJS({
5387
5387
  if (this._isStreamInput()) {
5388
5388
  this.on("finish", () => {
5389
5389
  this._flattenBufferIn();
5390
- sharp2.metadata(this.options, (err13, metadata2) => {
5391
- if (err13) {
5392
- callback(is2.nativeError(err13, stack2));
5390
+ sharp2.metadata(this.options, (err14, metadata2) => {
5391
+ if (err14) {
5392
+ callback(is2.nativeError(err14, stack2));
5393
5393
  } else {
5394
5394
  callback(null, metadata2);
5395
5395
  }
5396
5396
  });
5397
5397
  });
5398
5398
  } else {
5399
- sharp2.metadata(this.options, (err13, metadata2) => {
5400
- if (err13) {
5401
- callback(is2.nativeError(err13, stack2));
5399
+ sharp2.metadata(this.options, (err14, metadata2) => {
5400
+ if (err14) {
5401
+ callback(is2.nativeError(err14, stack2));
5402
5402
  } else {
5403
5403
  callback(null, metadata2);
5404
5404
  }
@@ -5410,9 +5410,9 @@ var require_input = __commonJS({
5410
5410
  return new Promise((resolve3, reject) => {
5411
5411
  const finished = () => {
5412
5412
  this._flattenBufferIn();
5413
- sharp2.metadata(this.options, (err13, metadata2) => {
5414
- if (err13) {
5415
- reject(is2.nativeError(err13, stack2));
5413
+ sharp2.metadata(this.options, (err14, metadata2) => {
5414
+ if (err14) {
5415
+ reject(is2.nativeError(err14, stack2));
5416
5416
  } else {
5417
5417
  resolve3(metadata2);
5418
5418
  }
@@ -5426,9 +5426,9 @@ var require_input = __commonJS({
5426
5426
  });
5427
5427
  } else {
5428
5428
  return new Promise((resolve3, reject) => {
5429
- sharp2.metadata(this.options, (err13, metadata2) => {
5430
- if (err13) {
5431
- reject(is2.nativeError(err13, stack2));
5429
+ sharp2.metadata(this.options, (err14, metadata2) => {
5430
+ if (err14) {
5431
+ reject(is2.nativeError(err14, stack2));
5432
5432
  } else {
5433
5433
  resolve3(metadata2);
5434
5434
  }
@@ -5443,18 +5443,18 @@ var require_input = __commonJS({
5443
5443
  if (this._isStreamInput()) {
5444
5444
  this.on("finish", () => {
5445
5445
  this._flattenBufferIn();
5446
- sharp2.stats(this.options, (err13, stats2) => {
5447
- if (err13) {
5448
- callback(is2.nativeError(err13, stack2));
5446
+ sharp2.stats(this.options, (err14, stats2) => {
5447
+ if (err14) {
5448
+ callback(is2.nativeError(err14, stack2));
5449
5449
  } else {
5450
5450
  callback(null, stats2);
5451
5451
  }
5452
5452
  });
5453
5453
  });
5454
5454
  } else {
5455
- sharp2.stats(this.options, (err13, stats2) => {
5456
- if (err13) {
5457
- callback(is2.nativeError(err13, stack2));
5455
+ sharp2.stats(this.options, (err14, stats2) => {
5456
+ if (err14) {
5457
+ callback(is2.nativeError(err14, stack2));
5458
5458
  } else {
5459
5459
  callback(null, stats2);
5460
5460
  }
@@ -5466,9 +5466,9 @@ var require_input = __commonJS({
5466
5466
  return new Promise((resolve3, reject) => {
5467
5467
  this.on("finish", function() {
5468
5468
  this._flattenBufferIn();
5469
- sharp2.stats(this.options, (err13, stats2) => {
5470
- if (err13) {
5471
- reject(is2.nativeError(err13, stack2));
5469
+ sharp2.stats(this.options, (err14, stats2) => {
5470
+ if (err14) {
5471
+ reject(is2.nativeError(err14, stack2));
5472
5472
  } else {
5473
5473
  resolve3(stats2);
5474
5474
  }
@@ -5477,9 +5477,9 @@ var require_input = __commonJS({
5477
5477
  });
5478
5478
  } else {
5479
5479
  return new Promise((resolve3, reject) => {
5480
- sharp2.stats(this.options, (err13, stats2) => {
5481
- if (err13) {
5482
- reject(is2.nativeError(err13, stack2));
5480
+ sharp2.stats(this.options, (err14, stats2) => {
5481
+ if (err14) {
5482
+ reject(is2.nativeError(err14, stack2));
5483
5483
  } else {
5484
5484
  resolve3(stats2);
5485
5485
  }
@@ -7475,15 +7475,15 @@ var require_color = __commonJS({
7475
7475
  };
7476
7476
  }
7477
7477
  function wrapConversion(toModel, graph) {
7478
- const path65 = [graph[toModel].parent, toModel];
7478
+ const path66 = [graph[toModel].parent, toModel];
7479
7479
  let fn2 = conversions_default[graph[toModel].parent][toModel];
7480
7480
  let cur = graph[toModel].parent;
7481
7481
  while (graph[cur].parent) {
7482
- path65.unshift(graph[cur].parent);
7482
+ path66.unshift(graph[cur].parent);
7483
7483
  fn2 = link(conversions_default[graph[cur].parent][cur], fn2);
7484
7484
  cur = graph[cur].parent;
7485
7485
  }
7486
- fn2.conversion = path65;
7486
+ fn2.conversion = path66;
7487
7487
  return fn2;
7488
7488
  }
7489
7489
  function route(fromModel) {
@@ -8100,7 +8100,7 @@ var require_channel = __commonJS({
8100
8100
  var require_output = __commonJS({
8101
8101
  "node_modules/sharp/lib/output.js"(exports, module) {
8102
8102
  "use strict";
8103
- var path65 = __require("path");
8103
+ var path66 = __require("path");
8104
8104
  var is2 = require_is();
8105
8105
  var sharp2 = require_sharp();
8106
8106
  var formats = /* @__PURE__ */ new Map([
@@ -8128,19 +8128,19 @@ var require_output = __commonJS({
8128
8128
  var errJp2Save = () => new Error("JP2 output requires libvips with support for OpenJPEG");
8129
8129
  var bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
8130
8130
  function toFile(fileOut, callback) {
8131
- let err13;
8131
+ let err14;
8132
8132
  if (!is2.string(fileOut)) {
8133
- err13 = new Error("Missing output file path");
8134
- } else if (is2.string(this.options.input.file) && path65.resolve(this.options.input.file) === path65.resolve(fileOut)) {
8135
- err13 = new Error("Cannot use same file for input and output");
8136
- } else if (jp2Regex.test(path65.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
8137
- err13 = errJp2Save();
8133
+ err14 = new Error("Missing output file path");
8134
+ } else if (is2.string(this.options.input.file) && path66.resolve(this.options.input.file) === path66.resolve(fileOut)) {
8135
+ err14 = new Error("Cannot use same file for input and output");
8136
+ } else if (jp2Regex.test(path66.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
8137
+ err14 = errJp2Save();
8138
8138
  }
8139
- if (err13) {
8139
+ if (err14) {
8140
8140
  if (is2.fn(callback)) {
8141
- callback(err13);
8141
+ callback(err14);
8142
8142
  } else {
8143
- return Promise.reject(err13);
8143
+ return Promise.reject(err14);
8144
8144
  }
8145
8145
  } else {
8146
8146
  this.options.fileOut = fileOut;
@@ -8854,18 +8854,18 @@ var require_output = __commonJS({
8854
8854
  if (this._isStreamInput()) {
8855
8855
  this.on("finish", () => {
8856
8856
  this._flattenBufferIn();
8857
- sharp2.pipeline(this.options, (err13, data, info) => {
8858
- if (err13) {
8859
- callback(is2.nativeError(err13, stack2));
8857
+ sharp2.pipeline(this.options, (err14, data, info) => {
8858
+ if (err14) {
8859
+ callback(is2.nativeError(err14, stack2));
8860
8860
  } else {
8861
8861
  callback(null, data, info);
8862
8862
  }
8863
8863
  });
8864
8864
  });
8865
8865
  } else {
8866
- sharp2.pipeline(this.options, (err13, data, info) => {
8867
- if (err13) {
8868
- callback(is2.nativeError(err13, stack2));
8866
+ sharp2.pipeline(this.options, (err14, data, info) => {
8867
+ if (err14) {
8868
+ callback(is2.nativeError(err14, stack2));
8869
8869
  } else {
8870
8870
  callback(null, data, info);
8871
8871
  }
@@ -8876,9 +8876,9 @@ var require_output = __commonJS({
8876
8876
  if (this._isStreamInput()) {
8877
8877
  this.once("finish", () => {
8878
8878
  this._flattenBufferIn();
8879
- sharp2.pipeline(this.options, (err13, data, info) => {
8880
- if (err13) {
8881
- this.emit("error", is2.nativeError(err13, stack2));
8879
+ sharp2.pipeline(this.options, (err14, data, info) => {
8880
+ if (err14) {
8881
+ this.emit("error", is2.nativeError(err14, stack2));
8882
8882
  } else {
8883
8883
  this.emit("info", info);
8884
8884
  this.push(data);
@@ -8891,9 +8891,9 @@ var require_output = __commonJS({
8891
8891
  this.emit("finish");
8892
8892
  }
8893
8893
  } else {
8894
- sharp2.pipeline(this.options, (err13, data, info) => {
8895
- if (err13) {
8896
- this.emit("error", is2.nativeError(err13, stack2));
8894
+ sharp2.pipeline(this.options, (err14, data, info) => {
8895
+ if (err14) {
8896
+ this.emit("error", is2.nativeError(err14, stack2));
8897
8897
  } else {
8898
8898
  this.emit("info", info);
8899
8899
  this.push(data);
@@ -8908,9 +8908,9 @@ var require_output = __commonJS({
8908
8908
  return new Promise((resolve3, reject) => {
8909
8909
  this.once("finish", () => {
8910
8910
  this._flattenBufferIn();
8911
- sharp2.pipeline(this.options, (err13, data, info) => {
8912
- if (err13) {
8913
- reject(is2.nativeError(err13, stack2));
8911
+ sharp2.pipeline(this.options, (err14, data, info) => {
8912
+ if (err14) {
8913
+ reject(is2.nativeError(err14, stack2));
8914
8914
  } else {
8915
8915
  if (this.options.resolveWithObject) {
8916
8916
  resolve3({ data, info });
@@ -8923,9 +8923,9 @@ var require_output = __commonJS({
8923
8923
  });
8924
8924
  } else {
8925
8925
  return new Promise((resolve3, reject) => {
8926
- sharp2.pipeline(this.options, (err13, data, info) => {
8927
- if (err13) {
8928
- reject(is2.nativeError(err13, stack2));
8926
+ sharp2.pipeline(this.options, (err14, data, info) => {
8927
+ if (err14) {
8928
+ reject(is2.nativeError(err14, stack2));
8929
8929
  } else {
8930
8930
  if (this.options.resolveWithObject) {
8931
8931
  resolve3({ data, info });
@@ -11561,9 +11561,9 @@ function memoizePromise(key, factory) {
11561
11561
  }
11562
11562
  const promise = factory().then(
11563
11563
  (value) => value,
11564
- (err13) => {
11564
+ (err14) => {
11565
11565
  cache.delete(key);
11566
- return Promise.reject(err13);
11566
+ return Promise.reject(err14);
11567
11567
  }
11568
11568
  );
11569
11569
  cache.put(key, promise);
@@ -11750,8 +11750,8 @@ async function storeCachedResource(path_or_repo_id, filename, cache2, cacheKey,
11750
11750
  headers
11751
11751
  }
11752
11752
  )
11753
- ).catch((err13) => {
11754
- logger2.warn(`Unable to add response to browser cache: ${err13}.`);
11753
+ ).catch((err14) => {
11754
+ logger2.warn(`Unable to add response to browser cache: ${err14}.`);
11755
11755
  });
11756
11756
  }
11757
11757
  }
@@ -11934,9 +11934,9 @@ async function getModelFile(path_or_repo_id, filename, fatal = true, options = {
11934
11934
  INFLIGHT_LOADS.delete(key);
11935
11935
  return result;
11936
11936
  },
11937
- (err13) => {
11937
+ (err14) => {
11938
11938
  INFLIGHT_LOADS.delete(key);
11939
- throw err13;
11939
+ throw err14;
11940
11940
  }
11941
11941
  );
11942
11942
  INFLIGHT_LOADS.set(key, pending);
@@ -14011,8 +14011,8 @@ async function ensureWasmLoaded() {
14011
14011
  ONNX_ENV.wasm.wasmBinary = wasmBinary;
14012
14012
  wasmBinaryLoaded = true;
14013
14013
  }
14014
- } catch (err13) {
14015
- logger2.warn("Failed to pre-load WASM binary:", err13);
14014
+ } catch (err14) {
14015
+ logger2.warn("Failed to pre-load WASM binary:", err14);
14016
14016
  }
14017
14017
  })() : Promise.resolve(),
14018
14018
  // Load and cache the WASM factory as a blob URL
@@ -14022,8 +14022,8 @@ async function ensureWasmLoaded() {
14022
14022
  if (wasmFactoryBlob) {
14023
14023
  ONNX_ENV.wasm.wasmPaths.mjs = wasmFactoryBlob;
14024
14024
  }
14025
- } catch (err13) {
14026
- logger2.warn("Failed to pre-load WASM factory:", err13);
14025
+ } catch (err14) {
14026
+ logger2.warn("Failed to pre-load WASM factory:", err14);
14027
14027
  }
14028
14028
  })() : Promise.resolve()
14029
14029
  ]);
@@ -21166,14 +21166,14 @@ var init_transformers_node = __esm({
21166
21166
  try {
21167
21167
  const evaluated = this.evaluateBlock(node.body, scope);
21168
21168
  result += evaluated.value;
21169
- } catch (err13) {
21170
- if (err13 instanceof ContinueControl) {
21169
+ } catch (err14) {
21170
+ if (err14 instanceof ContinueControl) {
21171
21171
  continue;
21172
21172
  }
21173
- if (err13 instanceof BreakControl) {
21173
+ if (err14 instanceof BreakControl) {
21174
21174
  break;
21175
21175
  }
21176
- throw err13;
21176
+ throw err14;
21177
21177
  }
21178
21178
  noIteration = false;
21179
21179
  }
@@ -21378,7 +21378,7 @@ var init_transformers_node = __esm({
21378
21378
  start(controller) {
21379
21379
  stream.on("data", (chunk2) => controller.enqueue(chunk2));
21380
21380
  stream.on("end", () => controller.close());
21381
- stream.on("error", (err13) => controller.error(err13));
21381
+ stream.on("error", (err14) => controller.error(err14));
21382
21382
  },
21383
21383
  cancel() {
21384
21384
  stream.destroy();
@@ -21655,9 +21655,9 @@ var init_transformers_node = __esm({
21655
21655
  break;
21656
21656
  }
21657
21657
  await new Promise((resolve3, reject) => {
21658
- fileStream.write(value, (err13) => {
21659
- if (err13) {
21660
- reject(err13);
21658
+ fileStream.write(value, (err14) => {
21659
+ if (err14) {
21660
+ reject(err14);
21661
21661
  return;
21662
21662
  }
21663
21663
  resolve3();
@@ -21668,7 +21668,7 @@ var init_transformers_node = __esm({
21668
21668
  progress_callback?.({ progress, loaded, total });
21669
21669
  }
21670
21670
  await new Promise((resolve3, reject) => {
21671
- fileStream.close((err13) => err13 ? reject(err13) : resolve3());
21671
+ fileStream.close((err14) => err14 ? reject(err14) : resolve3());
21672
21672
  });
21673
21673
  await fs3.promises.rename(tmpPath, filePath);
21674
21674
  } catch (error) {
@@ -42998,9 +42998,9 @@ var init_client = __esm({
42998
42998
  this.proc.stderr.on("data", (chunk2) => {
42999
42999
  logger.debug({ lsp: this.command, stderr: chunk2.toString().trim() }, "LSP stderr");
43000
43000
  });
43001
- this.proc.on("error", (err13) => {
43002
- logger.warn({ lsp: this.command, error: err13.message }, "LSP process error");
43003
- this.rejectAll(new Error(`LSP process error: ${err13.message}`));
43001
+ this.proc.on("error", (err14) => {
43002
+ logger.warn({ lsp: this.command, error: err14.message }, "LSP process error");
43003
+ this.rejectAll(new Error(`LSP process error: ${err14.message}`));
43004
43004
  });
43005
43005
  this.proc.on("exit", (code, signal) => {
43006
43006
  logger.debug({ lsp: this.command, code, signal }, "LSP process exited");
@@ -46337,8 +46337,8 @@ var SessionJournal = class _SessionJournal {
46337
46337
  };
46338
46338
  this.entries.push(entry);
46339
46339
  if (tool === "get_symbol" || tool === "get_outline") {
46340
- const path65 = params.path ?? params.file_path ?? "";
46341
- if (path65) this.filesRead.add(path65);
46340
+ const path66 = params.path ?? params.file_path ?? "";
46341
+ if (path66) this.filesRead.add(path66);
46342
46342
  }
46343
46343
  if (resultCount === 0 && this.isSearchTool(tool)) {
46344
46344
  this.zeroResultQueries.set(hash, summary);
@@ -47490,6 +47490,7 @@ var COMPACT_CORE_PARAMS = {
47490
47490
  scan_security: ["file_pattern", "rules"],
47491
47491
  check_quality_gates: ["scope"],
47492
47492
  taint_analysis: ["source_symbol_id", "file_pattern"],
47493
+ export_security_context: ["scope", "depth"],
47493
47494
  audit_config: [],
47494
47495
  // Framework
47495
47496
  get_request_flow: ["route", "method"],
@@ -47515,6 +47516,86 @@ var COMPACT_CORE_PARAMS = {
47515
47516
  batch: ["calls"]
47516
47517
  };
47517
47518
 
47519
+ // src/server/tool-annotations.ts
47520
+ var READ_ONLY = {
47521
+ readOnlyHint: true,
47522
+ destructiveHint: false,
47523
+ idempotentHint: true,
47524
+ openWorldHint: false
47525
+ };
47526
+ var INDEX_MUTATING = {
47527
+ readOnlyHint: false,
47528
+ destructiveHint: false,
47529
+ idempotentHint: true,
47530
+ openWorldHint: false
47531
+ };
47532
+ var FILE_WRITING = {
47533
+ readOnlyHint: false,
47534
+ destructiveHint: false,
47535
+ idempotentHint: false,
47536
+ openWorldHint: false
47537
+ };
47538
+ var FILE_DESTRUCTIVE = {
47539
+ readOnlyHint: false,
47540
+ destructiveHint: true,
47541
+ idempotentHint: false,
47542
+ openWorldHint: false
47543
+ };
47544
+ var OUTPUT_WRITING = {
47545
+ readOnlyHint: false,
47546
+ destructiveHint: false,
47547
+ idempotentHint: true,
47548
+ openWorldHint: false
47549
+ };
47550
+ var RUNTIME_READ = {
47551
+ readOnlyHint: true,
47552
+ destructiveHint: false,
47553
+ idempotentHint: true,
47554
+ openWorldHint: true
47555
+ };
47556
+ var OVERRIDES = {
47557
+ // ── Refactoring: file-destructive ──
47558
+ apply_codemod: FILE_DESTRUCTIVE,
47559
+ remove_dead_code: FILE_DESTRUCTIVE,
47560
+ // ── Refactoring: file-writing (non-destructive) ──
47561
+ apply_rename: FILE_WRITING,
47562
+ apply_move: FILE_WRITING,
47563
+ change_signature: FILE_WRITING,
47564
+ extract_function: FILE_WRITING,
47565
+ // ── Output generation (writes files but doesn't modify source) ──
47566
+ generate_docs: OUTPUT_WRITING,
47567
+ generate_sbom: OUTPUT_WRITING,
47568
+ visualize_graph: OUTPUT_WRITING,
47569
+ visualize_subproject_topology: OUTPUT_WRITING,
47570
+ // ── Index / store mutation (idempotent) ──
47571
+ reindex: INDEX_MUTATING,
47572
+ register_edit: INDEX_MUTATING,
47573
+ embed_repo: INDEX_MUTATING,
47574
+ subproject_add_repo: INDEX_MUTATING,
47575
+ subproject_sync: INDEX_MUTATING,
47576
+ invalidate_decision: INDEX_MUTATING,
47577
+ index_sessions: INDEX_MUTATING,
47578
+ mine_sessions: INDEX_MUTATING,
47579
+ refresh_co_changes: INDEX_MUTATING,
47580
+ detect_communities: INDEX_MUTATING,
47581
+ // ── Store mutation (not idempotent — creates new records) ──
47582
+ add_decision: {
47583
+ readOnlyHint: false,
47584
+ destructiveHint: false,
47585
+ idempotentHint: false,
47586
+ openWorldHint: false
47587
+ },
47588
+ // ── Runtime intelligence (reads external OTLP data) ──
47589
+ get_runtime_profile: RUNTIME_READ,
47590
+ get_runtime_call_graph: RUNTIME_READ,
47591
+ get_endpoint_analytics: RUNTIME_READ,
47592
+ get_runtime_deps: RUNTIME_READ
47593
+ };
47594
+ var DEFAULT_ANNOTATIONS = READ_ONLY;
47595
+ function getToolAnnotations(toolName) {
47596
+ return OVERRIDES[toolName] ?? DEFAULT_ANNOTATIONS;
47597
+ }
47598
+
47518
47599
  // src/server/tool-gate.ts
47519
47600
  function applyParamOverrides(schema, toolOverrides, sharedOverrides) {
47520
47601
  for (const paramName of Object.keys(schema)) {
@@ -47676,9 +47757,23 @@ function installToolGate(server, config, activePreset, savings, journal, j3, ext
47676
47757
  return result;
47677
47758
  };
47678
47759
  }
47760
+ const annotations = getToolAnnotations(name);
47761
+ const lastIdx = args.length - 1;
47762
+ if (typeof args[lastIdx] === "function") {
47763
+ args.splice(lastIdx, 0, annotations);
47764
+ }
47679
47765
  return _originalTool(...args);
47680
47766
  });
47681
- return { _originalTool, registeredToolNames, toolHandlers };
47767
+ const annotatedOriginalTool = ((...oArgs) => {
47768
+ const oName = oArgs[0];
47769
+ const ann = getToolAnnotations(oName);
47770
+ const oLastIdx = oArgs.length - 1;
47771
+ if (typeof oArgs[oLastIdx] === "function") {
47772
+ oArgs.splice(oLastIdx, 0, ann);
47773
+ }
47774
+ return _originalTool(...oArgs);
47775
+ });
47776
+ return { _originalTool: annotatedOriginalTool, registeredToolNames, toolHandlers };
47682
47777
  }
47683
47778
 
47684
47779
  // src/tools/register/core.ts
@@ -52474,7 +52569,7 @@ function registerCoreTools(server, ctx) {
52474
52569
  const { store, registry, config, projectRoot, guardPath, j: j3, jh, journal, vectorStore, embeddingService, progress } = ctx;
52475
52570
  server.tool(
52476
52571
  "get_index_health",
52477
- "Get index status, statistics, health information, and pipeline progress (indexing, summarization, embedding)",
52572
+ "Get index status, statistics, health information, and pipeline progress (indexing, summarization, embedding). Read-only, no side effects. Use to verify the index is ready before running queries. Returns JSON: { totalFiles, totalSymbols, languages, frameworks, pipelineProgress }.",
52478
52573
  {},
52479
52574
  async () => {
52480
52575
  const result = getIndexHealth(store, config);
@@ -52486,7 +52581,7 @@ function registerCoreTools(server, ctx) {
52486
52581
  );
52487
52582
  server.tool(
52488
52583
  "reindex",
52489
- "Trigger (re)indexing of the project or a subdirectory",
52584
+ "Trigger (re)indexing of the project or a subdirectory. Mutates the local index (SQLite). Use after major file changes; for single-file updates prefer register_edit instead. Idempotent \u2014 safe to re-run. Returns JSON: { status, totalFiles, indexed, skipped, errors, durationMs }.",
52490
52585
  {
52491
52586
  path: z2.string().max(512).optional().describe("Subdirectory to index (default: project root)"),
52492
52587
  force: z2.boolean().optional().describe("Skip hash check and reindex all files")
@@ -52504,7 +52599,7 @@ function registerCoreTools(server, ctx) {
52504
52599
  );
52505
52600
  server.tool(
52506
52601
  "embed_repo",
52507
- "Precompute and cache symbol embeddings for semantic / hybrid search. Embeddings are also computed lazily on first semantic query, but calling this once after a fresh index avoids the first-query latency spike. Requires AI provider to be enabled in config (ollama/openai). Set force=true to drop and recompute all existing embeddings.",
52602
+ "Precompute and cache symbol embeddings for semantic / hybrid search. Embeddings are also computed lazily on first semantic query, but calling this once after a fresh index avoids the first-query latency spike. Requires AI provider to be enabled in config (ollama/openai). Set force=true to drop and recompute all existing embeddings. Mutates the vector store; idempotent. Use after reindex when you plan to use semantic search. Returns JSON: { status, indexed_this_run, total_embedded, coverage_pct, duration_ms }.",
52508
52603
  {
52509
52604
  batch_size: z2.number().int().min(1).max(500).optional().describe("Symbols per embedding API batch (default 50)"),
52510
52605
  force: z2.boolean().optional().describe("Drop existing embeddings and re-embed everything (default false \u2014 incremental)")
@@ -52559,7 +52654,7 @@ function registerCoreTools(server, ctx) {
52559
52654
  );
52560
52655
  server.tool(
52561
52656
  "register_edit",
52562
- "Notify trace-mcp that a file was edited. Reindexes the single file and invalidates search caches. Call after Edit/Write to keep index fresh. Also checks for duplicate symbols \u2014 if `_duplication_warnings` appears in the response, you may be recreating existing logic; review the referenced symbols before continuing.",
52657
+ "Notify trace-mcp that a file was edited. Reindexes the single file and invalidates search caches. Call after Edit/Write to keep index fresh \u2014 much lighter than full reindex. Also checks for duplicate symbols \u2014 if `_duplication_warnings` appears in the response, you may be recreating existing logic; review the referenced symbols before continuing. Mutates the index; idempotent. Returns JSON: { status, file, totalFiles, indexed, _duplication_warnings? }.",
52563
52658
  {
52564
52659
  file_path: z2.string().min(1).max(512).describe("Relative path to the edited file")
52565
52660
  },
@@ -52595,7 +52690,7 @@ function registerCoreTools(server, ctx) {
52595
52690
  );
52596
52691
  server.tool(
52597
52692
  "get_project_map",
52598
- "Get project overview: detected frameworks, languages, file counts, structure. Call with summary_only=true at session start to orient yourself before diving into code.",
52693
+ "Get project overview: detected frameworks, languages, file counts, structure. Read-only, no side effects. Call with summary_only=true at session start to orient yourself before diving into code. Use instead of manual ls/find. Returns JSON: { frameworks, languages, fileCount, symbolCount, structure }.",
52599
52694
  {
52600
52695
  summary_only: z2.boolean().optional().describe("Return only framework list + counts (default false)")
52601
52696
  },
@@ -52607,7 +52702,7 @@ function registerCoreTools(server, ctx) {
52607
52702
  );
52608
52703
  server.tool(
52609
52704
  "get_env_vars",
52610
- "List environment variable keys from .env files with inferred value types/formats. Never exposes actual values \u2014 only keys, types (string/number/boolean/empty), and formats (url/email/ip/path/uuid/json/base64/csv/dsn/etc). Use to understand project configuration without accessing secrets.",
52705
+ "List environment variable keys from .env files with inferred value types/formats. Never exposes actual values \u2014 only keys, types (string/number/boolean/empty), and formats (url/email/ip/path/uuid/json/base64/csv/dsn/etc). Read-only, no side effects, safe for secrets. Use to understand project configuration without accessing actual values. Returns JSON grouped by file: { [file]: [{ key, type, format, comment }] }.",
52611
52706
  {
52612
52707
  pattern: z2.string().max(256).optional().describe('Filter keys by pattern (e.g. "DB_" or "REDIS")'),
52613
52708
  file: z2.string().max(512).optional().describe("Filter by specific .env file path")
@@ -53702,9 +53797,9 @@ function deduplicateByFile(rawDeps) {
53702
53797
  }
53703
53798
  }
53704
53799
  const result = [];
53705
- for (const [path65, entry] of fileMap) {
53800
+ for (const [path66, entry] of fileMap) {
53706
53801
  const dep = {
53707
- path: path65,
53802
+ path: path66,
53708
53803
  edgeTypes: [...entry.edgeTypes],
53709
53804
  depth: entry.depth
53710
53805
  };
@@ -56957,7 +57052,7 @@ function registerNavigationTools(server, ctx) {
56957
57052
  const { store, projectRoot, guardPath, j: j3, jh, savings, vectorStore, embeddingService, reranker, markExplored, decisionStore } = ctx;
56958
57053
  server.tool(
56959
57054
  "get_symbol",
56960
- "Look up a symbol by symbol_id or FQN and return its source code. Use instead of Read when you need one specific function/class/method \u2014 returns only the symbol, not the whole file.",
57055
+ "Look up a symbol by symbol_id or FQN and return its source code. Use instead of Read when you need one specific function/class/method \u2014 returns only the symbol, not the whole file. For multiple symbols at once, prefer get_context_bundle. Read-only. Returns JSON: { symbol_id, name, kind, fqn, signature, file, line_start, line_end, source }.",
56961
57056
  {
56962
57057
  symbol_id: z3.string().max(512).optional().describe("The symbol_id to look up"),
56963
57058
  fqn: z3.string().max(512).optional().describe("The fully qualified name to look up"),
@@ -56992,7 +57087,7 @@ function registerNavigationTools(server, ctx) {
56992
57087
  );
56993
57088
  server.tool(
56994
57089
  "search",
56995
- 'Search symbols by name, kind, or text. Use instead of Grep when looking for functions, classes, methods, or variables in source code. Supports kind/language/file_pattern filters. Set fuzzy=true for typo-tolerant search (trigram + Levenshtein). For natural-language / conceptual queries set semantic="on" (requires an AI provider configured + embed_repo run once). Set fusion=true for Signal Fusion \u2014 multi-channel ranking (BM25 + PageRank + embeddings + identity match) via Weighted Reciprocal Rank fusion.',
57090
+ 'Search symbols by name, kind, or text. Use instead of Grep when looking for functions, classes, methods, or variables in source code. For raw text/string/comment search use search_text instead. For finding who references a known symbol use find_usages instead. Supports kind/language/file_pattern filters. Set fuzzy=true for typo-tolerant search (trigram + Levenshtein). For natural-language / conceptual queries set semantic="on" (requires an AI provider configured + embed_repo run once). Set fusion=true for Signal Fusion \u2014 multi-channel ranking (BM25 + PageRank + embeddings + identity match) via Weighted Reciprocal Rank fusion. Read-only. Returns JSON: { items: [{ symbol_id, name, kind, fqn, signature, file, line, score }], total, search_mode }.',
56996
57091
  {
56997
57092
  query: z3.string().min(1).max(500).describe("Search query"),
56998
57093
  kind: z3.string().max(64).optional().describe("Filter by symbol kind (class, method, function, etc.)"),
@@ -57107,7 +57202,7 @@ function registerNavigationTools(server, ctx) {
57107
57202
  );
57108
57203
  server.tool(
57109
57204
  "get_outline",
57110
- "Get all symbols for a file (signatures only, no bodies). Use instead of Read to understand a file before editing \u2014 much cheaper in tokens.",
57205
+ "Get all symbols for a file (signatures only, no bodies). Use instead of Read to understand a file before editing \u2014 much cheaper in tokens. For reading one symbol's source, follow up with get_symbol. Read-only. Returns JSON: { path, language, symbols: [{ symbolId, name, kind, signature, lineStart, lineEnd }] }.",
57111
57206
  {
57112
57207
  path: z3.string().max(512).describe("Relative file path")
57113
57208
  },
@@ -57133,7 +57228,7 @@ function registerNavigationTools(server, ctx) {
57133
57228
  );
57134
57229
  server.tool(
57135
57230
  "get_change_impact",
57136
- "Full change impact report: risk score + mitigations, breaking change detection, enriched dependents (complexity, coverage, exports), module groups, affected tests, co-change hidden couplings. Supports diff-aware mode via symbol_ids to scope analysis to only changed symbols.",
57231
+ "Full change impact report: risk score + mitigations, breaking change detection, enriched dependents (complexity, coverage, exports), module groups, affected tests, co-change hidden couplings. Supports diff-aware mode via symbol_ids to scope analysis to only changed symbols. Use before modifying code to understand blast radius. For quick risk assessment without full report, use assess_change_risk instead. Read-only. Returns JSON: { risk, dependents, affectedTests, breakingChanges, totalAffected }.",
57137
57232
  {
57138
57233
  file_path: z3.string().max(512).optional().describe("Relative file path to analyze"),
57139
57234
  symbol_id: z3.string().max(512).optional().describe("Symbol ID to analyze"),
@@ -57176,7 +57271,7 @@ function registerNavigationTools(server, ctx) {
57176
57271
  );
57177
57272
  server.tool(
57178
57273
  "get_feature_context",
57179
- "Search code by keyword/topic \u2192 returns ranked source code snippets within a token budget. Use when you need to READ actual code for a concept or feature.",
57274
+ "Search code by keyword/topic \u2192 returns ranked source code snippets within a token budget. Use when you need to READ actual code for a concept or feature. For structured task context with tests and entry points, use get_task_context instead. For symbol metadata without source, use search. Read-only. Returns JSON: { items: [{ symbol_id, name, file, source, score }], token_usage }.",
57180
57275
  {
57181
57276
  description: z3.string().min(1).max(2e3).describe("Natural language description of the feature to find context for"),
57182
57277
  token_budget: z3.number().int().min(100).max(1e5).optional().describe("Max tokens for assembled context (default 4000)")
@@ -57195,7 +57290,7 @@ function registerNavigationTools(server, ctx) {
57195
57290
  );
57196
57291
  server.tool(
57197
57292
  "suggest_queries",
57198
- "Onboarding helper: shows top imported files, most connected symbols (PageRank), language stats, and example tool calls. Call this first when exploring an unfamiliar project.",
57293
+ "Onboarding helper: shows top imported files, most connected symbols (PageRank), language stats, and example tool calls. Call this first when exploring an unfamiliar project. For a structured project map use get_project_map instead. Read-only. Returns JSON: { topFiles, topSymbols, languageStats, exampleQueries }.",
57199
57294
  {},
57200
57295
  async () => {
57201
57296
  const result = suggestQueries(store);
@@ -57204,7 +57299,7 @@ function registerNavigationTools(server, ctx) {
57204
57299
  );
57205
57300
  server.tool(
57206
57301
  "get_related_symbols",
57207
- "Find symbols related via co-location (same file), shared importers, and name similarity. Useful for discovering related code when exploring a symbol.",
57302
+ "Find symbols related via co-location (same file), shared importers, and name similarity. Use when exploring a symbol to discover sibling code. For call-graph relationships use get_call_graph instead; for all usages use find_usages. Read-only. Returns JSON: { related: [{ symbol_id, name, kind, file, relation_type, score }] }.",
57208
57303
  {
57209
57304
  symbol_id: z3.string().max(512).describe("Symbol ID to find related symbols for"),
57210
57305
  max_results: z3.number().int().min(1).max(100).optional().describe("Max results (default 20)")
@@ -57219,7 +57314,7 @@ function registerNavigationTools(server, ctx) {
57219
57314
  );
57220
57315
  server.tool(
57221
57316
  "get_context_bundle",
57222
- "Get a symbol's source code + its import dependencies + optional callers, packed within a token budget. Supports batch queries with shared-import deduplication.",
57317
+ "Get a symbol's source code + its import dependencies + optional callers, packed within a token budget. Supports batch queries with shared-import deduplication. Use instead of chaining get_symbol calls \u2014 deduplicates shared imports across symbols. For a single symbol without imports, get_symbol is lighter. Read-only. Returns JSON: { primary: [{ symbol_id, file, source }], imports: [{ file, source }], token_usage }.",
57223
57318
  {
57224
57319
  symbol_id: z3.string().max(512).optional().describe("Single symbol ID"),
57225
57320
  symbol_ids: z3.array(z3.string().max(512)).max(20).optional().describe("Batch: multiple symbol IDs"),
@@ -57250,7 +57345,7 @@ function registerNavigationTools(server, ctx) {
57250
57345
  );
57251
57346
  server.tool(
57252
57347
  "get_task_context",
57253
- "All-in-one context for starting a dev task: execution paths, tests, entry points, adapted by task type. Use as your FIRST call when beginning any new task.",
57348
+ "All-in-one context for starting a dev task: execution paths, tests, entry points, adapted by task type. Use as your FIRST call when beginning any new task \u2014 replaces manual chaining of search \u2192 get_symbol \u2192 Read. For narrower feature-code lookup use get_feature_context instead. Read-only. Returns JSON: { symbols: [{ symbol_id, name, file, source }], tests, entryPoints, taskType, token_usage }.",
57254
57349
  {
57255
57350
  task: z3.string().min(1).max(2e3).describe("Natural language description of the task"),
57256
57351
  token_budget: z3.number().int().min(100).max(1e5).optional().describe("Max tokens (default 8000)"),
@@ -58913,7 +59008,7 @@ function registerFrameworkTools(server, ctx) {
58913
59008
  if (has("vue", "nuxt", "inertia")) {
58914
59009
  server.tool(
58915
59010
  "get_component_tree",
58916
- "Build a component render tree starting from a given .vue file",
59011
+ "Build a component render tree starting from a given .vue file. Use to visualize parent-child component hierarchy. Read-only. Returns JSON: { root, children: [{ component, props, slots, depth }], totalComponents }.",
58917
59012
  {
58918
59013
  component_path: z4.string().max(512).describe("Relative path to the root .vue file"),
58919
59014
  depth: z4.number().int().min(1).max(20).optional().describe("Max tree depth (default 3)"),
@@ -58933,7 +59028,7 @@ function registerFrameworkTools(server, ctx) {
58933
59028
  if (has("express", "nestjs", "laravel", "fastapi", "flask", "drf", "spring", "rails", "fastify", "hono", "trpc")) {
58934
59029
  server.tool(
58935
59030
  "get_request_flow",
58936
- "Trace request flow for a URL+method: route \u2192 middleware \u2192 controller \u2192 service (Laravel/Express/NestJS/Fastify/Hono/tRPC/FastAPI/Flask/DRF)",
59031
+ "Trace request flow for a URL+method: route \u2192 middleware \u2192 controller \u2192 service (Laravel/Express/NestJS/Fastify/Hono/tRPC/FastAPI/Flask/DRF). Use to understand how a request is handled end-to-end. For middleware-only analysis use get_middleware_chain instead. Read-only. Returns JSON: { route, steps: [{ type, symbol_id, name, file }] }.",
58937
59032
  {
58938
59033
  url: z4.string().max(512).describe("Route URL (e.g. /api/users)"),
58939
59034
  method: z4.string().max(64).optional().describe("HTTP method (default GET)")
@@ -58950,7 +59045,7 @@ function registerFrameworkTools(server, ctx) {
58950
59045
  if (has("express", "nestjs", "fastapi", "flask", "spring")) {
58951
59046
  server.tool(
58952
59047
  "get_middleware_chain",
58953
- "Trace middleware chain for a route URL (Express/NestJS/FastAPI/Flask)",
59048
+ "Trace middleware chain for a route URL (Express/NestJS/FastAPI/Flask). Use when you only need the middleware stack, not the full request flow. For full route\u2192controller\u2192service flow use get_request_flow instead. Read-only. Returns JSON: { url, middlewares: [{ name, file, order }] }.",
58954
59049
  {
58955
59050
  url: z4.string().max(512).describe("Route URL to trace middleware for")
58956
59051
  },
@@ -58966,7 +59061,7 @@ function registerFrameworkTools(server, ctx) {
58966
59061
  if (has("nestjs")) {
58967
59062
  server.tool(
58968
59063
  "get_module_graph",
58969
- "Build NestJS module dependency graph (module -> imports -> controllers -> providers -> exports)",
59064
+ "Build NestJS module dependency graph (module -> imports -> controllers -> providers -> exports). Use to understand NestJS module structure and DI wiring. For provider-level DI tree use get_di_tree instead. Read-only. Returns JSON: { module, imports, controllers, providers, exports, edges }.",
58970
59065
  {
58971
59066
  module_name: z4.string().max(256).describe("NestJS module class name (e.g. AppModule)")
58972
59067
  },
@@ -58980,7 +59075,7 @@ function registerFrameworkTools(server, ctx) {
58980
59075
  );
58981
59076
  server.tool(
58982
59077
  "get_di_tree",
58983
- "Trace NestJS dependency injection tree (what a service injects + who injects it)",
59078
+ "Trace NestJS dependency injection tree (what a service injects + who injects it). Use to understand DI wiring for a specific provider. For module-level graph use get_module_graph instead. Read-only. Returns JSON: { service, injects: [{ name, kind }], injected_by: [{ name, kind }] }.",
58984
59079
  {
58985
59080
  service_name: z4.string().max(256).describe("NestJS service/provider class name")
58986
59081
  },
@@ -58996,7 +59091,7 @@ function registerFrameworkTools(server, ctx) {
58996
59091
  if (has("react-native")) {
58997
59092
  server.tool(
58998
59093
  "get_navigation_graph",
58999
- "Build React Native navigation tree from screens, navigators, and deep links",
59094
+ "Build React Native navigation tree from screens, navigators, and deep links. Use to understand app navigation structure. For details on a specific screen use get_screen_context instead. Read-only. Returns JSON: { navigators, screens, deepLinks, edges }.",
59000
59095
  {},
59001
59096
  async () => {
59002
59097
  const result = getNavigationGraph(store);
@@ -59008,7 +59103,7 @@ function registerFrameworkTools(server, ctx) {
59008
59103
  );
59009
59104
  server.tool(
59010
59105
  "get_screen_context",
59011
- "Get full context for a React Native screen: navigator, navigation edges, deep link, platform variants, native modules",
59106
+ "Get full context for a React Native screen: navigator, navigation edges, deep link, platform variants, native modules. Use to understand a specific screen before modifying it. For the full navigation tree use get_navigation_graph instead. Read-only. Returns JSON: { screen, navigator, deepLink, platformVariants, nativeModules, navigationEdges }.",
59012
59107
  {
59013
59108
  screen_name: z4.string().max(256).describe("Screen name (e.g. ProfileScreen or Profile)")
59014
59109
  },
@@ -59024,7 +59119,7 @@ function registerFrameworkTools(server, ctx) {
59024
59119
  if (has("laravel", "mongoose", "sequelize", "prisma", "typeorm", "drizzle", "sqlalchemy")) {
59025
59120
  server.tool(
59026
59121
  "get_model_context",
59027
- "Get full model context: relationships, schema, and metadata (Eloquent/Mongoose/Sequelize/SQLAlchemy/Prisma/TypeORM/Drizzle)",
59122
+ "Get full model context: relationships, schema, and metadata (Eloquent/Mongoose/Sequelize/SQLAlchemy/Prisma/TypeORM/Drizzle). Use to understand a specific ORM model. For raw table schema without ORM context use get_schema instead. Read-only. Returns JSON: { model, table, relationships: [{ type, related, foreignKey }], fields, metadata }.",
59028
59123
  {
59029
59124
  model_name: z4.string().max(256).describe("Model class name (e.g. User, Post)")
59030
59125
  },
@@ -59038,7 +59133,7 @@ function registerFrameworkTools(server, ctx) {
59038
59133
  );
59039
59134
  server.tool(
59040
59135
  "get_schema",
59041
- "Get database schema reconstructed from migrations or ORM model definitions",
59136
+ "Get database schema reconstructed from migrations or ORM model definitions. Use to understand table structure. For ORM-level context with relationships use get_model_context instead. Read-only. Returns JSON: { tables: [{ name, columns: [{ name, type, nullable, default }], indexes }] }.",
59042
59137
  {
59043
59138
  table_name: z4.string().max(256).optional().describe("Table/collection/model name (omit for all)")
59044
59139
  },
@@ -59054,7 +59149,7 @@ function registerFrameworkTools(server, ctx) {
59054
59149
  if (has("laravel", "nestjs", "celery", "django", "socketio")) {
59055
59150
  server.tool(
59056
59151
  "get_event_graph",
59057
- "Get event/signal/task dispatch graph (Laravel events, Django signals, NestJS events, Celery tasks, Socket.io events)",
59152
+ "Get event/signal/task dispatch graph (Laravel events, Django signals, NestJS events, Celery tasks, Socket.io events). Use to understand event-driven architecture and trace event producers/consumers. Read-only. Returns JSON: { events: [{ name, dispatchers, listeners, file }] }.",
59058
59153
  {
59059
59154
  event_name: z4.string().max(256).optional().describe("Filter to a specific event class name")
59060
59155
  },
@@ -59069,7 +59164,7 @@ function registerFrameworkTools(server, ctx) {
59069
59164
  }
59070
59165
  server.tool(
59071
59166
  "find_usages",
59072
- "Find all places that reference a symbol or file (imports, calls, renders, dispatches). Use instead of Grep for symbol usages \u2014 understands semantic relationships, not just text matches.",
59167
+ "Find all places that reference a symbol or file (imports, calls, renders, dispatches). Use instead of Grep for symbol usages \u2014 understands semantic relationships, not just text matches. For bidirectional call graph use get_call_graph instead. Read-only. Returns JSON: { references: [{ file, line, kind, context }], total }.",
59073
59168
  {
59074
59169
  symbol_id: z4.string().max(512).optional().describe("Symbol ID to find references for"),
59075
59170
  fqn: z4.string().max(512).optional().describe("Fully qualified name to find references for"),
@@ -59103,7 +59198,7 @@ function registerFrameworkTools(server, ctx) {
59103
59198
  );
59104
59199
  server.tool(
59105
59200
  "get_call_graph",
59106
- "Build a bidirectional call graph centered on a symbol (who calls it + what it calls). Use to understand control flow through a function.",
59201
+ "Build a bidirectional call graph centered on a symbol (who calls it + what it calls). Use to understand control flow through a function. For flat list of all references use find_usages instead. Read-only. Returns JSON: { root: { symbol_id, name, calls: [...], called_by: [...] } }.",
59107
59202
  {
59108
59203
  symbol_id: z4.string().max(512).optional().describe("Symbol ID to center the graph on"),
59109
59204
  fqn: z4.string().max(512).optional().describe("Fully qualified name to center the graph on"),
@@ -59136,7 +59231,7 @@ function registerFrameworkTools(server, ctx) {
59136
59231
  );
59137
59232
  server.tool(
59138
59233
  "get_tests_for",
59139
- "Find test files and test functions that cover a given symbol or file. Use instead of Glob/Grep \u2014 understands test-to-source mapping, not just filename conventions.",
59234
+ "Find test files and test functions that cover a given symbol or file. Use instead of Glob/Grep \u2014 understands test-to-source mapping, not just filename conventions. For project-wide test coverage gaps use get_untested_symbols instead. Read-only. Returns JSON: { tests: [{ file, testName, symbol_id }], total }.",
59140
59235
  {
59141
59236
  symbol_id: z4.string().max(512).optional().describe("Symbol ID to find tests for"),
59142
59237
  fqn: z4.string().max(512).optional().describe("Fully qualified name to find tests for"),
@@ -59171,7 +59266,7 @@ function registerFrameworkTools(server, ctx) {
59171
59266
  if (has("laravel")) {
59172
59267
  server.tool(
59173
59268
  "get_livewire_context",
59174
- "Get full context for a Livewire component: properties, actions, events, view, child components",
59269
+ "Get full context for a Livewire component: properties, actions, events, view, child components. Use to understand a specific Livewire component before modifying it. Read-only. Returns JSON: { component, properties, actions, events, view, children }.",
59175
59270
  {
59176
59271
  component_name: z4.string().max(256).describe("Livewire component class name or FQN (e.g. UserProfile or App\\Livewire\\UserProfile)")
59177
59272
  },
@@ -59185,7 +59280,7 @@ function registerFrameworkTools(server, ctx) {
59185
59280
  );
59186
59281
  server.tool(
59187
59282
  "get_nova_resource",
59188
- "Get full context for a Laravel Nova resource: model, fields, actions, filters, lenses, metrics",
59283
+ "Get full context for a Laravel Nova resource: model, fields, actions, filters, lenses, metrics. Use to understand a Nova admin resource before modifying it. Read-only. Returns JSON: { resource, model, fields, actions, filters, lenses, metrics }.",
59189
59284
  {
59190
59285
  resource_name: z4.string().max(256).describe("Nova resource class name or FQN (e.g. User or App\\Nova\\User)")
59191
59286
  },
@@ -59201,7 +59296,7 @@ function registerFrameworkTools(server, ctx) {
59201
59296
  if (has("zustand-redux")) {
59202
59297
  server.tool(
59203
59298
  "get_state_stores",
59204
- "List all Zustand stores and Redux Toolkit slices with their state fields, actions/reducers, and dispatch sites",
59299
+ "List all Zustand stores and Redux Toolkit slices with their state fields, actions/reducers, and dispatch sites. Use to understand state management architecture. Read-only. Returns JSON: { stores: [{ type, name, handler, metadata }], dispatches, totalStores, totalDispatches }.",
59205
59300
  {},
59206
59301
  async () => {
59207
59302
  const routes = store.getAllRoutes();
@@ -60049,7 +60144,7 @@ function registerAnalysisTools(server, ctx) {
60049
60144
  );
60050
60145
  server.tool(
60051
60146
  "get_implementations",
60052
- "Find all classes that implement or extend a given interface or base class",
60147
+ "Find all classes that implement or extend a given interface or base class. Use when you know the interface name. For full hierarchy tree (ancestors + descendants) use get_type_hierarchy instead. Read-only. Returns JSON: { implementations: [{ symbol_id, name, kind, file, line }], total }.",
60053
60148
  {
60054
60149
  name: z5.string().max(256).describe("Interface or base class name (e.g. UserRepositoryInterface)")
60055
60150
  },
@@ -60075,7 +60170,7 @@ function registerAnalysisTools(server, ctx) {
60075
60170
  );
60076
60171
  server.tool(
60077
60172
  "get_api_surface",
60078
- "List all exported symbols (public API) of a file or matching files",
60173
+ "List all exported symbols (public API) of a file or matching files. Use to understand what a module exposes. For finding unused exports use get_dead_exports instead. Read-only. Returns JSON: { files: [{ path, exports: [{ name, kind, signature }] }] }.",
60079
60174
  {
60080
60175
  file_pattern: z5.string().max(512).optional().describe("Glob-style pattern to filter files (e.g. src/services/*.ts)")
60081
60176
  },
@@ -60086,7 +60181,7 @@ function registerAnalysisTools(server, ctx) {
60086
60181
  );
60087
60182
  server.tool(
60088
60183
  "get_plugin_registry",
60089
- "List all registered indexer plugins and the edge types they emit",
60184
+ "List all registered indexer plugins and the edge types they emit. Use for debugging indexer behavior or understanding which frameworks are supported. Read-only. Returns JSON: { languagePlugins, frameworkPlugins, edgeTypes }.",
60090
60185
  {},
60091
60186
  async () => {
60092
60187
  const result = getPluginRegistry(store, registry, frameworkNames);
@@ -60095,7 +60190,7 @@ function registerAnalysisTools(server, ctx) {
60095
60190
  );
60096
60191
  server.tool(
60097
60192
  "get_type_hierarchy",
60098
- "Walk TypeScript class/interface hierarchy: ancestors (what it extends/implements) and descendants (what extends/implements it)",
60193
+ "Walk TypeScript class/interface hierarchy: ancestors (what it extends/implements) and descendants (what extends/implements it). Use to understand inheritance trees. For a flat list of implementations only use get_implementations instead. Read-only. Returns JSON: { name, ancestors: [...], descendants: [...] }.",
60099
60194
  {
60100
60195
  name: z5.string().max(256).describe('Class or interface name (e.g. "LanguagePlugin", "Store")'),
60101
60196
  max_depth: z5.number().int().min(1).max(20).optional().describe("Max traversal depth (default 10)")
@@ -60122,7 +60217,7 @@ function registerAnalysisTools(server, ctx) {
60122
60217
  );
60123
60218
  server.tool(
60124
60219
  "get_dead_exports",
60125
- "Find exported symbols never imported by any other file \u2014 dead code candidates",
60220
+ "Find exported symbols never imported by any other file \u2014 dead code candidates. Use for quick export-level dead code scan. For deeper multi-signal dead code detection (including call graph) use get_dead_code instead. Read-only. Returns JSON: { deadExports: [{ symbol_id, name, kind, file }], total }.",
60126
60221
  {
60127
60222
  file_pattern: z5.string().max(512).optional().describe('Filter files by glob pattern (e.g. "src/tools/*.ts")')
60128
60223
  },
@@ -60133,7 +60228,7 @@ function registerAnalysisTools(server, ctx) {
60133
60228
  );
60134
60229
  server.tool(
60135
60230
  "get_import_graph",
60136
- "Show file-level dependency graph: what a file imports and what imports it (requires reindex for ESM edge resolution)",
60231
+ "Show file-level dependency graph: what a file imports and what imports it (requires reindex for ESM edge resolution). Use to understand module dependencies for a specific file. For project-wide coupling analysis use get_coupling; for visual diagram use get_dependency_diagram. Read-only. Returns JSON: { file, imports: [{ path }], importedBy: [{ path }] }.",
60137
60232
  {
60138
60233
  file_path: z5.string().max(512).describe('Relative file path to analyze (e.g. "src/server.ts")')
60139
60234
  },
@@ -60146,7 +60241,7 @@ function registerAnalysisTools(server, ctx) {
60146
60241
  );
60147
60242
  server.tool(
60148
60243
  "get_untested_exports",
60149
- "Find exported public symbols with no matching test file \u2014 test coverage gaps",
60244
+ "Find exported public symbols with no matching test file \u2014 test coverage gaps. For deeper analysis including non-exported symbols use get_untested_symbols instead. Read-only. Returns JSON: { untested: [{ symbol_id, name, kind, file }], total }.",
60150
60245
  {
60151
60246
  file_pattern: z5.string().max(512).optional().describe('Filter by file glob pattern (e.g. "src/tools/%")')
60152
60247
  },
@@ -60157,7 +60252,7 @@ function registerAnalysisTools(server, ctx) {
60157
60252
  );
60158
60253
  server.tool(
60159
60254
  "get_untested_symbols",
60160
- 'Find ALL symbols (not just exports) lacking test coverage. Classifies as "unreached" (no test file imports the source) or "imported_not_called" (test imports file but never references this symbol). Use for thorough coverage gap analysis.',
60255
+ 'Find ALL symbols (not just exports) lacking test coverage. Classifies as "unreached" (no test file imports the source) or "imported_not_called" (test imports file but never references this symbol). Use for thorough coverage gap analysis. For exports-only quick scan use get_untested_exports instead. Read-only. Returns JSON: { untested: [{ symbol_id, name, kind, file, classification }], total }.',
60161
60256
  {
60162
60257
  file_pattern: z5.string().max(512).optional().describe('Filter by file glob pattern (e.g. "src/tools/%")'),
60163
60258
  max_results: z5.number().int().min(1).max(500).optional().describe("Cap on returned items (default: all)")
@@ -60167,12 +60262,12 @@ function registerAnalysisTools(server, ctx) {
60167
60262
  return { content: [{ type: "text", text: jh("get_untested_symbols", result) }] };
60168
60263
  }
60169
60264
  );
60170
- server.tool("self_audit", "Dead code & coverage audit: dead exports, untested public symbols, heritage debt. Use for cleanup and coverage tasks.", {}, async () => {
60265
+ server.tool("self_audit", "Dead code & coverage audit: dead exports, untested public symbols, heritage debt. Use as a one-shot health check combining dead exports + untested symbols + heritage debt. For individual checks use get_dead_exports, get_untested_symbols, or get_dead_code separately. Read-only. Returns JSON: { deadExports, untestedSymbols, heritageDebt, summary }.", {}, async () => {
60171
60266
  return { content: [{ type: "text", text: j3(selfAudit(store)) }] };
60172
60267
  });
60173
60268
  server.tool(
60174
60269
  "get_coupling",
60175
- "Coupling analysis: afferent (Ca), efferent (Ce), instability index per file. Shows which modules are stable vs unstable",
60270
+ "Coupling analysis: afferent (Ca), efferent (Ce), instability index per file. Shows which modules are stable vs unstable. Use to identify fragile or overly-depended-on modules. For coupling changes over time use get_coupling_trend instead. Read-only. Returns JSON: [{ file, ca, ce, instability, assessment }].",
60176
60271
  {
60177
60272
  limit: z5.number().int().min(1).max(500).optional().describe("Max results (default: all)"),
60178
60273
  assessment: z5.enum(["stable", "neutral", "unstable", "isolated"]).optional().describe("Filter by stability assessment")
@@ -60186,7 +60281,7 @@ function registerAnalysisTools(server, ctx) {
60186
60281
  );
60187
60282
  server.tool(
60188
60283
  "get_circular_imports",
60189
- "Find circular dependency chains in the import graph (Kosaraju SCC algorithm)",
60284
+ "Find circular dependency chains in the import graph (Kosaraju SCC algorithm). Use to detect and break dependency cycles. Read-only. Returns JSON: { total_cycles, cycles: [[file1, file2, ...]] }.",
60190
60285
  {},
60191
60286
  async () => {
60192
60287
  const cycles = getDependencyCycles(store);
@@ -60205,7 +60300,7 @@ function registerAnalysisTools(server, ctx) {
60205
60300
  );
60206
60301
  server.tool(
60207
60302
  "get_pagerank",
60208
- "File importance ranking via PageRank on the import graph. Shows most central/important files",
60303
+ "File importance ranking via PageRank on the import graph. Shows most central/important files. Use to identify architecturally critical files. For combined health metrics use get_project_health instead. Read-only. Returns JSON: [{ file, score }].",
60209
60304
  {
60210
60305
  limit: z5.number().int().min(1).max(200).optional().describe("Max results (default: 50)")
60211
60306
  },
@@ -60216,7 +60311,7 @@ function registerAnalysisTools(server, ctx) {
60216
60311
  );
60217
60312
  server.tool(
60218
60313
  "get_refactor_candidates",
60219
- "Find functions with high complexity called from many files \u2014 candidates for extraction to shared modules",
60314
+ "Find functions with high complexity called from many files \u2014 candidates for extraction to shared modules. Use during architecture review to identify hotspots worth refactoring. Read-only. Returns JSON: [{ symbol_id, name, file, cyclomatic, callerCount }].",
60220
60315
  {
60221
60316
  min_cyclomatic: z5.number().int().min(1).optional().describe("Min cyclomatic complexity (default: 5)"),
60222
60317
  min_callers: z5.number().int().min(1).optional().describe("Min distinct caller files (default: 2)"),
@@ -60233,7 +60328,7 @@ function registerAnalysisTools(server, ctx) {
60233
60328
  );
60234
60329
  server.tool(
60235
60330
  "get_project_health",
60236
- "Structural health: coupling instability, dependency cycles, PageRank rankings, refactor candidates. Use for architecture review.",
60331
+ "Structural health: coupling instability, dependency cycles, PageRank rankings, refactor candidates. Use for architecture review as a single aggregated report. For individual metrics use get_coupling, get_circular_imports, or get_pagerank separately. Read-only. Returns JSON: { coupling, cycles, pagerank, refactorCandidates, hotspots }.",
60237
60332
  {},
60238
60333
  async () => {
60239
60334
  const result = getRepoHealth(store);
@@ -60243,7 +60338,7 @@ function registerAnalysisTools(server, ctx) {
60243
60338
  );
60244
60339
  server.tool(
60245
60340
  "check_architecture",
60246
- "Check architectural layer rules: detect forbidden imports between layers (e.g. domain importing infrastructure). Supports auto-detected presets (clean-architecture, hexagonal) or custom layers.",
60341
+ "Check architectural layer rules: detect forbidden imports between layers (e.g. domain importing infrastructure). Supports auto-detected presets (clean-architecture, hexagonal) or custom layers. Use to enforce architectural boundaries. Read-only. Returns JSON: { violations: [{ from, to, rule, file, line }], total, preset }.",
60247
60342
  {
60248
60343
  preset: z5.enum(["clean-architecture", "hexagonal"]).optional().describe("Use a built-in layer preset (auto-detected if omitted)"),
60249
60344
  layers: z5.array(z5.object({
@@ -60272,7 +60367,7 @@ function registerAnalysisTools(server, ctx) {
60272
60367
  );
60273
60368
  server.tool(
60274
60369
  "get_code_owners",
60275
- "Git-based code ownership: who contributed most to specific files (git shortlog). Requires git.",
60370
+ "Git-based code ownership: who contributed most to specific files (git shortlog). Requires git. Use to identify who to ask about specific files. For symbol-level ownership use get_symbol_owners instead. Read-only. Returns JSON: [{ file, owners: [{ author, commits, percentage }] }].",
60276
60371
  {
60277
60372
  file_paths: z5.array(z5.string().max(512)).min(1).max(20).describe("File paths to check ownership for")
60278
60373
  },
@@ -60290,7 +60385,7 @@ function registerAnalysisTools(server, ctx) {
60290
60385
  );
60291
60386
  server.tool(
60292
60387
  "get_symbol_owners",
60293
- "Git blame-based symbol ownership: who wrote which lines of a specific symbol. Requires git.",
60388
+ "Git blame-based symbol ownership: who wrote which lines of a specific symbol. Requires git. Use for fine-grained ownership of a specific function/class. For file-level ownership use get_code_owners instead. Read-only. Returns JSON: { symbol_id, owners: [{ author, lines, percentage }] }.",
60294
60389
  {
60295
60390
  symbol_id: z5.string().max(512).describe("Symbol ID to check ownership for")
60296
60391
  },
@@ -60304,7 +60399,7 @@ function registerAnalysisTools(server, ctx) {
60304
60399
  );
60305
60400
  server.tool(
60306
60401
  "get_complexity_trend",
60307
- "File complexity over git history: cyclomatic complexity at past commits. Shows if a file is getting more or less complex.",
60402
+ "File complexity over git history: cyclomatic complexity at past commits. Shows if a file is getting more or less complex. Requires git. Use to track whether a file is improving or degrading. For current snapshot use get_complexity_report; for symbol-level trends use get_symbol_complexity_trend. Read-only. Returns JSON: { file, snapshots: [{ commit, date, complexity }] }.",
60308
60403
  {
60309
60404
  file_path: z5.string().max(512).describe("File path to analyze"),
60310
60405
  snapshots: z5.number().int().min(2).max(20).optional().describe("Number of historical snapshots (default: 5)")
@@ -60321,7 +60416,7 @@ function registerAnalysisTools(server, ctx) {
60321
60416
  );
60322
60417
  server.tool(
60323
60418
  "get_coupling_trend",
60324
- "File coupling over git history: Ca/Ce/instability at past commits. Shows if a module is stabilizing or destabilizing.",
60419
+ "File coupling over git history: Ca/Ce/instability at past commits. Shows if a module is stabilizing or destabilizing. Requires git. Use to track module stability over time. For current coupling snapshot use get_coupling instead. Read-only. Returns JSON: { file, snapshots: [{ commit, date, ca, ce, instability }] }.",
60325
60420
  {
60326
60421
  file_path: z5.string().max(512).describe("File path to analyze"),
60327
60422
  since_days: z5.number().int().min(1).optional().describe("Analyze last N days (default: 90)"),
@@ -60342,7 +60437,7 @@ function registerAnalysisTools(server, ctx) {
60342
60437
  );
60343
60438
  server.tool(
60344
60439
  "get_symbol_complexity_trend",
60345
- "Single symbol complexity over git history: cyclomatic, nesting, params, lines at past commits.",
60440
+ "Single symbol complexity over git history: cyclomatic, nesting, params, lines at past commits. Requires git. Use to track a specific function's complexity evolution. For file-level trends use get_complexity_trend instead. Read-only. Returns JSON: { symbol_id, snapshots: [{ commit, date, cyclomatic, nesting, params, lines }] }.",
60346
60441
  {
60347
60442
  symbol_id: z5.string().min(1).max(512).describe("Symbol ID to analyze (from search or outline)"),
60348
60443
  since_days: z5.number().int().min(1).optional().describe("Analyze last N days (default: all history)"),
@@ -60361,7 +60456,7 @@ function registerAnalysisTools(server, ctx) {
60361
60456
  );
60362
60457
  server.tool(
60363
60458
  "check_duplication",
60364
- "Check if a function/class name already exists elsewhere in the codebase before creating it. Prevents duplicating existing logic. Call with just a name when planning new code, or symbol_id to check an existing symbol. Returns scored matches \u2014 score \u22650.7 means high likelihood of duplication, review the existing symbol before proceeding.",
60459
+ "Check if a function/class name already exists elsewhere in the codebase before creating it. Prevents duplicating existing logic. Call with just a name when planning new code, or symbol_id to check an existing symbol. Returns scored matches \u2014 score \u22650.7 means high likelihood of duplication, review the existing symbol before proceeding. Read-only. Returns JSON: { duplicates: [{ symbol_id, name, file, score }], hasDuplication }.",
60365
60460
  {
60366
60461
  symbol_id: z5.string().max(512).optional().describe("Existing symbol ID to check for duplicates"),
60367
60462
  name: z5.string().max(256).optional().describe("Function/class name to check (when symbol_id not available)"),
@@ -63516,7 +63611,7 @@ function registerGitTools(server, ctx) {
63516
63611
  const detectedFrameworks = registry.getAllFrameworkPlugins().map((p) => p.manifest.name);
63517
63612
  server.tool(
63518
63613
  "get_git_churn",
63519
- "Per-file git churn: commits, unique authors, frequency, volatility assessment. Requires git.",
63614
+ "Per-file git churn: commits, unique authors, frequency, volatility assessment. Requires git. Use to identify frequently-changed files. For combined churn+complexity hotspots use get_risk_hotspots instead. Read-only. Returns JSON: { results: [{ file, commits, authors, frequency, volatility }], total }.",
63520
63615
  {
63521
63616
  since_days: z6.number().int().min(1).optional().describe("Analyze commits from last N days (default: all history)"),
63522
63617
  limit: z6.number().int().min(1).max(500).optional().describe("Max results (default: 50)"),
@@ -63536,7 +63631,7 @@ function registerGitTools(server, ctx) {
63536
63631
  );
63537
63632
  server.tool(
63538
63633
  "get_risk_hotspots",
63539
- "Code hotspots: files with both high complexity AND high git churn (Adam Tornhill methodology). Score = complexity \xD7 log(1 + commits). Each entry includes a confidence_level (low/medium/multi_signal) counting how many of the two independent signals fired strongly. Result envelope includes _methodology disclosure and _warnings when git is unavailable.",
63634
+ "Code hotspots: files with both high complexity AND high git churn (Adam Tornhill methodology). Score = complexity \xD7 log(1 + commits). Each entry includes a confidence_level (low/medium/multi_signal) counting how many of the two independent signals fired strongly. Result envelope includes _methodology disclosure and _warnings when git is unavailable. Requires git. Use to prioritize refactoring. For per-file bug prediction use predict_bugs instead. Read-only. Returns JSON: { hotspots: [{ file, score, complexity, commits, confidence_level }], total }.",
63540
63635
  {
63541
63636
  since_days: z6.number().int().min(1).optional().describe("Git churn window in days (default: 90)"),
63542
63637
  limit: z6.number().int().min(1).max(100).optional().describe("Max results (default: 20)"),
@@ -63567,7 +63662,7 @@ function registerGitTools(server, ctx) {
63567
63662
  );
63568
63663
  server.tool(
63569
63664
  "get_dead_code",
63570
- 'Dead code detection. Two modes: (1) "multi-signal" (default) combines import graph, call graph, and barrel export analysis with confidence scores. (2) "reachability" runs forward BFS from auto-detected entry points (tests, package.json main/bin, src/{cli,main,index}, routes, framework-tagged controllers) \u2014 stricter but more accurate when entry points are enumerable. Pass entry_points to add custom roots. Both modes emit _methodology and _warnings.',
63665
+ 'Dead code detection. Two modes: (1) "multi-signal" (default) combines import graph, call graph, and barrel export analysis with confidence scores. (2) "reachability" runs forward BFS from auto-detected entry points (tests, package.json main/bin, src/{cli,main,index}, routes, framework-tagged controllers) \u2014 stricter but more accurate when entry points are enumerable. Pass entry_points to add custom roots. Both modes emit _methodology and _warnings. Use for comprehensive dead code analysis. For quick export-only scan use get_dead_exports; to safely remove detected dead code use remove_dead_code. Read-only. Returns JSON: { dead_symbols: [{ symbol_id, name, file, confidence, signals }], total }.',
63571
63666
  {
63572
63667
  file_pattern: z6.string().max(512).optional().describe('Filter by file glob pattern (e.g. "src/tools/%")'),
63573
63668
  threshold: z6.number().min(0).max(1).optional().describe("[multi-signal mode] Min confidence to report (default: 0.5 = at least 2 of 3 signals)"),
@@ -63597,7 +63692,7 @@ function registerGitTools(server, ctx) {
63597
63692
  );
63598
63693
  server.tool(
63599
63694
  "scan_security",
63600
- "Scan project files for OWASP Top-10 security vulnerabilities using pattern matching. Detects SQL injection (CWE-89), XSS (CWE-79), command injection (CWE-78), path traversal (CWE-22), hardcoded secrets (CWE-798), insecure crypto (CWE-327), open redirects (CWE-601), and SSRF (CWE-918). Skips test files.",
63695
+ "Scan project files for OWASP Top-10 security vulnerabilities using pattern matching. Detects SQL injection (CWE-89), XSS (CWE-79), command injection (CWE-78), path traversal (CWE-22), hardcoded secrets (CWE-798), insecure crypto (CWE-327), open redirects (CWE-601), and SSRF (CWE-918). Skips test files. Use for pattern-based security audit. For data-flow-aware analysis use taint_analysis instead. Read-only. Returns JSON: { findings: [{ rule, severity, cwe, file, line, message }], total, summary }.",
63601
63696
  {
63602
63697
  scope: z6.string().max(512).optional().describe("Directory to scan (default: whole project)"),
63603
63698
  rules: z6.array(z6.enum([
@@ -63631,7 +63726,7 @@ function registerGitTools(server, ctx) {
63631
63726
  );
63632
63727
  server.tool(
63633
63728
  "detect_antipatterns",
63634
- "Detect performance antipatterns: N+1 query risks, missing eager loading, unbounded queries, event listener leaks, circular model dependencies, missing indexes, memory leaks (unbounded caches, closure leaks). Static analysis across all indexed ORMs (Eloquent, Sequelize, Mongoose, Django, Prisma, TypeORM, Drizzle).",
63729
+ "Detect performance antipatterns: N+1 query risks, missing eager loading, unbounded queries, event listener leaks, circular model dependencies, missing indexes, memory leaks (unbounded caches, closure leaks). Static analysis across all indexed ORMs (Eloquent, Sequelize, Mongoose, Django, Prisma, TypeORM, Drizzle). Use to find performance issues. For code quality issues (TODOs, empty functions) use scan_code_smells instead. Read-only. Returns JSON: { findings: [{ category, severity, file, line, message, suggestion }], total }.",
63635
63730
  {
63636
63731
  category: z6.array(z6.enum([
63637
63732
  "n_plus_one_risk",
@@ -63661,7 +63756,7 @@ function registerGitTools(server, ctx) {
63661
63756
  );
63662
63757
  server.tool(
63663
63758
  "scan_code_smells",
63664
- "Find deferred work and shortcuts: TODO/FIXME/HACK/XXX comments, empty functions & stubs, hardcoded values (IPs, URLs, credentials, magic numbers, feature flags). Surfaces technical debt that grep alone misses by combining comment scanning, symbol body analysis, and context-aware false-positive filtering.",
63759
+ "Find deferred work and shortcuts: TODO/FIXME/HACK/XXX comments, empty functions & stubs, hardcoded values (IPs, URLs, credentials, magic numbers, feature flags). Surfaces technical debt that grep alone misses by combining comment scanning, symbol body analysis, and context-aware false-positive filtering. Use for code quality audit. For performance-specific antipatterns use detect_antipatterns; for security issues use scan_security. Read-only. Returns JSON: { findings: [{ category, priority, file, line, message }], total, summary }.",
63665
63760
  {
63666
63761
  category: z6.array(z6.enum([
63667
63762
  "todo_comment",
@@ -63695,7 +63790,7 @@ function registerGitTools(server, ctx) {
63695
63790
  );
63696
63791
  server.tool(
63697
63792
  "taint_analysis",
63698
- "Track flow of untrusted data from sources (HTTP params, env vars, file reads) to dangerous sinks (SQL queries, exec, innerHTML, redirects). Framework-aware: knows Express req.params, Laravel $request->input, Django request.GET, FastAPI Query(), etc. Reports unsanitized flows with CWE IDs and fix suggestions. More accurate than pattern-based scanning \u2014 traces actual data flow paths.",
63793
+ "Track flow of untrusted data from sources (HTTP params, env vars, file reads) to dangerous sinks (SQL queries, exec, innerHTML, redirects). Framework-aware: knows Express req.params, Laravel $request->input, Django request.GET, FastAPI Query(), etc. Reports unsanitized flows with CWE IDs and fix suggestions. More accurate than pattern-based scanning \u2014 traces actual data flow paths. Use for data-flow security analysis. For pattern-based OWASP scanning use scan_security instead. Read-only. Returns JSON: { flows: [{ source, sink, path, sanitized, cwe, suggestion }], total }.",
63699
63794
  {
63700
63795
  scope: z6.string().max(512).optional().describe("Directory to scan (default: whole project)"),
63701
63796
  sources: z6.array(z6.enum([
@@ -63741,7 +63836,7 @@ function registerGitTools(server, ctx) {
63741
63836
  );
63742
63837
  server.tool(
63743
63838
  "generate_sbom",
63744
- "Generate a Software Bill of Materials (SBOM) from package manifests and lockfiles. Supports npm, Composer, pip, Go, Cargo, Bundler, Maven. Outputs CycloneDX, SPDX, or plain JSON. Includes license compliance warnings for copyleft licenses.",
63839
+ "Generate a Software Bill of Materials (SBOM) from package manifests and lockfiles. Supports npm, Composer, pip, Go, Cargo, Bundler, Maven. Outputs CycloneDX, SPDX, or plain JSON. Includes license compliance warnings for copyleft licenses. Use for supply chain audits or compliance reports. Returns JSON/CycloneDX/SPDX: { components: [{ name, version, license, type }], warnings }.",
63745
63840
  {
63746
63841
  format: z6.enum(["cyclonedx", "spdx", "json"]).optional().describe("Output format (default: json)"),
63747
63842
  include_dev: z6.boolean().optional().describe("Include devDependencies (default: false)"),
@@ -63761,7 +63856,7 @@ function registerGitTools(server, ctx) {
63761
63856
  );
63762
63857
  server.tool(
63763
63858
  "get_artifacts",
63764
- "Surface non-code knowledge from the index: DB schemas (migrations, ORM models), API specs (routes, OpenAPI endpoints), infrastructure (docker-compose services, K8s resources), CI pipelines (jobs, stages), and config (env vars). All data from the existing index \u2014 no extra I/O.",
63859
+ "Surface non-code knowledge from the index: DB schemas (migrations, ORM models), API specs (routes, OpenAPI endpoints), infrastructure (docker-compose services, K8s resources), CI pipelines (jobs, stages), and config (env vars). All data from the existing index \u2014 no extra I/O. Use to discover infrastructure and config artifacts without reading files. Read-only. Returns JSON: { artifacts: [{ category, kind, name, file }], total }.",
63765
63860
  {
63766
63861
  category: z6.enum(["database", "api", "infra", "ci", "config", "all"]).optional().describe("Filter by artifact category (default: all)"),
63767
63862
  query: z6.string().max(256).optional().describe("Text filter on name/kind/file"),
@@ -63778,7 +63873,7 @@ function registerGitTools(server, ctx) {
63778
63873
  );
63779
63874
  server.tool(
63780
63875
  "plan_batch_change",
63781
- "Analyze the impact of updating a package/dependency. Shows all affected files, import references, and generates a PR template with checklist. Use before upgrading a dependency to understand blast radius.",
63876
+ "Analyze the impact of updating a package/dependency. Shows all affected files, import references, and generates a PR template with checklist. Use before upgrading a dependency to understand blast radius. Read-only (analysis only, does not modify files). Returns JSON: { package, affectedFiles, importReferences, prTemplate, checklist }.",
63782
63877
  {
63783
63878
  package: z6.string().min(1).max(256).describe('Package name (e.g. "express", "laravel/framework", "react")'),
63784
63879
  from_version: z6.string().max(64).optional().describe("Current version"),
@@ -63800,7 +63895,7 @@ function registerGitTools(server, ctx) {
63800
63895
  );
63801
63896
  server.tool(
63802
63897
  "get_complexity_report",
63803
- "Get complexity metrics (cyclomatic, max nesting, param count) for symbols in a file or across the project. Useful for identifying complex code before refactoring.",
63898
+ "Get complexity metrics (cyclomatic, max nesting, param count) for symbols in a file or across the project. Use to identify complex code before refactoring. For historical trends use get_complexity_trend instead. Read-only. Returns JSON: { symbols: [{ symbol_id, name, kind, file, line, cyclomatic, max_nesting, param_count }], total }.",
63804
63899
  {
63805
63900
  file_path: z6.string().max(512).optional().describe("File path to report on (omit for project-wide top complex symbols)"),
63806
63901
  min_cyclomatic: z6.number().int().min(1).optional().describe("Min cyclomatic complexity to include (default: 1 for file, 5 for project)"),
@@ -63835,7 +63930,7 @@ function registerGitTools(server, ctx) {
63835
63930
  );
63836
63931
  server.tool(
63837
63932
  "check_rename",
63838
- "Pre-rename collision detection: checks the symbol's own file and all importing files for existing symbols with the target name",
63933
+ "Pre-rename collision detection: checks the symbol's own file and all importing files for existing symbols with the target name. Use before apply_rename to verify safety. Read-only (does not modify files). Returns JSON: { safe, conflicts: [{ symbol_id, name, file }] }.",
63839
63934
  {
63840
63935
  symbol_id: z6.string().max(512).describe("Symbol ID to rename"),
63841
63936
  target_name: z6.string().min(1).max(256).describe("Proposed new name")
@@ -65701,7 +65796,7 @@ function registerRefactoringTools(server, ctx) {
65701
65796
  const { store, projectRoot, guardPath, j: j3 } = ctx;
65702
65797
  server.tool(
65703
65798
  "apply_rename",
65704
- "Rename a symbol across all usages (definition + all importing files). Runs collision detection first and aborts on conflicts. Returns the list of edits applied.",
65799
+ 'Rename a symbol across all usages (definition + all importing files). Runs collision detection first and aborts on conflicts. Returns the list of edits applied. Modifies source files. Use check_rename first to verify safety; use plan_refactoring with type="rename" to preview edits. Returns JSON: { success, edits: [{ file, old_text, new_text }], filesModified }.',
65705
65800
  {
65706
65801
  symbol_id: z7.string().max(512).describe("Symbol ID to rename (from search or outline)"),
65707
65802
  new_name: z7.string().min(1).max(256).describe("New name for the symbol"),
@@ -65717,7 +65812,7 @@ function registerRefactoringTools(server, ctx) {
65717
65812
  );
65718
65813
  server.tool(
65719
65814
  "remove_dead_code",
65720
- "Safely remove a dead symbol from its file. Verifies the symbol is actually dead (multi-signal detection or zero incoming edges) before removal. Warns about orphaned imports in other files.",
65815
+ "Safely remove a dead symbol from its file. Verifies the symbol is actually dead (multi-signal detection or zero incoming edges) before removal. Warns about orphaned imports in other files. Destructive \u2014 deletes code from source files. Use get_dead_code first to identify candidates. Returns JSON: { success, removed: { symbol_id, file }, orphanedImports }.",
65721
65816
  {
65722
65817
  symbol_id: z7.string().max(512).describe("Symbol ID to remove (from get_dead_code results)"),
65723
65818
  dry_run: z7.boolean().default(false).describe("Preview changes without applying (default: false)")
@@ -65732,7 +65827,7 @@ function registerRefactoringTools(server, ctx) {
65732
65827
  );
65733
65828
  server.tool(
65734
65829
  "extract_function",
65735
- "Extract a range of lines into a new named function. Detects parameters (variables from outer scope) and return values (variables used after the range). Supports TypeScript/JavaScript, Python, and Go.",
65830
+ 'Extract a range of lines into a new named function. Detects parameters (variables from outer scope) and return values (variables used after the range). Supports TypeScript/JavaScript, Python, and Go. Modifies source files. Use plan_refactoring with type="extract" to preview first. Returns JSON: { success, edits: [{ file, old_text, new_text }], extractedFunction }.',
65736
65831
  {
65737
65832
  file_path: z7.string().max(512).describe("File path (relative to project root)"),
65738
65833
  start_line: z7.number().int().min(1).describe("First line to extract (1-indexed, inclusive)"),
@@ -65752,7 +65847,7 @@ function registerRefactoringTools(server, ctx) {
65752
65847
  );
65753
65848
  server.tool(
65754
65849
  "apply_codemod",
65755
- "Bulk regex find-and-replace across files. Dry-run by default \u2014 first call shows preview, second call with dry_run=false applies. Use for mechanical changes like adding async/await, renaming patterns, updating imports across many files.",
65850
+ "Bulk regex find-and-replace across files. Dry-run by default \u2014 first call shows preview, second call with dry_run=false applies. Use for mechanical changes like adding async/await, renaming patterns, updating imports across many files. Potentially destructive \u2014 can modify or delete code. Always preview with dry_run=true first. Returns JSON: { success, matchedFiles, changes: [{ file, matches }], applied }.",
65756
65851
  {
65757
65852
  pattern: z7.string().min(1).max(1e3).describe("Regex pattern to match (JavaScript regex syntax)"),
65758
65853
  replacement: z7.string().max(1e3).describe("Replacement string ($1, $2 for capture groups)"),
@@ -65777,7 +65872,7 @@ function registerRefactoringTools(server, ctx) {
65777
65872
  );
65778
65873
  server.tool(
65779
65874
  "apply_move",
65780
- "Move a symbol to a different file or rename/move a file, updating all import paths across the codebase. Dry-run by default (safe preview).",
65875
+ 'Move a symbol to a different file or rename/move a file, updating all import paths across the codebase. Dry-run by default (safe preview). Modifies source files. Use plan_refactoring with type="move" to preview first. Returns JSON: { success, edits: [{ file, old_text, new_text }], filesModified }.',
65781
65876
  {
65782
65877
  symbol_id: z7.string().max(512).optional().describe("Symbol ID to move (mode: symbol)"),
65783
65878
  target_file: z7.string().max(512).optional().describe("Target file path for the symbol (mode: symbol)"),
@@ -65841,7 +65936,7 @@ function registerRefactoringTools(server, ctx) {
65841
65936
  });
65842
65937
  server.tool(
65843
65938
  "change_signature",
65844
- "Change a function/method signature (add/remove/rename/reorder parameters) and update all call sites. Dry-run by default (safe preview).",
65939
+ 'Change a function/method signature (add/remove/rename/reorder parameters) and update all call sites. Dry-run by default (safe preview). Modifies source files. Use plan_refactoring with type="signature" to preview first. Returns JSON: { success, edits: [{ file, old_text, new_text }], callSitesUpdated }.',
65845
65940
  {
65846
65941
  symbol_id: z7.string().max(512).describe("Symbol ID of the function/method to modify"),
65847
65942
  changes: z7.array(signatureChangeSchema).min(1).max(20).describe("Array of changes to apply"),
@@ -65871,7 +65966,7 @@ function registerRefactoringTools(server, ctx) {
65871
65966
  });
65872
65967
  server.tool(
65873
65968
  "plan_refactoring",
65874
- "Preview any refactoring (rename, move, extract, signature) without applying. Returns all edits as {old_text, new_text} pairs. Use to review changes before applying the real tool.",
65969
+ "Preview any refactoring (rename, move, extract, signature) without applying. Returns all edits as {old_text, new_text} pairs. Read-only (does not modify files). Use to review the blast radius before calling apply_rename, apply_move, change_signature, or extract_function. Returns JSON: { success, type, edits: [{ file, old_text, new_text }], filesAffected }.",
65875
65970
  {
65876
65971
  type: z7.enum(["rename", "move", "extract", "signature"]).describe("Type of refactoring to preview"),
65877
65972
  symbol_id: z7.string().max(512).optional().describe("Symbol ID (for rename, move symbol, signature)"),
@@ -66391,9 +66486,9 @@ var OtlpReceiver = class {
66391
66486
  if (this.options.port === 0) return;
66392
66487
  return new Promise((resolve3, reject) => {
66393
66488
  this.server = createServer((req, res) => this.handleRequest(req, res));
66394
- this.server.on("error", (err13) => {
66395
- logger.error({ error: err13 }, "OTLP receiver error");
66396
- reject(err13);
66489
+ this.server.on("error", (err14) => {
66490
+ logger.error({ error: err14 }, "OTLP receiver error");
66491
+ reject(err14);
66397
66492
  });
66398
66493
  this.server.listen(this.options.port, this.options.host, () => {
66399
66494
  logger.info(
@@ -68006,16 +68101,16 @@ function findShortestPath(store, startNodeId, endNodeId, maxDepth) {
68006
68101
  parent.set(nodeId, { from, edgeType: edge.edge_type_name });
68007
68102
  nextFrontier.push(nodeId);
68008
68103
  if (nodeId === endNodeId) {
68009
- const path65 = [endNodeId];
68104
+ const path66 = [endNodeId];
68010
68105
  const edgeTypes = [];
68011
68106
  let cur = endNodeId;
68012
68107
  while (cur !== startNodeId) {
68013
68108
  const p = parent.get(cur);
68014
- path65.unshift(p.from);
68109
+ path66.unshift(p.from);
68015
68110
  edgeTypes.unshift(p.edgeType);
68016
68111
  cur = p.from;
68017
68112
  }
68018
- return { path: path65, edgeTypes };
68113
+ return { path: path66, edgeTypes };
68019
68114
  }
68020
68115
  }
68021
68116
  }
@@ -68404,14 +68499,14 @@ var FileRepository = class {
68404
68499
  }
68405
68500
  db;
68406
68501
  _stmts;
68407
- insertFile(path65, language, contentHash, byteLength, workspace, mtimeMs, createNode) {
68408
- const result = this._stmts.insertFile.run(path65, language, contentHash, byteLength, workspace, mtimeMs);
68502
+ insertFile(path66, language, contentHash, byteLength, workspace, mtimeMs, createNode) {
68503
+ const result = this._stmts.insertFile.run(path66, language, contentHash, byteLength, workspace, mtimeMs);
68409
68504
  const fileId = Number(result.lastInsertRowid);
68410
68505
  createNode("file", fileId);
68411
68506
  return fileId;
68412
68507
  }
68413
- getFile(path65) {
68414
- return this._stmts.getFile.get(path65);
68508
+ getFile(path66) {
68509
+ return this._stmts.getFile.get(path66);
68415
68510
  }
68416
68511
  getFileById(id) {
68417
68512
  return this._stmts.getFileById.get(id);
@@ -69269,9 +69364,9 @@ var Store = class {
69269
69364
  domain;
69270
69365
  analytics;
69271
69366
  // --- Files (delegates to FileRepository) ---
69272
- insertFile(path65, language, contentHash, byteLength, workspace, mtimeMs) {
69367
+ insertFile(path66, language, contentHash, byteLength, workspace, mtimeMs) {
69273
69368
  return this.files.insertFile(
69274
- path65,
69369
+ path66,
69275
69370
  language,
69276
69371
  contentHash,
69277
69372
  byteLength,
@@ -69280,8 +69375,8 @@ var Store = class {
69280
69375
  (nodeType, refId) => this.graph.createNode(nodeType, refId)
69281
69376
  );
69282
69377
  }
69283
- getFile(path65) {
69284
- return this.files.getFile(path65);
69378
+ getFile(path66) {
69379
+ return this.files.getFile(path66);
69285
69380
  }
69286
69381
  getFileById(id) {
69287
69382
  return this.files.getFileById(id);
@@ -70292,6 +70387,7 @@ const preTicks = Math.min(300, Math.max(50, Math.ceil(Math.log(N + 1) * 40)));
70292
70387
  let ticksDone = 0;
70293
70388
  const TICK_BATCH = N > 5000 ? 5 : 15;
70294
70389
  let layoutDone = false;
70390
+ let frameRequested = false;
70295
70391
  (function tickBatch() {
70296
70392
  const t0 = performance.now();
70297
70393
  while (ticksDone < preTicks && performance.now() - t0 < 12) { sim.tick(); ticksDone++; }
@@ -70433,7 +70529,6 @@ let highlightSet = null;
70433
70529
  let hoveredNode = null;
70434
70530
  let searchQ = '';
70435
70531
  let animating = false;
70436
- let frameRequested = false;
70437
70532
 
70438
70533
  function scheduleFrame() {
70439
70534
  if (!frameRequested) { frameRequested = true; requestAnimationFrame(frame); }
@@ -72116,7 +72211,7 @@ function registerAdvancedTools(server, ctx) {
72116
72211
  const additionalRepos = config.topology.repos ?? [];
72117
72212
  server.tool(
72118
72213
  "get_service_map",
72119
- "Get map of all services, their APIs, and inter-service dependencies. Auto-detects services from Docker Compose or treats each repo as a service.",
72214
+ "Get map of all services, their APIs, and inter-service dependencies. Auto-detects services from Docker Compose or treats each repo as a service. Use to understand microservice topology. For subproject-level graph use get_subproject_graph instead. Read-only. Returns JSON: { services: [{ name, endpoints, dependencies }], total }.",
72120
72215
  {
72121
72216
  include_endpoints: z8.boolean().optional().describe("Include full endpoint list per service (default false)")
72122
72217
  },
@@ -72128,7 +72223,7 @@ function registerAdvancedTools(server, ctx) {
72128
72223
  );
72129
72224
  server.tool(
72130
72225
  "get_cross_service_impact",
72131
- "Analyze cross-service impact of changing an endpoint or event. Shows which services would be affected.",
72226
+ "Analyze cross-service impact of changing an endpoint or event. Shows which services would be affected. Use before modifying a shared endpoint. For within-codebase impact use get_change_impact instead. Read-only. Returns JSON: { service, affectedServices: [{ name, reason }], total }.",
72132
72227
  {
72133
72228
  service: z8.string().min(1).max(256).describe("Service name"),
72134
72229
  endpoint: z8.string().max(512).optional().describe("Endpoint path (e.g. /api/users/{id})"),
@@ -72142,7 +72237,7 @@ function registerAdvancedTools(server, ctx) {
72142
72237
  );
72143
72238
  server.tool(
72144
72239
  "get_api_contract",
72145
- "Get API contract (OpenAPI/gRPC/GraphQL) for a service. Parses spec files found in the service repo.",
72240
+ "Get API contract (OpenAPI/gRPC/GraphQL) for a service. Parses spec files found in the service repo. Use to inspect a service's public API. For detecting spec-vs-code mismatches use get_contract_drift instead. Read-only. Returns JSON: { service, contract_type, endpoints, schemas }.",
72146
72241
  {
72147
72242
  service: z8.string().min(1).max(256).describe("Service name"),
72148
72243
  contract_type: z8.enum(["openapi", "grpc", "graphql"]).optional().describe("Filter by contract type")
@@ -72155,7 +72250,7 @@ function registerAdvancedTools(server, ctx) {
72155
72250
  );
72156
72251
  server.tool(
72157
72252
  "get_service_deps",
72158
- "Get external service dependencies: which services this one calls (outgoing) and which call it (incoming).",
72253
+ "Get external service dependencies: which services this one calls (outgoing) and which call it (incoming). Use to understand a single service's dependency profile. For full topology use get_service_map instead. Read-only. Returns JSON: { service, outgoing, incoming }.",
72159
72254
  {
72160
72255
  service: z8.string().min(1).max(256).describe("Service name"),
72161
72256
  direction: z8.enum(["outgoing", "incoming", "both"]).optional().describe("Dependency direction (default both)")
@@ -72168,7 +72263,7 @@ function registerAdvancedTools(server, ctx) {
72168
72263
  );
72169
72264
  server.tool(
72170
72265
  "get_contract_drift",
72171
- "Detect mismatches between API spec and implementation: endpoints in spec but not in code, or in code but not in spec.",
72266
+ "Detect mismatches between API spec and implementation: endpoints in spec but not in code, or in code but not in spec. Use to verify API contract accuracy. For reading the contract itself use get_api_contract instead. Read-only. Returns JSON: { service, missingInCode, missingInSpec, total }.",
72172
72267
  {
72173
72268
  service: z8.string().min(1).max(256).describe("Service name")
72174
72269
  },
@@ -72180,7 +72275,7 @@ function registerAdvancedTools(server, ctx) {
72180
72275
  );
72181
72276
  server.tool(
72182
72277
  "get_subproject_graph",
72183
- "Show all subprojects and their cross-repo connections. A subproject is any working repository in your project ecosystem (microservices, frontends, backends, shared libraries, CLI tools, etc.). Displays repos, endpoints, client calls, and inter-repo dependency edges.",
72278
+ "Show all subprojects and their cross-repo connections. A subproject is any working repository in your project ecosystem (microservices, frontends, backends, shared libraries, CLI tools, etc.). Displays repos, endpoints, client calls, and inter-repo dependency edges. Use to understand multi-repo topology. Register repos first with subproject_add_repo. Read-only. Returns JSON: { repos, endpoints, clientCalls, edges }.",
72184
72279
  {},
72185
72280
  async () => {
72186
72281
  const result = getSubprojectGraph(topoStore);
@@ -72190,7 +72285,7 @@ function registerAdvancedTools(server, ctx) {
72190
72285
  );
72191
72286
  server.tool(
72192
72287
  "get_subproject_impact",
72193
- "Cross-repo impact analysis: find all client code across subprojects that would break if an endpoint changes. Resolves down to symbol level when per-repo indexes exist.",
72288
+ "Cross-repo impact analysis: find all client code across subprojects that would break if an endpoint changes. Resolves down to symbol level when per-repo indexes exist. Use before modifying a shared API endpoint. Read-only. Returns JSON: { endpoint, affectedClients: [{ repo, file, line, callType }], total }.",
72194
72289
  {
72195
72290
  endpoint: z8.string().max(512).optional().describe("Endpoint path pattern (e.g. /api/users)"),
72196
72291
  method: z8.string().max(10).optional().describe("HTTP method filter (e.g. GET, POST)"),
@@ -72204,7 +72299,7 @@ function registerAdvancedTools(server, ctx) {
72204
72299
  );
72205
72300
  server.tool(
72206
72301
  "subproject_add_repo",
72207
- "Add a repository as a subproject of the current project. A subproject is any working repository in your ecosystem: microservices, frontends, backends, shared libraries, CLI tools. Discovers services, parses API contracts (OpenAPI/gRPC/GraphQL), scans for HTTP client calls, and links them to known endpoints.",
72302
+ "Add a repository as a subproject of the current project. A subproject is any working repository in your ecosystem: microservices, frontends, backends, shared libraries, CLI tools. Discovers services, parses API contracts (OpenAPI/gRPC/GraphQL), scans for HTTP client calls, and links them to known endpoints. Mutates the topology store; idempotent. Use to build multi-repo intelligence. Returns JSON: { added, services, contracts, clientCalls }.",
72208
72303
  {
72209
72304
  repo_path: z8.string().min(1).max(1024).describe("Absolute or relative path to the repository/service"),
72210
72305
  name: z8.string().max(256).optional().describe("Display name for the repo (default: directory basename)"),
@@ -72220,7 +72315,7 @@ function registerAdvancedTools(server, ctx) {
72220
72315
  );
72221
72316
  server.tool(
72222
72317
  "subproject_sync",
72223
- "Re-scan all subprojects: re-discover services, re-parse contracts, re-scan client calls, and re-link everything.",
72318
+ "Re-scan all subprojects: re-discover services, re-parse contracts, re-scan client calls, and re-link everything. Mutates the topology store; idempotent. Use after code changes in subproject repos. Returns JSON: { synced, services, contracts, clientCalls }.",
72224
72319
  {},
72225
72320
  async () => {
72226
72321
  const result = subprojectSync(topoStore);
@@ -72230,7 +72325,7 @@ function registerAdvancedTools(server, ctx) {
72230
72325
  );
72231
72326
  server.tool(
72232
72327
  "get_subproject_clients",
72233
- "Find all client calls across subprojects that call a specific endpoint. Shows file, line, call type, and confidence.",
72328
+ "Find all client calls across subprojects that call a specific endpoint. Shows file, line, call type, and confidence. Use to find all consumers of an endpoint before modifying it. Read-only. Returns JSON: { endpoint, clients: [{ repo, file, line, callType, confidence }], total }.",
72234
72329
  {
72235
72330
  endpoint: z8.string().min(1).max(512).describe("Endpoint path to search for (e.g. /api/users)"),
72236
72331
  method: z8.string().max(10).optional().describe("HTTP method filter")
@@ -72243,7 +72338,7 @@ function registerAdvancedTools(server, ctx) {
72243
72338
  );
72244
72339
  server.tool(
72245
72340
  "get_contract_versions",
72246
- "Show version history for a service API contract with breaking change detection between versions. Compares request/response schemas across snapshots to flag removed fields, type changes, and renames.",
72341
+ "Show version history for a service API contract with breaking change detection between versions. Compares request/response schemas across snapshots to flag removed fields, type changes, and renames. Use to review API evolution. For current spec-vs-code drift use get_contract_drift instead. Read-only. Returns JSON: { service, versions: [{ version, date, breakingChanges }] }.",
72247
72342
  {
72248
72343
  service: z8.string().min(1).max(256).describe("Service name"),
72249
72344
  limit: z8.number().int().min(1).max(50).optional().describe("Max versions to show (default 10)")
@@ -72256,7 +72351,7 @@ function registerAdvancedTools(server, ctx) {
72256
72351
  );
72257
72352
  server.tool(
72258
72353
  "discover_claude_sessions",
72259
- "Scan ~/.claude/projects for projects Claude Code has touched on this machine, decode each directory name back to its absolute path, and report which ones still exist plus session-file count and last activity. With add_as_subprojects=true, every existing project is registered as a subproject in one call \u2014 useful for spinning up multi-repo intelligence after a fresh clone.",
72354
+ "Scan ~/.claude/projects for projects Claude Code has touched on this machine, decode each directory name back to its absolute path, and report which ones still exist plus session-file count and last activity. With add_as_subprojects=true, every existing project is registered as a subproject in one call \u2014 useful for spinning up multi-repo intelligence after a fresh clone. Reads local filesystem; with add_as_subprojects=true also mutates topology store. Returns JSON: { projects: [{ path, sessions, lastActivity }], total }.",
72260
72355
  {
72261
72356
  scan_root: z8.string().max(1024).optional().describe("Override the scan root (default: ~/.claude/projects)"),
72262
72357
  exclude_current: z8.boolean().optional().describe("Exclude the current project from results (default: true)"),
@@ -72278,7 +72373,7 @@ function registerAdvancedTools(server, ctx) {
72278
72373
  );
72279
72374
  server.tool(
72280
72375
  "visualize_subproject_topology",
72281
- "Open interactive HTML visualization of the subproject topology: services as nodes, API calls as edges, health/risk indicators per service. Node size = endpoint count, color = health (green/yellow/red).",
72376
+ "Open interactive HTML visualization of the subproject topology: services as nodes, API calls as edges, health/risk indicators per service. Node size = endpoint count, color = health (green/yellow/red). Writes an HTML file to disk. Use for visual architecture review. Returns JSON: { outputPath, services, edges }.",
72282
72377
  {
72283
72378
  output: z8.string().max(512).optional().describe("Output file path (default: /tmp/trace-mcp-subproject-topology.html)"),
72284
72379
  layout: z8.enum(["force", "hierarchical", "radial"]).optional().describe("Graph layout (default force)")
@@ -72300,7 +72395,7 @@ function registerAdvancedTools(server, ctx) {
72300
72395
  runtimeIntelligence.start().catch((e) => logger.error({ error: e }, "Failed to start Runtime Intelligence"));
72301
72396
  server.tool(
72302
72397
  "get_runtime_profile",
72303
- "Runtime profile for a symbol or route: call count, latency percentiles (p50/p95/p99), error rate, calls per hour. Requires OTLP trace ingestion.",
72398
+ "Runtime profile for a symbol or route: call count, latency percentiles (p50/p95/p99), error rate, calls per hour. Requires OTLP trace ingestion. Read-only, queries external runtime data. Use for performance analysis of specific endpoints. Returns JSON: { symbol_id, callCount, latency: { p50, p95, p99 }, errorRate, callsPerHour }.",
72304
72399
  {
72305
72400
  symbol_id: z8.string().max(512).optional().describe("Symbol ID to profile"),
72306
72401
  fqn: z8.string().max(512).optional().describe("Fully qualified name"),
@@ -72315,7 +72410,7 @@ function registerAdvancedTools(server, ctx) {
72315
72410
  );
72316
72411
  server.tool(
72317
72412
  "get_runtime_call_graph",
72318
- "Actual call graph from runtime traces (vs static analysis). Shows observed call paths with call counts and latency.",
72413
+ "Actual call graph from runtime traces (vs static analysis). Shows observed call paths with call counts and latency. Requires OTLP trace ingestion. Read-only, queries external runtime data. For static call graph use get_call_graph instead. Returns JSON: { root, calls: [{ symbol, count, latency }] }.",
72319
72414
  {
72320
72415
  symbol_id: z8.string().max(512).optional().describe("Symbol ID as root"),
72321
72416
  fqn: z8.string().max(512).optional().describe("Fully qualified name as root"),
@@ -72330,7 +72425,7 @@ function registerAdvancedTools(server, ctx) {
72330
72425
  );
72331
72426
  server.tool(
72332
72427
  "get_endpoint_analytics",
72333
- "Per-route analytics: request count, error rate, latency, caller services. Requires OTLP trace ingestion.",
72428
+ "Per-route analytics: request count, error rate, latency, caller services. Requires OTLP trace ingestion. Read-only, queries external runtime data. Use to understand endpoint performance and traffic. Returns JSON: { uri, method, requestCount, errorRate, latency, callerServices }.",
72334
72429
  {
72335
72430
  uri: z8.string().max(512).describe('Route URI (e.g. "/api/users/{id}")'),
72336
72431
  method: z8.string().max(10).optional().describe("HTTP method filter"),
@@ -72344,7 +72439,7 @@ function registerAdvancedTools(server, ctx) {
72344
72439
  );
72345
72440
  server.tool(
72346
72441
  "get_runtime_deps",
72347
- "Which external services (databases, caches, APIs, queues) does this code actually call at runtime. Based on OTLP traces.",
72442
+ "Which external services (databases, caches, APIs, queues) does this code actually call at runtime. Based on OTLP traces. Read-only, queries external runtime data. Use to discover actual runtime dependencies vs static analysis. Returns JSON: { dependencies: [{ type, name, callCount }] }.",
72348
72443
  {
72349
72444
  symbol_id: z8.string().max(512).optional().describe("Symbol ID"),
72350
72445
  fqn: z8.string().max(512).optional().describe("Fully qualified name"),
@@ -72363,7 +72458,7 @@ function registerAdvancedTools(server, ctx) {
72363
72458
  }
72364
72459
  server.tool(
72365
72460
  "query_by_intent",
72366
- "Map a business question to domain taxonomy \u2192 returns domain ownership and relevance scores (no source code). Use when you need to know WHICH DOMAIN owns specific functionality.",
72461
+ "Map a business question to domain taxonomy \u2192 returns domain ownership and relevance scores (no source code). Use when you need to know WHICH DOMAIN owns specific functionality. For actual source code use get_feature_context instead. Read-only. Returns JSON: { symbols: [{ symbol_id, domain, relevance }] }.",
72367
72462
  {
72368
72463
  query: z8.string().min(1).max(500).describe("Business-level question about the codebase"),
72369
72464
  limit: z8.number().int().min(1).max(50).optional().describe("Max symbols to return (default 15)")
@@ -72381,7 +72476,7 @@ function registerAdvancedTools(server, ctx) {
72381
72476
  );
72382
72477
  server.tool(
72383
72478
  "get_domain_map",
72384
- "Get hierarchical map of business domains with key symbols per domain. Auto-builds domain taxonomy on first call using heuristic classification.",
72479
+ "Get hierarchical map of business domains with key symbols per domain. Auto-builds domain taxonomy on first call using heuristic classification. Use to understand business domain boundaries. For specific domain code use get_domain_context instead. Read-only. Returns JSON: { domains: [{ name, children, symbols }] }.",
72385
72480
  {
72386
72481
  depth: z8.number().int().min(1).max(5).optional().describe("Max taxonomy depth (default 3)"),
72387
72482
  include_symbols: z8.boolean().optional().describe("Include top symbols per domain (default true)"),
@@ -72395,7 +72490,7 @@ function registerAdvancedTools(server, ctx) {
72395
72490
  );
72396
72491
  server.tool(
72397
72492
  "get_domain_context",
72398
- 'Get all code related to a specific business domain. Supports "parent/child" notation (e.g. "payments/refunds").',
72493
+ 'Get all code related to a specific business domain. Supports "parent/child" notation (e.g. "payments/refunds"). Use to explore code within a domain boundary. For the full domain taxonomy use get_domain_map instead. Read-only. Returns JSON: { domain, symbols: [{ symbol_id, name, file, source }], relatedDomains }.',
72399
72494
  {
72400
72495
  domain: z8.string().min(1).max(256).describe('Domain name (e.g. "payments" or "payments/refunds")'),
72401
72496
  include_related: z8.boolean().optional().describe("Include symbols from related domains (default false)"),
@@ -72409,7 +72504,7 @@ function registerAdvancedTools(server, ctx) {
72409
72504
  );
72410
72505
  server.tool(
72411
72506
  "get_cross_domain_deps",
72412
- "Show which business domains depend on which. Based on edges between symbols in different domains.",
72507
+ "Show which business domains depend on which. Based on edges between symbols in different domains. Use to understand domain coupling. Read-only. Returns JSON: { dependencies: [{ from, to, edgeCount }] }.",
72413
72508
  {
72414
72509
  domain: z8.string().max(256).optional().describe("Focus on a specific domain (default: all)")
72415
72510
  },
@@ -72421,7 +72516,7 @@ function registerAdvancedTools(server, ctx) {
72421
72516
  );
72422
72517
  server.tool(
72423
72518
  "graph_query",
72424
- 'Trace how named symbols relate in the dependency graph \u2192 returns subgraph + Mermaid diagram. Input must contain symbol/class names (e.g. "How does AuthService reach Database?", "What depends on UserModel?").',
72519
+ 'Trace how named symbols relate in the dependency graph \u2192 returns subgraph + Mermaid diagram. Input must contain symbol/class names (e.g. "How does AuthService reach Database?", "What depends on UserModel?"). Use for ad-hoc graph exploration. For structured call graph use get_call_graph instead. Read-only. Returns JSON: { nodes, edges, mermaid }.',
72425
72520
  {
72426
72521
  query: z8.string().min(1).max(500).describe("Natural language question about code relationships"),
72427
72522
  depth: z8.number().int().min(1).max(6).optional().describe("Max traversal depth (default 3)"),
@@ -72435,7 +72530,7 @@ function registerAdvancedTools(server, ctx) {
72435
72530
  );
72436
72531
  server.tool(
72437
72532
  "get_dataflow",
72438
- "Intra-function dataflow analysis: track how each parameter flows through the function body \u2014 into which calls, where it gets mutated, and what is returned. Phase 1: single function scope.",
72533
+ "Intra-function dataflow analysis: track how each parameter flows through the function body \u2014 into which calls, where it gets mutated, and what is returned. Phase 1: single function scope. Use to understand data transformations within a function. For security-focused data flow use taint_analysis instead. Read-only. Returns JSON: { symbol_id, params: [{ name, flows: [{ target, mutated }] }], returnPaths }.",
72439
72534
  {
72440
72535
  symbol_id: z8.string().max(512).optional().describe("Symbol ID of the function/method to analyze"),
72441
72536
  fqn: z8.string().max(512).optional().describe("Fully qualified name of the function/method"),
@@ -72450,7 +72545,7 @@ function registerAdvancedTools(server, ctx) {
72450
72545
  );
72451
72546
  server.tool(
72452
72547
  "visualize_graph",
72453
- "Open interactive HTML graph in browser showing file/symbol dependencies. Supports force/hierarchical/radial layouts, community coloring. Use granularity=symbol to see individual functions/classes/methods as nodes instead of files.",
72548
+ "Open interactive HTML graph in browser showing file/symbol dependencies. Supports force/hierarchical/radial layouts, community coloring. Use granularity=symbol to see individual functions/classes/methods as nodes instead of files. Writes an HTML file to disk. For static Mermaid/DOT output use get_dependency_diagram instead. Returns JSON: { outputPath, nodes, edges }.",
72454
72549
  {
72455
72550
  scope: z8.string().min(1).max(512).describe('Scope: file path, directory (e.g. "src/"), or "project"'),
72456
72551
  depth: z8.number().int().min(1).max(5).optional().describe("Max hops from scope (default 2)"),
@@ -72486,7 +72581,7 @@ function registerAdvancedTools(server, ctx) {
72486
72581
  );
72487
72582
  server.tool(
72488
72583
  "get_dependency_diagram",
72489
- 'Render dependency diagram for a file/directory path as Mermaid or DOT. Input: a path like "src/tools/" \u2014 not a question. Trims to max_nodes most important nodes.',
72584
+ 'Render dependency diagram for a file/directory path as Mermaid or DOT. Input: a path like "src/tools/" \u2014 not a question. Trims to max_nodes most important nodes. Read-only. For interactive HTML visualization use visualize_graph instead. Returns JSON: { format, diagram, nodes, edges }.',
72490
72585
  {
72491
72586
  scope: z8.string().min(1).max(512).describe('Scope: file path, directory, or "project"'),
72492
72587
  depth: z8.number().int().min(1).max(5).optional().describe("Max hops from scope (default 2)"),
@@ -72501,7 +72596,7 @@ function registerAdvancedTools(server, ctx) {
72501
72596
  );
72502
72597
  server.tool(
72503
72598
  "search_text",
72504
- "Full-text search across all indexed files. Supports regex, glob file patterns, language filter. Use for finding strings, comments, TODOs, config values, error messages \u2014 anything not captured as a symbol.",
72599
+ "Full-text search across all indexed files. Supports regex, glob file patterns, language filter. Use for finding strings, comments, TODOs, config values, error messages \u2014 anything not captured as a symbol. For symbol search (functions, classes) use search instead. Read-only. Returns JSON: { matches: [{ file, line, text, context }], total_matches }.",
72505
72600
  {
72506
72601
  query: z8.string().min(1).max(1e3).describe("Search string or regex pattern"),
72507
72602
  is_regex: z8.boolean().optional().describe("Treat query as regex (default false)"),
@@ -72532,7 +72627,7 @@ function registerAdvancedTools(server, ctx) {
72532
72627
  );
72533
72628
  server.tool(
72534
72629
  "predict_bugs",
72535
- "Predict which files are most likely to contain bugs. Multi-signal scoring: git churn, fix-commit ratio, complexity, coupling, PageRank importance, author count. Each prediction includes a numeric score, risk bucket (low/medium/high/critical) AND a confidence_level (low/medium/high/multi_signal) counting how many independent signals actually fired. Result envelope includes _methodology disclosure. Cached for 1 hour; use refresh=true to recompute.",
72630
+ "Predict which files are most likely to contain bugs. Multi-signal scoring: git churn, fix-commit ratio, complexity, coupling, PageRank importance, author count. Each prediction includes a numeric score, risk bucket (low/medium/high/critical) AND a confidence_level (low/medium/high/multi_signal) counting how many independent signals actually fired. Result envelope includes _methodology disclosure. Cached for 1 hour; use refresh=true to recompute. Requires git. Use for proactive bug hunting. For complexity+churn hotspots only use get_risk_hotspots instead. Read-only. Returns JSON: { predictions: [{ file, score, risk, confidence_level, signals }], total }.",
72536
72631
  {
72537
72632
  limit: z8.number().int().min(1).max(200).optional().describe("Max results (default: 50)"),
72538
72633
  min_score: z8.number().min(0).max(1).optional().describe("Min bug probability score to include (default: 0)"),
@@ -72555,7 +72650,7 @@ function registerAdvancedTools(server, ctx) {
72555
72650
  );
72556
72651
  server.tool(
72557
72652
  "detect_drift",
72558
- "Detect architectural drift: cross-module co-change anomalies (files in different modules that always change together) and shotgun surgery patterns (commits touching 3+ modules). Requires git.",
72653
+ "Detect architectural drift: cross-module co-change anomalies (files in different modules that always change together) and shotgun surgery patterns (commits touching 3+ modules). Requires git. Use to identify hidden coupling across modules. For file-pair co-changes use get_co_changes instead. Read-only. Returns JSON: { anomalies, shotgunSurgery, total }.",
72559
72654
  {
72560
72655
  since_days: z8.number().int().min(1).optional().describe("Analyze commits from last N days (default: 180)"),
72561
72656
  min_confidence: z8.number().min(0).max(1).optional().describe("Min Jaccard confidence for co-change anomalies (default: 0.3)")
@@ -72572,7 +72667,7 @@ function registerAdvancedTools(server, ctx) {
72572
72667
  );
72573
72668
  server.tool(
72574
72669
  "get_tech_debt",
72575
- "Per-module tech debt score (A\u2013F grade) combining: complexity, coupling instability, test coverage gaps, and git churn. Includes actionable recommendations.",
72670
+ "Per-module tech debt score (A\u2013F grade) combining: complexity, coupling instability, test coverage gaps, and git churn. Includes actionable recommendations. Use for architecture review and prioritizing cleanup. Read-only. Returns JSON: { modules: [{ module, grade, score, factors, recommendations }] }.",
72576
72671
  {
72577
72672
  module: z8.string().max(256).optional().describe('Focus on a specific module path (e.g. "src/tools")'),
72578
72673
  refresh: z8.boolean().optional().describe("Force recomputation (default: false)")
@@ -72591,7 +72686,7 @@ function registerAdvancedTools(server, ctx) {
72591
72686
  );
72592
72687
  server.tool(
72593
72688
  "assess_change_risk",
72594
- "Before modifying a file or symbol, predict risk level (low/medium/high/critical) with contributing factors and recommended mitigations. Combines blast radius, complexity, git churn, test coverage, and coupling.",
72689
+ "Before modifying a file or symbol, predict risk level (low/medium/high/critical) with contributing factors and recommended mitigations. Combines blast radius, complexity, git churn, test coverage, and coupling. Use as a quick risk check. For full impact report with affected tests and dependents use get_change_impact instead. Read-only. Returns JSON: { risk, level, factors: [{ name, value }], mitigations }.",
72595
72690
  {
72596
72691
  file_path: z8.string().max(512).optional().describe("File path to assess"),
72597
72692
  symbol_id: z8.string().max(512).optional().describe("Symbol ID to assess")
@@ -72613,7 +72708,7 @@ function registerAdvancedTools(server, ctx) {
72613
72708
  );
72614
72709
  server.tool(
72615
72710
  "get_health_trends",
72616
- "Time-series health metrics for a file or module: bug score, complexity, coupling, churn over time. Populated by predict_bugs runs.",
72711
+ "Time-series health metrics for a file or module: bug score, complexity, coupling, churn over time. Populated by predict_bugs runs. Use to track if a module is improving or degrading. Read-only. Returns JSON: { dataPoints: [{ date, bugScore, complexity, coupling, churn }] }.",
72617
72712
  {
72618
72713
  file_path: z8.string().max(512).optional().describe("File path to check"),
72619
72714
  module: z8.string().max(256).optional().describe("Module path prefix to check"),
@@ -72631,7 +72726,7 @@ function registerAdvancedTools(server, ctx) {
72631
72726
  );
72632
72727
  server.tool(
72633
72728
  "get_workspace_map",
72634
- "List all detected monorepo workspaces with file counts, symbol counts, and languages. Returns dependency graph between workspaces showing cross-workspace imports.",
72729
+ "List all detected monorepo workspaces with file counts, symbol counts, and languages. Returns dependency graph between workspaces showing cross-workspace imports. Use for monorepo structure overview. For impact of changes on other workspaces use get_cross_workspace_impact instead. Read-only. Returns JSON: { workspaces: [{ name, files, symbols, languages }], dependencies }.",
72635
72730
  {
72636
72731
  include_dependencies: z8.boolean().optional().describe("Include cross-workspace dependency graph (default: true)")
72637
72732
  },
@@ -72662,7 +72757,7 @@ function registerAdvancedTools(server, ctx) {
72662
72757
  );
72663
72758
  server.tool(
72664
72759
  "get_cross_workspace_impact",
72665
- "Show which workspaces are affected by changes in a given workspace. Lists all cross-workspace edges, affected symbols, and the public API surface consumed by other workspaces.",
72760
+ "Show which workspaces are affected by changes in a given workspace. Lists all cross-workspace edges, affected symbols, and the public API surface consumed by other workspaces. Use before modifying shared code in a monorepo. Read-only. Returns JSON: { workspace, public_api, consumed_by, depends_on, cross_workspace_edges }.",
72666
72761
  {
72667
72762
  workspace: z8.string().max(256).describe("Workspace name to analyze")
72668
72763
  },
@@ -74493,28 +74588,331 @@ function evaluateQualityGates(store, projectRoot, gatesConfig, options = {}) {
74493
74588
  };
74494
74589
  }
74495
74590
 
74591
+ // src/tools/quality/security-context-export.ts
74592
+ import { readFileSync as readFileSync7 } from "fs";
74593
+ import path57 from "path";
74594
+ import { ok as ok10 } from "neverthrow";
74595
+ var CATEGORY_PATTERNS = [
74596
+ // file_read
74597
+ { pattern: /^(?:readFile|readFileSync|readdir|readdirSync|createReadStream|promises\.readFile)$/, category: "file_read" },
74598
+ { pattern: /^(?:fs\.read|fs\.promises\.read|fsPromises\.read)/, category: "file_read" },
74599
+ // file_write
74600
+ { pattern: /^(?:writeFile|writeFileSync|appendFile|appendFileSync|createWriteStream|promises\.writeFile)$/, category: "file_write" },
74601
+ { pattern: /^(?:unlink|unlinkSync|rm|rmSync|rmdir|rmdirSync|rename|renameSync|copyFile|copyFileSync)$/, category: "file_write" },
74602
+ { pattern: /^(?:fs\.write|fs\.promises\.write|fs\.unlink|fs\.rm|fsPromises\.write)/, category: "file_write" },
74603
+ { pattern: /^(?:mkdir|mkdirSync|promises\.mkdir)$/, category: "file_write" },
74604
+ // network_outbound
74605
+ { pattern: /^(?:fetch|request|got|axios)$/, category: "network_outbound" },
74606
+ { pattern: /^(?:http\.request|https\.request|http\.get|https\.get)$/, category: "network_outbound" },
74607
+ { pattern: /^(?:net\.connect|net\.createConnection|tls\.connect)$/, category: "network_outbound" },
74608
+ { pattern: /^(?:XMLHttpRequest|WebSocket)$/, category: "network_outbound" },
74609
+ // env_read
74610
+ { pattern: /^(?:process\.env|env\.)/, category: "env_read" },
74611
+ { pattern: /^(?:getenv|os\.environ|dotenv)/, category: "env_read" },
74612
+ // shell_exec
74613
+ { pattern: /^(?:exec|execSync|execFile|execFileSync|spawn|spawnSync|fork)$/, category: "shell_exec" },
74614
+ { pattern: /^(?:child_process\.|cp\.)/, category: "shell_exec" },
74615
+ // crypto
74616
+ { pattern: /^(?:createHash|createCipher|createCipheriv|createDecipher|createDecipheriv|createSign|createVerify|createHmac)$/, category: "crypto" },
74617
+ { pattern: /^(?:crypto\.)/, category: "crypto" },
74618
+ // serialization
74619
+ { pattern: /^(?:eval|Function)$/, category: "serialization" },
74620
+ { pattern: /^(?:deserialize|unserialize|pickle\.loads|yaml\.load)$/, category: "serialization" }
74621
+ ];
74622
+ function classifyFunction(name) {
74623
+ for (const { pattern, category } of CATEGORY_PATTERNS) {
74624
+ if (pattern.test(name)) return category;
74625
+ }
74626
+ return null;
74627
+ }
74628
+ var ANNOTATION_RE = /\{\s*(?:readOnlyHint|destructiveHint|idempotentHint|openWorldHint)\s*:/;
74629
+ function parseAnnotations(source, toolCallIndex) {
74630
+ const searchRegion = source.slice(toolCallIndex, toolCallIndex + 2e3);
74631
+ const annotationMatch = searchRegion.match(
74632
+ /\{\s*(readOnlyHint\s*:\s*(true|false)\s*,?\s*)?(destructiveHint\s*:\s*(true|false)\s*,?\s*)?(idempotentHint\s*:\s*(true|false)\s*,?\s*)?(openWorldHint\s*:\s*(true|false)\s*,?\s*)\}/
74633
+ );
74634
+ if (!annotationMatch) return null;
74635
+ const annotations = {};
74636
+ if (annotationMatch[2]) annotations.readOnlyHint = annotationMatch[2] === "true";
74637
+ if (annotationMatch[4]) annotations.destructiveHint = annotationMatch[4] === "true";
74638
+ if (annotationMatch[6]) annotations.idempotentHint = annotationMatch[6] === "true";
74639
+ if (annotationMatch[8]) annotations.openWorldHint = annotationMatch[8] === "true";
74640
+ return Object.keys(annotations).length > 0 ? annotations : null;
74641
+ }
74642
+ function parseAnnotationsFlexible(source, toolCallIndex) {
74643
+ const searchRegion = source.slice(toolCallIndex, toolCallIndex + 3e3);
74644
+ if (!ANNOTATION_RE.test(searchRegion)) return null;
74645
+ const annotations = {};
74646
+ const hints = ["readOnlyHint", "destructiveHint", "idempotentHint", "openWorldHint"];
74647
+ for (const hint of hints) {
74648
+ const re = new RegExp(`${hint}\\s*:\\s*(true|false)`);
74649
+ const m = searchRegion.match(re);
74650
+ if (m) annotations[hint] = m[1] === "true";
74651
+ }
74652
+ return Object.keys(annotations).length > 0 ? annotations : null;
74653
+ }
74654
+ function collectCallsFromGraph(node, visited, results) {
74655
+ if (visited.has(node.symbol_id)) return;
74656
+ visited.add(node.symbol_id);
74657
+ const category = classifyFunction(node.name);
74658
+ if (category && node.file && node.line) {
74659
+ results.push({
74660
+ function: node.name,
74661
+ file: node.file,
74662
+ line: node.line,
74663
+ category
74664
+ });
74665
+ }
74666
+ if (node.calls) {
74667
+ for (const callee of node.calls) {
74668
+ collectCallsFromGraph(callee, visited, results);
74669
+ }
74670
+ }
74671
+ }
74672
+ var SECURITY_CALL_RE = /\b((?:fs|http|https|net|crypto|child_process|cp)\.\w+|fetch|exec|execSync|spawn|spawnSync|eval|require|writeFile\w*|readFile\w*|unlink\w*|request|axios|got)\s*\(/g;
74673
+ var ENV_ACCESS_RE = /process\.env\b/g;
74674
+ var GENERIC_CALL_RE = /\b([a-zA-Z_$]\w*)\s*\(/g;
74675
+ var SKIP_KEYWORDS = /* @__PURE__ */ new Set([
74676
+ "if",
74677
+ "for",
74678
+ "while",
74679
+ "switch",
74680
+ "catch",
74681
+ "return",
74682
+ "typeof",
74683
+ "new",
74684
+ "async",
74685
+ "await",
74686
+ "function",
74687
+ "const",
74688
+ "let",
74689
+ "var",
74690
+ "class",
74691
+ "import",
74692
+ "export",
74693
+ "throw",
74694
+ "delete",
74695
+ "void",
74696
+ "yield",
74697
+ "as",
74698
+ "from"
74699
+ ]);
74700
+ function findHandlerBounds(source, toolCallIndex) {
74701
+ const region = source.slice(toolCallIndex, toolCallIndex + 1e4);
74702
+ const arrowMatch = region.match(/async\s+(?:\([^)]*\)|[^=]*?)\s*=>\s*\{/);
74703
+ const funcMatch = region.match(/async\s+function\s*\([^)]*\)\s*\{/);
74704
+ const match = arrowMatch ?? funcMatch;
74705
+ if (!match || match.index === void 0) return null;
74706
+ const bodyBraceOffset = match.index + match[0].length - 1;
74707
+ const afterBrace = region.slice(bodyBraceOffset);
74708
+ let depth = 1;
74709
+ let end = -1;
74710
+ for (let i = 1; i < afterBrace.length; i++) {
74711
+ if (afterBrace[i] === "{") depth++;
74712
+ else if (afterBrace[i] === "}") {
74713
+ depth--;
74714
+ if (depth === 0) {
74715
+ end = i + 1;
74716
+ break;
74717
+ }
74718
+ }
74719
+ }
74720
+ if (end === -1) end = Math.min(afterBrace.length, 5e3);
74721
+ const absStart = toolCallIndex + bodyBraceOffset;
74722
+ const absEnd = toolCallIndex + bodyBraceOffset + end;
74723
+ return { start: absStart, end: absEnd };
74724
+ }
74725
+ function scanInlineHandler(source, toolCallIndex, filePath, store, projectRoot, depth) {
74726
+ const bounds = findHandlerBounds(source, toolCallIndex);
74727
+ if (!bounds) return { calls: [], calledSymbolIds: [] };
74728
+ const handlerBody = source.slice(bounds.start, bounds.end);
74729
+ const results = [];
74730
+ const calledSymbolIds = [];
74731
+ let m;
74732
+ const secRe = new RegExp(SECURITY_CALL_RE.source, "g");
74733
+ while ((m = secRe.exec(handlerBody)) !== null) {
74734
+ const funcName = m[1];
74735
+ const category = classifyFunction(funcName);
74736
+ if (category) {
74737
+ const lineOffset = source.slice(0, bounds.start + m.index).split("\n").length;
74738
+ results.push({ function: funcName, file: filePath, line: lineOffset, category });
74739
+ }
74740
+ }
74741
+ const envRe = new RegExp(ENV_ACCESS_RE.source, "g");
74742
+ while ((m = envRe.exec(handlerBody)) !== null) {
74743
+ const lineOffset = source.slice(0, bounds.start + m.index).split("\n").length;
74744
+ results.push({ function: "process.env", file: filePath, line: lineOffset, category: "env_read" });
74745
+ }
74746
+ const genericRe = new RegExp(GENERIC_CALL_RE.source, "g");
74747
+ const seenFuncs = /* @__PURE__ */ new Set();
74748
+ while ((m = genericRe.exec(handlerBody)) !== null) {
74749
+ const funcName = m[1];
74750
+ if (SKIP_KEYWORDS.has(funcName) || seenFuncs.has(funcName)) continue;
74751
+ seenFuncs.add(funcName);
74752
+ const sym = store.getSymbolByName(funcName, "function") ?? store.getSymbolByName(funcName, "method");
74753
+ if (!sym) continue;
74754
+ calledSymbolIds.push(sym.symbol_id);
74755
+ const cgResult = getCallGraph(store, { symbolId: sym.symbol_id }, depth);
74756
+ if (cgResult.isOk() && cgResult.value.root) {
74757
+ const visited = /* @__PURE__ */ new Set();
74758
+ collectCallsFromGraph(cgResult.value.root, visited, results);
74759
+ }
74760
+ const file = sym.file_id ? store.getFileById(sym.file_id) : null;
74761
+ if (file && sym.line_start && sym.line_end) {
74762
+ const symAbsPath = path57.resolve(projectRoot, file.path);
74763
+ try {
74764
+ const symSource = readFileSync7(symAbsPath, "utf-8");
74765
+ const lines = symSource.split("\n");
74766
+ const symBody = lines.slice(sym.line_start - 1, sym.line_end).join("\n");
74767
+ scanSourceForSecurityCalls(symBody, file.path, sym.line_start, results);
74768
+ } catch {
74769
+ }
74770
+ }
74771
+ }
74772
+ return { calls: results, calledSymbolIds };
74773
+ }
74774
+ function scanSourceForSecurityCalls(body, filePath, startLine, results) {
74775
+ let m;
74776
+ const secRe = new RegExp(SECURITY_CALL_RE.source, "g");
74777
+ while ((m = secRe.exec(body)) !== null) {
74778
+ const funcName = m[1];
74779
+ const category = classifyFunction(funcName);
74780
+ if (category) {
74781
+ const lineOffset = startLine + body.slice(0, m.index).split("\n").length - 1;
74782
+ results.push({ function: funcName, file: filePath, line: lineOffset, category });
74783
+ }
74784
+ }
74785
+ const envRe = new RegExp(ENV_ACCESS_RE.source, "g");
74786
+ while ((m = envRe.exec(body)) !== null) {
74787
+ const lineOffset = startLine + body.slice(0, m.index).split("\n").length - 1;
74788
+ results.push({ function: "process.env", file: filePath, line: lineOffset, category: "env_read" });
74789
+ }
74790
+ }
74791
+ var PKG_VERSION = true ? "1.22.0" : "0.0.0-dev";
74792
+ function exportSecurityContext(store, projectRoot, opts = {}) {
74793
+ const depth = Math.min(opts.depth ?? 3, 5);
74794
+ const warnings = [];
74795
+ const allRoutes = store.getAllRoutes();
74796
+ const toolRoutes = allRoutes.filter((r) => r.method === "TOOL");
74797
+ if (toolRoutes.length === 0) {
74798
+ warnings.push("No MCP tool registrations found in the index. Ensure the project uses @modelcontextprotocol/sdk and has been indexed.");
74799
+ }
74800
+ const toolRegistrations = [];
74801
+ const capabilityMap = {};
74802
+ for (const route of toolRoutes) {
74803
+ const fileRow = route.file_id ? store.getFileById(route.file_id) : null;
74804
+ if (!fileRow) continue;
74805
+ if (opts.scope && !fileRow.path.startsWith(opts.scope)) continue;
74806
+ const absPath = path57.resolve(projectRoot, fileRow.path);
74807
+ let source;
74808
+ try {
74809
+ source = readFileSync7(absPath, "utf-8");
74810
+ } catch {
74811
+ continue;
74812
+ }
74813
+ const toolNameEscaped = route.uri.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
74814
+ const toolCallRe = new RegExp(`\\.tool\\(\\s*['"]${toolNameEscaped}['"]`);
74815
+ const toolCallMatch = toolCallRe.exec(source);
74816
+ const toolCallIndex = toolCallMatch?.index ?? 0;
74817
+ const toolLine = toolCallIndex > 0 ? source.slice(0, toolCallIndex).split("\n").length : route.line ?? 0;
74818
+ const annotations = parseAnnotationsFlexible(source, toolCallIndex) ?? parseAnnotations(source, toolCallIndex);
74819
+ let handlerCalls = [];
74820
+ let handlerResolved = false;
74821
+ if (toolCallIndex > 0) {
74822
+ const scanResult = scanInlineHandler(source, toolCallIndex, fileRow.path, store, projectRoot, depth);
74823
+ handlerCalls = scanResult.calls;
74824
+ handlerResolved = handlerCalls.length > 0 || scanResult.calledSymbolIds.length > 0;
74825
+ }
74826
+ const seen = /* @__PURE__ */ new Set();
74827
+ handlerCalls = handlerCalls.filter((c) => {
74828
+ const key = `${c.function}:${c.file}:${c.line}`;
74829
+ if (seen.has(key)) return false;
74830
+ seen.add(key);
74831
+ return true;
74832
+ });
74833
+ for (const call of handlerCalls) {
74834
+ if (!capabilityMap[call.file]) capabilityMap[call.file] = /* @__PURE__ */ new Set();
74835
+ capabilityMap[call.file].add(call.category);
74836
+ }
74837
+ toolRegistrations.push({
74838
+ name: route.uri,
74839
+ description: route.name ?? null,
74840
+ file: fileRow.path,
74841
+ line: toolLine,
74842
+ annotations,
74843
+ handler_resolved: handlerResolved,
74844
+ handler_calls: handlerCalls
74845
+ });
74846
+ }
74847
+ const sensitiveFlows = [];
74848
+ const mcpServerFiles = new Set(toolRegistrations.map((t) => t.file));
74849
+ if (mcpServerFiles.size > 0) {
74850
+ const mcpDirs = [...new Set([...mcpServerFiles].map((f) => path57.dirname(f)))];
74851
+ for (const dir of mcpDirs) {
74852
+ const taintResult = taintAnalysis(store, projectRoot, {
74853
+ scope: dir,
74854
+ sources: ["env", "file_read"],
74855
+ includeSanitized: false,
74856
+ limit: 50
74857
+ });
74858
+ if (taintResult.isOk()) {
74859
+ for (const flow of taintResult.value.flows) {
74860
+ sensitiveFlows.push({
74861
+ source: {
74862
+ kind: flow.source.kind,
74863
+ name: flow.source.variable,
74864
+ file: flow.file,
74865
+ line: flow.source.line
74866
+ },
74867
+ sink: {
74868
+ kind: flow.sink.kind,
74869
+ file: flow.file,
74870
+ line: flow.sink.line
74871
+ },
74872
+ hops: flow.path.map((step) => `${flow.file}:${step.line}`)
74873
+ });
74874
+ }
74875
+ }
74876
+ }
74877
+ }
74878
+ const capabilityMapOutput = {};
74879
+ for (const [file, categories] of Object.entries(capabilityMap)) {
74880
+ capabilityMapOutput[file] = [...categories].sort();
74881
+ }
74882
+ return ok10({
74883
+ $schema: "https://skill-scan.dev/schemas/enrichment/v1.json",
74884
+ version: "1",
74885
+ generator: `trace-mcp/${PKG_VERSION}`,
74886
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
74887
+ tool_registrations: toolRegistrations,
74888
+ sensitive_flows: sensitiveFlows,
74889
+ capability_map: capabilityMapOutput,
74890
+ warnings
74891
+ });
74892
+ }
74893
+
74496
74894
  // src/tools/register/quality.ts
74497
74895
  function registerQualityTools(server, ctx) {
74498
74896
  const { store, registry, config, projectRoot, j: j3 } = ctx;
74499
- server.tool("get_co_changes", "Find files that frequently change together in git history (temporal coupling).", { file: z10.string().min(1).max(512).describe("File path to analyze"), min_confidence: z10.number().min(0).max(1).optional().describe("Minimum confidence threshold (default 0.3)"), min_count: z10.number().int().min(1).optional().describe("Minimum co-change count (default 3)"), window_days: z10.number().int().min(1).max(730).optional().describe("Git history window in days (default 180)"), limit: z10.number().int().min(1).max(100).optional().describe("Max results (default 20)") }, async ({ file, min_confidence, min_count, window_days, limit: lim }) => {
74897
+ server.tool("get_co_changes", "Find files that frequently change together in git history (temporal coupling). Requires git. Use to discover hidden dependencies between files. For cross-module co-change anomalies use detect_drift instead. Read-only. Returns JSON: { file, coChanges: [{ file, confidence, count }] }.", { file: z10.string().min(1).max(512).describe("File path to analyze"), min_confidence: z10.number().min(0).max(1).optional().describe("Minimum confidence threshold (default 0.3)"), min_count: z10.number().int().min(1).optional().describe("Minimum co-change count (default 3)"), window_days: z10.number().int().min(1).max(730).optional().describe("Git history window in days (default 180)"), limit: z10.number().int().min(1).max(100).optional().describe("Max results (default 20)") }, async ({ file, min_confidence, min_count, window_days, limit: lim }) => {
74500
74898
  const result = getCoChanges(store, { file, minConfidence: min_confidence, minCount: min_count, windowDays: window_days, limit: lim });
74501
74899
  if (result.isErr()) return { content: [{ type: "text", text: j3(formatToolError(result.error)) }], isError: true };
74502
74900
  return { content: [{ type: "text", text: j3(result.value) }] };
74503
74901
  });
74504
- server.tool("refresh_co_changes", "Rebuild co-change index from git history.", { window_days: z10.number().int().min(1).max(730).optional().describe("Git history window in days (default 180)") }, async ({ window_days }) => {
74902
+ server.tool("refresh_co_changes", "Rebuild co-change index from git history. Mutates the co-change index; idempotent. Use after significant git history changes. Returns JSON: { status, pairs_stored, window_days }.", { window_days: z10.number().int().min(1).max(730).optional().describe("Git history window in days (default 180)") }, async ({ window_days }) => {
74505
74903
  const days = window_days ?? 180;
74506
74904
  const pairs = collectCoChanges(projectRoot, days);
74507
74905
  const count2 = persistCoChanges(store, pairs, projectRoot, days);
74508
74906
  return { content: [{ type: "text", text: j3({ status: "completed", pairs_stored: count2, window_days: days }) }] };
74509
74907
  });
74510
- server.tool("get_changed_symbols", 'Map a git diff to affected symbols (functions, classes, methods). For PR review. If "since" is omitted, auto-detects main/master as the base.', { since: z10.string().min(1).max(256).optional().describe("Git ref to compare from (SHA, branch, tag). If omitted, auto-detects main/master merge-base"), until: z10.string().max(256).optional().describe("Git ref to compare to (default: HEAD)"), include_blast_radius: z10.boolean().optional().describe("Include blast radius for each changed symbol (default false)"), max_blast_depth: z10.number().int().min(1).max(10).optional().describe("Max blast radius traversal depth (default 3)") }, async ({ since, until, include_blast_radius, max_blast_depth }) => {
74908
+ server.tool("get_changed_symbols", 'Map a git diff to affected symbols (functions, classes, methods). For PR review. If "since" is omitted, auto-detects main/master as the base. Requires git. Use for PR review to see which symbols changed. For full branch comparison with risk assessment use compare_branches instead. Read-only. Returns JSON: { changes: [{ symbol_id, name, kind, file, changeType }], total }.', { since: z10.string().min(1).max(256).optional().describe("Git ref to compare from (SHA, branch, tag). If omitted, auto-detects main/master merge-base"), until: z10.string().max(256).optional().describe("Git ref to compare to (default: HEAD)"), include_blast_radius: z10.boolean().optional().describe("Include blast radius for each changed symbol (default false)"), max_blast_depth: z10.number().int().min(1).max(10).optional().describe("Max blast radius traversal depth (default 3)") }, async ({ since, until, include_blast_radius, max_blast_depth }) => {
74511
74909
  const result = getChangedSymbols(store, projectRoot, { since, until, includeBlastRadius: include_blast_radius, maxBlastDepth: max_blast_depth, defaultBaseBranch: config.git?.defaultBaseBranch });
74512
74910
  if (result.isErr()) return { content: [{ type: "text", text: j3(formatToolError(result.error)) }], isError: true };
74513
74911
  return { content: [{ type: "text", text: j3(result.value) }] };
74514
74912
  });
74515
74913
  server.tool(
74516
74914
  "compare_branches",
74517
- "Compare two branches at symbol level: what was added, modified, removed. Resolves merge-base automatically, groups by category/file/risk, includes blast radius and risk assessment.",
74915
+ "Compare two branches at symbol level: what was added, modified, removed. Resolves merge-base automatically, groups by category/file/risk, includes blast radius and risk assessment. Requires git. Use for comprehensive PR comparison. For a quick list of changed symbols without risk analysis use get_changed_symbols instead. Read-only. Returns JSON: { branch, base, mergeBase, changes: [{ symbol_id, category, risk }], summary }.",
74518
74916
  {
74519
74917
  branch: z10.string().min(1).max(256).describe('Branch to compare (e.g. "feature/payments")'),
74520
74918
  base: z10.string().max(256).optional().describe('Base branch (default: "main")'),
@@ -74535,28 +74933,28 @@ function registerQualityTools(server, ctx) {
74535
74933
  return { content: [{ type: "text", text: j3(result.value) }] };
74536
74934
  }
74537
74935
  );
74538
- server.tool("detect_communities", "Run Leiden community detection on the file dependency graph. Identifies tightly-coupled file clusters (modules).", { resolution: z10.number().min(0.1).max(5).optional().describe("Resolution parameter \u2014 higher values produce more communities (default 1.0)") }, async ({ resolution }) => {
74936
+ server.tool("detect_communities", "Run Leiden community detection on the file dependency graph. Identifies tightly-coupled file clusters (modules). Mutates the community index (stores results); idempotent. Use before get_communities or get_community. Returns JSON: { communities: [{ id, files, size }], modularity }.", { resolution: z10.number().min(0.1).max(5).optional().describe("Resolution parameter \u2014 higher values produce more communities (default 1.0)") }, async ({ resolution }) => {
74539
74937
  const result = detectCommunities2(store, resolution ?? 1);
74540
74938
  if (result.isErr()) return { content: [{ type: "text", text: j3(formatToolError(result.error)) }], isError: true };
74541
74939
  return { content: [{ type: "text", text: j3(result.value) }] };
74542
74940
  });
74543
- server.tool("get_communities", "Get previously detected communities (file clusters). Run detect_communities first.", {}, async () => {
74941
+ server.tool("get_communities", "Get previously detected communities (file clusters). Run detect_communities first. Read-only. Returns JSON: { communities: [{ id, files, size }], total }.", {}, async () => {
74544
74942
  const result = getCommunities(store);
74545
74943
  if (result.isErr()) return { content: [{ type: "text", text: j3(formatToolError(result.error)) }], isError: true };
74546
74944
  return { content: [{ type: "text", text: j3(result.value) }] };
74547
74945
  });
74548
- server.tool("get_community", "Get details for a specific community: files, inter-community dependencies.", { id: z10.number().int().min(0).describe("Community ID") }, async ({ id }) => {
74946
+ server.tool("get_community", "Get details for a specific community: files, inter-community dependencies. Read-only. Use after detect_communities to drill into a specific cluster. Returns JSON: { id, files, interCommunityDeps }.", { id: z10.number().int().min(0).describe("Community ID") }, async ({ id }) => {
74549
74947
  const result = getCommunityDetail(store, id);
74550
74948
  if (result.isErr()) return { content: [{ type: "text", text: j3(formatToolError(result.error)) }], isError: true };
74551
74949
  return { content: [{ type: "text", text: j3(result.value) }] };
74552
74950
  });
74553
- server.tool("audit_config", "Scan AI agent config files for stale references, dead paths, and token bloat.", { config_files: z10.array(z10.string().max(512)).optional().describe("Specific config files to audit (default: auto-detect)"), fix_suggestions: z10.boolean().optional().describe("Include fix suggestions (default true)") }, async ({ config_files, fix_suggestions }) => {
74951
+ server.tool("audit_config", "Scan AI agent config files for stale references, dead paths, and token bloat. Read-only. Use periodically to clean up CLAUDE.md and settings. Returns JSON: { issues: [{ file, type, message, suggestion }], total }.", { config_files: z10.array(z10.string().max(512)).optional().describe("Specific config files to audit (default: auto-detect)"), fix_suggestions: z10.boolean().optional().describe("Include fix suggestions (default true)") }, async ({ config_files, fix_suggestions }) => {
74554
74952
  const result = auditConfig(store, projectRoot, { configFiles: config_files, fixSuggestions: fix_suggestions ?? true });
74555
74953
  return { content: [{ type: "text", text: j3(result) }] };
74556
74954
  });
74557
74955
  server.tool(
74558
74956
  "get_control_flow",
74559
- "Build a Control Flow Graph (CFG) for a function/method: if/else branches, loops, try/catch, returns, throws. Shows logical paths through the code. Outputs Mermaid diagram, ASCII, or JSON.",
74957
+ "Build a Control Flow Graph (CFG) for a function/method: if/else branches, loops, try/catch, returns, throws. Shows logical paths through the code. Outputs Mermaid diagram, ASCII, or JSON. Use to understand branching logic before modifying complex functions. For call-level graph (who calls whom) use get_call_graph instead. Read-only. Returns Mermaid/ASCII/JSON: { nodes, edges, entryPoint, exitPoints }.",
74560
74958
  {
74561
74959
  symbol_id: z10.string().max(512).optional().describe("Symbol ID of the function/method"),
74562
74960
  fqn: z10.string().max(512).optional().describe("Fully qualified name of the function/method"),
@@ -74578,7 +74976,7 @@ function registerQualityTools(server, ctx) {
74578
74976
  );
74579
74977
  server.tool(
74580
74978
  "get_package_deps",
74581
- "Cross-repo package dependency analysis: find which registered projects depend on a package, or what packages a project publishes. Scans package.json/composer.json/pyproject.toml across all repos in the registry.",
74979
+ "Cross-repo package dependency analysis: find which registered projects depend on a package, or what packages a project publishes. Scans package.json/composer.json/pyproject.toml across all repos in the registry. Use for cross-project dependency mapping. For impact of upgrading a specific package use plan_batch_change instead. Read-only. Returns JSON: { dependents, dependencies, package }.",
74582
74980
  {
74583
74981
  package: z10.string().max(256).optional().describe('Package name to analyze (e.g. "@myorg/shared-utils")'),
74584
74982
  project: z10.string().max(256).optional().describe("Project name \u2014 analyze all packages it publishes"),
@@ -74595,7 +74993,7 @@ function registerQualityTools(server, ctx) {
74595
74993
  );
74596
74994
  server.tool(
74597
74995
  "generate_docs",
74598
- "Auto-generate project documentation from the code graph. Produces structured docs with architecture, API surface, data models, components, and dependency analysis.",
74996
+ "Auto-generate project documentation from the code graph. Produces structured docs with architecture, API surface, data models, components, and dependency analysis. Writes output file (markdown or HTML). Use when you need a comprehensive documentation snapshot. Returns JSON: { format, sections, outputPath }.",
74599
74997
  {
74600
74998
  scope: z10.enum(["project", "module", "directory"]).optional().describe("Scope (default: project)"),
74601
74999
  path: z10.string().max(512).optional().describe("Path for module/directory scope"),
@@ -74615,7 +75013,7 @@ function registerQualityTools(server, ctx) {
74615
75013
  );
74616
75014
  server.tool(
74617
75015
  "pack_context",
74618
- "Pack project context into a single document for external LLMs. Intelligent selection by graph importance, fits within token budget. Better than Repomix for focused context. Strategies: most_relevant (default \u2014 feature/PageRank ranked), core_first (PageRank always wins, surfaces architecturally central code), compact (signatures only \u2014 drops source bodies, lets outlines cover much more of the repo per token).",
75016
+ "Pack project context into a single document for external LLMs. Intelligent selection by graph importance, fits within token budget. Better than Repomix for focused context. Strategies: most_relevant (default \u2014 feature/PageRank ranked), core_first (PageRank always wins, surfaces architecturally central code), compact (signatures only \u2014 drops source bodies, lets outlines cover much more of the repo per token). Read-only. Use when sharing project context with external tools. Returns XML/Markdown/JSON with selected code within budget.",
74619
75017
  {
74620
75018
  scope: z10.enum(["project", "module", "feature"]).describe("Scope: project (whole repo), module (subdirectory), feature (NL query)"),
74621
75019
  path: z10.string().max(512).optional().describe("Subdirectory path (for module scope)"),
@@ -74645,7 +75043,7 @@ function registerQualityTools(server, ctx) {
74645
75043
  );
74646
75044
  server.tool(
74647
75045
  "check_quality_gates",
74648
- "Run configurable quality gate checks against the project. Returns pass/fail for each gate (complexity, coupling, circular imports, dead exports, tech debt, security, antipatterns, code smells). Designed for CI integration \u2014 AI can verify gates pass before committing.",
75046
+ "Run configurable quality gate checks against the project. Returns pass/fail for each gate (complexity, coupling, circular imports, dead exports, tech debt, security, antipatterns, code smells). Designed for CI integration \u2014 AI can verify gates pass before committing. Use before PR/commit to ensure quality standards. Read-only. Returns JSON: { passed, gates: [{ name, status, value, threshold }], summary }.",
74649
75047
  {
74650
75048
  scope: z10.enum(["project", "changed"]).optional().describe('Scope: "project" (all) or "changed" (git diff). Default: project'),
74651
75049
  since: z10.string().max(128).optional().describe('Git ref for "changed" scope (e.g. "main")'),
@@ -74690,6 +75088,21 @@ function registerQualityTools(server, ctx) {
74690
75088
  return { content: [{ type: "text", text: j3(report) }] };
74691
75089
  }
74692
75090
  );
75091
+ server.tool(
75092
+ "export_security_context",
75093
+ "Export security context for MCP server analysis. Generates enrichment JSON for skill-scan: tool registrations with annotations, transitive call graphs classified by security category (file_read, file_write, network_outbound, env_read, shell_exec, crypto, serialization), sensitive data flows, and per-file capability maps. Use to analyze MCP server security before installation. Read-only. Returns JSON: { tool_registrations, sensitive_flows, capability_map, warnings }.",
75094
+ {
75095
+ scope: z10.string().max(512).optional().describe("Limit analysis to directory (relative to project root)"),
75096
+ depth: z10.number().int().min(1).max(5).optional().describe("Call graph traversal depth (default: 3)")
75097
+ },
75098
+ async ({ scope, depth }) => {
75099
+ const result = exportSecurityContext(store, projectRoot, { scope, depth });
75100
+ if (result.isErr()) {
75101
+ return { content: [{ type: "text", text: j3(formatToolError(result.error)) }], isError: true };
75102
+ }
75103
+ return { content: [{ type: "text", text: j3(result.value) }] };
75104
+ }
75105
+ );
74693
75106
  }
74694
75107
 
74695
75108
  // src/tools/register/session.ts
@@ -74697,7 +75110,7 @@ import { z as z13 } from "zod";
74697
75110
 
74698
75111
  // src/tools/ai/ai-tools.ts
74699
75112
  import { z as z11 } from "zod";
74700
- import path57 from "path";
75113
+ import path58 from "path";
74701
75114
  function j(value) {
74702
75115
  return JSON.stringify(value);
74703
75116
  }
@@ -74713,7 +75126,7 @@ function symbolToContextItem(sym, file, projectRoot, score = 1) {
74713
75126
  }
74714
75127
  function readSourceSafe(filePath, byteStart, byteEnd, projectRoot, gitignored) {
74715
75128
  try {
74716
- const absPath = path57.resolve(projectRoot, filePath);
75129
+ const absPath = path58.resolve(projectRoot, filePath);
74717
75130
  return readByteRange(absPath, byteStart, byteEnd, gitignored);
74718
75131
  } catch {
74719
75132
  return null;
@@ -75029,16 +75442,16 @@ function registerAITools(server, ctx) {
75029
75442
  // src/bundles.ts
75030
75443
  init_logger();
75031
75444
  import Database5 from "better-sqlite3";
75032
- import path58 from "path";
75445
+ import path59 from "path";
75033
75446
  import fs49 from "fs";
75034
75447
  import crypto6 from "crypto";
75035
- var BUNDLES_DIR = path58.join(TRACE_MCP_HOME, "bundles");
75448
+ var BUNDLES_DIR = path59.join(TRACE_MCP_HOME, "bundles");
75036
75449
  function getBundlePath(packageName, version2) {
75037
75450
  const safeName = packageName.replace(/[^a-zA-Z0-9._-]/g, "_");
75038
- return path58.join(BUNDLES_DIR, `${safeName}-${version2}.bundle.db`);
75451
+ return path59.join(BUNDLES_DIR, `${safeName}-${version2}.bundle.db`);
75039
75452
  }
75040
75453
  function getManifestPath() {
75041
- return path58.join(BUNDLES_DIR, "manifest.json");
75454
+ return path59.join(BUNDLES_DIR, "manifest.json");
75042
75455
  }
75043
75456
  function loadManifest() {
75044
75457
  const p = getManifestPath();
@@ -75108,8 +75521,8 @@ function searchBundles(bundles, query, opts = {}) {
75108
75521
 
75109
75522
  // src/analytics/analytics-store.ts
75110
75523
  import Database6 from "better-sqlite3";
75111
- import path59 from "path";
75112
- var ANALYTICS_DB_PATH = path59.join(TRACE_MCP_HOME, "analytics.db");
75524
+ import path60 from "path";
75525
+ var ANALYTICS_DB_PATH = path60.join(TRACE_MCP_HOME, "analytics.db");
75113
75526
  var SCHEMA_SQL = `
75114
75527
  CREATE TABLE IF NOT EXISTS sessions (
75115
75528
  id TEXT PRIMARY KEY,
@@ -75342,9 +75755,9 @@ var AnalyticsStore = class {
75342
75755
  // src/analytics/log-parser.ts
75343
75756
  init_logger();
75344
75757
  import fs50 from "fs";
75345
- import path60 from "path";
75758
+ import path61 from "path";
75346
75759
  import os7 from "os";
75347
- var CLAUDE_PROJECTS_DIR = path60.join(os7.homedir(), ".claude", "projects");
75760
+ var CLAUDE_PROJECTS_DIR = path61.join(os7.homedir(), ".claude", "projects");
75348
75761
  var CLAW_SESSIONS_DIR_NAME = ".claw/sessions";
75349
75762
  function parseToolName(fullName) {
75350
75763
  const match = fullName.match(/^mcp__([^_]+)__(.+)$/);
@@ -75421,7 +75834,7 @@ function parseSessionFile(filePath, projectPath) {
75421
75834
  try {
75422
75835
  const content = fs50.readFileSync(filePath, "utf-8");
75423
75836
  const lines = content.split("\n").filter((l) => l.trim());
75424
- const sessionId = path60.basename(filePath, ".jsonl");
75837
+ const sessionId = path61.basename(filePath, ".jsonl");
75425
75838
  const toolCalls = [];
75426
75839
  const toolResults = /* @__PURE__ */ new Map();
75427
75840
  let model = "";
@@ -75542,11 +75955,11 @@ function listProjectDirs() {
75542
75955
  }));
75543
75956
  }
75544
75957
  function listSessionFiles(projectDirName) {
75545
- const dir = path60.join(CLAUDE_PROJECTS_DIR, projectDirName);
75958
+ const dir = path61.join(CLAUDE_PROJECTS_DIR, projectDirName);
75546
75959
  if (!fs50.existsSync(dir)) return [];
75547
75960
  const entries = fs50.readdirSync(dir, { withFileTypes: true });
75548
75961
  return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
75549
- const filePath = path60.join(dir, e.name);
75962
+ const filePath = path61.join(dir, e.name);
75550
75963
  const stat = fs50.statSync(filePath);
75551
75964
  return { filePath, mtime: stat.mtimeMs };
75552
75965
  });
@@ -75560,13 +75973,13 @@ function listAllSessions() {
75560
75973
  }
75561
75974
  const clawProjectPaths = discoverClawProjects();
75562
75975
  for (const projectPath of clawProjectPaths) {
75563
- const sessionsDir = path60.join(projectPath, CLAW_SESSIONS_DIR_NAME);
75976
+ const sessionsDir = path61.join(projectPath, CLAW_SESSIONS_DIR_NAME);
75564
75977
  if (!fs50.existsSync(sessionsDir)) continue;
75565
75978
  try {
75566
75979
  const entries = fs50.readdirSync(sessionsDir, { withFileTypes: true });
75567
75980
  for (const e of entries) {
75568
75981
  if (!e.isFile() || !e.name.endsWith(".jsonl")) continue;
75569
- const filePath = path60.join(sessionsDir, e.name);
75982
+ const filePath = path61.join(sessionsDir, e.name);
75570
75983
  try {
75571
75984
  const stat = fs50.statSync(filePath);
75572
75985
  results.push({ filePath, projectPath, client: "claw-code", mtime: stat.mtimeMs });
@@ -75581,25 +75994,25 @@ function listAllSessions() {
75581
75994
  function discoverClawProjects() {
75582
75995
  const paths = /* @__PURE__ */ new Set();
75583
75996
  for (const { projectPath } of listProjectDirs()) {
75584
- if (fs50.existsSync(path60.join(projectPath, CLAW_SESSIONS_DIR_NAME))) {
75997
+ if (fs50.existsSync(path61.join(projectPath, CLAW_SESSIONS_DIR_NAME))) {
75585
75998
  paths.add(projectPath);
75586
75999
  }
75587
76000
  }
75588
76001
  const cwd = process.cwd();
75589
- if (fs50.existsSync(path60.join(cwd, CLAW_SESSIONS_DIR_NAME))) {
76002
+ if (fs50.existsSync(path61.join(cwd, CLAW_SESSIONS_DIR_NAME))) {
75590
76003
  paths.add(cwd);
75591
76004
  }
75592
76005
  const home = os7.homedir();
75593
76006
  const commonRoots = ["Projects", "projects", "dev", "workspace", "code", "PhpstormProjects", "WebstormProjects", "src"];
75594
76007
  for (const root of commonRoots) {
75595
- const rootDir = path60.join(home, root);
76008
+ const rootDir = path61.join(home, root);
75596
76009
  if (!fs50.existsSync(rootDir)) continue;
75597
76010
  try {
75598
76011
  const entries = fs50.readdirSync(rootDir, { withFileTypes: true });
75599
76012
  for (const e of entries) {
75600
76013
  if (!e.isDirectory()) continue;
75601
- const projectDir = path60.join(rootDir, e.name);
75602
- if (fs50.existsSync(path60.join(projectDir, CLAW_SESSIONS_DIR_NAME))) {
76014
+ const projectDir = path61.join(rootDir, e.name);
76015
+ if (fs50.existsSync(path61.join(projectDir, CLAW_SESSIONS_DIR_NAME))) {
75603
76016
  paths.add(projectDir);
75604
76017
  }
75605
76018
  }
@@ -76305,7 +76718,7 @@ function formatBenchmarkMarkdown(result) {
76305
76718
 
76306
76719
  // src/analytics/tech-detector.ts
76307
76720
  import fs51 from "fs";
76308
- import path61 from "path";
76721
+ import path62 from "path";
76309
76722
 
76310
76723
  // src/analytics/known-packages.ts
76311
76724
  var KNOWN_PACKAGES = {
@@ -77969,7 +78382,7 @@ function detectCoverage(projectRoot, opts = {}) {
77969
78382
  const manifestsFound = [];
77970
78383
  const allDeps = [];
77971
78384
  for (const { file, ecosystem, parser } of MANIFEST_PARSERS) {
77972
- const filePath = path61.join(projectRoot, file);
78385
+ const filePath = path62.join(projectRoot, file);
77973
78386
  if (!fs51.existsSync(filePath)) continue;
77974
78387
  manifestsFound.push(file);
77975
78388
  const rawDeps = parser(filePath);
@@ -79146,7 +79559,7 @@ function registerSessionTools(server, ctx) {
79146
79559
  }
79147
79560
  server.tool(
79148
79561
  "search_bundles",
79149
- "Search pre-indexed bundles for symbols from popular libraries (React, Express, etc.). Returns symbol definitions from dependency bundles \u2014 useful for go-to-definition into node_modules/vendor. Install bundles via CLI: `trace-mcp bundles export`.",
79562
+ "Search pre-indexed bundles for symbols from popular libraries (React, Express, etc.). Returns symbol definitions from dependency bundles \u2014 useful for go-to-definition into node_modules/vendor. Install bundles via CLI: `trace-mcp bundles export`. For project source code search use search instead. Read-only. Returns JSON: { results: [{ name, kind, signature, bundle }], bundles_searched }.",
79150
79563
  {
79151
79564
  query: z13.string().min(1).max(256).describe("Symbol name or FQN to search"),
79152
79565
  kind: z13.string().max(64).optional().describe("Filter by symbol kind (function, class, interface, etc.)"),
@@ -79164,7 +79577,7 @@ function registerSessionTools(server, ctx) {
79164
79577
  );
79165
79578
  server.tool(
79166
79579
  "list_bundles",
79167
- "List installed pre-indexed bundles for dependency libraries. Shows package name, version, symbol/edge counts, and size.",
79580
+ "List installed pre-indexed bundles for dependency libraries. Shows package name, version, symbol/edge counts, and size. Read-only. Returns JSON: { bundles: [{ name, version, symbols, edges, size }], total }.",
79168
79581
  {},
79169
79582
  async () => {
79170
79583
  const bundles = listBundles();
@@ -79173,7 +79586,7 @@ function registerSessionTools(server, ctx) {
79173
79586
  );
79174
79587
  _originalTool(
79175
79588
  "get_preset_info",
79176
- "Show active tool preset, available presets, and which tools are registered in this session",
79589
+ "Show active tool preset, available presets, and which tools are registered in this session. Read-only. Returns JSON: { active_preset, registered_tools, tool_names, available_presets }.",
79177
79590
  {},
79178
79591
  async () => {
79179
79592
  const presets = listPresets();
@@ -79192,7 +79605,7 @@ function registerSessionTools(server, ctx) {
79192
79605
  );
79193
79606
  _originalTool(
79194
79607
  "get_session_analytics",
79195
- "Analyze AI agent session logs: token usage, cost breakdown by tool/server, top files, models used. Parses Claude Code JSONL logs automatically.",
79608
+ "Analyze AI agent session logs: token usage, cost breakdown by tool/server, top files, models used. Parses Claude Code JSONL logs automatically. Read-only. For waste detection use get_optimization_report; for cost trends use get_usage_trends. Returns JSON: { sessions, tokens, cost_usd, tools, models, topFiles }.",
79196
79609
  {
79197
79610
  period: z13.enum(["today", "week", "month", "all"]).optional().describe("Time period (default: week)"),
79198
79611
  session_id: z13.string().max(128).optional().describe("Specific session ID to analyze")
@@ -79213,7 +79626,7 @@ function registerSessionTools(server, ctx) {
79213
79626
  );
79214
79627
  _originalTool(
79215
79628
  "get_optimization_report",
79216
- "Detect token waste patterns in AI agent sessions: repeated file reads, Bash grep instead of search, large file reads, unused trace-mcp tools. Provides savings estimates.",
79629
+ "Detect token waste patterns in AI agent sessions: repeated file reads, Bash grep instead of search, large file reads, unused trace-mcp tools. Provides savings estimates. Read-only. For usage/cost overview use get_session_analytics; for A/B savings comparison use get_real_savings. Returns JSON: { patterns: [{ type, description, savings_estimate }], total_waste }.",
79217
79630
  {
79218
79631
  period: z13.enum(["today", "week", "month", "all"]).optional().describe("Time period (default: week)")
79219
79632
  },
@@ -79233,7 +79646,7 @@ function registerSessionTools(server, ctx) {
79233
79646
  );
79234
79647
  server.tool(
79235
79648
  "benchmark_project",
79236
- "Synthetic token efficiency benchmark: compare raw file reads vs trace-mcp compact responses across symbol lookup, file exploration, search, and impact analysis scenarios.",
79649
+ "Synthetic token efficiency benchmark: compare raw file reads vs trace-mcp compact responses across symbol lookup, file exploration, search, and impact analysis scenarios. Read-only, no side effects. Use to quantify token savings. Returns JSON: { scenarios: [{ name, raw_tokens, compact_tokens, savings_pct }], summary }.",
79237
79650
  {
79238
79651
  queries: z13.number().int().min(1).max(50).optional().describe("Queries per scenario (default 10)"),
79239
79652
  seed: z13.number().int().optional().describe("Random seed for reproducibility (default 42)"),
@@ -79249,7 +79662,7 @@ function registerSessionTools(server, ctx) {
79249
79662
  );
79250
79663
  _originalTool(
79251
79664
  "get_coverage_report",
79252
- "Technology profile of the project: detected frameworks/ORMs/UI libs from manifests (package.json, composer.json, etc.), which are covered by trace-mcp plugins, and coverage gaps.",
79665
+ "Technology profile of the project: detected frameworks/ORMs/UI libs from manifests (package.json, composer.json, etc.), which are covered by trace-mcp plugins, and coverage gaps. Read-only. Returns JSON: { detected, covered, gaps }.",
79253
79666
  {},
79254
79667
  async () => {
79255
79668
  try {
@@ -79262,7 +79675,7 @@ function registerSessionTools(server, ctx) {
79262
79675
  );
79263
79676
  _originalTool(
79264
79677
  "get_real_savings",
79265
- "A/B comparison: how many tokens could be saved by using trace-mcp instead of raw Read/Bash file reads. Per-file breakdown.",
79678
+ "A/B comparison: how many tokens could be saved by using trace-mcp instead of raw Read/Bash file reads. Per-file breakdown. Read-only. For pattern-based waste detection use get_optimization_report instead. Returns JSON: { files: [{ file, raw_tokens, compact_tokens, savings }], total_savings }.",
79266
79679
  {
79267
79680
  period: z13.enum(["today", "week", "month", "all"]).optional().describe("Time period (default: week)")
79268
79681
  },
@@ -79284,7 +79697,7 @@ function registerSessionTools(server, ctx) {
79284
79697
  );
79285
79698
  _originalTool(
79286
79699
  "get_usage_trends",
79287
- "Daily token usage time-series: sessions, tokens, estimated cost, tool calls per day. For spotting cost spikes.",
79700
+ "Daily token usage time-series: sessions, tokens, estimated cost, tool calls per day. For spotting cost spikes. Read-only. For detailed session breakdown use get_session_analytics instead. Returns JSON: { days, daily: [{ date, sessions, tokens, cost_usd, tool_calls }], totals }.",
79288
79701
  {
79289
79702
  days: z13.number().int().min(1).max(365).optional().describe("Number of days to show (default: 30)")
79290
79703
  },
@@ -79311,7 +79724,7 @@ function registerSessionTools(server, ctx) {
79311
79724
  );
79312
79725
  _originalTool(
79313
79726
  "get_session_stats",
79314
- "Token savings stats for this session: per-tool call counts, estimated token savings, reduction percentage, dedup savings.",
79727
+ "Token savings stats for this session: per-tool call counts, estimated token savings, reduction percentage, dedup savings. Read-only. Returns JSON: { total_calls, total_raw_tokens, total_compact_tokens, savings_pct, dedup_saved_tokens, per_tool }.",
79315
79728
  {},
79316
79729
  async () => {
79317
79730
  const stats = savings.getFullStats();
@@ -79329,7 +79742,7 @@ function registerSessionTools(server, ctx) {
79329
79742
  );
79330
79743
  server.tool(
79331
79744
  "get_session_journal",
79332
- "Session history: all tool calls made, files read, zero-result searches, and duplicate queries. Use to avoid repeating work.",
79745
+ "Session history: all tool calls made, files read, zero-result searches, and duplicate queries. Use to avoid repeating work. For a compact snapshot use get_session_snapshot instead. Read-only. Returns JSON: { calls, filesRead, zeroResults, duplicates }.",
79333
79746
  {},
79334
79747
  async () => {
79335
79748
  const summary = journal.getSummary();
@@ -79338,7 +79751,7 @@ function registerSessionTools(server, ctx) {
79338
79751
  );
79339
79752
  server.tool(
79340
79753
  "get_session_snapshot",
79341
- "Compact session snapshot (~200 tokens) for context recovery after compaction. Returns focus files (by read count), edited files, key searches, and dead ends. Also used by the PreCompact hook to preserve session orientation automatically.",
79754
+ "Compact session snapshot (~200 tokens) for context recovery after compaction. Returns focus files (by read count), edited files, key searches, and dead ends. Also used by the PreCompact hook to preserve session orientation automatically. Read-only. For full journal use get_session_journal; for cross-session context use get_session_resume. Returns JSON: { focusFiles, editedFiles, keySearches, deadEnds }.",
79342
79755
  {
79343
79756
  max_files: z13.number().int().min(1).max(50).optional().describe("Max focus files to include (default: 10)"),
79344
79757
  max_searches: z13.number().int().min(1).max(20).optional().describe("Max key searches to include (default: 5)"),
@@ -79357,7 +79770,7 @@ function registerSessionTools(server, ctx) {
79357
79770
  );
79358
79771
  server.tool(
79359
79772
  "get_session_resume",
79360
- "Cross-session context carryover: shows what was explored in recent past sessions (files touched, tools used, dead-end searches). Call at session start to orient yourself without re-reading files. Much cheaper than re-exploring the codebase.",
79773
+ "Cross-session context carryover: shows what was explored in recent past sessions (files touched, tools used, dead-end searches). Call at session start to orient yourself without re-reading files. Much cheaper than re-exploring the codebase. Read-only. For decision-aware wake-up use get_wake_up instead. Returns JSON: { sessions: [{ files, tools, deadEnds }], active_decisions }.",
79361
79774
  {
79362
79775
  max_sessions: z13.number().int().min(1).max(20).optional().describe("Number of past sessions to include (default: 5)")
79363
79776
  },
@@ -79376,7 +79789,7 @@ function registerSessionTools(server, ctx) {
79376
79789
  );
79377
79790
  _originalTool(
79378
79791
  "plan_turn",
79379
- "Opening-move router for new tasks. Combines BM25/PageRank search + session journal (negative evidence + focus signals) + framework-aware insertion-point suggestions + change-risk + turn-budget advisor into ONE call. Returns verdict (exists/partial/missing/ambiguous), confidence, ranked targets with provenance, scaffold hints when missing, and recommended next tool calls. Call this FIRST on a new task to break the empty-result hallucination chain.",
79792
+ "Opening-move router for new tasks. Combines BM25/PageRank search + session journal (negative evidence + focus signals) + framework-aware insertion-point suggestions + change-risk + turn-budget advisor into ONE call. Returns verdict (exists/partial/missing/ambiguous), confidence, ranked targets with provenance, scaffold hints when missing, and recommended next tool calls. Call this FIRST on a new task to break the empty-result hallucination chain. Read-only. For broader task context with source code use get_task_context instead. Returns JSON: { verdict, confidence, targets, scaffoldHints, nextSteps }.",
79380
79793
  {
79381
79794
  task: z13.string().min(1).max(512).describe('Natural-language task description (e.g. "add a webhook endpoint for stripe payments")'),
79382
79795
  intent: z13.enum(["bugfix", "new_feature", "refactor", "understand"]).optional().describe("Optional intent hint; auto-classified from task if omitted"),
@@ -79410,7 +79823,7 @@ function registerSessionTools(server, ctx) {
79410
79823
  );
79411
79824
  _originalTool(
79412
79825
  "batch",
79413
- "Execute multiple trace-mcp tools in a single MCP request. Returns results for all calls. Use to reduce round-trips when you need several independent queries (e.g., get_outline for 3 files, or search + get_symbol together).",
79826
+ "Execute multiple trace-mcp tools in a single MCP request. Returns results for all calls. Use to reduce round-trips when you need several independent queries (e.g., get_outline for 3 files, or search + get_symbol together). Read-only (delegates to other tools). Returns JSON: { batch_results: [{ tool, result }], total }.",
79414
79827
  {
79415
79828
  calls: z13.array(z13.object({
79416
79829
  tool: z13.string().describe('Tool name (e.g., "get_outline", "get_symbol", "search")'),
@@ -79460,7 +79873,7 @@ import { z as z14 } from "zod";
79460
79873
 
79461
79874
  // src/memory/conversation-miner.ts
79462
79875
  import * as fs52 from "fs";
79463
- import * as path62 from "path";
79876
+ import * as path63 from "path";
79464
79877
  init_logger();
79465
79878
  var DECISION_PATTERNS = [
79466
79879
  // Architecture decisions: "decided to", "we'll use", "going with", "chose X over Y"
@@ -79688,7 +80101,7 @@ function mineSessions(decisionStore, opts = {}) {
79688
80101
  file_path: d.file_path,
79689
80102
  tags: d.tags,
79690
80103
  valid_from: d.timestamp,
79691
- session_id: path62.basename(session.filePath, ".jsonl"),
80104
+ session_id: path63.basename(session.filePath, ".jsonl"),
79692
80105
  source: "mined",
79693
80106
  confidence: d.confidence
79694
80107
  }));
@@ -79714,7 +80127,7 @@ function mineSessions(decisionStore, opts = {}) {
79714
80127
 
79715
80128
  // src/memory/session-indexer.ts
79716
80129
  import * as fs53 from "fs";
79717
- import * as path63 from "path";
80130
+ import * as path64 from "path";
79718
80131
  init_logger();
79719
80132
  var MAX_CHUNK_CHARS = 2e3;
79720
80133
  var MIN_MESSAGE_CHARS = 50;
@@ -79755,7 +80168,7 @@ function truncateChunk(text) {
79755
80168
  function indexSessionFile(filePath, projectPath, decisionStore) {
79756
80169
  const content = fs53.readFileSync(filePath, "utf-8");
79757
80170
  const lines = content.split("\n").filter((l) => l.trim());
79758
- const sessionId = path63.basename(filePath, ".jsonl");
80171
+ const sessionId = path64.basename(filePath, ".jsonl");
79759
80172
  const chunks = [];
79760
80173
  let chunkIndex = 0;
79761
80174
  for (const line of lines) {
@@ -79806,7 +80219,7 @@ function indexSessions(decisionStore, opts = {}) {
79806
80219
  skipped++;
79807
80220
  continue;
79808
80221
  }
79809
- const sessionId = path63.basename(session.filePath, ".jsonl");
80222
+ const sessionId = path64.basename(session.filePath, ".jsonl");
79810
80223
  if (!opts.force && decisionStore.isSessionIndexed(sessionId)) {
79811
80224
  skipped++;
79812
80225
  continue;
@@ -79831,7 +80244,7 @@ function indexSessions(decisionStore, opts = {}) {
79831
80244
  }
79832
80245
 
79833
80246
  // src/memory/wake-up.ts
79834
- import * as path64 from "path";
80247
+ import * as path65 from "path";
79835
80248
  function compactDecision(d) {
79836
80249
  const entry = {
79837
80250
  id: d.id,
@@ -79855,7 +80268,7 @@ function assembleWakeUp(decisionStore, projectRoot, opts = {}) {
79855
80268
  const indexedSessions = decisionStore.getIndexedSessionIds(projectRoot);
79856
80269
  const result = {
79857
80270
  project: {
79858
- name: path64.basename(projectRoot),
80271
+ name: path65.basename(projectRoot),
79859
80272
  root: projectRoot
79860
80273
  },
79861
80274
  decisions: {
@@ -79899,7 +80312,7 @@ function registerMemoryTools(server, ctx) {
79899
80312
  }
79900
80313
  server.tool(
79901
80314
  "mine_sessions",
79902
- "Mine Claude Code / Claw Code session logs for architectural decisions, tech choices, bug root causes, and preferences. Extracts decision-like content using pattern matching (no LLM calls). Skips already-mined sessions unless force=true.",
80315
+ "Mine Claude Code / Claw Code session logs for architectural decisions, tech choices, bug root causes, and preferences. Extracts decision-like content using pattern matching (no LLM calls). Skips already-mined sessions unless force=true. Mutates the decision store; idempotent. Use to populate the decision knowledge graph. Returns JSON: { mined, decisions_extracted, sessions_processed }.",
79903
80316
  {
79904
80317
  project_root: z14.string().max(1024).optional().describe("Only mine sessions for this project path (default: all projects)"),
79905
80318
  force: z14.boolean().optional().describe("Re-mine already processed sessions (default: false)"),
@@ -79916,7 +80329,7 @@ function registerMemoryTools(server, ctx) {
79916
80329
  );
79917
80330
  server.tool(
79918
80331
  "add_decision",
79919
- "Manually record an architectural decision, tech choice, preference, or convention. Links to code symbols/files and optionally to a specific subproject for code-aware memory. Decisions have temporal validity \u2014 they can be invalidated later when they become outdated.",
80332
+ "Manually record an architectural decision, tech choice, preference, or convention. Links to code symbols/files and optionally to a specific subproject for code-aware memory. Decisions have temporal validity \u2014 they can be invalidated later when they become outdated. Mutates the decision store (creates a new record). For automated extraction from session logs use mine_sessions instead. Returns JSON: { added: { id, title, type } }.",
79920
80333
  {
79921
80334
  title: z14.string().min(1).max(200).describe("Short summary of the decision"),
79922
80335
  content: z14.string().min(1).max(5e3).describe("Full decision text \u2014 reasoning, context, tradeoffs"),
@@ -79944,7 +80357,7 @@ function registerMemoryTools(server, ctx) {
79944
80357
  );
79945
80358
  server.tool(
79946
80359
  "query_decisions",
79947
- 'Query the decision knowledge graph. Filter by type, subproject, code symbol, file path, tag, or time. Returns decisions linked to code \u2014 "why was this architecture chosen?" answered with the actual decision record. Use service_name to filter by a specific subproject within the project.',
80360
+ 'Query the decision knowledge graph. Filter by type, subproject, code symbol, file path, tag, or time. Returns decisions linked to code \u2014 "why was this architecture chosen?" answered with the actual decision record. Use service_name to filter by a specific subproject within the project. Read-only. Returns JSON: { decisions: [{ id, title, type, content, tags }], total_results }.',
79948
80361
  {
79949
80362
  type: z14.enum(DECISION_TYPES).optional().describe("Filter by decision type"),
79950
80363
  service_name: z14.string().max(256).optional().describe('Filter by subproject name (e.g., "auth-api")'),
@@ -79980,14 +80393,14 @@ function registerMemoryTools(server, ctx) {
79980
80393
  );
79981
80394
  server.tool(
79982
80395
  "invalidate_decision",
79983
- "Mark a decision as no longer valid. The decision remains in the knowledge graph for historical queries but is excluded from active queries. Use when a decision is superseded or reversed.",
80396
+ "Mark a decision as no longer valid. The decision remains in the knowledge graph for historical queries but is excluded from active queries. Use when a decision is superseded or reversed. Mutates the decision store; idempotent. Returns JSON: { invalidated: { id, title, valid_until } }.",
79984
80397
  {
79985
80398
  id: z14.number().int().min(1).describe("Decision ID to invalidate"),
79986
80399
  valid_until: z14.string().max(30).optional().describe("ISO timestamp when decision became invalid (default: now)")
79987
80400
  },
79988
80401
  async ({ id, valid_until }) => {
79989
- const ok10 = decisionStore.invalidateDecision(id, valid_until);
79990
- if (!ok10) {
80402
+ const ok11 = decisionStore.invalidateDecision(id, valid_until);
80403
+ if (!ok11) {
79991
80404
  return { content: [{ type: "text", text: j3({ error: `Decision ${id} not found or already invalidated` }) }], isError: true };
79992
80405
  }
79993
80406
  const updated = decisionStore.getDecision(id);
@@ -79996,7 +80409,7 @@ function registerMemoryTools(server, ctx) {
79996
80409
  );
79997
80410
  server.tool(
79998
80411
  "get_decision_timeline",
79999
- "Chronological timeline of decisions for a project, symbol, or file. Shows when decisions were made and invalidated \u2014 like git log but for architectural decisions.",
80412
+ "Chronological timeline of decisions for a project, symbol, or file. Shows when decisions were made and invalidated \u2014 like git log but for architectural decisions. Read-only. Use to review decision history. Returns JSON: { timeline: [{ id, title, type, created_at, valid_until }], count }.",
80000
80413
  {
80001
80414
  symbol_id: z14.string().max(512).optional().describe("Filter timeline to decisions about this symbol"),
80002
80415
  file_path: z14.string().max(1024).optional().describe("Filter timeline to decisions about this file"),
@@ -80014,7 +80427,7 @@ function registerMemoryTools(server, ctx) {
80014
80427
  );
80015
80428
  server.tool(
80016
80429
  "get_decision_stats",
80017
- "Overview of the decision knowledge graph: total decisions, active/invalidated counts, breakdown by type and source. Shows how much institutional knowledge is captured.",
80430
+ "Overview of the decision knowledge graph: total decisions, active/invalidated counts, breakdown by type and source. Shows how much institutional knowledge is captured. Read-only. Returns JSON: { total, active, invalidated, by_type, by_source, sessions_mined }.",
80018
80431
  {},
80019
80432
  async () => {
80020
80433
  const stats = decisionStore.getStats(projectRoot);
@@ -80026,7 +80439,7 @@ function registerMemoryTools(server, ctx) {
80026
80439
  );
80027
80440
  server.tool(
80028
80441
  "index_sessions",
80029
- 'Index conversation content from Claude Code / Claw Code sessions for cross-session search. Stores chunked messages in FTS5 \u2014 enables "what did we discuss about X?" queries across all past sessions. Skips already-indexed sessions unless force=true.',
80442
+ 'Index conversation content from Claude Code / Claw Code sessions for cross-session search. Stores chunked messages in FTS5 \u2014 enables "what did we discuss about X?" queries across all past sessions. Skips already-indexed sessions unless force=true. Mutates the session index; idempotent. Use before search_sessions. Returns JSON: { indexed, sessions_processed, chunks_stored }.',
80030
80443
  {
80031
80444
  project_root: z14.string().max(1024).optional().describe("Only index sessions for this project path (default: current project)"),
80032
80445
  force: z14.boolean().optional().describe("Re-index already processed sessions (default: false)")
@@ -80041,7 +80454,7 @@ function registerMemoryTools(server, ctx) {
80041
80454
  );
80042
80455
  server.tool(
80043
80456
  "search_sessions",
80044
- 'Search across all past session conversations. Finds what was discussed, decided, or debugged in previous sessions. Full-text search with porter stemming \u2014 e.g., "why did we switch to GraphQL", "auth middleware bug", "database migration approach".',
80457
+ 'Search across all past session conversations. Finds what was discussed, decided, or debugged in previous sessions. Full-text search with porter stemming \u2014 e.g., "why did we switch to GraphQL", "auth middleware bug", "database migration approach". Requires index_sessions to be run first. Read-only. Returns JSON: { results: [{ session_id, text, score }], total_results }.',
80045
80458
  {
80046
80459
  query: z14.string().min(1).max(500).describe("Search query (FTS5 with porter stemming)"),
80047
80460
  limit: z14.number().int().min(1).max(50).optional().describe("Max results (default: 20)")
@@ -80060,7 +80473,7 @@ function registerMemoryTools(server, ctx) {
80060
80473
  );
80061
80474
  server.tool(
80062
80475
  "get_wake_up",
80063
- "Compact orientation context (~300 tokens) for session start. Returns: project identity, active architectural decisions (linked to code symbols/files), and memory stats. Auto-mines sessions on first call if no decisions exist yet. Like MemPalace wake-up but code-aware \u2014 decisions are tied to the dependency graph.",
80476
+ "Compact orientation context (~300 tokens) for session start. Returns: project identity, active architectural decisions (linked to code symbols/files), and memory stats. Auto-mines sessions on first call if no decisions exist yet. Like MemPalace wake-up but code-aware \u2014 decisions are tied to the dependency graph. Use at session start for context recovery. For cross-session file/tool history use get_session_resume instead. Returns JSON: { project, decisions, stats }.",
80064
80477
  {
80065
80478
  max_decisions: z14.number().int().min(1).max(30).optional().describe("Max recent decisions to include (default: 10)"),
80066
80479
  auto_mine: z14.boolean().optional().describe("Auto-mine sessions if decision store is empty (default: true)")
@@ -81053,7 +81466,7 @@ var DecisionStore = class {
81053
81466
  };
81054
81467
 
81055
81468
  // src/server/server.ts
81056
- var PKG_VERSION = true ? "1.21.2" : "0.0.0-dev";
81469
+ var PKG_VERSION2 = true ? "1.22.0" : "0.0.0-dev";
81057
81470
  function j2(value) {
81058
81471
  return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
81059
81472
  }
@@ -81169,7 +81582,7 @@ function createServer2(store, registry, config, rootPath, progress) {
81169
81582
  const detectedFrameworks = [...frameworkNames].join(", ") || "none";
81170
81583
  const instructionsVerbosity = config.tools?.instructions_verbosity ?? "full";
81171
81584
  const server = new McpServer(
81172
- { name: "trace-mcp", version: PKG_VERSION },
81585
+ { name: "trace-mcp", version: PKG_VERSION2 },
81173
81586
  { instructions: buildInstructions(detectedFrameworks, instructionsVerbosity) }
81174
81587
  );
81175
81588
  const savings = new SavingsTracker(projectRoot);