trace-mcp 1.22.0 → 1.23.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) => (path66) => {
14
- var fn2 = map[path66];
13
+ var __glob = (map) => (path67) => {
14
+ var fn2 = map[path67];
15
15
  if (fn2) return fn2();
16
- throw new Error("Module not found in bundle: " + path66);
16
+ throw new Error("Module not found in bundle: " + path67);
17
17
  };
18
18
  var __esm = (fn2, res) => function __init() {
19
19
  return fn2 && (res = (0, fn2[__getOwnPropNames(fn2)[0]])(fn2 = 0)), res;
@@ -2637,27 +2637,27 @@ var require_process = __commonJS({
2637
2637
  var require_filesystem = __commonJS({
2638
2638
  "node_modules/detect-libc/lib/filesystem.js"(exports, module) {
2639
2639
  "use strict";
2640
- var fs56 = __require("fs");
2640
+ var fs57 = __require("fs");
2641
2641
  var LDD_PATH = "/usr/bin/ldd";
2642
2642
  var SELF_PATH = "/proc/self/exe";
2643
2643
  var MAX_LENGTH = 2048;
2644
- var readFileSync10 = (path66) => {
2645
- const fd = fs56.openSync(path66, "r");
2644
+ var readFileSync11 = (path67) => {
2645
+ const fd = fs57.openSync(path67, "r");
2646
2646
  const buffer = Buffer.alloc(MAX_LENGTH);
2647
- const bytesRead = fs56.readSync(fd, buffer, 0, MAX_LENGTH, 0);
2648
- fs56.close(fd, () => {
2647
+ const bytesRead = fs57.readSync(fd, buffer, 0, MAX_LENGTH, 0);
2648
+ fs57.close(fd, () => {
2649
2649
  });
2650
2650
  return buffer.subarray(0, bytesRead);
2651
2651
  };
2652
- var readFile = (path66) => new Promise((resolve3, reject) => {
2653
- fs56.open(path66, "r", (err14, fd) => {
2652
+ var readFile = (path67) => new Promise((resolve3, reject) => {
2653
+ fs57.open(path67, "r", (err14, fd) => {
2654
2654
  if (err14) {
2655
2655
  reject(err14);
2656
2656
  } else {
2657
2657
  const buffer = Buffer.alloc(MAX_LENGTH);
2658
- fs56.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
2658
+ fs57.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
2659
2659
  resolve3(buffer.subarray(0, bytesRead));
2660
- fs56.close(fd, () => {
2660
+ fs57.close(fd, () => {
2661
2661
  });
2662
2662
  });
2663
2663
  }
@@ -2666,7 +2666,7 @@ var require_filesystem = __commonJS({
2666
2666
  module.exports = {
2667
2667
  LDD_PATH,
2668
2668
  SELF_PATH,
2669
- readFileSync: readFileSync10,
2669
+ readFileSync: readFileSync11,
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: readFileSync10 } = require_filesystem();
2718
+ var { LDD_PATH, SELF_PATH, readFile, readFileSync: readFileSync11 } = require_filesystem();
2719
2719
  var { interpreterPath } = require_elf();
2720
2720
  var cachedFamilyInterpreter;
2721
2721
  var cachedFamilyFilesystem;
@@ -2769,11 +2769,11 @@ var require_detect_libc = __commonJS({
2769
2769
  }
2770
2770
  return null;
2771
2771
  };
2772
- var familyFromInterpreterPath = (path66) => {
2773
- if (path66) {
2774
- if (path66.includes("/ld-musl-")) {
2772
+ var familyFromInterpreterPath = (path67) => {
2773
+ if (path67) {
2774
+ if (path67.includes("/ld-musl-")) {
2775
2775
  return MUSL;
2776
- } else if (path66.includes("/ld-linux-")) {
2776
+ } else if (path67.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 = readFileSync10(LDD_PATH);
2810
+ const lddContent = readFileSync11(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 path66 = interpreterPath(selfContent);
2824
- cachedFamilyInterpreter = familyFromInterpreterPath(path66);
2823
+ const path67 = interpreterPath(selfContent);
2824
+ cachedFamilyInterpreter = familyFromInterpreterPath(path67);
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 = readFileSync10(SELF_PATH);
2836
- const path66 = interpreterPath(selfContent);
2837
- cachedFamilyInterpreter = familyFromInterpreterPath(path66);
2835
+ const selfContent = readFileSync11(SELF_PATH);
2836
+ const path67 = interpreterPath(selfContent);
2837
+ cachedFamilyInterpreter = familyFromInterpreterPath(path67);
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 = readFileSync10(LDD_PATH);
2899
+ const lddContent = readFileSync11(LDD_PATH);
2900
2900
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
2901
2901
  if (versionMatch) {
2902
2902
  cachedVersionFilesystem = versionMatch[1];
@@ -4553,18 +4553,18 @@ var require_sharp = __commonJS({
4553
4553
  `@img/sharp-${runtimePlatform}/sharp.node`,
4554
4554
  "@img/sharp-wasm32/sharp.node"
4555
4555
  ];
4556
- var path66;
4556
+ var path67;
4557
4557
  var sharp2;
4558
4558
  var errors = [];
4559
- for (path66 of paths) {
4559
+ for (path67 of paths) {
4560
4560
  try {
4561
- sharp2 = __require(path66);
4561
+ sharp2 = __require(path67);
4562
4562
  break;
4563
4563
  } catch (err14) {
4564
4564
  errors.push(err14);
4565
4565
  }
4566
4566
  }
4567
- if (sharp2 && path66.startsWith("@img/sharp-linux-x64") && !sharp2._isUsingX64V2()) {
4567
+ if (sharp2 && path67.startsWith("@img/sharp-linux-x64") && !sharp2._isUsingX64V2()) {
4568
4568
  const err14 = new Error("Prebuilt binaries for linux-x64 require v2 microarchitecture");
4569
4569
  err14.code = "Unsupported CPU";
4570
4570
  errors.push(err14);
@@ -7475,15 +7475,15 @@ var require_color = __commonJS({
7475
7475
  };
7476
7476
  }
7477
7477
  function wrapConversion(toModel, graph) {
7478
- const path66 = [graph[toModel].parent, toModel];
7478
+ const path67 = [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
- path66.unshift(graph[cur].parent);
7482
+ path67.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 = path66;
7486
+ fn2.conversion = path67;
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 path66 = __require("path");
8103
+ var path67 = __require("path");
8104
8104
  var is2 = require_is();
8105
8105
  var sharp2 = require_sharp();
8106
8106
  var formats = /* @__PURE__ */ new Map([
@@ -8131,9 +8131,9 @@ var require_output = __commonJS({
8131
8131
  let err14;
8132
8132
  if (!is2.string(fileOut)) {
8133
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)) {
8134
+ } else if (is2.string(this.options.input.file) && path67.resolve(this.options.input.file) === path67.resolve(fileOut)) {
8135
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) {
8136
+ } else if (jp2Regex.test(path67.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
8137
8137
  err14 = errJp2Save();
8138
8138
  }
8139
8139
  if (err14) {
@@ -42840,9 +42840,9 @@ ${this.boa_token}${this.audio_token.repeat(this._compute_audio_num_tokens(audio_
42840
42840
  });
42841
42841
 
42842
42842
  // src/lsp/config.ts
42843
- import { existsSync } from "fs";
42843
+ import { existsSync as existsSync2 } from "fs";
42844
42844
  import { execSync } from "child_process";
42845
- import { join } from "path";
42845
+ import { join as join2 } from "path";
42846
42846
  function fileLanguageToLspLanguage(fileLanguage) {
42847
42847
  if (!fileLanguage) return null;
42848
42848
  const map = {
@@ -42913,7 +42913,7 @@ var init_config = __esm({
42913
42913
  language: "typescript",
42914
42914
  command: "npx",
42915
42915
  args: ["typescript-language-server", "--stdio"],
42916
- detect: (root) => existsSync(join(root, "tsconfig.json")) || existsSync(join(root, "package.json")),
42916
+ detect: (root) => existsSync2(join2(root, "tsconfig.json")) || existsSync2(join2(root, "package.json")),
42917
42917
  initializationOptions: {
42918
42918
  preferences: { includeInlayParameterNameHints: "none" }
42919
42919
  }
@@ -42922,19 +42922,19 @@ var init_config = __esm({
42922
42922
  language: "python",
42923
42923
  command: "pyright-langserver",
42924
42924
  args: ["--stdio"],
42925
- detect: (root) => existsSync(join(root, "pyproject.toml")) || existsSync(join(root, "requirements.txt")) || existsSync(join(root, "setup.py"))
42925
+ detect: (root) => existsSync2(join2(root, "pyproject.toml")) || existsSync2(join2(root, "requirements.txt")) || existsSync2(join2(root, "setup.py"))
42926
42926
  },
42927
42927
  {
42928
42928
  language: "go",
42929
42929
  command: "gopls",
42930
42930
  args: ["serve"],
42931
- detect: (root) => existsSync(join(root, "go.mod"))
42931
+ detect: (root) => existsSync2(join2(root, "go.mod"))
42932
42932
  },
42933
42933
  {
42934
42934
  language: "rust",
42935
42935
  command: "rust-analyzer",
42936
42936
  args: [],
42937
- detect: (root) => existsSync(join(root, "Cargo.toml"))
42937
+ detect: (root) => existsSync2(join2(root, "Cargo.toml"))
42938
42938
  }
42939
42939
  ];
42940
42940
  EXTENSION_TO_LANGUAGE = {
@@ -43328,7 +43328,7 @@ var init_mappers = __esm({
43328
43328
  });
43329
43329
 
43330
43330
  // src/lsp/enrichment.ts
43331
- import { readFileSync } from "fs";
43331
+ import { readFileSync as readFileSync2 } from "fs";
43332
43332
  import { resolve as resolve2 } from "path";
43333
43333
  import { pathToFileURL as pathToFileURL3 } from "url";
43334
43334
  var CALL_EDGE_TYPES, LspEnrichmentPass;
@@ -43447,7 +43447,7 @@ var init_enrichment = __esm({
43447
43447
  const uri = pathToFileURL3(absPath).href;
43448
43448
  if (!openedFiles.has(uri)) {
43449
43449
  try {
43450
- const content = readFileSync(absPath, "utf-8");
43450
+ const content = readFileSync2(absPath, "utf-8");
43451
43451
  const langId = getLanguageId(file.path) ?? language;
43452
43452
  await client.openDocument(uri, langId, content);
43453
43453
  openedFiles.add(uri);
@@ -43976,8 +43976,8 @@ var init_layer_violations = __esm({
43976
43976
  });
43977
43977
 
43978
43978
  // src/tools/refactoring/pack-context.ts
43979
- import fs48 from "fs";
43980
- import path56 from "path";
43979
+ import fs49 from "fs";
43980
+ import path57 from "path";
43981
43981
  function estimateTokens2(text) {
43982
43982
  return Math.ceil(text.length / 4);
43983
43983
  }
@@ -44084,11 +44084,11 @@ ${outlineLines.join("\n")}
44084
44084
  let sourceFilesIncluded = 0;
44085
44085
  let sourceTruncated = false;
44086
44086
  for (const f of files) {
44087
- const absPath = path56.join(projectRoot, f.path);
44088
- if (!fs48.existsSync(absPath)) continue;
44087
+ const absPath = path57.join(projectRoot, f.path);
44088
+ if (!fs49.existsSync(absPath)) continue;
44089
44089
  let content;
44090
44090
  try {
44091
- content = fs48.readFileSync(absPath, "utf-8");
44091
+ content = fs49.readFileSync(absPath, "utf-8");
44092
44092
  } catch {
44093
44093
  continue;
44094
44094
  }
@@ -44267,7 +44267,7 @@ function buildFileTree(paths) {
44267
44267
  for (const p of sorted.slice(0, 200)) {
44268
44268
  const depth = p.split("/").length - 1;
44269
44269
  const indent = " ".repeat(Math.min(depth, 6));
44270
- lines.push(`${indent}${path56.basename(p)}`);
44270
+ lines.push(`${indent}${path57.basename(p)}`);
44271
44271
  }
44272
44272
  if (sorted.length > 200) {
44273
44273
  lines.push(` ... and ${sorted.length - 200} more files`);
@@ -44497,6 +44497,35 @@ async function* parseOpenAIStream(body) {
44497
44497
  reader.releaseLock();
44498
44498
  }
44499
44499
  }
44500
+ async function* parseAnthropicStream(body) {
44501
+ const reader = body.getReader();
44502
+ const decoder = new TextDecoder();
44503
+ let buffer = "";
44504
+ try {
44505
+ while (true) {
44506
+ const { done, value } = await reader.read();
44507
+ if (done) break;
44508
+ buffer += decoder.decode(value, { stream: true });
44509
+ const lines = buffer.split("\n");
44510
+ buffer = lines.pop();
44511
+ for (const line of lines) {
44512
+ const trimmed = line.trim();
44513
+ if (!trimmed || !trimmed.startsWith("data: ")) continue;
44514
+ const payload = trimmed.slice(6);
44515
+ try {
44516
+ const event = JSON.parse(payload);
44517
+ if (event.type === "content_block_delta" && event.delta?.text) {
44518
+ yield event.delta.text;
44519
+ }
44520
+ if (event.type === "message_stop") return;
44521
+ } catch {
44522
+ }
44523
+ }
44524
+ }
44525
+ } finally {
44526
+ reader.releaseLock();
44527
+ }
44528
+ }
44500
44529
  async function* parseOllamaChatStream(body) {
44501
44530
  const reader = body.getReader();
44502
44531
  const decoder = new TextDecoder();
@@ -44911,6 +44940,278 @@ var OnnxProvider = class {
44911
44940
  }
44912
44941
  };
44913
44942
 
44943
+ // src/ai/gemini.ts
44944
+ init_logger();
44945
+ var BASE_URL = "https://generativelanguage.googleapis.com";
44946
+ var GeminiEmbeddingService = class {
44947
+ constructor(apiKey, model, dims) {
44948
+ this.apiKey = apiKey;
44949
+ this.model = model;
44950
+ this.dims = dims;
44951
+ }
44952
+ apiKey;
44953
+ model;
44954
+ dims;
44955
+ async embed(text) {
44956
+ const results = await this.embedBatch([text]);
44957
+ return results[0] ?? [];
44958
+ }
44959
+ async embedBatch(texts) {
44960
+ return withRetry(async () => {
44961
+ const requests = texts.map((text) => ({
44962
+ model: `models/${this.model}`,
44963
+ content: { parts: [{ text }] },
44964
+ outputDimensionality: this.dims
44965
+ }));
44966
+ const resp = await fetch(
44967
+ `${BASE_URL}/v1beta/models/${this.model}:batchEmbedContents?key=${this.apiKey}`,
44968
+ {
44969
+ method: "POST",
44970
+ headers: { "Content-Type": "application/json" },
44971
+ body: JSON.stringify({ requests }),
44972
+ signal: AbortSignal.timeout(3e4)
44973
+ }
44974
+ );
44975
+ if (!resp.ok) {
44976
+ const body = await resp.text().catch(() => "");
44977
+ const safeBody = body.length > 200 ? body.slice(0, 200) + "\u2026" : body;
44978
+ throw new Error(`Gemini embeddings failed: ${resp.status} ${resp.statusText} \u2014 ${safeBody}`);
44979
+ }
44980
+ const data = await resp.json();
44981
+ return data.embeddings.map((e) => e.values);
44982
+ }, { label: "Gemini embeddings" });
44983
+ }
44984
+ dimensions() {
44985
+ return this.dims;
44986
+ }
44987
+ };
44988
+ var GeminiInferenceService = class {
44989
+ constructor(apiKey, model) {
44990
+ this.apiKey = apiKey;
44991
+ this.model = model;
44992
+ }
44993
+ apiKey;
44994
+ model;
44995
+ async generate(prompt, options) {
44996
+ return withRetry(async () => {
44997
+ const resp = await fetch(
44998
+ `${BASE_URL}/v1beta/models/${this.model}:generateContent?key=${this.apiKey}`,
44999
+ {
45000
+ method: "POST",
45001
+ headers: { "Content-Type": "application/json" },
45002
+ body: JSON.stringify({
45003
+ contents: [{ parts: [{ text: prompt }] }],
45004
+ generationConfig: {
45005
+ ...options?.maxTokens ? { maxOutputTokens: options.maxTokens } : {},
45006
+ ...options?.temperature !== void 0 ? { temperature: options.temperature } : {}
45007
+ }
45008
+ }),
45009
+ signal: AbortSignal.timeout(6e4)
45010
+ }
45011
+ );
45012
+ if (!resp.ok) {
45013
+ const body = await resp.text().catch(() => "");
45014
+ const safeBody = body.length > 200 ? body.slice(0, 200) + "\u2026" : body;
45015
+ throw new Error(`Gemini generate failed: ${resp.status} ${resp.statusText} \u2014 ${safeBody}`);
45016
+ }
45017
+ const data = await resp.json();
45018
+ return data.candidates?.[0]?.content?.parts?.[0]?.text ?? "";
45019
+ }, { label: "Gemini generate" });
45020
+ }
45021
+ async *generateStream(messages, options) {
45022
+ const contents = messages.map((m) => ({
45023
+ role: m.role === "assistant" ? "model" : "user",
45024
+ parts: [{ text: m.content }]
45025
+ }));
45026
+ const resp = await fetch(
45027
+ `${BASE_URL}/v1beta/models/${this.model}:streamGenerateContent?alt=sse&key=${this.apiKey}`,
45028
+ {
45029
+ method: "POST",
45030
+ headers: { "Content-Type": "application/json" },
45031
+ body: JSON.stringify({
45032
+ contents,
45033
+ generationConfig: {
45034
+ ...options?.maxTokens ? { maxOutputTokens: options.maxTokens } : {},
45035
+ ...options?.temperature !== void 0 ? { temperature: options.temperature } : {}
45036
+ }
45037
+ }),
45038
+ signal: AbortSignal.timeout(12e4)
45039
+ }
45040
+ );
45041
+ if (!resp.ok) {
45042
+ const body = await resp.text().catch(() => "");
45043
+ const safeBody = body.length > 200 ? body.slice(0, 200) + "\u2026" : body;
45044
+ throw new Error(`Gemini stream failed: ${resp.status} ${resp.statusText} \u2014 ${safeBody}`);
45045
+ }
45046
+ const reader = resp.body.getReader();
45047
+ const decoder = new TextDecoder();
45048
+ let buf = "";
45049
+ while (true) {
45050
+ const { value, done } = await reader.read();
45051
+ if (done) break;
45052
+ buf += decoder.decode(value, { stream: true });
45053
+ const lines = buf.split("\n");
45054
+ buf = lines.pop();
45055
+ for (const line of lines) {
45056
+ if (!line.startsWith("data: ")) continue;
45057
+ const json = line.slice(6).trim();
45058
+ if (!json || json === "[DONE]") continue;
45059
+ try {
45060
+ const chunk2 = JSON.parse(json);
45061
+ const text = chunk2.candidates?.[0]?.content?.parts?.[0]?.text;
45062
+ if (text) yield text;
45063
+ } catch {
45064
+ }
45065
+ }
45066
+ }
45067
+ }
45068
+ };
45069
+ var GeminiProvider = class {
45070
+ config;
45071
+ constructor(config) {
45072
+ this.config = config;
45073
+ }
45074
+ async isAvailable() {
45075
+ try {
45076
+ const resp = await fetch(
45077
+ `${BASE_URL}/v1beta/models?key=${this.config.apiKey}`,
45078
+ { signal: AbortSignal.timeout(3e3) }
45079
+ );
45080
+ return resp.ok;
45081
+ } catch {
45082
+ logger.debug("Gemini not available");
45083
+ return false;
45084
+ }
45085
+ }
45086
+ embedding() {
45087
+ return new GeminiEmbeddingService(
45088
+ this.config.apiKey,
45089
+ this.config.embeddingModel,
45090
+ this.config.embeddingDimensions
45091
+ );
45092
+ }
45093
+ inference() {
45094
+ return new GeminiInferenceService(this.config.apiKey, this.config.inferenceModel);
45095
+ }
45096
+ fastInference() {
45097
+ return new GeminiInferenceService(this.config.apiKey, this.config.fastModel);
45098
+ }
45099
+ };
45100
+
45101
+ // src/ai/anthropic.ts
45102
+ init_logger();
45103
+ var BASE_URL2 = "https://api.anthropic.com";
45104
+ var NoEmbeddingService = class {
45105
+ async embed(_text) {
45106
+ return [];
45107
+ }
45108
+ async embedBatch(_texts) {
45109
+ return [];
45110
+ }
45111
+ dimensions() {
45112
+ return 0;
45113
+ }
45114
+ };
45115
+ var AnthropicInferenceService = class {
45116
+ constructor(apiKey, model) {
45117
+ this.apiKey = apiKey;
45118
+ this.model = model;
45119
+ }
45120
+ apiKey;
45121
+ model;
45122
+ async generate(prompt, options) {
45123
+ return withRetry(async () => {
45124
+ const resp = await fetch(`${BASE_URL2}/v1/messages`, {
45125
+ method: "POST",
45126
+ headers: {
45127
+ "Content-Type": "application/json",
45128
+ "x-api-key": this.apiKey,
45129
+ "anthropic-version": "2023-06-01"
45130
+ },
45131
+ body: JSON.stringify({
45132
+ model: this.model,
45133
+ max_tokens: options?.maxTokens ?? 4096,
45134
+ messages: [{ role: "user", content: prompt }],
45135
+ ...options?.temperature !== void 0 ? { temperature: options.temperature } : {}
45136
+ }),
45137
+ signal: AbortSignal.timeout(6e4)
45138
+ });
45139
+ if (!resp.ok) {
45140
+ const body = await resp.text().catch(() => "");
45141
+ const safeBody = body.length > 200 ? body.slice(0, 200) + "\u2026" : body;
45142
+ throw new Error(`Anthropic API error: ${resp.status} ${resp.statusText} \u2014 ${safeBody}`);
45143
+ }
45144
+ const data = await resp.json();
45145
+ return data.content?.find((c) => c.type === "text")?.text ?? "";
45146
+ }, { label: "Anthropic generate" });
45147
+ }
45148
+ async *generateStream(messages, options) {
45149
+ const systemMsg = messages.find((m) => m.role === "system");
45150
+ const nonSystemMsgs = messages.filter((m) => m.role !== "system");
45151
+ const resp = await fetch(`${BASE_URL2}/v1/messages`, {
45152
+ method: "POST",
45153
+ headers: {
45154
+ "Content-Type": "application/json",
45155
+ "x-api-key": this.apiKey,
45156
+ "anthropic-version": "2023-06-01"
45157
+ },
45158
+ body: JSON.stringify({
45159
+ model: this.model,
45160
+ max_tokens: options?.maxTokens ?? 4096,
45161
+ stream: true,
45162
+ ...systemMsg ? { system: systemMsg.content } : {},
45163
+ ...options?.temperature !== void 0 ? { temperature: options.temperature } : {},
45164
+ messages: nonSystemMsgs.map((m) => ({ role: m.role, content: m.content }))
45165
+ }),
45166
+ signal: AbortSignal.timeout(12e4)
45167
+ });
45168
+ if (!resp.ok) {
45169
+ const body = await resp.text().catch(() => "");
45170
+ const safeBody = body.length > 200 ? body.slice(0, 200) + "\u2026" : body;
45171
+ throw new Error(`Anthropic stream error: ${resp.status} ${resp.statusText} \u2014 ${safeBody}`);
45172
+ }
45173
+ yield* parseAnthropicStream(resp.body);
45174
+ }
45175
+ };
45176
+ var AnthropicProvider = class {
45177
+ config;
45178
+ constructor(config) {
45179
+ this.config = config;
45180
+ }
45181
+ async isAvailable() {
45182
+ try {
45183
+ const resp = await fetch(`${BASE_URL2}/v1/messages`, {
45184
+ method: "POST",
45185
+ headers: {
45186
+ "Content-Type": "application/json",
45187
+ "x-api-key": this.config.apiKey,
45188
+ "anthropic-version": "2023-06-01"
45189
+ },
45190
+ body: JSON.stringify({
45191
+ model: this.config.inferenceModel,
45192
+ max_tokens: 1,
45193
+ messages: [{ role: "user", content: "ping" }]
45194
+ }),
45195
+ signal: AbortSignal.timeout(5e3)
45196
+ });
45197
+ return resp.ok || resp.status === 400;
45198
+ } catch {
45199
+ logger.debug("Anthropic not available");
45200
+ return false;
45201
+ }
45202
+ }
45203
+ embedding() {
45204
+ logger.warn("Anthropic does not provide embeddings \u2014 use a separate embedding provider (ONNX, Ollama, or OpenAI)");
45205
+ return new NoEmbeddingService();
45206
+ }
45207
+ inference() {
45208
+ return new AnthropicInferenceService(this.config.apiKey, this.config.inferenceModel);
45209
+ }
45210
+ fastInference() {
45211
+ return new AnthropicInferenceService(this.config.apiKey, this.config.fastModel);
45212
+ }
45213
+ };
45214
+
44914
45215
  // src/ai/index.ts
44915
45216
  init_logger();
44916
45217
 
@@ -45400,32 +45701,76 @@ var LLMReranker = class {
45400
45701
  };
45401
45702
 
45402
45703
  // src/ai/index.ts
45704
+ var OPENAI_COMPAT_DEFAULTS = {
45705
+ openai: {
45706
+ baseUrl: "https://api.openai.com/v1",
45707
+ embeddingModel: "text-embedding-3-small",
45708
+ embeddingDimensions: 1536,
45709
+ inferenceModel: "gpt-4o-mini",
45710
+ fastModel: "gpt-4o-mini",
45711
+ envKey: "OPENAI_API_KEY"
45712
+ },
45713
+ lmstudio: {
45714
+ baseUrl: "http://localhost:1234/v1",
45715
+ embeddingModel: "nomic-embed-text-v1.5",
45716
+ embeddingDimensions: 768,
45717
+ inferenceModel: "qwen2.5-coder-7b-instruct",
45718
+ fastModel: "qwen2.5-coder-7b-instruct",
45719
+ envKey: "LMSTUDIO_API_KEY"
45720
+ },
45721
+ groq: {
45722
+ baseUrl: "https://api.groq.com/openai/v1",
45723
+ embeddingModel: "nomic-embed-text-v1.5",
45724
+ embeddingDimensions: 768,
45725
+ inferenceModel: "llama-3.3-70b-versatile",
45726
+ fastModel: "llama-3.1-8b-instant",
45727
+ envKey: "GROQ_API_KEY"
45728
+ },
45729
+ together: {
45730
+ baseUrl: "https://api.together.xyz/v1",
45731
+ embeddingModel: "togethercomputer/m2-bert-80M-8k-retrieval",
45732
+ embeddingDimensions: 768,
45733
+ inferenceModel: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
45734
+ fastModel: "meta-llama/Llama-3.1-8B-Instruct-Turbo",
45735
+ envKey: "TOGETHER_API_KEY"
45736
+ },
45737
+ deepseek: {
45738
+ baseUrl: "https://api.deepseek.com/v1",
45739
+ embeddingModel: "deepseek-chat",
45740
+ embeddingDimensions: 1536,
45741
+ inferenceModel: "deepseek-chat",
45742
+ fastModel: "deepseek-chat",
45743
+ envKey: "DEEPSEEK_API_KEY"
45744
+ },
45745
+ mistral: {
45746
+ baseUrl: "https://api.mistral.ai/v1",
45747
+ embeddingModel: "mistral-embed",
45748
+ embeddingDimensions: 1024,
45749
+ inferenceModel: "mistral-small-latest",
45750
+ fastModel: "mistral-small-latest",
45751
+ envKey: "MISTRAL_API_KEY"
45752
+ },
45753
+ xai: {
45754
+ baseUrl: "https://api.x.ai/v1",
45755
+ embeddingModel: "grok-2",
45756
+ embeddingDimensions: 1536,
45757
+ inferenceModel: "grok-2",
45758
+ fastModel: "grok-2",
45759
+ envKey: "XAI_API_KEY"
45760
+ }
45761
+ };
45403
45762
  function createAIProvider(config) {
45404
45763
  if (!config.ai?.enabled) {
45405
45764
  return new FallbackProvider();
45406
45765
  }
45407
- if (config.ai.provider === "onnx") {
45766
+ const provider = config.ai.provider;
45767
+ if (provider === "onnx") {
45408
45768
  return new OnnxProvider({
45409
45769
  model: config.ai.embedding_model,
45410
45770
  dimensions: config.ai.embedding_dimensions
45411
45771
  });
45412
45772
  }
45413
- if (config.ai.provider === "openai") {
45414
- const apiKey = config.ai.api_key ?? process.env.OPENAI_API_KEY ?? "";
45415
- if (!apiKey) {
45416
- logger.warn("OpenAI provider selected but no api_key configured \u2014 falling back to FallbackProvider");
45417
- return new FallbackProvider();
45418
- }
45419
- return new OpenAIProvider({
45420
- apiKey,
45421
- baseUrl: config.ai.base_url ?? "https://api.openai.com/v1",
45422
- embeddingModel: config.ai.embedding_model ?? "text-embedding-3-small",
45423
- embeddingDimensions: config.ai.embedding_dimensions ?? 1536,
45424
- inferenceModel: config.ai.inference_model ?? "gpt-4o-mini",
45425
- fastModel: config.ai.fast_model ?? "gpt-4o-mini"
45426
- });
45427
- }
45428
- if (config.ai.provider === "ollama") {
45773
+ if (provider === "ollama") {
45429
45774
  return new OllamaProvider({
45430
45775
  baseUrl: config.ai.base_url ?? "http://localhost:11434",
45431
45776
  embeddingModel: config.ai.embedding_model ?? "qwen3-embedding:0.6b",
@@ -45434,6 +45779,48 @@ function createAIProvider(config) {
45434
45779
  embeddingDimensions: config.ai.embedding_dimensions
45435
45780
  });
45436
45781
  }
45782
+ if (provider === "gemini") {
45783
+ const apiKey = config.ai.api_key ?? process.env.GEMINI_API_KEY ?? "";
45784
+ if (!apiKey) {
45785
+ logger.warn("Gemini provider selected but no api_key configured \u2014 falling back");
45786
+ return new FallbackProvider();
45787
+ }
45788
+ return new GeminiProvider({
45789
+ apiKey,
45790
+ embeddingModel: config.ai.embedding_model ?? "text-embedding-004",
45791
+ embeddingDimensions: config.ai.embedding_dimensions ?? 768,
45792
+ inferenceModel: config.ai.inference_model ?? "gemini-2.0-flash",
45793
+ fastModel: config.ai.fast_model ?? "gemini-2.0-flash"
45794
+ });
45795
+ }
45796
+ if (provider === "anthropic") {
45797
+ const apiKey = config.ai.api_key ?? process.env.ANTHROPIC_API_KEY ?? "";
45798
+ if (!apiKey) {
45799
+ logger.warn("Anthropic provider selected but no api_key configured \u2014 falling back");
45800
+ return new FallbackProvider();
45801
+ }
45802
+ return new AnthropicProvider({
45803
+ apiKey,
45804
+ inferenceModel: config.ai.inference_model ?? "claude-sonnet-4-20250514",
45805
+ fastModel: config.ai.fast_model ?? "claude-haiku-4-5-20251001"
45806
+ });
45807
+ }
45808
+ const defaults = OPENAI_COMPAT_DEFAULTS[provider];
45809
+ if (defaults) {
45810
+ const apiKey = config.ai.api_key ?? process.env[defaults.envKey] ?? "";
45811
+ if (!apiKey && provider !== "lmstudio") {
45812
+ logger.warn(`${provider} provider selected but no api_key configured \u2014 falling back`);
45813
+ return new FallbackProvider();
45814
+ }
45815
+ return new OpenAIProvider({
45816
+ apiKey,
45817
+ baseUrl: config.ai.base_url ?? defaults.baseUrl,
45818
+ embeddingModel: config.ai.embedding_model ?? defaults.embeddingModel,
45819
+ embeddingDimensions: config.ai.embedding_dimensions ?? defaults.embeddingDimensions,
45820
+ inferenceModel: config.ai.inference_model ?? defaults.inferenceModel,
45821
+ fastModel: config.ai.fast_model ?? defaults.fastModel
45822
+ });
45823
+ }
45437
45824
  return new FallbackProvider();
45438
45825
  }
45439
45826
 
@@ -46337,8 +46724,8 @@ var SessionJournal = class _SessionJournal {
46337
46724
  };
46338
46725
  this.entries.push(entry);
46339
46726
  if (tool === "get_symbol" || tool === "get_outline") {
46340
- const path66 = params.path ?? params.file_path ?? "";
46341
- if (path66) this.filesRead.add(path66);
46727
+ const path67 = params.path ?? params.file_path ?? "";
46728
+ if (path67) this.filesRead.add(path67);
46342
46729
  }
46343
46730
  if (resultCount === 0 && this.isSearchTool(tool)) {
46344
46731
  this.zeroResultQueries.set(hash, summary);
@@ -47781,8 +48168,8 @@ init_logger();
47781
48168
  import { z as z2 } from "zod";
47782
48169
 
47783
48170
  // src/indexer/pipeline.ts
47784
- import fs21 from "fs";
47785
- import path20 from "path";
48171
+ import fs23 from "fs";
48172
+ import path21 from "path";
47786
48173
  import { cpus } from "os";
47787
48174
  import fg3 from "fast-glob";
47788
48175
  init_logger();
@@ -50262,7 +50649,7 @@ var FilePersister = class {
50262
50649
  };
50263
50650
 
50264
50651
  // src/indexer/edge-resolver.ts
50265
- import path17 from "path";
50652
+ import path18 from "path";
50266
50653
 
50267
50654
  // src/plugin-api/executor.ts
50268
50655
  init_logger();
@@ -51419,6 +51806,251 @@ function buildFromPendingImports(state) {
51419
51806
  return result;
51420
51807
  }
51421
51808
 
51809
+ // src/indexer/edge-resolvers/php-imports.ts
51810
+ import * as path17 from "path";
51811
+ import * as fs18 from "fs";
51812
+
51813
+ // src/indexer/resolvers/psr4.ts
51814
+ import { readFileSync } from "fs";
51815
+ var Psr4Resolver = class _Psr4Resolver {
51816
+ constructor(mappings, rootPath) {
51817
+ this.rootPath = rootPath;
51818
+ this.mappings = /* @__PURE__ */ new Map();
51819
+ for (const [prefix, dir] of mappings) {
51820
+ const normPrefix = prefix.endsWith("\\") ? prefix : prefix + "\\";
51821
+ const normDir = dir.endsWith("/") ? dir : dir + "/";
51822
+ this.mappings.set(normPrefix, normDir);
51823
+ }
51824
+ }
51825
+ rootPath;
51826
+ /** namespace prefix (with trailing backslash) → directory (with trailing slash) */
51827
+ mappings;
51828
+ /**
51829
+ * Create a Psr4Resolver from a composer.json file.
51830
+ * Reads both autoload.psr-4 and autoload-dev.psr-4 sections.
51831
+ */
51832
+ static fromComposerJson(composerJsonPath, rootPath) {
51833
+ try {
51834
+ const raw = readFileSync(composerJsonPath, "utf-8");
51835
+ const json = JSON.parse(raw);
51836
+ const mappings = /* @__PURE__ */ new Map();
51837
+ const extractPsr4 = (section) => {
51838
+ if (section && typeof section === "object" && !Array.isArray(section)) {
51839
+ const psr4 = section["psr-4"];
51840
+ if (psr4 && typeof psr4 === "object" && !Array.isArray(psr4)) {
51841
+ for (const [prefix, dir] of Object.entries(psr4)) {
51842
+ if (typeof dir === "string") {
51843
+ mappings.set(prefix, dir);
51844
+ }
51845
+ if (Array.isArray(dir)) {
51846
+ for (const d of dir) {
51847
+ if (typeof d === "string") {
51848
+ mappings.set(prefix, d);
51849
+ }
51850
+ }
51851
+ }
51852
+ }
51853
+ }
51854
+ }
51855
+ };
51856
+ extractPsr4(json["autoload"]);
51857
+ extractPsr4(json["autoload-dev"]);
51858
+ if (mappings.size === 0) return void 0;
51859
+ return new _Psr4Resolver(mappings, rootPath);
51860
+ } catch {
51861
+ return void 0;
51862
+ }
51863
+ }
51864
+ /**
51865
+ * Resolve a fully qualified class name to a relative file path.
51866
+ * Matches the longest prefix first.
51867
+ *
51868
+ * Example: "App\Models\User" → "app/Models/User.php"
51869
+ */
51870
+ resolve(fqn) {
51871
+ let bestPrefix = "";
51872
+ let bestDir = "";
51873
+ for (const [prefix, dir] of this.mappings) {
51874
+ if (fqn.startsWith(prefix) && prefix.length > bestPrefix.length) {
51875
+ bestPrefix = prefix;
51876
+ bestDir = dir;
51877
+ }
51878
+ const prefixNoSlash = prefix.slice(0, -1);
51879
+ if (fqn === prefixNoSlash && prefix.length > bestPrefix.length) {
51880
+ bestPrefix = prefix;
51881
+ bestDir = dir;
51882
+ }
51883
+ }
51884
+ if (!bestPrefix) return void 0;
51885
+ const remainder = fqn.substring(bestPrefix.length);
51886
+ const relativePath = remainder.replace(/\\/g, "/");
51887
+ return bestDir + relativePath + ".php";
51888
+ }
51889
+ /**
51890
+ * Reverse-resolve a relative file path to a fully qualified class name.
51891
+ *
51892
+ * Example: "app/Models/User.php" → "App\Models\User"
51893
+ */
51894
+ resolveToFqn(filePath) {
51895
+ let normalised = filePath.replace(/\\/g, "/");
51896
+ const rootNorm = this.rootPath.replace(/\\/g, "/").replace(/\/$/, "") + "/";
51897
+ if (normalised.startsWith(rootNorm)) {
51898
+ normalised = normalised.substring(rootNorm.length);
51899
+ }
51900
+ if (!normalised.endsWith(".php")) return void 0;
51901
+ const withoutExt = normalised.slice(0, -4);
51902
+ let bestPrefix = "";
51903
+ let bestDir = "";
51904
+ for (const [prefix, dir] of this.mappings) {
51905
+ if (withoutExt.startsWith(dir) && dir.length > bestDir.length) {
51906
+ bestPrefix = prefix;
51907
+ bestDir = dir;
51908
+ }
51909
+ const dirNoSlash = dir.replace(/\/$/, "");
51910
+ if (withoutExt.startsWith(dirNoSlash + "/") && dir.length > bestDir.length) {
51911
+ bestPrefix = prefix;
51912
+ bestDir = dir;
51913
+ }
51914
+ }
51915
+ if (!bestDir) return void 0;
51916
+ const remainder = withoutExt.substring(bestDir.length);
51917
+ const fqnSuffix = remainder.replace(/\//g, "\\");
51918
+ return bestPrefix + fqnSuffix;
51919
+ }
51920
+ /** Get all registered mappings (for debugging / inspection). */
51921
+ getMappings() {
51922
+ return this.mappings;
51923
+ }
51924
+ };
51925
+
51926
+ // src/indexer/edge-resolvers/php-imports.ts
51927
+ init_logger();
51928
+ function resolvePhpImportEdges(state) {
51929
+ const { store } = state;
51930
+ if (state.pendingImports.size === 0) return;
51931
+ const allPhpFiles = store.db.prepare(
51932
+ `SELECT id, path, workspace FROM files WHERE language = 'php'`
51933
+ ).all();
51934
+ if (allPhpFiles.length === 0) return;
51935
+ const fqnToFile = /* @__PURE__ */ new Map();
51936
+ const fqnRows = store.db.prepare(
51937
+ `SELECT s.fqn, f.id, f.path, f.workspace FROM symbols s
51938
+ JOIN files f ON s.file_id = f.id
51939
+ WHERE s.fqn IS NOT NULL AND f.language = 'php'
51940
+ AND s.kind IN ('class', 'interface', 'trait', 'enum')`
51941
+ ).all();
51942
+ for (const row of fqnRows) {
51943
+ const key = `${row.workspace ?? ""}\0${row.fqn}`;
51944
+ fqnToFile.set(key, { id: row.id, path: row.path, workspace: row.workspace });
51945
+ }
51946
+ const psr4Resolvers = /* @__PURE__ */ new Map();
51947
+ const workspacePaths = /* @__PURE__ */ new Set();
51948
+ for (const f of allPhpFiles) {
51949
+ if (f.workspace) workspacePaths.add(f.workspace);
51950
+ }
51951
+ for (const wsPath of workspacePaths) {
51952
+ const wsRoot = path17.join(state.rootPath, wsPath);
51953
+ const composerPath = path17.join(wsRoot, "composer.json");
51954
+ if (fs18.existsSync(composerPath)) {
51955
+ const resolver = Psr4Resolver.fromComposerJson(composerPath, wsRoot);
51956
+ if (resolver) psr4Resolvers.set(wsPath, resolver);
51957
+ }
51958
+ }
51959
+ const rootComposer = path17.join(state.rootPath, "composer.json");
51960
+ if (fs18.existsSync(rootComposer)) {
51961
+ const resolver = Psr4Resolver.fromComposerJson(rootComposer, state.rootPath);
51962
+ if (resolver) psr4Resolvers.set("", resolver);
51963
+ }
51964
+ const pathToFile = /* @__PURE__ */ new Map();
51965
+ for (const f of allPhpFiles) {
51966
+ pathToFile.set(f.path, f);
51967
+ }
51968
+ const fileNodeMap = /* @__PURE__ */ new Map();
51969
+ const allFileIds = allPhpFiles.map((f) => f.id);
51970
+ const CHUNK = 500;
51971
+ for (let i = 0; i < allFileIds.length; i += CHUNK) {
51972
+ for (const [k2, v] of store.getNodeIdsBatch("file", allFileIds.slice(i, i + CHUNK))) {
51973
+ fileNodeMap.set(k2, v);
51974
+ }
51975
+ }
51976
+ const importsEdgeType = store.db.prepare(
51977
+ `SELECT id FROM edge_types WHERE name = ?`
51978
+ ).get("imports");
51979
+ if (!importsEdgeType) return;
51980
+ const insertStmt = store.db.prepare(
51981
+ `INSERT INTO edges (source_node_id, target_node_id, edge_type_id, resolved, metadata, is_cross_ws)
51982
+ VALUES (?, ?, ?, 1, ?, ?)
51983
+ ON CONFLICT(source_node_id, target_node_id, edge_type_id)
51984
+ DO UPDATE SET metadata = excluded.metadata`
51985
+ );
51986
+ const pendingFileIds = Array.from(state.pendingImports.keys());
51987
+ const fileMap = store.getFilesByIds(pendingFileIds);
51988
+ const fileWorkspace = /* @__PURE__ */ new Map();
51989
+ for (const f of allPhpFiles) {
51990
+ fileWorkspace.set(f.id, f.workspace);
51991
+ }
51992
+ for (let i = 0; i < pendingFileIds.length; i += CHUNK) {
51993
+ for (const [k2, v] of store.getNodeIdsBatch("file", pendingFileIds.slice(i, i + CHUNK))) {
51994
+ fileNodeMap.set(k2, v);
51995
+ }
51996
+ }
51997
+ let created = 0;
51998
+ store.db.transaction(() => {
51999
+ for (const [fileId, imports] of state.pendingImports) {
52000
+ const file = fileMap.get(fileId);
52001
+ if (!file || file.language !== "php") continue;
52002
+ const sourceNodeId = fileNodeMap.get(fileId);
52003
+ if (sourceNodeId == null) continue;
52004
+ const sourceWs = fileWorkspace.get(fileId) ?? null;
52005
+ const consolidated = /* @__PURE__ */ new Map();
52006
+ for (const { from, specifiers } of imports) {
52007
+ const existing = consolidated.get(from);
52008
+ if (existing) existing.push(...specifiers);
52009
+ else consolidated.set(from, [...specifiers]);
52010
+ }
52011
+ for (const [fqn, specifiers] of consolidated) {
52012
+ const target = resolvePhpFqn(fqn, sourceWs, fqnToFile, psr4Resolvers, pathToFile, state.rootPath);
52013
+ if (!target) continue;
52014
+ const targetNodeId = fileNodeMap.get(target.id);
52015
+ if (targetNodeId == null) continue;
52016
+ if (sourceNodeId === targetNodeId) continue;
52017
+ const isCrossWs = sourceWs !== target.workspace ? 1 : 0;
52018
+ insertStmt.run(
52019
+ sourceNodeId,
52020
+ targetNodeId,
52021
+ importsEdgeType.id,
52022
+ JSON.stringify({ from: fqn, specifiers }),
52023
+ isCrossWs
52024
+ );
52025
+ created++;
52026
+ }
52027
+ }
52028
+ })();
52029
+ if (created > 0) {
52030
+ logger.info({ edges: created }, "PHP import edges resolved");
52031
+ }
52032
+ }
52033
+ function resolvePhpFqn(fqn, sourceWorkspace, fqnIndex, psr4Resolvers, pathIndex, rootPath) {
52034
+ if (!fqn) return null;
52035
+ const wsKey = sourceWorkspace ?? "";
52036
+ const sameWs = fqnIndex.get(`${wsKey}\0${fqn}`);
52037
+ if (sameWs) return sameWs;
52038
+ const psr4 = psr4Resolvers.get(wsKey);
52039
+ if (psr4) {
52040
+ const resolved = psr4.resolve(fqn);
52041
+ if (resolved) {
52042
+ const projectRelative = sourceWorkspace ? `${sourceWorkspace}/${resolved}` : resolved;
52043
+ const fromPath = pathIndex.get(projectRelative);
52044
+ if (fromPath) return fromPath;
52045
+ }
52046
+ }
52047
+ if (sourceWorkspace) {
52048
+ const noWs = fqnIndex.get(`\0${fqn}`);
52049
+ if (noWs) return noWs;
52050
+ }
52051
+ return null;
52052
+ }
52053
+
51422
52054
  // src/indexer/edge-resolvers/tests.ts
51423
52055
  init_logger();
51424
52056
  var TEST_PATH_RE = /\.(test|spec)\.[jt]sx?$|__tests__\/|(?:^|[/\\])test_[^/\\]+\.py$|(?:^|[/\\])[^/\\]+_test\.py$|conftest\.py$/;
@@ -51521,7 +52153,7 @@ var EdgeResolver = class {
51521
52153
  for (const p of activeResult.value) seen.add(p.manifest.name);
51522
52154
  }
51523
52155
  for (const ws2 of this.state.workspaces) {
51524
- const wsRoot = path17.join(this.state.rootPath, ws2.path);
52156
+ const wsRoot = path18.join(this.state.rootPath, ws2.path);
51525
52157
  const wsCtx = buildProjectContext(wsRoot);
51526
52158
  const wsPlugins = this.state.registry.getAllFrameworkPlugins().filter((p) => !seen.has(p.manifest.name) && p.detect(wsCtx));
51527
52159
  if (wsPlugins.length === 0) continue;
@@ -51558,6 +52190,10 @@ var EdgeResolver = class {
51558
52190
  resolvePythonImportEdges() {
51559
52191
  resolvePythonImportEdges(this.state);
51560
52192
  }
52193
+ /** Pass 2e2: PHP import edges (PSR-4 use statements). */
52194
+ resolvePhpImportEdges() {
52195
+ resolvePhpImportEdges(this.state);
52196
+ }
51561
52197
  /** Pass 2f: Python heritage edges (class inheritance). */
51562
52198
  resolvePythonHeritageEdges() {
51563
52199
  resolvePythonHeritageEdges(this.state);
@@ -51730,12 +52366,12 @@ var EdgeResolver = class {
51730
52366
  };
51731
52367
 
51732
52368
  // src/indexer/file-extractor.ts
51733
- import fs19 from "fs";
51734
- import path18 from "path";
52369
+ import fs20 from "fs";
52370
+ import path19 from "path";
51735
52371
 
51736
52372
  // src/utils/hasher.ts
51737
52373
  import crypto5 from "crypto";
51738
- import fs18 from "fs";
52374
+ import fs19 from "fs";
51739
52375
  function hashContent(content) {
51740
52376
  return crypto5.createHash("md5").update(content).digest("hex");
51741
52377
  }
@@ -51749,7 +52385,7 @@ var FileExtractor = class {
51749
52385
  ctx;
51750
52386
  async extract(relPath, force) {
51751
52387
  const { store, registry, rootPath } = this.ctx;
51752
- const absPath = path18.resolve(rootPath, relPath);
52388
+ const absPath = path19.resolve(rootPath, relPath);
51753
52389
  const pathCheck = validatePath(relPath, rootPath);
51754
52390
  if (pathCheck.isErr()) {
51755
52391
  logger.warn({ file: relPath }, "Path traversal blocked");
@@ -51757,7 +52393,7 @@ var FileExtractor = class {
51757
52393
  }
51758
52394
  let fileMtimeMs = null;
51759
52395
  try {
51760
- const stat = fs19.lstatSync(absPath);
52396
+ const stat = fs20.lstatSync(absPath);
51761
52397
  if (stat.isSymbolicLink()) {
51762
52398
  logger.warn({ file: relPath }, "Symlink skipped");
51763
52399
  return "error";
@@ -51775,7 +52411,7 @@ var FileExtractor = class {
51775
52411
  }
51776
52412
  let content;
51777
52413
  try {
51778
- content = fs19.readFileSync(absPath);
52414
+ content = fs20.readFileSync(absPath);
51779
52415
  } catch {
51780
52416
  logger.warn({ file: relPath }, "Cannot read file");
51781
52417
  return "error";
@@ -51812,7 +52448,7 @@ var FileExtractor = class {
51812
52448
  const importEdges = [];
51813
52449
  if (parsed.edges?.length) {
51814
52450
  for (const edge of parsed.edges) {
51815
- const isImportEdge = (edge.edgeType === "imports" || edge.edgeType === "py_imports") && !edge.sourceNodeType && !edge.sourceSymbolId;
52451
+ const isImportEdge = (edge.edgeType === "imports" || edge.edgeType === "py_imports" || edge.edgeType === "php_imports") && !edge.sourceNodeType && !edge.sourceSymbolId;
51816
52452
  if (isImportEdge) {
51817
52453
  importEdges.push({
51818
52454
  from: edge.metadata?.["from"] ?? "",
@@ -51881,7 +52517,7 @@ var FileExtractor = class {
51881
52517
  if (wsPath) {
51882
52518
  let cached = this.wsPluginCache.get(wsPath);
51883
52519
  if (cached === void 0) {
51884
- const wsRoot = path18.join(this.ctx.rootPath, wsPath);
52520
+ const wsRoot = path19.join(this.ctx.rootPath, wsPath);
51885
52521
  const wsCtx = buildProjectContext(wsRoot);
51886
52522
  cached = this.ctx.registry.getAllFrameworkPlugins().filter((p) => p.detect(wsCtx));
51887
52523
  this.wsPluginCache.set(wsPath, cached);
@@ -51918,7 +52554,7 @@ var FileExtractor = class {
51918
52554
  return null;
51919
52555
  }
51920
52556
  detectLanguage(filePath) {
51921
- const ext = path18.extname(filePath).slice(1);
52557
+ const ext = path19.extname(filePath).slice(1);
51922
52558
  const map = {
51923
52559
  php: "php",
51924
52560
  ts: "typescript",
@@ -51934,8 +52570,8 @@ var FileExtractor = class {
51934
52570
  };
51935
52571
 
51936
52572
  // src/indexer/env-indexer.ts
51937
- import fs20 from "fs";
51938
- import path19 from "path";
52573
+ import fs21 from "fs";
52574
+ import path20 from "path";
51939
52575
  import fg2 from "fast-glob";
51940
52576
 
51941
52577
  // src/utils/env-parser.ts
@@ -52066,12 +52702,12 @@ var EnvIndexer = class {
52066
52702
  logger.debug({ file: relPath }, ".env file skipped by .traceignore");
52067
52703
  continue;
52068
52704
  }
52069
- const absPath = path19.resolve(this.rootPath, relPath);
52705
+ const absPath = path20.resolve(this.rootPath, relPath);
52070
52706
  const pathCheck = validatePath(relPath, this.rootPath);
52071
52707
  if (pathCheck.isErr()) continue;
52072
52708
  let content;
52073
52709
  try {
52074
- content = fs20.readFileSync(absPath, "utf-8");
52710
+ content = fs21.readFileSync(absPath, "utf-8");
52075
52711
  } catch {
52076
52712
  logger.warn({ file: relPath }, "Cannot read .env file");
52077
52713
  continue;
@@ -52165,7 +52801,7 @@ var IndexingPipeline = class _IndexingPipeline {
52165
52801
  if (filePaths.length === 0) return;
52166
52802
  this.store.db.transaction(() => {
52167
52803
  for (const fp of filePaths) {
52168
- const relPath = path20.isAbsolute(fp) ? path20.relative(this.rootPath, fp) : fp;
52804
+ const relPath = path21.isAbsolute(fp) ? path21.relative(this.rootPath, fp) : fp;
52169
52805
  const file = this.store.getFile(relPath);
52170
52806
  if (file) {
52171
52807
  this.store.deleteFile(file.id);
@@ -52180,7 +52816,7 @@ var IndexingPipeline = class _IndexingPipeline {
52180
52816
  const start = Date.now();
52181
52817
  const relPaths = [];
52182
52818
  for (const fp of filePaths) {
52183
- const rel = path20.isAbsolute(fp) ? path20.relative(this.rootPath, fp) : fp;
52819
+ const rel = path21.isAbsolute(fp) ? path21.relative(this.rootPath, fp) : fp;
52184
52820
  const check = validatePath(rel, this.rootPath);
52185
52821
  if (check.isErr()) {
52186
52822
  logger.warn({ file: fp }, "Path traversal blocked in indexFiles");
@@ -52298,6 +52934,7 @@ var IndexingPipeline = class _IndexingPipeline {
52298
52934
  edgeResolver.resolveTypeScriptHeritageEdges();
52299
52935
  edgeResolver.resolveEsmImportEdges();
52300
52936
  edgeResolver.resolvePythonImportEdges();
52937
+ edgeResolver.resolvePhpImportEdges();
52301
52938
  edgeResolver.resolvePythonHeritageEdges();
52302
52939
  edgeResolver.resolvePythonCallEdges();
52303
52940
  edgeResolver.resolveTestCoversEdges();
@@ -52343,7 +52980,7 @@ var IndexingPipeline = class _IndexingPipeline {
52343
52980
  const activeResult = this.registry.getActiveFrameworkPlugins(ctx);
52344
52981
  if (activeResult.isOk()) registerSchema(activeResult.value);
52345
52982
  for (const ws2 of this.workspaces) {
52346
- const wsRoot = path20.join(this.rootPath, ws2.path);
52983
+ const wsRoot = path21.join(this.rootPath, ws2.path);
52347
52984
  const wsCtx = buildProjectContext(wsRoot);
52348
52985
  const wsPlugins = this.registry.getAllFrameworkPlugins().filter((p) => p.detect(wsCtx));
52349
52986
  registerSchema(wsPlugins);
@@ -52378,7 +53015,7 @@ var IndexingPipeline = class _IndexingPipeline {
52378
53015
  const cached = this._fileContentCache.get(relPath);
52379
53016
  if (cached !== void 0) return cached;
52380
53017
  try {
52381
- return fs21.readFileSync(path20.resolve(this.rootPath, relPath), "utf-8");
53018
+ return fs23.readFileSync(path21.resolve(this.rootPath, relPath), "utf-8");
52382
53019
  } catch {
52383
53020
  return void 0;
52384
53021
  }
@@ -52734,28 +53371,28 @@ function registerCoreTools(server, ctx) {
52734
53371
  import { z as z3 } from "zod";
52735
53372
 
52736
53373
  // src/tools/navigation/navigation.ts
52737
- import path23 from "path";
53374
+ import path24 from "path";
52738
53375
 
52739
53376
  // src/utils/source-reader.ts
52740
- import fs23 from "fs";
52741
- import path21 from "path";
53377
+ import fs24 from "fs";
53378
+ import path23 from "path";
52742
53379
  var GITIGNORED_NOTICE = "[content hidden \u2014 file is gitignored]";
52743
53380
  function readByteRange(filePath, byteStart, byteEnd, gitignored) {
52744
53381
  if (gitignored && !isEnvFile(filePath)) return GITIGNORED_NOTICE;
52745
53382
  if (byteEnd <= byteStart || byteStart < 0) return "";
52746
- const fd = fs23.openSync(filePath, "r");
53383
+ const fd = fs24.openSync(filePath, "r");
52747
53384
  try {
52748
53385
  const length = byteEnd - byteStart;
52749
53386
  const buffer = Buffer.alloc(length);
52750
- const bytesRead = fs23.readSync(fd, buffer, 0, length, byteStart);
53387
+ const bytesRead = fs24.readSync(fd, buffer, 0, length, byteStart);
52751
53388
  return buffer.subarray(0, bytesRead).toString("utf8");
52752
53389
  } finally {
52753
- fs23.closeSync(fd);
53390
+ fs24.closeSync(fd);
52754
53391
  }
52755
53392
  }
52756
53393
  var ENV_BASENAME_RE = /^\.env(\..+)?$/;
52757
53394
  function isEnvFile(filePath) {
52758
- return ENV_BASENAME_RE.test(path21.basename(filePath));
53395
+ return ENV_BASENAME_RE.test(path23.basename(filePath));
52759
53396
  }
52760
53397
 
52761
53398
  // src/scoring/signal-fusion.ts
@@ -52910,7 +53547,7 @@ function getSymbol(store, rootPath, opts) {
52910
53547
  if (!file) {
52911
53548
  return err3(notFound(`file:${symbol.file_id}`));
52912
53549
  }
52913
- const absPath = path23.resolve(rootPath, file.path);
53550
+ const absPath = path24.resolve(rootPath, file.path);
52914
53551
  let source;
52915
53552
  let truncated;
52916
53553
  try {
@@ -53797,9 +54434,9 @@ function deduplicateByFile(rawDeps) {
53797
54434
  }
53798
54435
  }
53799
54436
  const result = [];
53800
- for (const [path66, entry] of fileMap) {
54437
+ for (const [path67, entry] of fileMap) {
53801
54438
  const dep = {
53802
- path: path66,
54439
+ path: path67,
53803
54440
  edgeTypes: [...entry.edgeTypes],
53804
54441
  depth: entry.depth
53805
54442
  };
@@ -53992,7 +54629,7 @@ function getPennantImpact(store, name) {
53992
54629
  }
53993
54630
 
53994
54631
  // src/tools/navigation/context.ts
53995
- import path24 from "path";
54632
+ import path25 from "path";
53996
54633
 
53997
54634
  // src/utils/token-counter.ts
53998
54635
  function estimateTokens(text) {
@@ -54252,7 +54889,7 @@ function getFeatureContext(store, rootPath, description, tokenBudget = 4e3) {
54252
54889
  const meta = `[${item.symbol.kind}] ${item.symbol.fqn ?? item.symbol.name} (${item.file.path})`;
54253
54890
  let source;
54254
54891
  try {
54255
- const absPath = path24.resolve(rootPath, item.file.path);
54892
+ const absPath = path25.resolve(rootPath, item.file.path);
54256
54893
  source = readByteRange(absPath, item.symbol.byte_start, item.symbol.byte_end, !!item.file.gitignored);
54257
54894
  } catch {
54258
54895
  }
@@ -54288,7 +54925,7 @@ function getFeatureContext(store, rootPath, description, tokenBudget = 4e3) {
54288
54925
  }
54289
54926
 
54290
54927
  // src/tools/navigation/context-bundle.ts
54291
- import path25 from "path";
54928
+ import path26 from "path";
54292
54929
  import { ok as ok3, err as err4 } from "neverthrow";
54293
54930
 
54294
54931
  // src/scoring/structured-assembly.ts
@@ -54376,9 +55013,9 @@ var FileReadCache = class {
54376
55013
  let buf = this.cache.get(file.id);
54377
55014
  if (buf === void 0) {
54378
55015
  try {
54379
- const absPath = path25.resolve(this.rootPath, file.path);
54380
- const fs56 = __require("fs");
54381
- buf = fs56.readFileSync(absPath);
55016
+ const absPath = path26.resolve(this.rootPath, file.path);
55017
+ const fs57 = __require("fs");
55018
+ buf = fs57.readFileSync(absPath);
54382
55019
  } catch {
54383
55020
  buf = null;
54384
55021
  }
@@ -54536,7 +55173,7 @@ function getContextBundle(store, rootPath, opts) {
54536
55173
  }
54537
55174
 
54538
55175
  // src/tools/navigation/task-context.ts
54539
- import path26 from "path";
55176
+ import path27 from "path";
54540
55177
  var INTENT_PATTERNS = [
54541
55178
  [/\b(fix|bug|error|crash(?:es|ed|ing)?|broken|issue|fail(?:ing|s|ed)?|wrong|regression|debug|patch|hotfix)\b/i, "bugfix"],
54542
55179
  [/\b(add|create|new|implement|build|introduce|feature|setup|integrate|wire|connect)\b/i, "new_feature"],
@@ -54763,7 +55400,7 @@ async function getTaskContext(store, rootPath, opts, ai) {
54763
55400
  const meta = `[${entry.symbol.kind}] ${entry.symbol.fqn ?? entry.symbol.name} (${entry.file.path})`;
54764
55401
  let source;
54765
55402
  try {
54766
- const absPath = path26.resolve(rootPath, entry.file.path);
55403
+ const absPath = path27.resolve(rootPath, entry.file.path);
54767
55404
  source = readByteRange(absPath, entry.symbol.byte_start, entry.symbol.byte_end, !!entry.file.gitignored);
54768
55405
  } catch {
54769
55406
  }
@@ -55132,8 +55769,8 @@ function buildNegativeEvidence(filesOrOpts, indexedSymbols, queryExpanded, toolN
55132
55769
  }
55133
55770
 
55134
55771
  // src/tools/navigation/search-text.ts
55135
- import fs24 from "fs";
55136
- import path27 from "path";
55772
+ import fs25 from "fs";
55773
+ import path28 from "path";
55137
55774
  import picomatch from "picomatch";
55138
55775
  function searchText(store, projectRoot, opts) {
55139
55776
  const {
@@ -55177,14 +55814,14 @@ function searchText(store, projectRoot, opts) {
55177
55814
  truncated = true;
55178
55815
  break;
55179
55816
  }
55180
- const absPath = path27.resolve(projectRoot, file.path);
55817
+ const absPath = path28.resolve(projectRoot, file.path);
55181
55818
  const pathCheck = validatePath(file.path, projectRoot);
55182
55819
  if (pathCheck.isErr()) continue;
55183
55820
  let content;
55184
55821
  try {
55185
- const stat = fs24.statSync(absPath);
55822
+ const stat = fs25.statSync(absPath);
55186
55823
  if (stat.size > 1048576) continue;
55187
- content = fs24.readFileSync(absPath, "utf-8");
55824
+ content = fs25.readFileSync(absPath, "utf-8");
55188
55825
  } catch {
55189
55826
  continue;
55190
55827
  }
@@ -55232,8 +55869,8 @@ function escapeRegex(str2) {
55232
55869
 
55233
55870
  // src/tools/navigation/zero-index.ts
55234
55871
  import { execFileSync as execFileSync3 } from "child_process";
55235
- import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
55236
- import path28 from "path";
55872
+ import { readFileSync as readFileSync3, readdirSync, statSync } from "fs";
55873
+ import path29 from "path";
55237
55874
  var RG_ARGS_BASE = [
55238
55875
  "--json",
55239
55876
  "--max-count=200",
@@ -55264,7 +55901,7 @@ function searchWithRipgrep(projectRoot, query, opts) {
55264
55901
  const parsed = JSON.parse(line);
55265
55902
  if (parsed.type !== "match") continue;
55266
55903
  const data = parsed.data;
55267
- const relPath = path28.relative(projectRoot, data.path.text);
55904
+ const relPath = path29.relative(projectRoot, data.path.text);
55268
55905
  matches.push({
55269
55906
  file: relPath,
55270
55907
  line: data.line_number,
@@ -55300,7 +55937,7 @@ function manualSearch(projectRoot, query, opts) {
55300
55937
  for (const entry of entries) {
55301
55938
  if (matches.length >= limit) return;
55302
55939
  if (traceignore.isSkippedDir(entry)) continue;
55303
- const full2 = path28.join(dir, entry);
55940
+ const full2 = path29.join(dir, entry);
55304
55941
  let stat;
55305
55942
  try {
55306
55943
  stat = statSync(full2);
@@ -55310,17 +55947,17 @@ function manualSearch(projectRoot, query, opts) {
55310
55947
  if (stat.isDirectory()) {
55311
55948
  walk(full2);
55312
55949
  } else if (stat.isFile() && stat.size < 512 * 1024) {
55313
- const rel = path28.relative(projectRoot, full2);
55950
+ const rel = path29.relative(projectRoot, full2);
55314
55951
  if (traceignore.isIgnored(rel)) continue;
55315
55952
  try {
55316
- const content = readFileSync2(full2, "utf-8");
55953
+ const content = readFileSync3(full2, "utf-8");
55317
55954
  const lines = content.split("\n");
55318
55955
  for (let i = 0; i < lines.length; i++) {
55319
55956
  regex.lastIndex = 0;
55320
55957
  const m = regex.exec(lines[i]);
55321
55958
  if (m) {
55322
55959
  matches.push({
55323
- file: path28.relative(projectRoot, full2),
55960
+ file: path29.relative(projectRoot, full2),
55324
55961
  line: i + 1,
55325
55962
  column: m.index + 1,
55326
55963
  text: lines[i].trimEnd()
@@ -55387,7 +56024,7 @@ SYMBOL_PATTERNS["tsx"] = SYMBOL_PATTERNS["typescript"];
55387
56024
  SYMBOL_PATTERNS["jsx"] = SYMBOL_PATTERNS["javascript"];
55388
56025
  SYMBOL_PATTERNS["kotlin"] = SYMBOL_PATTERNS["java"];
55389
56026
  function detectLanguage(filePath) {
55390
- const ext = path28.extname(filePath).toLowerCase();
56027
+ const ext = path29.extname(filePath).toLowerCase();
55391
56028
  const map = {
55392
56029
  ".ts": "typescript",
55393
56030
  ".tsx": "typescript",
@@ -55438,8 +56075,8 @@ function fallbackSearch(projectRoot, query, opts = {}) {
55438
56075
  };
55439
56076
  }
55440
56077
  function fallbackOutline(projectRoot, filePath) {
55441
- const absPath = path28.resolve(projectRoot, filePath);
55442
- const content = readFileSync2(absPath, "utf-8");
56078
+ const absPath = path29.resolve(projectRoot, filePath);
56079
+ const content = readFileSync3(absPath, "utf-8");
55443
56080
  const language = detectLanguage(filePath);
55444
56081
  const symbols = extractSymbolsFromContent(content, language);
55445
56082
  return {
@@ -55495,13 +56132,13 @@ function computeAdaptiveBudget(toolName, state, userBudget) {
55495
56132
  }
55496
56133
 
55497
56134
  // src/subproject/manager.ts
55498
- import path33 from "path";
55499
- import fs30 from "fs";
56135
+ import path34 from "path";
56136
+ import fs31 from "fs";
55500
56137
 
55501
56138
  // src/topology/contract-parser.ts
55502
56139
  init_logger();
55503
- import fs25 from "fs";
55504
- import path29 from "path";
56140
+ import fs26 from "fs";
56141
+ import path30 from "path";
55505
56142
  import Database2 from "better-sqlite3";
55506
56143
  var EXCLUDE_DIRS = ["node_modules", "vendor", ".git", "dist", "build", "__pycache__"];
55507
56144
  function parseContracts(repoRoot) {
@@ -55509,8 +56146,8 @@ function parseContracts(repoRoot) {
55509
56146
  const specFiles = findSpecFiles(repoRoot);
55510
56147
  for (const { filePath, type } of specFiles) {
55511
56148
  try {
55512
- const content = fs25.readFileSync(filePath, "utf-8");
55513
- const relPath = path29.relative(repoRoot, filePath);
56149
+ const content = fs26.readFileSync(filePath, "utf-8");
56150
+ const relPath = path30.relative(repoRoot, filePath);
55514
56151
  let contract = null;
55515
56152
  switch (type) {
55516
56153
  case "openapi":
@@ -55683,13 +56320,13 @@ function findSpecFiles(root) {
55683
56320
  if (depth > 5) return;
55684
56321
  let entries;
55685
56322
  try {
55686
- entries = fs25.readdirSync(dir, { withFileTypes: true });
56323
+ entries = fs26.readdirSync(dir, { withFileTypes: true });
55687
56324
  } catch {
55688
56325
  return;
55689
56326
  }
55690
56327
  for (const entry of entries) {
55691
56328
  if (EXCLUDE_DIRS.includes(entry.name)) continue;
55692
- const fullPath = path29.join(dir, entry.name);
56329
+ const fullPath = path30.join(dir, entry.name);
55693
56330
  if (entry.isDirectory()) {
55694
56331
  walk(fullPath, depth + 1);
55695
56332
  } else if (entry.isFile()) {
@@ -55707,8 +56344,9 @@ function findSpecFiles(root) {
55707
56344
  walk(root, 0);
55708
56345
  return results;
55709
56346
  }
56347
+ var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "ANY"]);
55710
56348
  function extractRoutesFromDb(dbPath, pathPrefix) {
55711
- if (!fs25.existsSync(dbPath)) return null;
56349
+ if (!fs26.existsSync(dbPath)) return null;
55712
56350
  try {
55713
56351
  const db = new Database2(dbPath, { readonly: true });
55714
56352
  try {
@@ -55727,6 +56365,7 @@ function extractRoutesFromDb(dbPath, pathPrefix) {
55727
56365
  "SELECT method, uri, name FROM routes ORDER BY uri"
55728
56366
  ).all();
55729
56367
  }
56368
+ rows = rows.filter((r) => HTTP_METHODS.has(r.method));
55730
56369
  if (rows.length === 0) return null;
55731
56370
  const endpoints = rows.map((r) => ({
55732
56371
  method: r.method === "ANY" ? null : r.method,
@@ -55752,12 +56391,12 @@ function extractRoutesFromDb(dbPath, pathPrefix) {
55752
56391
 
55753
56392
  // src/topology/service-detector.ts
55754
56393
  init_logger();
55755
- import fs27 from "fs";
55756
- import path31 from "path";
56394
+ import fs28 from "fs";
56395
+ import path32 from "path";
55757
56396
 
55758
56397
  // src/project-root.ts
55759
- import fs26 from "fs";
55760
- import path30 from "path";
56398
+ import fs27 from "fs";
56399
+ import path31 from "path";
55761
56400
  var ROOT_MARKERS = [
55762
56401
  // VCS
55763
56402
  ".git",
@@ -55821,7 +56460,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([".git", "node_modules", "vendor", ".sv
55821
56460
  function detectServices(repoRoots) {
55822
56461
  const services = [];
55823
56462
  for (const root of repoRoots) {
55824
- const absRoot = path31.resolve(root);
56463
+ const absRoot = path32.resolve(root);
55825
56464
  const composeServices = detectFromDockerCompose(absRoot);
55826
56465
  if (composeServices.length > 0) {
55827
56466
  services.push(...composeServices);
@@ -55833,7 +56472,7 @@ function detectServices(repoRoots) {
55833
56472
  continue;
55834
56473
  }
55835
56474
  services.push({
55836
- name: path31.basename(absRoot),
56475
+ name: path32.basename(absRoot),
55837
56476
  repoRoot: absRoot,
55838
56477
  serviceType: "monolith",
55839
56478
  detectionSource: "workspace"
@@ -55844,7 +56483,7 @@ function detectServices(repoRoots) {
55844
56483
  function detectFromWorkspaceStructure(root) {
55845
56484
  let entries;
55846
56485
  try {
55847
- entries = fs27.readdirSync(root, { withFileTypes: true });
56486
+ entries = fs28.readdirSync(root, { withFileTypes: true });
55848
56487
  } catch {
55849
56488
  return [];
55850
56489
  }
@@ -55853,7 +56492,7 @@ function detectFromWorkspaceStructure(root) {
55853
56492
  for (const entry of entries) {
55854
56493
  if (!entry.isDirectory()) continue;
55855
56494
  if (entry.name.startsWith(".") || SKIP_DIRS2.has(entry.name)) continue;
55856
- const childDir = path31.join(root, entry.name);
56495
+ const childDir = path32.join(root, entry.name);
55857
56496
  if (hasRootMarker(childDir)) {
55858
56497
  flatServices.push({
55859
56498
  name: entry.name,
@@ -55873,14 +56512,14 @@ function detectFromWorkspaceStructure(root) {
55873
56512
  for (const { name: groupName, dir: groupDir } of dirsWithoutMarker) {
55874
56513
  let childEntries;
55875
56514
  try {
55876
- childEntries = fs27.readdirSync(groupDir, { withFileTypes: true });
56515
+ childEntries = fs28.readdirSync(groupDir, { withFileTypes: true });
55877
56516
  } catch {
55878
56517
  continue;
55879
56518
  }
55880
56519
  for (const childEntry of childEntries) {
55881
56520
  if (!childEntry.isDirectory()) continue;
55882
56521
  if (childEntry.name.startsWith(".") || SKIP_DIRS2.has(childEntry.name)) continue;
55883
- const childDir = path31.join(groupDir, childEntry.name);
56522
+ const childDir = path32.join(groupDir, childEntry.name);
55884
56523
  if (hasRootMarker(childDir)) {
55885
56524
  groupedServices.push({
55886
56525
  name: `${groupName}/${childEntry.name}`,
@@ -55901,7 +56540,7 @@ function detectFromWorkspaceStructure(root) {
55901
56540
  }
55902
56541
  function hasRootMarker(dir) {
55903
56542
  for (const marker of ROOT_MARKERS) {
55904
- if (fs27.existsSync(path31.join(dir, marker))) return true;
56543
+ if (fs28.existsSync(path32.join(dir, marker))) return true;
55905
56544
  }
55906
56545
  return false;
55907
56546
  }
@@ -55909,15 +56548,15 @@ function detectFromDockerCompose(root) {
55909
56548
  const composeFiles = ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"];
55910
56549
  let composePath;
55911
56550
  for (const f of composeFiles) {
55912
- const p = path31.join(root, f);
55913
- if (fs27.existsSync(p)) {
56551
+ const p = path32.join(root, f);
56552
+ if (fs28.existsSync(p)) {
55914
56553
  composePath = p;
55915
56554
  break;
55916
56555
  }
55917
56556
  }
55918
56557
  if (!composePath) return [];
55919
56558
  try {
55920
- const content = fs27.readFileSync(composePath, "utf-8");
56559
+ const content = fs28.readFileSync(composePath, "utf-8");
55921
56560
  const services = [];
55922
56561
  const lines = content.split("\n");
55923
56562
  let inServices = false;
@@ -55964,8 +56603,8 @@ function detectFromDockerCompose(root) {
55964
56603
 
55965
56604
  // src/subproject/scanner.ts
55966
56605
  init_logger();
55967
- import fs28 from "fs";
55968
- import path32 from "path";
56606
+ import fs29 from "fs";
56607
+ import path33 from "path";
55969
56608
  var CALL_PATTERNS = [
55970
56609
  // fetch('url') / fetch(`url`)
55971
56610
  {
@@ -56092,19 +56731,19 @@ function walkAndScan(dir, repoRoot, results, depth) {
56092
56731
  if (depth > 10) return;
56093
56732
  let entries;
56094
56733
  try {
56095
- entries = fs28.readdirSync(dir, { withFileTypes: true });
56734
+ entries = fs29.readdirSync(dir, { withFileTypes: true });
56096
56735
  } catch {
56097
56736
  return;
56098
56737
  }
56099
56738
  for (const entry of entries) {
56100
56739
  if (EXCLUDE_DIRS2.has(entry.name)) continue;
56101
- const fullPath = path32.join(dir, entry.name);
56740
+ const fullPath = path33.join(dir, entry.name);
56102
56741
  if (entry.isDirectory()) {
56103
56742
  walkAndScan(fullPath, repoRoot, results, depth + 1);
56104
- } else if (entry.isFile() && CODE_EXTENSIONS.has(path32.extname(entry.name).toLowerCase())) {
56743
+ } else if (entry.isFile() && CODE_EXTENSIONS.has(path33.extname(entry.name).toLowerCase())) {
56105
56744
  try {
56106
- const content = fs28.readFileSync(fullPath, "utf-8");
56107
- const relPath = path32.relative(repoRoot, fullPath);
56745
+ const content = fs29.readFileSync(fullPath, "utf-8");
56746
+ const relPath = path33.relative(repoRoot, fullPath);
56108
56747
  scanFileContent(relPath, content, results);
56109
56748
  } catch {
56110
56749
  }
@@ -56144,7 +56783,7 @@ function scanFileContent(filePath, content, results) {
56144
56783
  init_logger();
56145
56784
 
56146
56785
  // src/subproject/subproject-search.ts
56147
- import fs29 from "fs";
56786
+ import fs30 from "fs";
56148
56787
  import Database3 from "better-sqlite3";
56149
56788
  init_logger();
56150
56789
  function subprojectSearch(topoStore, query, filters, limit = 20, excludeRoot) {
@@ -56153,7 +56792,7 @@ function subprojectSearch(topoStore, query, filters, limit = 20, excludeRoot) {
56153
56792
  let reposSearched = 0;
56154
56793
  const normalizedExclude = excludeRoot?.replace(/\/+$/, "");
56155
56794
  for (const repo of repos) {
56156
- if (!repo.db_path || !fs29.existsSync(repo.db_path)) continue;
56795
+ if (!repo.db_path || !fs30.existsSync(repo.db_path)) continue;
56157
56796
  if (normalizedExclude && repo.repo_root.replace(/\/+$/, "") === normalizedExclude) continue;
56158
56797
  let db = null;
56159
56798
  try {
@@ -56462,18 +57101,18 @@ var SubprojectManager = class {
56462
57101
  * @param opts - optional name, contract paths
56463
57102
  */
56464
57103
  add(repoRoot, projectRoot, opts) {
56465
- const absRoot = path33.resolve(repoRoot);
56466
- const absProjectRoot = path33.resolve(projectRoot);
56467
- if (!fs30.existsSync(absRoot)) {
57104
+ const absRoot = path34.resolve(repoRoot);
57105
+ const absProjectRoot = path34.resolve(projectRoot);
57106
+ if (!fs31.existsSync(absRoot)) {
56468
57107
  throw new Error(`Repository path does not exist: ${absRoot}`);
56469
57108
  }
56470
- const repoName = opts?.name ?? path33.basename(absRoot);
57109
+ const repoName = opts?.name ?? path34.basename(absRoot);
56471
57110
  const dbPath = getDbPath(absRoot);
56472
57111
  const repoId = this.topoStore.upsertSubproject({
56473
57112
  name: repoName,
56474
57113
  repoRoot: absRoot,
56475
57114
  projectRoot: absProjectRoot,
56476
- dbPath: fs30.existsSync(dbPath) ? dbPath : void 0,
57115
+ dbPath: fs31.existsSync(dbPath) ? dbPath : void 0,
56477
57116
  contractPaths: opts?.contractPaths
56478
57117
  });
56479
57118
  const detected = detectServices([absRoot]);
@@ -56487,6 +57126,7 @@ var SubprojectManager = class {
56487
57126
  projectGroup: svc.projectGroup,
56488
57127
  metadata: svc.metadata
56489
57128
  });
57129
+ this.topoStore.deleteContractsByService(serviceId);
56490
57130
  this.registerContracts(serviceId, svc.repoRoot, absRoot, opts?.contractPaths);
56491
57131
  }
56492
57132
  const clientCalls = this.scanAndLinkClientCalls(repoId, absRoot);
@@ -56509,8 +57149,8 @@ var SubprojectManager = class {
56509
57149
  * sub-services (from docker-compose, workspace structure, or root markers).
56510
57150
  */
56511
57151
  autoDiscoverSubprojects(projectRoot, opts) {
56512
- const absProjectRoot = path33.resolve(projectRoot);
56513
- if (!fs30.existsSync(absProjectRoot)) {
57152
+ const absProjectRoot = path34.resolve(projectRoot);
57153
+ if (!fs31.existsSync(absProjectRoot)) {
56514
57154
  throw new Error(`Project path does not exist: ${absProjectRoot}`);
56515
57155
  }
56516
57156
  const detected = detectServices([absProjectRoot]);
@@ -56522,7 +57162,7 @@ var SubprojectManager = class {
56522
57162
  name: repoName,
56523
57163
  repoRoot: svc.repoRoot,
56524
57164
  projectRoot: absProjectRoot,
56525
- dbPath: fs30.existsSync(dbPath) ? dbPath : void 0,
57165
+ dbPath: fs31.existsSync(dbPath) ? dbPath : void 0,
56526
57166
  contractPaths: opts?.contractPaths
56527
57167
  });
56528
57168
  const serviceId = this.topoStore.upsertService({
@@ -56534,6 +57174,7 @@ var SubprojectManager = class {
56534
57174
  projectGroup: svc.projectGroup,
56535
57175
  metadata: svc.metadata
56536
57176
  });
57177
+ this.topoStore.deleteContractsByService(serviceId);
56537
57178
  this.registerContracts(serviceId, svc.repoRoot, svc.repoRoot, opts?.contractPaths);
56538
57179
  const clientCalls = this.scanAndLinkClientCalls(repoId, svc.repoRoot);
56539
57180
  this.topoStore.updateSubprojectSyncTime(repoId);
@@ -56636,7 +57277,7 @@ var SubprojectManager = class {
56636
57277
  let endpointsUpdated = 0;
56637
57278
  let clientCallsScanned = 0;
56638
57279
  for (const repo of repos) {
56639
- if (!fs30.existsSync(repo.repo_root)) {
57280
+ if (!fs31.existsSync(repo.repo_root)) {
56640
57281
  logger.warn({ repo: repo.name, root: repo.repo_root }, "Subproject repo no longer exists, skipping");
56641
57282
  continue;
56642
57283
  }
@@ -56712,11 +57353,11 @@ var SubprojectManager = class {
56712
57353
  const contracts = parseContracts(serviceRoot);
56713
57354
  if (explicitPaths && repoRoot) {
56714
57355
  for (const cp of explicitPaths) {
56715
- const absContract = path33.resolve(repoRoot, cp);
56716
- if (fs30.existsSync(absContract)) {
56717
- const additional = parseContracts(path33.dirname(absContract));
57356
+ const absContract = path34.resolve(repoRoot, cp);
57357
+ if (fs31.existsSync(absContract)) {
57358
+ const additional = parseContracts(path34.dirname(absContract));
56718
57359
  contracts.push(...additional.filter(
56719
- (c) => path33.resolve(repoRoot, c.specPath) === absContract
57360
+ (c) => path34.resolve(repoRoot, c.specPath) === absContract
56720
57361
  ));
56721
57362
  }
56722
57363
  }
@@ -56817,7 +57458,7 @@ var SubprojectManager = class {
56817
57458
  for (const [repoName, calls] of byRepo) {
56818
57459
  const repo = this.topoStore.getSubproject(repoName);
56819
57460
  for (const call of calls) {
56820
- const symbols = repo?.db_path && fs30.existsSync(repo.db_path) ? resolveSymbolsAtLocation(repo.db_path, call.file_path, call.line) : [];
57461
+ const symbols = repo?.db_path && fs31.existsSync(repo.db_path) ? resolveSymbolsAtLocation(repo.db_path, call.file_path, call.line) : [];
56821
57462
  clients.push({
56822
57463
  repo: repoName,
56823
57464
  filePath: call.file_path,
@@ -57583,8 +58224,8 @@ function splitControllerRef(ref) {
57583
58224
  }
57584
58225
 
57585
58226
  // src/tools/framework/middleware-chain.ts
57586
- import fs31 from "fs";
57587
- import path34 from "path";
58227
+ import fs32 from "fs";
58228
+ import path35 from "path";
57588
58229
  function getMiddlewareChain(store, rootPath, url2) {
57589
58230
  const allRoutes = store.getAllRoutes();
57590
58231
  const matchingRoute = allRoutes.find((r) => {
@@ -57633,7 +58274,7 @@ function detectFramework(allFiles) {
57633
58274
  }
57634
58275
  function readSource(rootPath, filePath) {
57635
58276
  try {
57636
- return fs31.readFileSync(path34.resolve(rootPath, filePath), "utf-8");
58277
+ return fs32.readFileSync(path35.resolve(rootPath, filePath), "utf-8");
57637
58278
  } catch {
57638
58279
  return void 0;
57639
58280
  }
@@ -57791,12 +58432,12 @@ function buildDjangoChain(store, rootPath, allFiles, chain) {
57791
58432
  }
57792
58433
 
57793
58434
  // src/tools/analysis/module-graph.ts
57794
- import fs33 from "fs";
57795
- import path36 from "path";
58435
+ import fs34 from "fs";
58436
+ import path37 from "path";
57796
58437
 
57797
58438
  // src/indexer/plugins/integration/framework/nestjs/index.ts
57798
- import fs32 from "fs";
57799
- import path35 from "path";
58439
+ import fs33 from "fs";
58440
+ import path36 from "path";
57800
58441
  var MODULE_RE = /@Module\(\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}\s*\)/s;
57801
58442
  function extractModuleArray(body, prop) {
57802
58443
  const re = new RegExp(`${escapeRegExp(prop)}\\s*:\\s*\\[([^\\]]*?)\\]`, "s");
@@ -57827,14 +58468,14 @@ function getModuleGraph(store, rootPath, moduleName) {
57827
58468
  for (const file of moduleFiles) {
57828
58469
  let source;
57829
58470
  try {
57830
- source = fs33.readFileSync(path36.resolve(rootPath, file.path), "utf-8");
58471
+ source = fs34.readFileSync(path37.resolve(rootPath, file.path), "utf-8");
57831
58472
  } catch {
57832
58473
  continue;
57833
58474
  }
57834
58475
  const info = extractModuleInfo(source);
57835
58476
  if (!info) continue;
57836
58477
  const classMatch = source.match(/export\s+class\s+(\w+)/);
57837
- const name = classMatch?.[1] ?? path36.basename(file.path, path36.extname(file.path));
58478
+ const name = classMatch?.[1] ?? path37.basename(file.path, path37.extname(file.path));
57838
58479
  moduleMap.set(name, {
57839
58480
  name,
57840
58481
  file: file.path,
@@ -58915,7 +59556,7 @@ function findNovaSymbol(store, name) {
58915
59556
  }
58916
59557
 
58917
59558
  // src/tools/framework/tests.ts
58918
- import path37 from "path";
59559
+ import path38 from "path";
58919
59560
  import { ok as ok8, err as err9 } from "neverthrow";
58920
59561
  function getTestsFor(store, opts) {
58921
59562
  let targetFile;
@@ -58969,14 +59610,14 @@ function getTestsFor(store, opts) {
58969
59610
  }
58970
59611
  }
58971
59612
  if (targetFile) {
58972
- const baseName = path37.basename(targetFile, path37.extname(targetFile));
59613
+ const baseName = path38.basename(targetFile, path38.extname(targetFile));
58973
59614
  const normalized = baseName.replace(/([A-Z])/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, "").toLowerCase();
58974
59615
  const variants = [baseName.toLowerCase(), normalized];
58975
59616
  const allFiles = store.getAllFiles();
58976
59617
  for (const f of allFiles) {
58977
59618
  if (seen.has(f.path)) continue;
58978
59619
  if (!isTestFile(f.path)) continue;
58979
- const testBase = path37.basename(f.path).toLowerCase();
59620
+ const testBase = path38.basename(f.path).toLowerCase();
58980
59621
  if (variants.some((v) => testBase.includes(v))) {
58981
59622
  seen.add(f.path);
58982
59623
  tests.push({ test_file: f.path, edge_type: "heuristic_path" });
@@ -60485,8 +61126,8 @@ import { z as z6 } from "zod";
60485
61126
 
60486
61127
  // src/tools/refactoring/dead-code.ts
60487
61128
  init_logger();
60488
- import fs34 from "fs";
60489
- import path38 from "path";
61129
+ import fs35 from "fs";
61130
+ import path39 from "path";
60490
61131
  var DECORATOR_DRIVEN_FRAMEWORKS = /* @__PURE__ */ new Set([
60491
61132
  "nestjs",
60492
61133
  "laravel",
@@ -60521,7 +61162,7 @@ var BARREL_PATTERNS = [
60521
61162
  /^main\.[jt]sx?$/
60522
61163
  ];
60523
61164
  function isBarrelFile(filePath) {
60524
- const base = path38.basename(filePath);
61165
+ const base = path39.basename(filePath);
60525
61166
  return BARREL_PATTERNS.some((p) => p.test(base));
60526
61167
  }
60527
61168
  function buildImportedNamesSet(store) {
@@ -60721,12 +61362,12 @@ function readPackageEntries(projectRoot) {
60721
61362
  const paths = /* @__PURE__ */ new Set();
60722
61363
  const sources = {};
60723
61364
  if (!projectRoot) return { paths, sources };
60724
- const pkgPath = path38.join(projectRoot, "package.json");
60725
- if (!fs34.existsSync(pkgPath)) return { paths, sources };
61365
+ const pkgPath = path39.join(projectRoot, "package.json");
61366
+ if (!fs35.existsSync(pkgPath)) return { paths, sources };
60726
61367
  try {
60727
- const pkg = JSON.parse(fs34.readFileSync(pkgPath, "utf-8"));
61368
+ const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
60728
61369
  const add = (rel, src) => {
60729
- const norm = path38.normalize(rel).replace(/\\/g, "/");
61370
+ const norm = path39.normalize(rel).replace(/\\/g, "/");
60730
61371
  paths.add(norm);
60731
61372
  sources[norm] = src;
60732
61373
  };
@@ -60944,8 +61585,8 @@ function getDeadCodeReachability(store, options = {}) {
60944
61585
  }
60945
61586
 
60946
61587
  // src/tools/quality/security-scan.ts
60947
- import { readFileSync as readFileSync3 } from "fs";
60948
- import path39 from "path";
61588
+ import { readFileSync as readFileSync4 } from "fs";
61589
+ import path40 from "path";
60949
61590
  var ALL_LANGUAGES = /* @__PURE__ */ new Set([
60950
61591
  "typescript",
60951
61592
  "javascript",
@@ -61227,10 +61868,10 @@ function scanSecurity(store, projectRoot, opts) {
61227
61868
  const batch = files.slice(i, i + BATCH_SIZE);
61228
61869
  for (const file of batch) {
61229
61870
  if (/\.(?:test|spec)\.|__tests__|\/tests?\//i.test(file.path)) continue;
61230
- const absPath = path39.resolve(projectRoot, file.path);
61871
+ const absPath = path40.resolve(projectRoot, file.path);
61231
61872
  let content;
61232
61873
  try {
61233
- const buf = readFileSync3(absPath);
61874
+ const buf = readFileSync4(absPath);
61234
61875
  if (buf.length > MAX_FILE_SIZE) continue;
61235
61876
  content = buf.toString("utf-8");
61236
61877
  } catch {
@@ -61925,8 +62566,8 @@ function detectAntipatterns(store, _projectRoot, opts = {}) {
61925
62566
  }
61926
62567
 
61927
62568
  // src/tools/quality/code-smells.ts
61928
- import { readFileSync as readFileSync4 } from "fs";
61929
- import path40 from "path";
62569
+ import { readFileSync as readFileSync5 } from "fs";
62570
+ import path41 from "path";
61930
62571
  var ALL_CATEGORIES2 = [
61931
62572
  "todo_comment",
61932
62573
  "empty_function",
@@ -62181,10 +62822,10 @@ function scanCodeSmells(store, projectRoot, opts = {}) {
62181
62822
  const batch = files.slice(i, i + BATCH_SIZE2);
62182
62823
  for (const file of batch) {
62183
62824
  if (!includeTests && /\.(?:test|spec)\.|__tests__|\/tests?\//i.test(file.path)) continue;
62184
- const absPath = path40.resolve(projectRoot, file.path);
62825
+ const absPath = path41.resolve(projectRoot, file.path);
62185
62826
  let content;
62186
62827
  try {
62187
- const buf = readFileSync4(absPath);
62828
+ const buf = readFileSync5(absPath);
62188
62829
  if (buf.length > MAX_FILE_SIZE2) continue;
62189
62830
  content = buf.toString("utf-8");
62190
62831
  } catch {
@@ -62237,8 +62878,8 @@ function scanCodeSmells(store, projectRoot, opts = {}) {
62237
62878
  }
62238
62879
 
62239
62880
  // src/tools/quality/taint-analysis.ts
62240
- import { readFileSync as readFileSync5 } from "fs";
62241
- import path41 from "path";
62881
+ import { readFileSync as readFileSync6 } from "fs";
62882
+ import path42 from "path";
62242
62883
  var JS_TS2 = /* @__PURE__ */ new Set(["typescript", "javascript"]);
62243
62884
  var PY2 = /* @__PURE__ */ new Set(["python"]);
62244
62885
  var PHP2 = /* @__PURE__ */ new Set(["php"]);
@@ -62594,10 +63235,10 @@ function interProceduralAnalysis(store, projectRoot, perFileResults, limit) {
62594
63235
  for (const [filePath, fileData] of perFileResults) {
62595
63236
  if (crossFileFlows.length >= limit) break;
62596
63237
  if (fileData.sources.length === 0) continue;
62597
- const absPath = path41.join(projectRoot, filePath);
63238
+ const absPath = path42.join(projectRoot, filePath);
62598
63239
  let content;
62599
63240
  try {
62600
- content = readFileSync5(absPath, "utf-8");
63241
+ content = readFileSync6(absPath, "utf-8");
62601
63242
  } catch {
62602
63243
  continue;
62603
63244
  }
@@ -62616,10 +63257,10 @@ function interProceduralAnalysis(store, projectRoot, perFileResults, limit) {
62616
63257
  ).all(call.funcName, filePath);
62617
63258
  for (const sym of symbols) {
62618
63259
  if (crossFileFlows.length >= limit) break;
62619
- const targetPath = path41.join(projectRoot, sym.file_path);
63260
+ const targetPath = path42.join(projectRoot, sym.file_path);
62620
63261
  let targetContent;
62621
63262
  try {
62622
- targetContent = readFileSync5(targetPath, "utf-8");
63263
+ targetContent = readFileSync6(targetPath, "utf-8");
62623
63264
  } catch {
62624
63265
  continue;
62625
63266
  }
@@ -62677,10 +63318,10 @@ function taintAnalysis(store, projectRoot, opts = {}) {
62677
63318
  const perFileData = /* @__PURE__ */ new Map();
62678
63319
  for (const file of sourceFiles) {
62679
63320
  if (allFlows.length >= limit) break;
62680
- const absPath = path41.join(projectRoot, file.path);
63321
+ const absPath = path42.join(projectRoot, file.path);
62681
63322
  let content;
62682
63323
  try {
62683
- content = readFileSync5(absPath, "utf-8");
63324
+ content = readFileSync6(absPath, "utf-8");
62684
63325
  } catch {
62685
63326
  continue;
62686
63327
  }
@@ -62762,8 +63403,8 @@ function taintAnalysis(store, projectRoot, opts = {}) {
62762
63403
  }
62763
63404
 
62764
63405
  // src/tools/project/sbom.ts
62765
- import { readFileSync as readFileSync6, existsSync as existsSync2 } from "fs";
62766
- import path42 from "path";
63406
+ import { readFileSync as readFileSync7, existsSync as existsSync3 } from "fs";
63407
+ import path43 from "path";
62767
63408
  var COPYLEFT_LICENSES = /* @__PURE__ */ new Set([
62768
63409
  "GPL-2.0",
62769
63410
  "GPL-2.0-only",
@@ -62798,21 +63439,21 @@ function checkLicenseWarning(name, version2, license) {
62798
63439
  }
62799
63440
  function readJson(filePath) {
62800
63441
  try {
62801
- return JSON.parse(readFileSync6(filePath, "utf-8"));
63442
+ return JSON.parse(readFileSync7(filePath, "utf-8"));
62802
63443
  } catch {
62803
63444
  return null;
62804
63445
  }
62805
63446
  }
62806
63447
  function readLines(filePath) {
62807
63448
  try {
62808
- return readFileSync6(filePath, "utf-8").split("\n");
63449
+ return readFileSync7(filePath, "utf-8").split("\n");
62809
63450
  } catch {
62810
63451
  return [];
62811
63452
  }
62812
63453
  }
62813
63454
  function parseNpm(root, includeDev, includeTransitive) {
62814
- const pkgPath = path42.join(root, "package.json");
62815
- const lockPath = path42.join(root, "package-lock.json");
63455
+ const pkgPath = path43.join(root, "package.json");
63456
+ const lockPath = path43.join(root, "package-lock.json");
62816
63457
  const pkg = readJson(pkgPath);
62817
63458
  if (!pkg) return [];
62818
63459
  const deps = pkg.dependencies ?? {};
@@ -62852,8 +63493,8 @@ function parseNpm(root, includeDev, includeTransitive) {
62852
63493
  return components;
62853
63494
  }
62854
63495
  function parseComposer(root, includeDev, includeTransitive) {
62855
- const lockPath = path42.join(root, "composer.lock");
62856
- const manifestPath = path42.join(root, "composer.json");
63496
+ const lockPath = path43.join(root, "composer.lock");
63497
+ const manifestPath = path43.join(root, "composer.json");
62857
63498
  const manifest = readJson(manifestPath);
62858
63499
  const directNames = /* @__PURE__ */ new Set();
62859
63500
  if (manifest) {
@@ -62896,8 +63537,8 @@ function parseComposer(root, includeDev, includeTransitive) {
62896
63537
  }
62897
63538
  function parsePip(root, includeDev, includeTransitive) {
62898
63539
  const components = [];
62899
- const reqPath = path42.join(root, "requirements.txt");
62900
- if (existsSync2(reqPath)) {
63540
+ const reqPath = path43.join(root, "requirements.txt");
63541
+ if (existsSync3(reqPath)) {
62901
63542
  for (const line of readLines(reqPath)) {
62902
63543
  const trimmed = line.trim();
62903
63544
  if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) continue;
@@ -62912,9 +63553,9 @@ function parsePip(root, includeDev, includeTransitive) {
62912
63553
  }
62913
63554
  }
62914
63555
  }
62915
- const poetryLock = path42.join(root, "poetry.lock");
62916
- if (existsSync2(poetryLock) && includeTransitive) {
62917
- const content = readFileSync6(poetryLock, "utf-8");
63556
+ const poetryLock = path43.join(root, "poetry.lock");
63557
+ if (existsSync3(poetryLock) && includeTransitive) {
63558
+ const content = readFileSync7(poetryLock, "utf-8");
62918
63559
  const directNames = new Set(components.map((c) => c.name.toLowerCase()));
62919
63560
  let currentName = "";
62920
63561
  let currentVersion = "";
@@ -62943,8 +63584,8 @@ function parsePip(root, includeDev, includeTransitive) {
62943
63584
  return components;
62944
63585
  }
62945
63586
  function parseGo(root, _includeDev, includeTransitive) {
62946
- const modPath = path42.join(root, "go.mod");
62947
- if (!existsSync2(modPath)) return [];
63587
+ const modPath = path43.join(root, "go.mod");
63588
+ if (!existsSync3(modPath)) return [];
62948
63589
  const components = [];
62949
63590
  const directNames = /* @__PURE__ */ new Set();
62950
63591
  let inRequire = false;
@@ -62976,10 +63617,10 @@ function parseGo(root, _includeDev, includeTransitive) {
62976
63617
  return components;
62977
63618
  }
62978
63619
  function parseCargo(root, _includeDev, includeTransitive) {
62979
- const lockPath = path42.join(root, "Cargo.lock");
62980
- if (!existsSync2(lockPath)) {
62981
- const tomlPath = path42.join(root, "Cargo.toml");
62982
- if (!existsSync2(tomlPath)) return [];
63620
+ const lockPath = path43.join(root, "Cargo.lock");
63621
+ if (!existsSync3(lockPath)) {
63622
+ const tomlPath = path43.join(root, "Cargo.toml");
63623
+ if (!existsSync3(tomlPath)) return [];
62983
63624
  const components2 = [];
62984
63625
  let inDeps = false;
62985
63626
  for (const line of readLines(tomlPath)) {
@@ -63043,8 +63684,8 @@ function parseCargo(root, _includeDev, includeTransitive) {
63043
63684
  });
63044
63685
  }
63045
63686
  if (!includeTransitive) {
63046
- const tomlPath = path42.join(root, "Cargo.toml");
63047
- if (existsSync2(tomlPath)) {
63687
+ const tomlPath = path43.join(root, "Cargo.toml");
63688
+ if (existsSync3(tomlPath)) {
63048
63689
  const directNames = /* @__PURE__ */ new Set();
63049
63690
  let inDeps = false;
63050
63691
  for (const line of readLines(tomlPath)) {
@@ -63068,8 +63709,8 @@ function parseCargo(root, _includeDev, includeTransitive) {
63068
63709
  return components;
63069
63710
  }
63070
63711
  function parseBundler(root, _includeDev, includeTransitive) {
63071
- const lockPath = path42.join(root, "Gemfile.lock");
63072
- if (!existsSync2(lockPath)) return [];
63712
+ const lockPath = path43.join(root, "Gemfile.lock");
63713
+ if (!existsSync3(lockPath)) return [];
63073
63714
  const components = [];
63074
63715
  let inSpecs = false;
63075
63716
  for (const line of readLines(lockPath)) {
@@ -63107,9 +63748,9 @@ function parseBundler(root, _includeDev, includeTransitive) {
63107
63748
  return components;
63108
63749
  }
63109
63750
  function parseMaven(root, _includeDev, _includeTransitive) {
63110
- const pomPath = path42.join(root, "pom.xml");
63111
- if (!existsSync2(pomPath)) return [];
63112
- const content = readFileSync6(pomPath, "utf-8");
63751
+ const pomPath = path43.join(root, "pom.xml");
63752
+ if (!existsSync3(pomPath)) return [];
63753
+ const content = readFileSync7(pomPath, "utf-8");
63113
63754
  const components = [];
63114
63755
  const depRegex = /<dependency>\s*<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>\s*(?:<version>([^<]+)<\/version>)?/gs;
63115
63756
  let match;
@@ -63946,13 +64587,13 @@ function registerGitTools(server, ctx) {
63946
64587
  import { z as z7 } from "zod";
63947
64588
 
63948
64589
  // src/tools/refactoring/refactor.ts
63949
- import fs37 from "fs";
63950
- import path45 from "path";
64590
+ import fs38 from "fs";
64591
+ import path46 from "path";
63951
64592
  import fg5 from "fast-glob";
63952
64593
 
63953
64594
  // src/tools/refactoring/shared.ts
63954
- import fs35 from "fs";
63955
- import path43 from "path";
64595
+ import fs36 from "fs";
64596
+ import path44 from "path";
63956
64597
  var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
63957
64598
  ".png",
63958
64599
  ".jpg",
@@ -63995,10 +64636,10 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
63995
64636
  ]);
63996
64637
  var SKIP_DIRS3 = ["node_modules", ".git", "dist", "build", "vendor", "__pycache__", ".next", ".nuxt"];
63997
64638
  function readLines2(filePath) {
63998
- return fs35.readFileSync(filePath, "utf-8").split("\n");
64639
+ return fs36.readFileSync(filePath, "utf-8").split("\n");
63999
64640
  }
64000
64641
  function writeLines(filePath, lines) {
64001
- fs35.writeFileSync(filePath, lines.join("\n"), "utf-8");
64642
+ fs36.writeFileSync(filePath, lines.join("\n"), "utf-8");
64002
64643
  }
64003
64644
  function buildRenameRegex(name) {
64004
64645
  const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -64024,7 +64665,7 @@ function getImportingFiles(store, fileId, projectRoot) {
64024
64665
  seen.add(importFileId);
64025
64666
  const file = store.getFileById(importFileId);
64026
64667
  if (file) {
64027
- importingFiles.push({ filePath: path43.resolve(projectRoot, file.path), fileId: importFileId });
64668
+ importingFiles.push({ filePath: path44.resolve(projectRoot, file.path), fileId: importFileId });
64028
64669
  }
64029
64670
  }
64030
64671
  }
@@ -64216,8 +64857,8 @@ function getIndent(line) {
64216
64857
  }
64217
64858
 
64218
64859
  // src/tools/refactoring/non-code-scanner.ts
64219
- import fs36 from "fs";
64220
- import path44 from "path";
64860
+ import fs37 from "fs";
64861
+ import path45 from "path";
64221
64862
  import fg4 from "fast-glob";
64222
64863
  var NON_CODE_PATTERNS = [
64223
64864
  "**/*.yaml",
@@ -64260,14 +64901,14 @@ function scanNonCodeFiles(projectRoot, oldName, newName) {
64260
64901
  } catch {
64261
64902
  return mentions;
64262
64903
  }
64263
- files = files.filter((f) => !SKIP_FILES.has(path44.basename(f)));
64904
+ files = files.filter((f) => !SKIP_FILES.has(path45.basename(f)));
64264
64905
  for (const relPath of files) {
64265
- const absPath = path44.resolve(projectRoot, relPath);
64906
+ const absPath = path45.resolve(projectRoot, relPath);
64266
64907
  let content;
64267
64908
  try {
64268
- const stat = fs36.statSync(absPath);
64909
+ const stat = fs37.statSync(absPath);
64269
64910
  if (stat.size > 1e6) continue;
64270
- content = fs36.readFileSync(absPath, "utf-8");
64911
+ content = fs37.readFileSync(absPath, "utf-8");
64271
64912
  } catch {
64272
64913
  continue;
64273
64914
  }
@@ -64322,7 +64963,7 @@ function applyRename(store, projectRoot, symbolId, newName, dryRun = false) {
64322
64963
  result.error = `File not found for symbol ${symbolId}`;
64323
64964
  return result;
64324
64965
  }
64325
- const definitionFilePath = path45.resolve(projectRoot, symbolFile.path);
64966
+ const definitionFilePath = path46.resolve(projectRoot, symbolFile.path);
64326
64967
  const importingFiles = getImportingFiles(store, symbol.file_id, projectRoot);
64327
64968
  const allFiles = [
64328
64969
  { filePath: definitionFilePath, fileId: symbol.file_id },
@@ -64331,7 +64972,7 @@ function applyRename(store, projectRoot, symbolId, newName, dryRun = false) {
64331
64972
  const regex = buildRenameRegex(oldName);
64332
64973
  const modifiedFiles = /* @__PURE__ */ new Set();
64333
64974
  for (const { filePath } of allFiles) {
64334
- if (!fs37.existsSync(filePath)) {
64975
+ if (!fs38.existsSync(filePath)) {
64335
64976
  result.warnings.push(`File not found on disk: ${filePath}`);
64336
64977
  continue;
64337
64978
  }
@@ -64344,7 +64985,7 @@ function applyRename(store, projectRoot, symbolId, newName, dryRun = false) {
64344
64985
  regex.lastIndex = 0;
64345
64986
  if (newLine !== line) {
64346
64987
  result.edits.push({
64347
- file: path45.relative(projectRoot, filePath),
64988
+ file: path46.relative(projectRoot, filePath),
64348
64989
  original_line: i + 1,
64349
64990
  original_text: line.trimStart(),
64350
64991
  new_text: newLine.trimStart()
@@ -64359,7 +65000,7 @@ function applyRename(store, projectRoot, symbolId, newName, dryRun = false) {
64359
65000
  if (!dryRun) {
64360
65001
  writeLines(filePath, lines);
64361
65002
  }
64362
- modifiedFiles.add(path45.relative(projectRoot, filePath));
65003
+ modifiedFiles.add(path46.relative(projectRoot, filePath));
64363
65004
  }
64364
65005
  }
64365
65006
  result.success = true;
@@ -64420,8 +65061,8 @@ function removeDeadCode(store, projectRoot, symbolId, dryRun = false) {
64420
65061
  result.error = `Symbol "${symbol.name}" has no line range \u2014 cannot remove`;
64421
65062
  return result;
64422
65063
  }
64423
- const filePath = path45.resolve(projectRoot, symbolFile.path);
64424
- if (!fs37.existsSync(filePath)) {
65064
+ const filePath = path46.resolve(projectRoot, symbolFile.path);
65065
+ if (!fs38.existsSync(filePath)) {
64425
65066
  result.error = `File not found on disk: ${filePath}`;
64426
65067
  return result;
64427
65068
  }
@@ -64473,7 +65114,7 @@ function removeDeadCode(store, projectRoot, symbolId, dryRun = false) {
64473
65114
  const importers = getImportingFiles(store, symbol.file_id, projectRoot);
64474
65115
  if (importers.length > 0) {
64475
65116
  result.warnings.push(
64476
- `Removed the last exported symbol from ${symbolFile.path}. ${importers.length} file(s) still import from it \u2014 review for unused imports: ` + importers.map((f) => path45.relative(projectRoot, f.filePath)).join(", ")
65117
+ `Removed the last exported symbol from ${symbolFile.path}. ${importers.length} file(s) still import from it \u2014 review for unused imports: ` + importers.map((f) => path46.relative(projectRoot, f.filePath)).join(", ")
64477
65118
  );
64478
65119
  }
64479
65120
  }
@@ -64489,8 +65130,8 @@ function extractFunction(store, projectRoot, filePath, startLine, endLine, funct
64489
65130
  files_modified: [],
64490
65131
  warnings: []
64491
65132
  };
64492
- const absPath = path45.resolve(projectRoot, filePath);
64493
- if (!fs37.existsSync(absPath)) {
65133
+ const absPath = path46.resolve(projectRoot, filePath);
65134
+ if (!fs38.existsSync(absPath)) {
64494
65135
  result.error = `File not found: ${filePath}`;
64495
65136
  return result;
64496
65137
  }
@@ -64499,7 +65140,7 @@ function extractFunction(store, projectRoot, filePath, startLine, endLine, funct
64499
65140
  result.error = `Invalid line range ${startLine}-${endLine} (file has ${lines.length} lines)`;
64500
65141
  return result;
64501
65142
  }
64502
- const ext = path45.extname(filePath).toLowerCase();
65143
+ const ext = path46.extname(filePath).toLowerCase();
64503
65144
  const lang = detectLanguage2(ext);
64504
65145
  const extractedLines = lines.slice(startLine - 1, endLine);
64505
65146
  const extractedText = extractedLines.join("\n");
@@ -64637,7 +65278,7 @@ function applyCodemod(projectRoot, pattern, replacement, filePattern, options) {
64637
65278
  result.error = `Invalid file pattern: ${e.message}`;
64638
65279
  return result;
64639
65280
  }
64640
- files = files.filter((f) => !BINARY_EXTENSIONS.has(path45.extname(f).toLowerCase()));
65281
+ files = files.filter((f) => !BINARY_EXTENSIONS.has(path46.extname(f).toLowerCase()));
64641
65282
  if (files.length === 0) {
64642
65283
  result.error = `No files matched pattern: ${filePattern}`;
64643
65284
  return result;
@@ -64645,11 +65286,11 @@ function applyCodemod(projectRoot, pattern, replacement, filePattern, options) {
64645
65286
  const allMatches = [];
64646
65287
  const filesWithMatches = /* @__PURE__ */ new Set();
64647
65288
  for (const relPath of files) {
64648
- const absPath = path45.resolve(projectRoot, relPath);
64649
- if (!fs37.existsSync(absPath)) continue;
65289
+ const absPath = path46.resolve(projectRoot, relPath);
65290
+ if (!fs38.existsSync(absPath)) continue;
64650
65291
  let content;
64651
65292
  try {
64652
- content = fs37.readFileSync(absPath, "utf-8");
65293
+ content = fs38.readFileSync(absPath, "utf-8");
64653
65294
  } catch {
64654
65295
  result.warnings.push(`Could not read: ${relPath}`);
64655
65296
  continue;
@@ -64731,14 +65372,14 @@ function applyCodemod(projectRoot, pattern, replacement, filePattern, options) {
64731
65372
  return result;
64732
65373
  }
64733
65374
  for (const relPath of filesWithMatches) {
64734
- const absPath = path45.resolve(projectRoot, relPath);
65375
+ const absPath = path46.resolve(projectRoot, relPath);
64735
65376
  try {
64736
- const content = fs37.readFileSync(absPath, "utf-8");
65377
+ const content = fs38.readFileSync(absPath, "utf-8");
64737
65378
  const flags = options.multiline ? "gms" : "gm";
64738
65379
  const freshRegex = new RegExp(pattern, flags);
64739
65380
  const newContent = content.replace(freshRegex, replacement);
64740
65381
  if (newContent !== content) {
64741
- fs37.writeFileSync(absPath, newContent, "utf-8");
65382
+ fs38.writeFileSync(absPath, newContent, "utf-8");
64742
65383
  result.files_modified.push(relPath);
64743
65384
  }
64744
65385
  } catch (e) {
@@ -64751,12 +65392,12 @@ function applyCodemod(projectRoot, pattern, replacement, filePattern, options) {
64751
65392
  }
64752
65393
 
64753
65394
  // src/tools/refactoring/move.ts
64754
- import fs39 from "fs";
64755
- import path47 from "path";
65395
+ import fs40 from "fs";
65396
+ import path48 from "path";
64756
65397
 
64757
65398
  // src/tools/refactoring/import-rewriter.ts
64758
- import fs38 from "fs";
64759
- import path46 from "path";
65399
+ import fs39 from "fs";
65400
+ import path47 from "path";
64760
65401
  var STRIP_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"]);
64761
65402
  var IMPORT_PATTERNS = [
64762
65403
  // import ... from 'specifier'
@@ -64776,8 +65417,8 @@ function computeNewImportSpecifier(importingFilePath, oldSpecifier, newTargetAbs
64776
65417
  const oldSuffix = oldSpecifier.slice(aliasPrefix.length);
64777
65418
  const resolved = resolver.resolve(aliasPrefix, importingFilePath);
64778
65419
  if (resolved) {
64779
- const aliasRoot = path46.dirname(resolved);
64780
- const newRelToAlias = path46.relative(aliasRoot, newTargetAbsPath);
65420
+ const aliasRoot = path47.dirname(resolved);
65421
+ const newRelToAlias = path47.relative(aliasRoot, newTargetAbsPath);
64781
65422
  if (!newRelToAlias.startsWith("..")) {
64782
65423
  const newSpecifier = aliasPrefix + "/" + stripExtension(newRelToAlias);
64783
65424
  const check = resolver.resolve(newSpecifier, importingFilePath);
@@ -64791,8 +65432,8 @@ function computeNewImportSpecifier(importingFilePath, oldSpecifier, newTargetAbs
64791
65432
  return computeRelativeSpecifier(importingFilePath, newTargetAbsPath);
64792
65433
  }
64793
65434
  function computeRelativeSpecifier(fromFile, toFile) {
64794
- const fromDir = path46.dirname(fromFile);
64795
- let rel = path46.relative(fromDir, toFile);
65435
+ const fromDir = path47.dirname(fromFile);
65436
+ let rel = path47.relative(fromDir, toFile);
64796
65437
  rel = stripExtension(rel);
64797
65438
  if (rel.endsWith("/index") || rel === "index") {
64798
65439
  rel = rel === "index" ? "." : rel.slice(0, -6);
@@ -64803,22 +65444,22 @@ function computeRelativeSpecifier(fromFile, toFile) {
64803
65444
  return rel.replace(/\\/g, "/");
64804
65445
  }
64805
65446
  function stripExtension(filePath) {
64806
- const ext = path46.extname(filePath);
65447
+ const ext = path47.extname(filePath);
64807
65448
  if (STRIP_EXTENSIONS.has(ext)) {
64808
65449
  return filePath.slice(0, -ext.length);
64809
65450
  }
64810
65451
  return filePath;
64811
65452
  }
64812
65453
  function normalizePath(p) {
64813
- return path46.resolve(p).replace(/\\/g, "/");
65454
+ return path47.resolve(p).replace(/\\/g, "/");
64814
65455
  }
64815
65456
  function rewriteImportSpecifiers(filePath, projectRoot, oldSpecifier, newSpecifier, dryRun) {
64816
65457
  const edits = [];
64817
- if (!fs38.existsSync(filePath)) {
65458
+ if (!fs39.existsSync(filePath)) {
64818
65459
  return { edits, lines: [] };
64819
65460
  }
64820
65461
  const lines = readLines2(filePath);
64821
- const relPath = path46.relative(projectRoot, filePath);
65462
+ const relPath = path47.relative(projectRoot, filePath);
64822
65463
  let modified = false;
64823
65464
  for (let i = 0; i < lines.length; i++) {
64824
65465
  const line = lines[i];
@@ -64850,7 +65491,7 @@ function rewriteImportSpecifiers(filePath, projectRoot, oldSpecifier, newSpecifi
64850
65491
  return { edits, lines };
64851
65492
  }
64852
65493
  function findImportSpecifier(sourceFilePath, targetAbsPath, projectRoot, resolver) {
64853
- if (!fs38.existsSync(sourceFilePath)) return void 0;
65494
+ if (!fs39.existsSync(sourceFilePath)) return void 0;
64854
65495
  const lines = readLines2(sourceFilePath);
64855
65496
  const fullText = lines.join("\n");
64856
65497
  for (const pattern of IMPORT_PATTERNS) {
@@ -64864,11 +65505,11 @@ function findImportSpecifier(sourceFilePath, targetAbsPath, projectRoot, resolve
64864
65505
  resolvedPath = resolver.resolve(specifier, sourceFilePath);
64865
65506
  }
64866
65507
  if (!resolvedPath && (specifier.startsWith(".") || specifier.startsWith("/"))) {
64867
- const fromDir = path46.dirname(sourceFilePath);
64868
- const candidate = path46.resolve(fromDir, specifier);
65508
+ const fromDir = path47.dirname(sourceFilePath);
65509
+ const candidate = path47.resolve(fromDir, specifier);
64869
65510
  for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"]) {
64870
65511
  const full2 = candidate + ext;
64871
- if (fs38.existsSync(full2)) {
65512
+ if (fs39.existsSync(full2)) {
64872
65513
  resolvedPath = full2;
64873
65514
  break;
64874
65515
  }
@@ -64932,13 +65573,13 @@ function moveSymbol(store, projectRoot, params, resolver) {
64932
65573
  result.error = `Source file not found for symbol ${params.symbol_id}`;
64933
65574
  return result;
64934
65575
  }
64935
- const sourceAbsPath = path47.resolve(projectRoot, sourceFile.path);
64936
- const targetAbsPath = path47.resolve(projectRoot, params.target_file);
64937
- if (!fs39.existsSync(sourceAbsPath)) {
65576
+ const sourceAbsPath = path48.resolve(projectRoot, sourceFile.path);
65577
+ const targetAbsPath = path48.resolve(projectRoot, params.target_file);
65578
+ if (!fs40.existsSync(sourceAbsPath)) {
64938
65579
  result.error = `Source file not found on disk: ${sourceFile.path}`;
64939
65580
  return result;
64940
65581
  }
64941
- if (fs39.existsSync(targetAbsPath)) {
65582
+ if (fs40.existsSync(targetAbsPath)) {
64942
65583
  const targetFile = store.getFile(params.target_file);
64943
65584
  if (targetFile) {
64944
65585
  const targetSymbols = store.getSymbolsByFile(targetFile.id);
@@ -64965,7 +65606,7 @@ function moveSymbol(store, projectRoot, params, resolver) {
64965
65606
  const meta = symbol.metadata ? typeof symbol.metadata === "string" ? JSON.parse(symbol.metadata) : symbol.metadata : {};
64966
65607
  const isExported = !!meta?.exported;
64967
65608
  let targetLines;
64968
- const targetExists = fs39.existsSync(targetAbsPath);
65609
+ const targetExists = fs40.existsSync(targetAbsPath);
64969
65610
  if (targetExists) {
64970
65611
  targetLines = readLines2(targetAbsPath);
64971
65612
  } else {
@@ -65011,7 +65652,7 @@ function moveSymbol(store, projectRoot, params, resolver) {
65011
65652
  if (isExported) {
65012
65653
  const importingFiles = getImportingFiles(store, symbol.file_id, projectRoot);
65013
65654
  for (const { filePath: importerAbsPath } of importingFiles) {
65014
- if (!fs39.existsSync(importerAbsPath)) continue;
65655
+ if (!fs40.existsSync(importerAbsPath)) continue;
65015
65656
  const importerLines = readLines2(importerAbsPath);
65016
65657
  const importerContent = importerLines.join("\n");
65017
65658
  const nameRegex = new RegExp(`\\b${escapeRegex3(symbol.name)}\\b`);
@@ -65027,7 +65668,7 @@ function moveSymbol(store, projectRoot, params, resolver) {
65027
65668
  );
65028
65669
  result.edits.push(...importEdits);
65029
65670
  if (importEdits.length > 0) {
65030
- result.files_modified.push(path47.relative(projectRoot, importerAbsPath));
65671
+ result.files_modified.push(path48.relative(projectRoot, importerAbsPath));
65031
65672
  }
65032
65673
  }
65033
65674
  const remainingExports = fileSymbols.filter((s) => {
@@ -65055,9 +65696,9 @@ function moveSymbol(store, projectRoot, params, resolver) {
65055
65696
  tLines.splice(insertIdx, 0, ...insertLines);
65056
65697
  writeLines(targetAbsPath, tLines);
65057
65698
  } else {
65058
- const targetDir = path47.dirname(targetAbsPath);
65059
- if (!fs39.existsSync(targetDir)) {
65060
- fs39.mkdirSync(targetDir, { recursive: true });
65699
+ const targetDir = path48.dirname(targetAbsPath);
65700
+ if (!fs40.existsSync(targetDir)) {
65701
+ fs40.mkdirSync(targetDir, { recursive: true });
65061
65702
  }
65062
65703
  writeLines(targetAbsPath, insertLines);
65063
65704
  }
@@ -65077,13 +65718,13 @@ function moveFile(store, projectRoot, params, resolver) {
65077
65718
  files_modified: [],
65078
65719
  warnings: []
65079
65720
  };
65080
- const sourceAbsPath = path47.resolve(projectRoot, params.source_file);
65081
- const targetAbsPath = path47.resolve(projectRoot, params.new_path);
65082
- if (!fs39.existsSync(sourceAbsPath)) {
65721
+ const sourceAbsPath = path48.resolve(projectRoot, params.source_file);
65722
+ const targetAbsPath = path48.resolve(projectRoot, params.new_path);
65723
+ if (!fs40.existsSync(sourceAbsPath)) {
65083
65724
  result.error = `Source file not found: ${params.source_file}`;
65084
65725
  return result;
65085
65726
  }
65086
- if (fs39.existsSync(targetAbsPath)) {
65727
+ if (fs40.existsSync(targetAbsPath)) {
65087
65728
  result.error = `Target path already exists: ${params.new_path}`;
65088
65729
  return result;
65089
65730
  }
@@ -65094,7 +65735,7 @@ function moveFile(store, projectRoot, params, resolver) {
65094
65735
  }
65095
65736
  const importingFiles = getImportingFiles(store, fileRow.id, projectRoot);
65096
65737
  for (const { filePath: importerAbsPath } of importingFiles) {
65097
- if (!fs39.existsSync(importerAbsPath)) continue;
65738
+ if (!fs40.existsSync(importerAbsPath)) continue;
65098
65739
  const edits = rewriteImportForMovedTarget(
65099
65740
  importerAbsPath,
65100
65741
  sourceAbsPath,
@@ -65105,7 +65746,7 @@ function moveFile(store, projectRoot, params, resolver) {
65105
65746
  );
65106
65747
  result.edits.push(...edits);
65107
65748
  if (edits.length > 0) {
65108
- result.files_modified.push(path47.relative(projectRoot, importerAbsPath));
65749
+ result.files_modified.push(path48.relative(projectRoot, importerAbsPath));
65109
65750
  }
65110
65751
  }
65111
65752
  const selfEdits = rewriteOwnImports(
@@ -65124,14 +65765,14 @@ function moveFile(store, projectRoot, params, resolver) {
65124
65765
  new_text: `(moved to ${params.new_path})`
65125
65766
  });
65126
65767
  if (!dryRun) {
65127
- const targetDir = path47.dirname(targetAbsPath);
65128
- if (!fs39.existsSync(targetDir)) {
65129
- fs39.mkdirSync(targetDir, { recursive: true });
65768
+ const targetDir = path48.dirname(targetAbsPath);
65769
+ if (!fs40.existsSync(targetDir)) {
65770
+ fs40.mkdirSync(targetDir, { recursive: true });
65130
65771
  }
65131
65772
  if (selfEdits.length > 0) {
65132
- fs39.renameSync(sourceAbsPath, targetAbsPath);
65773
+ fs40.renameSync(sourceAbsPath, targetAbsPath);
65133
65774
  } else {
65134
- fs39.renameSync(sourceAbsPath, targetAbsPath);
65775
+ fs40.renameSync(sourceAbsPath, targetAbsPath);
65135
65776
  }
65136
65777
  }
65137
65778
  result.files_modified.push(params.source_file, params.new_path);
@@ -65141,9 +65782,9 @@ function moveFile(store, projectRoot, params, resolver) {
65141
65782
  function rewriteOwnImports(currentAbsPath, newAbsPath, projectRoot, store, dryRun, resolver) {
65142
65783
  const lines = readLines2(currentAbsPath);
65143
65784
  const edits = [];
65144
- const relPath = path47.relative(projectRoot, currentAbsPath);
65145
- const currentDir = path47.dirname(currentAbsPath);
65146
- const newDir = path47.dirname(newAbsPath);
65785
+ const relPath = path48.relative(projectRoot, currentAbsPath);
65786
+ const currentDir = path48.dirname(currentAbsPath);
65787
+ const newDir = path48.dirname(newAbsPath);
65147
65788
  if (currentDir === newDir) return edits;
65148
65789
  let modified = false;
65149
65790
  for (let i = 0; i < lines.length; i++) {
@@ -65181,11 +65822,11 @@ function extractSpecifierFromLine(line) {
65181
65822
  return void 0;
65182
65823
  }
65183
65824
  function resolveRelativeSpecifier(fromDir, specifier) {
65184
- const candidate = path47.resolve(fromDir, specifier);
65825
+ const candidate = path48.resolve(fromDir, specifier);
65185
65826
  const extensions = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"];
65186
65827
  for (const ext of extensions) {
65187
65828
  const full2 = candidate + ext;
65188
- if (fs39.existsSync(full2)) {
65829
+ if (fs40.existsSync(full2)) {
65189
65830
  return full2;
65190
65831
  }
65191
65832
  }
@@ -65194,8 +65835,8 @@ function resolveRelativeSpecifier(fromDir, specifier) {
65194
65835
  function rewriteSymbolImport(importerAbsPath, sourceAbsPath, targetAbsPath, symbolName, projectRoot, dryRun, resolver) {
65195
65836
  const edits = [];
65196
65837
  const lines = readLines2(importerAbsPath);
65197
- const relPath = path47.relative(projectRoot, importerAbsPath);
65198
- const importerDir = path47.dirname(importerAbsPath);
65838
+ const relPath = path48.relative(projectRoot, importerAbsPath);
65839
+ const importerDir = path48.dirname(importerAbsPath);
65199
65840
  let modified = false;
65200
65841
  for (let i = 0; i < lines.length; i++) {
65201
65842
  const line = lines[i];
@@ -65203,8 +65844,8 @@ function rewriteSymbolImport(importerAbsPath, sourceAbsPath, targetAbsPath, symb
65203
65844
  if (!specifier) continue;
65204
65845
  const resolvedAbs = resolver ? resolver.resolve(specifier, importerAbsPath) : resolveRelativeSpecifier(importerDir, specifier);
65205
65846
  if (!resolvedAbs) continue;
65206
- const normalizedResolved = path47.resolve(resolvedAbs).replace(/\\/g, "/");
65207
- const normalizedSource = path47.resolve(sourceAbsPath).replace(/\\/g, "/");
65847
+ const normalizedResolved = path48.resolve(resolvedAbs).replace(/\\/g, "/");
65848
+ const normalizedSource = path48.resolve(sourceAbsPath).replace(/\\/g, "/");
65208
65849
  if (normalizedResolved !== normalizedSource) continue;
65209
65850
  const nameRegex = new RegExp(`\\b${escapeRegex3(symbolName)}\\b`);
65210
65851
  if (!nameRegex.test(line)) continue;
@@ -65281,8 +65922,8 @@ function escapeRegex3(str2) {
65281
65922
  }
65282
65923
 
65283
65924
  // src/tools/refactoring/change-signature.ts
65284
- import fs40 from "fs";
65285
- import path48 from "path";
65925
+ import fs41 from "fs";
65926
+ import path49 from "path";
65286
65927
  function changeSignature(store, projectRoot, symbolId, changes, dryRun = true) {
65287
65928
  const result = {
65288
65929
  success: false,
@@ -65314,12 +65955,12 @@ function changeSignature(store, projectRoot, symbolId, changes, dryRun = true) {
65314
65955
  result.error = `File not found for symbol ${symbolId}`;
65315
65956
  return result;
65316
65957
  }
65317
- const filePath = path48.resolve(projectRoot, symbolFile.path);
65318
- if (!fs40.existsSync(filePath)) {
65958
+ const filePath = path49.resolve(projectRoot, symbolFile.path);
65959
+ if (!fs41.existsSync(filePath)) {
65319
65960
  result.error = `File not found on disk: ${symbolFile.path}`;
65320
65961
  return result;
65321
65962
  }
65322
- const ext = path48.extname(symbolFile.path).toLowerCase();
65963
+ const ext = path49.extname(symbolFile.path).toLowerCase();
65323
65964
  const lang = detectLanguage2(ext);
65324
65965
  const lines = readLines2(filePath);
65325
65966
  const defStartIdx = symbol.line_start - 1;
@@ -65573,8 +66214,8 @@ function updateCallSites(store, projectRoot, symbol, oldParams, newParams, chang
65573
66214
  for (const fileId of fileIds) {
65574
66215
  const file = store.getFileById(fileId);
65575
66216
  if (!file) continue;
65576
- const filePath = path48.resolve(projectRoot, file.path);
65577
- if (!fs40.existsSync(filePath)) continue;
66217
+ const filePath = path49.resolve(projectRoot, file.path);
66218
+ if (!fs41.existsSync(filePath)) continue;
65578
66219
  const lines = readLines2(filePath);
65579
66220
  let fileModified = false;
65580
66221
  const callPattern = new RegExp(`\\b${escapeRegex4(symbol.name)}\\s*\\(`, "g");
@@ -66352,8 +66993,8 @@ function getContractVersions(topoStore, opts) {
66352
66993
  }
66353
66994
 
66354
66995
  // src/tools/advanced/claude-sessions.ts
66355
- import fs41 from "fs";
66356
- import path49 from "path";
66996
+ import fs42 from "fs";
66997
+ import path50 from "path";
66357
66998
  import os6 from "os";
66358
66999
  import { err as err11, ok as ok9 } from "neverthrow";
66359
67000
  init_logger();
@@ -66366,9 +67007,9 @@ function decodeClaudeProjectName(name) {
66366
67007
  while (i < tokens.length) {
66367
67008
  let entries = [];
66368
67009
  try {
66369
- entries = fs41.readdirSync(current);
67010
+ entries = fs42.readdirSync(current);
66370
67011
  } catch {
66371
- return current === "/" ? "/" + tokens.slice(i).join("/") : path49.join(current, tokens.slice(i).join("/"));
67012
+ return current === "/" ? "/" + tokens.slice(i).join("/") : path50.join(current, tokens.slice(i).join("/"));
66372
67013
  }
66373
67014
  const entrySet = new Set(entries);
66374
67015
  let matched = -1;
@@ -66376,12 +67017,12 @@ function decodeClaudeProjectName(name) {
66376
67017
  const candidate = tokens.slice(i, j3).join("-");
66377
67018
  if (entrySet.has(candidate)) {
66378
67019
  matched = j3;
66379
- current = current === "/" ? "/" + candidate : path49.join(current, candidate);
67020
+ current = current === "/" ? "/" + candidate : path50.join(current, candidate);
66380
67021
  break;
66381
67022
  }
66382
67023
  }
66383
67024
  if (matched === -1) {
66384
- return current === "/" ? "/" + tokens.slice(i).join("/") : path49.join(current, tokens.slice(i).join("/"));
67025
+ return current === "/" ? "/" + tokens.slice(i).join("/") : path50.join(current, tokens.slice(i).join("/"));
66385
67026
  }
66386
67027
  i = matched;
66387
67028
  }
@@ -66389,7 +67030,7 @@ function decodeClaudeProjectName(name) {
66389
67030
  }
66390
67031
  function countSessionFiles(dir) {
66391
67032
  try {
66392
- const entries = fs41.readdirSync(dir, { withFileTypes: true });
67033
+ const entries = fs42.readdirSync(dir, { withFileTypes: true });
66393
67034
  return entries.filter((e) => e.isFile() && (e.name.endsWith(".jsonl") || e.name.endsWith(".json"))).length;
66394
67035
  } catch {
66395
67036
  return 0;
@@ -66397,19 +67038,19 @@ function countSessionFiles(dir) {
66397
67038
  }
66398
67039
  function dirMtime(dir) {
66399
67040
  try {
66400
- return fs41.statSync(dir).mtimeMs;
67041
+ return fs42.statSync(dir).mtimeMs;
66401
67042
  } catch {
66402
67043
  return null;
66403
67044
  }
66404
67045
  }
66405
67046
  function discoverClaudeSessions(opts = {}) {
66406
- const scanRoot = opts.scanRoot ?? path49.join(os6.homedir(), ".claude", "projects");
66407
- if (!fs41.existsSync(scanRoot)) {
67047
+ const scanRoot = opts.scanRoot ?? path50.join(os6.homedir(), ".claude", "projects");
67048
+ if (!fs42.existsSync(scanRoot)) {
66408
67049
  return err11(validationError(`Claude projects root not found: ${scanRoot}`));
66409
67050
  }
66410
67051
  let entries;
66411
67052
  try {
66412
- entries = fs41.readdirSync(scanRoot, { withFileTypes: true });
67053
+ entries = fs42.readdirSync(scanRoot, { withFileTypes: true });
66413
67054
  } catch (e) {
66414
67055
  return err11(validationError(`Failed to read ${scanRoot}: ${e.message}`));
66415
67056
  }
@@ -66421,17 +67062,17 @@ function discoverClaudeSessions(opts = {}) {
66421
67062
  if (opts.excludePrefix && decoded === opts.excludePrefix) continue;
66422
67063
  let exists = false;
66423
67064
  try {
66424
- exists = fs41.statSync(decoded).isDirectory();
67065
+ exists = fs42.statSync(decoded).isDirectory();
66425
67066
  } catch {
66426
67067
  exists = false;
66427
67068
  }
66428
67069
  if (opts.onlyExisting && !exists) continue;
66429
- const projectStateDir = path49.join(scanRoot, entry.name);
67070
+ const projectStateDir = path50.join(scanRoot, entry.name);
66430
67071
  sessions.push({
66431
67072
  projectPath: decoded,
66432
67073
  exists,
66433
67074
  sessionFiles: countSessionFiles(projectStateDir),
66434
- hasMemory: fs41.existsSync(path49.join(projectStateDir, "memory")),
67075
+ hasMemory: fs42.existsSync(path50.join(projectStateDir, "memory")),
66435
67076
  lastActiveMs: dirMtime(projectStateDir)
66436
67077
  });
66437
67078
  }
@@ -66456,7 +67097,7 @@ function discoverAndRegisterSubprojects(topoStore, opts = {}) {
66456
67097
  const totalEndpoints = services.reduce((sum, s) => sum + s.endpoints, 0);
66457
67098
  added.push({
66458
67099
  repo: session.projectPath,
66459
- name: path49.basename(session.projectPath),
67100
+ name: path50.basename(session.projectPath),
66460
67101
  services: services.length,
66461
67102
  endpoints: totalEndpoints
66462
67103
  });
@@ -68101,16 +68742,16 @@ function findShortestPath(store, startNodeId, endNodeId, maxDepth) {
68101
68742
  parent.set(nodeId, { from, edgeType: edge.edge_type_name });
68102
68743
  nextFrontier.push(nodeId);
68103
68744
  if (nodeId === endNodeId) {
68104
- const path66 = [endNodeId];
68745
+ const path67 = [endNodeId];
68105
68746
  const edgeTypes = [];
68106
68747
  let cur = endNodeId;
68107
68748
  while (cur !== startNodeId) {
68108
68749
  const p = parent.get(cur);
68109
- path66.unshift(p.from);
68750
+ path67.unshift(p.from);
68110
68751
  edgeTypes.unshift(p.edgeType);
68111
68752
  cur = p.from;
68112
68753
  }
68113
- return { path: path66, edgeTypes };
68754
+ return { path: path67, edgeTypes };
68114
68755
  }
68115
68756
  }
68116
68757
  }
@@ -68304,8 +68945,8 @@ function graphQuery(store, query, options = {}) {
68304
68945
  }
68305
68946
 
68306
68947
  // src/tools/analysis/dataflow.ts
68307
- import fs42 from "fs";
68308
- import path50 from "path";
68948
+ import fs43 from "fs";
68949
+ import path51 from "path";
68309
68950
  function getDataflow(store, projectRoot, opts) {
68310
68951
  const symbol = opts.symbolId ? store.getSymbolBySymbolId(opts.symbolId) : opts.fqn ? store.getSymbolByFqn(opts.fqn) : void 0;
68311
68952
  if (!symbol) {
@@ -68316,10 +68957,10 @@ function getDataflow(store, projectRoot, opts) {
68316
68957
  }
68317
68958
  const file = store.getFileById(symbol.file_id);
68318
68959
  if (!file) return err(notFound(`file for ${symbol.symbol_id}`));
68319
- const absPath = path50.resolve(projectRoot, file.path);
68960
+ const absPath = path51.resolve(projectRoot, file.path);
68320
68961
  let content;
68321
68962
  try {
68322
- content = fs42.readFileSync(absPath, "utf-8");
68963
+ content = fs43.readFileSync(absPath, "utf-8");
68323
68964
  } catch {
68324
68965
  return err(validationError(`Cannot read file: ${file.path}`));
68325
68966
  }
@@ -68472,8 +69113,8 @@ function escapeRegex5(str2) {
68472
69113
  }
68473
69114
 
68474
69115
  // src/tools/analysis/visualize.ts
68475
- import fs43 from "fs";
68476
- import path51 from "path";
69116
+ import fs44 from "fs";
69117
+ import path52 from "path";
68477
69118
 
68478
69119
  // src/db/repositories/file-repository.ts
68479
69120
  var FileRepository = class {
@@ -68499,14 +69140,14 @@ var FileRepository = class {
68499
69140
  }
68500
69141
  db;
68501
69142
  _stmts;
68502
- insertFile(path66, language, contentHash, byteLength, workspace, mtimeMs, createNode) {
68503
- const result = this._stmts.insertFile.run(path66, language, contentHash, byteLength, workspace, mtimeMs);
69143
+ insertFile(path67, language, contentHash, byteLength, workspace, mtimeMs, createNode) {
69144
+ const result = this._stmts.insertFile.run(path67, language, contentHash, byteLength, workspace, mtimeMs);
68504
69145
  const fileId = Number(result.lastInsertRowid);
68505
69146
  createNode("file", fileId);
68506
69147
  return fileId;
68507
69148
  }
68508
- getFile(path66) {
68509
- return this._stmts.getFile.get(path66);
69149
+ getFile(path67) {
69150
+ return this._stmts.getFile.get(path67);
68510
69151
  }
68511
69152
  getFileById(id) {
68512
69153
  return this._stmts.getFileById.get(id);
@@ -69364,9 +70005,9 @@ var Store = class {
69364
70005
  domain;
69365
70006
  analytics;
69366
70007
  // --- Files (delegates to FileRepository) ---
69367
- insertFile(path66, language, contentHash, byteLength, workspace, mtimeMs) {
70008
+ insertFile(path67, language, contentHash, byteLength, workspace, mtimeMs) {
69368
70009
  return this.files.insertFile(
69369
- path66,
70010
+ path67,
69370
70011
  language,
69371
70012
  contentHash,
69372
70013
  byteLength,
@@ -69375,8 +70016,8 @@ var Store = class {
69375
70016
  (nodeType, refId) => this.graph.createNode(nodeType, refId)
69376
70017
  );
69377
70018
  }
69378
- getFile(path66) {
69379
- return this.files.getFile(path66);
70019
+ getFile(path67) {
70020
+ return this.files.getFile(path67);
69380
70021
  }
69381
70022
  getFileById(id) {
69382
70023
  return this.files.getFileById(id);
@@ -69756,14 +70397,14 @@ function buildSubprojectGraph(mainStore, opts) {
69756
70397
  opts.granularity ?? "file",
69757
70398
  opts.hideIsolated === true
69758
70399
  );
69759
- const mainPrefix = allRepos.find((r) => r.repo_root === projectRoot)?.name ?? path51.basename(projectRoot);
70400
+ const mainPrefix = allRepos.find((r) => r.repo_root === projectRoot)?.name ?? path52.basename(projectRoot);
69760
70401
  for (const n of mainResult.nodes) {
69761
70402
  n.repo = mainPrefix;
69762
70403
  allNodes.push(n);
69763
70404
  }
69764
70405
  allEdges.push(...mainResult.edges);
69765
70406
  for (const repo of repos) {
69766
- if (!repo.db_path || !fs43.existsSync(repo.db_path)) continue;
70407
+ if (!repo.db_path || !fs44.existsSync(repo.db_path)) continue;
69767
70408
  let db = null;
69768
70409
  try {
69769
70410
  db = initializeDatabase(repo.db_path);
@@ -69892,7 +70533,7 @@ function buildFileGraph2(store, seedFiles, depth, edgeFilter, hideIsolated, opts
69892
70533
  if (isExcludedPath(file.path)) continue;
69893
70534
  vizNodeMap.set(file.path, {
69894
70535
  id: file.path,
69895
- label: path51.basename(file.path),
70536
+ label: path52.basename(file.path),
69896
70537
  type: "file",
69897
70538
  language: file.language,
69898
70539
  framework_role: file.framework_role,
@@ -71161,8 +71802,8 @@ function visualizeGraph(store, opts) {
71161
71802
  }
71162
71803
  const layout = opts.layout ?? "force";
71163
71804
  const html = generateHtml(nodes, edges, communities, layout, { highlightDepth: opts.highlightDepth });
71164
- const outputPath = opts.output ?? path51.join(process.env.TMPDIR ?? "/tmp", "trace-mcp-graph.html");
71165
- fs43.writeFileSync(outputPath, html, "utf-8");
71805
+ const outputPath = opts.output ?? path52.join(process.env.TMPDIR ?? "/tmp", "trace-mcp-graph.html");
71806
+ fs44.writeFileSync(outputPath, html, "utf-8");
71166
71807
  return ok({
71167
71808
  outputPath,
71168
71809
  nodes: nodes.length,
@@ -71212,8 +71853,8 @@ function getDependencyDiagram(store, opts) {
71212
71853
  }
71213
71854
 
71214
71855
  // src/tools/analysis/visualize-subproject.ts
71215
- import fs44 from "fs";
71216
- import path52 from "path";
71856
+ import fs45 from "fs";
71857
+ import path53 from "path";
71217
71858
  function buildSubprojectData(topoStore) {
71218
71859
  const services = topoStore.getAllServices();
71219
71860
  const allEndpoints = topoStore.getAllEndpoints();
@@ -71471,8 +72112,8 @@ function visualizeSubprojectTopology(topoStore, opts) {
71471
72112
  }
71472
72113
  const layout = opts?.layout ?? "force";
71473
72114
  const html = generateSubprojectHtml(nodes, edges, layout);
71474
- const outputPath = opts?.output ?? path52.join(process.env.TMPDIR ?? "/tmp", "trace-mcp-subproject-topology.html");
71475
- fs44.writeFileSync(outputPath, html, "utf-8");
72115
+ const outputPath = opts?.output ?? path53.join(process.env.TMPDIR ?? "/tmp", "trace-mcp-subproject-topology.html");
72116
+ fs45.writeFileSync(outputPath, html, "utf-8");
71476
72117
  return ok({
71477
72118
  outputPath,
71478
72119
  services: nodes.length,
@@ -73453,8 +74094,8 @@ function getCommunityDetail(store, communityId) {
73453
74094
  }
73454
74095
 
73455
74096
  // src/tools/quality/audit-config.ts
73456
- import fs45 from "fs";
73457
- import path53 from "path";
74097
+ import fs46 from "fs";
74098
+ import path54 from "path";
73458
74099
  var CONFIG_PATTERNS = [
73459
74100
  "CLAUDE.md",
73460
74101
  ".claude/CLAUDE.md",
@@ -73482,9 +74123,9 @@ function auditConfig(store, projectRoot, options = {}) {
73482
74123
  const fileContents = /* @__PURE__ */ new Map();
73483
74124
  for (const file of configFiles) {
73484
74125
  const absPath = resolveConfigPath(file, projectRoot);
73485
- if (!fs45.existsSync(absPath)) continue;
74126
+ if (!fs46.existsSync(absPath)) continue;
73486
74127
  try {
73487
- const content = fs45.readFileSync(absPath, "utf-8");
74128
+ const content = fs46.readFileSync(absPath, "utf-8");
73488
74129
  fileContents.set(file, { content, lines: content.split("\n") });
73489
74130
  totalTokens += Math.ceil(content.length / 4);
73490
74131
  } catch {
@@ -73496,8 +74137,8 @@ function auditConfig(store, projectRoot, options = {}) {
73496
74137
  const pathMatches = line.match(/(?:src|lib|app|routes|tests?|components?|pages?)\/[\w/.-]+\.\w+/g);
73497
74138
  if (pathMatches) {
73498
74139
  for (const ref of pathMatches) {
73499
- const refPath = path53.join(projectRoot, ref);
73500
- if (!fs45.existsSync(refPath) && !store.getFile(ref)) {
74140
+ const refPath = path54.join(projectRoot, ref);
74141
+ if (!fs46.existsSync(refPath) && !store.getFile(ref)) {
73501
74142
  issues.push({
73502
74143
  file,
73503
74144
  line: i + 1,
@@ -73609,19 +74250,19 @@ function checkSymbol(store, name, file, line, fixSuggestions, issues) {
73609
74250
  function findConfigFiles(projectRoot) {
73610
74251
  const found = [];
73611
74252
  for (const pattern of CONFIG_PATTERNS) {
73612
- const absPath = path53.join(projectRoot, pattern);
73613
- if (fs45.existsSync(absPath)) found.push(pattern);
74253
+ const absPath = path54.join(projectRoot, pattern);
74254
+ if (fs46.existsSync(absPath)) found.push(pattern);
73614
74255
  }
73615
74256
  for (const pattern of GLOBAL_CONFIG_PATTERNS) {
73616
74257
  const absPath = pattern.replace("~", process.env.HOME ?? "");
73617
- if (fs45.existsSync(absPath)) found.push(pattern);
74258
+ if (fs46.existsSync(absPath)) found.push(pattern);
73618
74259
  }
73619
74260
  return found;
73620
74261
  }
73621
74262
  function resolveConfigPath(file, projectRoot) {
73622
74263
  if (file.startsWith("~")) return file.replace("~", process.env.HOME ?? "");
73623
- if (path53.isAbsolute(file)) return file;
73624
- return path53.join(projectRoot, file);
74264
+ if (path54.isAbsolute(file)) return file;
74265
+ return path54.join(projectRoot, file);
73625
74266
  }
73626
74267
  function isGlobalConfig(file) {
73627
74268
  return file.startsWith("~") || file.includes(".claude/CLAUDE.md") || file.includes(".claw/settings.json");
@@ -73876,8 +74517,8 @@ function cfgToAscii(cfg) {
73876
74517
  }
73877
74518
 
73878
74519
  // src/tools/analysis/control-flow.ts
73879
- import fs46 from "fs";
73880
- import path54 from "path";
74520
+ import fs47 from "fs";
74521
+ import path55 from "path";
73881
74522
  function getControlFlow(store, projectRoot, options) {
73882
74523
  const { symbolId, fqn, format: format2, simplify } = options;
73883
74524
  let symbol = null;
@@ -73900,11 +74541,11 @@ function getControlFlow(store, projectRoot, options) {
73900
74541
  if (!file) {
73901
74542
  return err(notFound(`file for symbol`));
73902
74543
  }
73903
- const absPath = path54.isAbsolute(file.path) ? file.path : path54.join(projectRoot, file.path);
73904
- if (!fs46.existsSync(absPath)) {
74544
+ const absPath = path55.isAbsolute(file.path) ? file.path : path55.join(projectRoot, file.path);
74545
+ if (!fs47.existsSync(absPath)) {
73905
74546
  return err(validationError(`File not on disk: ${file.path}`));
73906
74547
  }
73907
- const content = fs46.readFileSync(absPath, "utf-8");
74548
+ const content = fs47.readFileSync(absPath, "utf-8");
73908
74549
  const lines = content.split("\n");
73909
74550
  const startLine = symbol.line_start ?? 1;
73910
74551
  const endLine = symbol.line_end ?? lines.length;
@@ -73965,12 +74606,12 @@ function simplifyCFG(cfg) {
73965
74606
  }
73966
74607
 
73967
74608
  // src/tools/project/package-deps.ts
73968
- import fs47 from "fs";
73969
- import path55 from "path";
74609
+ import fs48 from "fs";
74610
+ import path56 from "path";
73970
74611
  function loadRegistry() {
73971
- if (!fs47.existsSync(REGISTRY_PATH)) return {};
74612
+ if (!fs48.existsSync(REGISTRY_PATH)) return {};
73972
74613
  try {
73973
- const raw = JSON.parse(fs47.readFileSync(REGISTRY_PATH, "utf-8"));
74614
+ const raw = JSON.parse(fs48.readFileSync(REGISTRY_PATH, "utf-8"));
73974
74615
  return raw.projects ?? raw ?? {};
73975
74616
  } catch {
73976
74617
  return {};
@@ -73978,10 +74619,10 @@ function loadRegistry() {
73978
74619
  }
73979
74620
  function readManifest(repoPath) {
73980
74621
  const result = { name: void 0, deps: /* @__PURE__ */ new Map(), publishes: [] };
73981
- const pkgJsonPath = path55.join(repoPath, "package.json");
73982
- if (fs47.existsSync(pkgJsonPath)) {
74622
+ const pkgJsonPath = path56.join(repoPath, "package.json");
74623
+ if (fs48.existsSync(pkgJsonPath)) {
73983
74624
  try {
73984
- const pkg = JSON.parse(fs47.readFileSync(pkgJsonPath, "utf-8"));
74625
+ const pkg = JSON.parse(fs48.readFileSync(pkgJsonPath, "utf-8"));
73985
74626
  result.name = pkg.name;
73986
74627
  if (pkg.name) result.publishes.push(pkg.name);
73987
74628
  for (const [dep, ver] of Object.entries(pkg.dependencies ?? {})) {
@@ -73993,10 +74634,10 @@ function readManifest(repoPath) {
73993
74634
  } catch {
73994
74635
  }
73995
74636
  }
73996
- const composerPath = path55.join(repoPath, "composer.json");
73997
- if (fs47.existsSync(composerPath)) {
74637
+ const composerPath = path56.join(repoPath, "composer.json");
74638
+ if (fs48.existsSync(composerPath)) {
73998
74639
  try {
73999
- const composer = JSON.parse(fs47.readFileSync(composerPath, "utf-8"));
74640
+ const composer = JSON.parse(fs48.readFileSync(composerPath, "utf-8"));
74000
74641
  if (composer.name) {
74001
74642
  result.name ??= composer.name;
74002
74643
  result.publishes.push(composer.name);
@@ -74011,10 +74652,10 @@ function readManifest(repoPath) {
74011
74652
  } catch {
74012
74653
  }
74013
74654
  }
74014
- const pyprojectPath = path55.join(repoPath, "pyproject.toml");
74015
- if (fs47.existsSync(pyprojectPath)) {
74655
+ const pyprojectPath = path56.join(repoPath, "pyproject.toml");
74656
+ if (fs48.existsSync(pyprojectPath)) {
74016
74657
  try {
74017
- const content = fs47.readFileSync(pyprojectPath, "utf-8");
74658
+ const content = fs48.readFileSync(pyprojectPath, "utf-8");
74018
74659
  const nameMatch = content.match(/^name\s*=\s*"([^"]+)"/m);
74019
74660
  if (nameMatch) {
74020
74661
  result.name ??= nameMatch[1];
@@ -74039,12 +74680,12 @@ function getPackageDeps(options) {
74039
74680
  const publishMap = /* @__PURE__ */ new Map();
74040
74681
  for (const [repoPath, entry] of Object.entries(registry)) {
74041
74682
  const absPath = repoPath;
74042
- if (!fs47.existsSync(absPath)) continue;
74683
+ if (!fs48.existsSync(absPath)) continue;
74043
74684
  const manifest = readManifest(absPath);
74044
74685
  repoManifests.set(repoPath, manifest);
74045
74686
  const allPublishes = [...entry.publishes ?? [], ...manifest.publishes];
74046
74687
  for (const p of new Set(allPublishes)) {
74047
- publishMap.set(p, { repo: entry.name ?? path55.basename(repoPath), repoPath });
74688
+ publishMap.set(p, { repo: entry.name ?? path56.basename(repoPath), repoPath });
74048
74689
  }
74049
74690
  }
74050
74691
  const results = [];
@@ -74054,7 +74695,7 @@ function getPackageDeps(options) {
74054
74695
  } else if (project) {
74055
74696
  for (const [repoPath, manifest] of repoManifests) {
74056
74697
  const entry = registry[repoPath];
74057
- if (entry?.name === project || path55.basename(repoPath) === project) {
74698
+ if (entry?.name === project || path56.basename(repoPath) === project) {
74058
74699
  for (const p of manifest.publishes) {
74059
74700
  targetPackages.add(p);
74060
74701
  }
@@ -74073,7 +74714,7 @@ function getPackageDeps(options) {
74073
74714
  if (direction === "dependents" || direction === "both") {
74074
74715
  for (const [repoPath, manifest] of repoManifests) {
74075
74716
  const entry = registry[repoPath];
74076
- const repoName = entry?.name ?? path55.basename(repoPath);
74717
+ const repoName = entry?.name ?? path56.basename(repoPath);
74077
74718
  for (const targetPkg of targetPackages) {
74078
74719
  const dep = manifest.deps.get(targetPkg);
74079
74720
  if (dep) {
@@ -74092,7 +74733,7 @@ function getPackageDeps(options) {
74092
74733
  if (direction === "dependencies" || direction === "both") {
74093
74734
  for (const [repoPath, manifest] of repoManifests) {
74094
74735
  const entry = registry[repoPath];
74095
- const repoName = entry?.name ?? path55.basename(repoPath);
74736
+ const repoName = entry?.name ?? path56.basename(repoPath);
74096
74737
  const isTarget = manifest.publishes.some((p) => targetPackages.has(p));
74097
74738
  if (!isTarget && targetPackages.size > 0) continue;
74098
74739
  for (const [dep, info] of manifest.deps) {
@@ -74589,8 +75230,8 @@ function evaluateQualityGates(store, projectRoot, gatesConfig, options = {}) {
74589
75230
  }
74590
75231
 
74591
75232
  // src/tools/quality/security-context-export.ts
74592
- import { readFileSync as readFileSync7 } from "fs";
74593
- import path57 from "path";
75233
+ import { readFileSync as readFileSync8 } from "fs";
75234
+ import path58 from "path";
74594
75235
  import { ok as ok10 } from "neverthrow";
74595
75236
  var CATEGORY_PATTERNS = [
74596
75237
  // file_read
@@ -74759,9 +75400,9 @@ function scanInlineHandler(source, toolCallIndex, filePath, store, projectRoot,
74759
75400
  }
74760
75401
  const file = sym.file_id ? store.getFileById(sym.file_id) : null;
74761
75402
  if (file && sym.line_start && sym.line_end) {
74762
- const symAbsPath = path57.resolve(projectRoot, file.path);
75403
+ const symAbsPath = path58.resolve(projectRoot, file.path);
74763
75404
  try {
74764
- const symSource = readFileSync7(symAbsPath, "utf-8");
75405
+ const symSource = readFileSync8(symAbsPath, "utf-8");
74765
75406
  const lines = symSource.split("\n");
74766
75407
  const symBody = lines.slice(sym.line_start - 1, sym.line_end).join("\n");
74767
75408
  scanSourceForSecurityCalls(symBody, file.path, sym.line_start, results);
@@ -74788,7 +75429,7 @@ function scanSourceForSecurityCalls(body, filePath, startLine, results) {
74788
75429
  results.push({ function: "process.env", file: filePath, line: lineOffset, category: "env_read" });
74789
75430
  }
74790
75431
  }
74791
- var PKG_VERSION = true ? "1.22.0" : "0.0.0-dev";
75432
+ var PKG_VERSION = true ? "1.23.0" : "0.0.0-dev";
74792
75433
  function exportSecurityContext(store, projectRoot, opts = {}) {
74793
75434
  const depth = Math.min(opts.depth ?? 3, 5);
74794
75435
  const warnings = [];
@@ -74803,10 +75444,10 @@ function exportSecurityContext(store, projectRoot, opts = {}) {
74803
75444
  const fileRow = route.file_id ? store.getFileById(route.file_id) : null;
74804
75445
  if (!fileRow) continue;
74805
75446
  if (opts.scope && !fileRow.path.startsWith(opts.scope)) continue;
74806
- const absPath = path57.resolve(projectRoot, fileRow.path);
75447
+ const absPath = path58.resolve(projectRoot, fileRow.path);
74807
75448
  let source;
74808
75449
  try {
74809
- source = readFileSync7(absPath, "utf-8");
75450
+ source = readFileSync8(absPath, "utf-8");
74810
75451
  } catch {
74811
75452
  continue;
74812
75453
  }
@@ -74847,7 +75488,7 @@ function exportSecurityContext(store, projectRoot, opts = {}) {
74847
75488
  const sensitiveFlows = [];
74848
75489
  const mcpServerFiles = new Set(toolRegistrations.map((t) => t.file));
74849
75490
  if (mcpServerFiles.size > 0) {
74850
- const mcpDirs = [...new Set([...mcpServerFiles].map((f) => path57.dirname(f)))];
75491
+ const mcpDirs = [...new Set([...mcpServerFiles].map((f) => path58.dirname(f)))];
74851
75492
  for (const dir of mcpDirs) {
74852
75493
  const taintResult = taintAnalysis(store, projectRoot, {
74853
75494
  scope: dir,
@@ -75110,7 +75751,7 @@ import { z as z13 } from "zod";
75110
75751
 
75111
75752
  // src/tools/ai/ai-tools.ts
75112
75753
  import { z as z11 } from "zod";
75113
- import path58 from "path";
75754
+ import path59 from "path";
75114
75755
  function j(value) {
75115
75756
  return JSON.stringify(value);
75116
75757
  }
@@ -75126,7 +75767,7 @@ function symbolToContextItem(sym, file, projectRoot, score = 1) {
75126
75767
  }
75127
75768
  function readSourceSafe(filePath, byteStart, byteEnd, projectRoot, gitignored) {
75128
75769
  try {
75129
- const absPath = path58.resolve(projectRoot, filePath);
75770
+ const absPath = path59.resolve(projectRoot, filePath);
75130
75771
  return readByteRange(absPath, byteStart, byteEnd, gitignored);
75131
75772
  } catch {
75132
75773
  return null;
@@ -75442,22 +76083,22 @@ function registerAITools(server, ctx) {
75442
76083
  // src/bundles.ts
75443
76084
  init_logger();
75444
76085
  import Database5 from "better-sqlite3";
75445
- import path59 from "path";
75446
- import fs49 from "fs";
76086
+ import path60 from "path";
76087
+ import fs50 from "fs";
75447
76088
  import crypto6 from "crypto";
75448
- var BUNDLES_DIR = path59.join(TRACE_MCP_HOME, "bundles");
76089
+ var BUNDLES_DIR = path60.join(TRACE_MCP_HOME, "bundles");
75449
76090
  function getBundlePath(packageName, version2) {
75450
76091
  const safeName = packageName.replace(/[^a-zA-Z0-9._-]/g, "_");
75451
- return path59.join(BUNDLES_DIR, `${safeName}-${version2}.bundle.db`);
76092
+ return path60.join(BUNDLES_DIR, `${safeName}-${version2}.bundle.db`);
75452
76093
  }
75453
76094
  function getManifestPath() {
75454
- return path59.join(BUNDLES_DIR, "manifest.json");
76095
+ return path60.join(BUNDLES_DIR, "manifest.json");
75455
76096
  }
75456
76097
  function loadManifest() {
75457
76098
  const p = getManifestPath();
75458
- if (!fs49.existsSync(p)) return { bundles: [] };
76099
+ if (!fs50.existsSync(p)) return { bundles: [] };
75459
76100
  try {
75460
- return JSON.parse(fs49.readFileSync(p, "utf-8"));
76101
+ return JSON.parse(fs50.readFileSync(p, "utf-8"));
75461
76102
  } catch {
75462
76103
  return { bundles: [] };
75463
76104
  }
@@ -75467,7 +76108,7 @@ function listBundles() {
75467
76108
  }
75468
76109
  function loadBundle(packageName, version2) {
75469
76110
  const bundlePath = getBundlePath(packageName, version2);
75470
- if (!fs49.existsSync(bundlePath)) return null;
76111
+ if (!fs50.existsSync(bundlePath)) return null;
75471
76112
  try {
75472
76113
  const db = new Database5(bundlePath, { readonly: true });
75473
76114
  return { package: packageName, version: version2, db };
@@ -75521,8 +76162,8 @@ function searchBundles(bundles, query, opts = {}) {
75521
76162
 
75522
76163
  // src/analytics/analytics-store.ts
75523
76164
  import Database6 from "better-sqlite3";
75524
- import path60 from "path";
75525
- var ANALYTICS_DB_PATH = path60.join(TRACE_MCP_HOME, "analytics.db");
76165
+ import path61 from "path";
76166
+ var ANALYTICS_DB_PATH = path61.join(TRACE_MCP_HOME, "analytics.db");
75526
76167
  var SCHEMA_SQL = `
75527
76168
  CREATE TABLE IF NOT EXISTS sessions (
75528
76169
  id TEXT PRIMARY KEY,
@@ -75754,10 +76395,10 @@ var AnalyticsStore = class {
75754
76395
 
75755
76396
  // src/analytics/log-parser.ts
75756
76397
  init_logger();
75757
- import fs50 from "fs";
75758
- import path61 from "path";
76398
+ import fs51 from "fs";
76399
+ import path62 from "path";
75759
76400
  import os7 from "os";
75760
- var CLAUDE_PROJECTS_DIR = path61.join(os7.homedir(), ".claude", "projects");
76401
+ var CLAUDE_PROJECTS_DIR = path62.join(os7.homedir(), ".claude", "projects");
75761
76402
  var CLAW_SESSIONS_DIR_NAME = ".claw/sessions";
75762
76403
  function parseToolName(fullName) {
75763
76404
  const match = fullName.match(/^mcp__([^_]+)__(.+)$/);
@@ -75832,9 +76473,9 @@ function processToolResult(item, toolResults) {
75832
76473
  }
75833
76474
  function parseSessionFile(filePath, projectPath) {
75834
76475
  try {
75835
- const content = fs50.readFileSync(filePath, "utf-8");
76476
+ const content = fs51.readFileSync(filePath, "utf-8");
75836
76477
  const lines = content.split("\n").filter((l) => l.trim());
75837
- const sessionId = path61.basename(filePath, ".jsonl");
76478
+ const sessionId = path62.basename(filePath, ".jsonl");
75838
76479
  const toolCalls = [];
75839
76480
  const toolResults = /* @__PURE__ */ new Map();
75840
76481
  let model = "";
@@ -75924,7 +76565,7 @@ function decodeDirName(dirName) {
75924
76565
  const seg = parts[i];
75925
76566
  const candidate = base + "/" + (tail ? tail + "-" + seg : seg);
75926
76567
  try {
75927
- if (fs50.statSync(candidate).isDirectory()) {
76568
+ if (fs51.statSync(candidate).isDirectory()) {
75928
76569
  base = candidate;
75929
76570
  tail = "";
75930
76571
  continue;
@@ -75934,7 +76575,7 @@ function decodeDirName(dirName) {
75934
76575
  if (tail) {
75935
76576
  const slashCandidate = base + "/" + tail;
75936
76577
  try {
75937
- if (fs50.statSync(slashCandidate).isDirectory()) {
76578
+ if (fs51.statSync(slashCandidate).isDirectory()) {
75938
76579
  base = slashCandidate;
75939
76580
  tail = seg;
75940
76581
  continue;
@@ -75947,20 +76588,20 @@ function decodeDirName(dirName) {
75947
76588
  return tail ? base + "/" + tail : base;
75948
76589
  }
75949
76590
  function listProjectDirs() {
75950
- if (!fs50.existsSync(CLAUDE_PROJECTS_DIR)) return [];
75951
- const entries = fs50.readdirSync(CLAUDE_PROJECTS_DIR, { withFileTypes: true });
76591
+ if (!fs51.existsSync(CLAUDE_PROJECTS_DIR)) return [];
76592
+ const entries = fs51.readdirSync(CLAUDE_PROJECTS_DIR, { withFileTypes: true });
75952
76593
  return entries.filter((e) => e.isDirectory()).map((e) => ({
75953
76594
  dirName: e.name,
75954
76595
  projectPath: decodeDirName(e.name)
75955
76596
  }));
75956
76597
  }
75957
76598
  function listSessionFiles(projectDirName) {
75958
- const dir = path61.join(CLAUDE_PROJECTS_DIR, projectDirName);
75959
- if (!fs50.existsSync(dir)) return [];
75960
- const entries = fs50.readdirSync(dir, { withFileTypes: true });
76599
+ const dir = path62.join(CLAUDE_PROJECTS_DIR, projectDirName);
76600
+ if (!fs51.existsSync(dir)) return [];
76601
+ const entries = fs51.readdirSync(dir, { withFileTypes: true });
75961
76602
  return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
75962
- const filePath = path61.join(dir, e.name);
75963
- const stat = fs50.statSync(filePath);
76603
+ const filePath = path62.join(dir, e.name);
76604
+ const stat = fs51.statSync(filePath);
75964
76605
  return { filePath, mtime: stat.mtimeMs };
75965
76606
  });
75966
76607
  }
@@ -75973,15 +76614,15 @@ function listAllSessions() {
75973
76614
  }
75974
76615
  const clawProjectPaths = discoverClawProjects();
75975
76616
  for (const projectPath of clawProjectPaths) {
75976
- const sessionsDir = path61.join(projectPath, CLAW_SESSIONS_DIR_NAME);
75977
- if (!fs50.existsSync(sessionsDir)) continue;
76617
+ const sessionsDir = path62.join(projectPath, CLAW_SESSIONS_DIR_NAME);
76618
+ if (!fs51.existsSync(sessionsDir)) continue;
75978
76619
  try {
75979
- const entries = fs50.readdirSync(sessionsDir, { withFileTypes: true });
76620
+ const entries = fs51.readdirSync(sessionsDir, { withFileTypes: true });
75980
76621
  for (const e of entries) {
75981
76622
  if (!e.isFile() || !e.name.endsWith(".jsonl")) continue;
75982
- const filePath = path61.join(sessionsDir, e.name);
76623
+ const filePath = path62.join(sessionsDir, e.name);
75983
76624
  try {
75984
- const stat = fs50.statSync(filePath);
76625
+ const stat = fs51.statSync(filePath);
75985
76626
  results.push({ filePath, projectPath, client: "claw-code", mtime: stat.mtimeMs });
75986
76627
  } catch {
75987
76628
  }
@@ -75994,25 +76635,25 @@ function listAllSessions() {
75994
76635
  function discoverClawProjects() {
75995
76636
  const paths = /* @__PURE__ */ new Set();
75996
76637
  for (const { projectPath } of listProjectDirs()) {
75997
- if (fs50.existsSync(path61.join(projectPath, CLAW_SESSIONS_DIR_NAME))) {
76638
+ if (fs51.existsSync(path62.join(projectPath, CLAW_SESSIONS_DIR_NAME))) {
75998
76639
  paths.add(projectPath);
75999
76640
  }
76000
76641
  }
76001
76642
  const cwd = process.cwd();
76002
- if (fs50.existsSync(path61.join(cwd, CLAW_SESSIONS_DIR_NAME))) {
76643
+ if (fs51.existsSync(path62.join(cwd, CLAW_SESSIONS_DIR_NAME))) {
76003
76644
  paths.add(cwd);
76004
76645
  }
76005
76646
  const home = os7.homedir();
76006
76647
  const commonRoots = ["Projects", "projects", "dev", "workspace", "code", "PhpstormProjects", "WebstormProjects", "src"];
76007
76648
  for (const root of commonRoots) {
76008
- const rootDir = path61.join(home, root);
76009
- if (!fs50.existsSync(rootDir)) continue;
76649
+ const rootDir = path62.join(home, root);
76650
+ if (!fs51.existsSync(rootDir)) continue;
76010
76651
  try {
76011
- const entries = fs50.readdirSync(rootDir, { withFileTypes: true });
76652
+ const entries = fs51.readdirSync(rootDir, { withFileTypes: true });
76012
76653
  for (const e of entries) {
76013
76654
  if (!e.isDirectory()) continue;
76014
- const projectDir = path61.join(rootDir, e.name);
76015
- if (fs50.existsSync(path61.join(projectDir, CLAW_SESSIONS_DIR_NAME))) {
76655
+ const projectDir = path62.join(rootDir, e.name);
76656
+ if (fs51.existsSync(path62.join(projectDir, CLAW_SESSIONS_DIR_NAME))) {
76016
76657
  paths.add(projectDir);
76017
76658
  }
76018
76659
  }
@@ -76717,8 +77358,8 @@ function formatBenchmarkMarkdown(result) {
76717
77358
  }
76718
77359
 
76719
77360
  // src/analytics/tech-detector.ts
76720
- import fs51 from "fs";
76721
- import path62 from "path";
77361
+ import fs52 from "fs";
77362
+ import path63 from "path";
76722
77363
 
76723
77364
  // src/analytics/known-packages.ts
76724
77365
  var KNOWN_PACKAGES = {
@@ -78229,7 +78870,7 @@ var KNOWN_PACKAGES = {
78229
78870
  // src/analytics/tech-detector.ts
78230
78871
  function parsePackageJson(filePath) {
78231
78872
  try {
78232
- const pkg = JSON.parse(fs51.readFileSync(filePath, "utf-8"));
78873
+ const pkg = JSON.parse(fs52.readFileSync(filePath, "utf-8"));
78233
78874
  const deps = [];
78234
78875
  for (const [name, version2] of Object.entries(pkg.dependencies ?? {})) {
78235
78876
  deps.push({ name, version: String(version2), isDev: false });
@@ -78244,7 +78885,7 @@ function parsePackageJson(filePath) {
78244
78885
  }
78245
78886
  function parseComposerJson(filePath) {
78246
78887
  try {
78247
- const pkg = JSON.parse(fs51.readFileSync(filePath, "utf-8"));
78888
+ const pkg = JSON.parse(fs52.readFileSync(filePath, "utf-8"));
78248
78889
  const deps = [];
78249
78890
  for (const [name, version2] of Object.entries(pkg.require ?? {})) {
78250
78891
  if (name === "php" || name.startsWith("ext-")) continue;
@@ -78260,7 +78901,7 @@ function parseComposerJson(filePath) {
78260
78901
  }
78261
78902
  function parseRequirementsTxt(filePath) {
78262
78903
  try {
78263
- const content = fs51.readFileSync(filePath, "utf-8");
78904
+ const content = fs52.readFileSync(filePath, "utf-8");
78264
78905
  const deps = [];
78265
78906
  for (const line of content.split("\n")) {
78266
78907
  const trimmed = line.trim();
@@ -78277,7 +78918,7 @@ function parseRequirementsTxt(filePath) {
78277
78918
  }
78278
78919
  function parsePyprojectToml(filePath) {
78279
78920
  try {
78280
- const content = fs51.readFileSync(filePath, "utf-8");
78921
+ const content = fs52.readFileSync(filePath, "utf-8");
78281
78922
  const deps = [];
78282
78923
  const depSection = content.match(/\[project\][\s\S]*?dependencies\s*=\s*\[([\s\S]*?)\]/);
78283
78924
  if (depSection) {
@@ -78293,7 +78934,7 @@ function parsePyprojectToml(filePath) {
78293
78934
  }
78294
78935
  function parseGoMod(filePath) {
78295
78936
  try {
78296
- const content = fs51.readFileSync(filePath, "utf-8");
78937
+ const content = fs52.readFileSync(filePath, "utf-8");
78297
78938
  const deps = [];
78298
78939
  const requireBlock = content.match(/require\s*\(([\s\S]*?)\)/);
78299
78940
  if (requireBlock) {
@@ -78313,7 +78954,7 @@ function parseGoMod(filePath) {
78313
78954
  }
78314
78955
  function parseGemfile(filePath) {
78315
78956
  try {
78316
- const content = fs51.readFileSync(filePath, "utf-8");
78957
+ const content = fs52.readFileSync(filePath, "utf-8");
78317
78958
  const deps = [];
78318
78959
  for (const line of content.split("\n")) {
78319
78960
  const m = line.match(/gem\s+['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]*)['""])?/);
@@ -78382,8 +79023,8 @@ function detectCoverage(projectRoot, opts = {}) {
78382
79023
  const manifestsFound = [];
78383
79024
  const allDeps = [];
78384
79025
  for (const { file, ecosystem, parser } of MANIFEST_PARSERS) {
78385
- const filePath = path62.join(projectRoot, file);
78386
- if (!fs51.existsSync(filePath)) continue;
79026
+ const filePath = path63.join(projectRoot, file);
79027
+ if (!fs52.existsSync(filePath)) continue;
78387
79028
  manifestsFound.push(file);
78388
79029
  const rawDeps = parser(filePath);
78389
79030
  for (const raw of rawDeps) {
@@ -79872,8 +80513,8 @@ function registerSessionTools(server, ctx) {
79872
80513
  import { z as z14 } from "zod";
79873
80514
 
79874
80515
  // src/memory/conversation-miner.ts
79875
- import * as fs52 from "fs";
79876
- import * as path63 from "path";
80516
+ import * as fs53 from "fs";
80517
+ import * as path64 from "path";
79877
80518
  init_logger();
79878
80519
  var DECISION_PATTERNS = [
79879
80520
  // Architecture decisions: "decided to", "we'll use", "going with", "chose X over Y"
@@ -79948,7 +80589,7 @@ function truncateTitle(s) {
79948
80589
  return clean.slice(0, 77) + "...";
79949
80590
  }
79950
80591
  function parseConversationTurns(filePath) {
79951
- const content = fs52.readFileSync(filePath, "utf-8");
80592
+ const content = fs53.readFileSync(filePath, "utf-8");
79952
80593
  const lines = content.split("\n").filter((l) => l.trim());
79953
80594
  const turns = [];
79954
80595
  for (const line of lines) {
@@ -80101,7 +80742,7 @@ function mineSessions(decisionStore, opts = {}) {
80101
80742
  file_path: d.file_path,
80102
80743
  tags: d.tags,
80103
80744
  valid_from: d.timestamp,
80104
- session_id: path63.basename(session.filePath, ".jsonl"),
80745
+ session_id: path64.basename(session.filePath, ".jsonl"),
80105
80746
  source: "mined",
80106
80747
  confidence: d.confidence
80107
80748
  }));
@@ -80126,8 +80767,8 @@ function mineSessions(decisionStore, opts = {}) {
80126
80767
  }
80127
80768
 
80128
80769
  // src/memory/session-indexer.ts
80129
- import * as fs53 from "fs";
80130
- import * as path64 from "path";
80770
+ import * as fs54 from "fs";
80771
+ import * as path65 from "path";
80131
80772
  init_logger();
80132
80773
  var MAX_CHUNK_CHARS = 2e3;
80133
80774
  var MIN_MESSAGE_CHARS = 50;
@@ -80166,9 +80807,9 @@ function truncateChunk(text) {
80166
80807
  return truncated + "...";
80167
80808
  }
80168
80809
  function indexSessionFile(filePath, projectPath, decisionStore) {
80169
- const content = fs53.readFileSync(filePath, "utf-8");
80810
+ const content = fs54.readFileSync(filePath, "utf-8");
80170
80811
  const lines = content.split("\n").filter((l) => l.trim());
80171
- const sessionId = path64.basename(filePath, ".jsonl");
80812
+ const sessionId = path65.basename(filePath, ".jsonl");
80172
80813
  const chunks = [];
80173
80814
  let chunkIndex = 0;
80174
80815
  for (const line of lines) {
@@ -80219,7 +80860,7 @@ function indexSessions(decisionStore, opts = {}) {
80219
80860
  skipped++;
80220
80861
  continue;
80221
80862
  }
80222
- const sessionId = path64.basename(session.filePath, ".jsonl");
80863
+ const sessionId = path65.basename(session.filePath, ".jsonl");
80223
80864
  if (!opts.force && decisionStore.isSessionIndexed(sessionId)) {
80224
80865
  skipped++;
80225
80866
  continue;
@@ -80244,7 +80885,7 @@ function indexSessions(decisionStore, opts = {}) {
80244
80885
  }
80245
80886
 
80246
80887
  // src/memory/wake-up.ts
80247
- import * as path65 from "path";
80888
+ import * as path66 from "path";
80248
80889
  function compactDecision(d) {
80249
80890
  const entry = {
80250
80891
  id: d.id,
@@ -80268,7 +80909,7 @@ function assembleWakeUp(decisionStore, projectRoot, opts = {}) {
80268
80909
  const indexedSessions = decisionStore.getIndexedSessionIds(projectRoot);
80269
80910
  const result = {
80270
80911
  project: {
80271
- name: path65.basename(projectRoot),
80912
+ name: path66.basename(projectRoot),
80272
80913
  root: projectRoot
80273
80914
  },
80274
80915
  decisions: {
@@ -80715,6 +81356,45 @@ var TopologyStore = class {
80715
81356
  `);
80716
81357
  logger.info("Migration: rebuilt subprojects with project_root column, old data cleared");
80717
81358
  }
81359
+ this.runOnce("clean_duplicate_contracts_v1", () => {
81360
+ const services = this.db.prepare(
81361
+ `SELECT DISTINCT service_id FROM api_contracts WHERE contract_type = 'framework_routes'`
81362
+ ).all();
81363
+ let deletedContracts = 0;
81364
+ let deletedEndpoints = 0;
81365
+ for (const { service_id } of services) {
81366
+ const latest = this.db.prepare(
81367
+ `SELECT id FROM api_contracts WHERE service_id = ? AND contract_type = 'framework_routes' ORDER BY id DESC LIMIT 1`
81368
+ ).get(service_id);
81369
+ if (!latest) continue;
81370
+ const result = this.db.prepare(
81371
+ `DELETE FROM api_contracts WHERE service_id = ? AND contract_type = 'framework_routes' AND id != ?`
81372
+ ).run(service_id, latest.id);
81373
+ deletedContracts += result.changes;
81374
+ const httpMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "ANY"];
81375
+ const placeholders = httpMethods.map(() => "?").join(",");
81376
+ const epResult = this.db.prepare(
81377
+ `DELETE FROM api_endpoints WHERE contract_id = ? AND method IS NOT NULL AND method NOT IN (${placeholders})`
81378
+ ).run(latest.id, ...httpMethods);
81379
+ deletedEndpoints += epResult.changes;
81380
+ }
81381
+ if (deletedContracts > 0 || deletedEndpoints > 0) {
81382
+ logger.info({ deletedContracts, deletedEndpoints }, "Migration: cleaned duplicate contracts and non-HTTP endpoints");
81383
+ }
81384
+ });
81385
+ this.runOnce("rebuild_cross_service_edges_v1", () => {
81386
+ const result = this.db.prepare("DELETE FROM cross_service_edges").run();
81387
+ if (result.changes > 0) {
81388
+ logger.info({ deleted: result.changes }, "Migration: cleared stale cross-service edges for rebuild");
81389
+ }
81390
+ });
81391
+ }
81392
+ /** Run a migration block exactly once, tracked by key in topology_meta. */
81393
+ runOnce(key, fn2) {
81394
+ const existing = this.db.prepare("SELECT value FROM topology_meta WHERE key = ?").get(key);
81395
+ if (existing) return;
81396
+ fn2();
81397
+ this.db.prepare("INSERT OR REPLACE INTO topology_meta (key, value) VALUES (?, ?)").run(key, (/* @__PURE__ */ new Date()).toISOString());
80718
81398
  }
80719
81399
  close() {
80720
81400
  this.db.close();
@@ -80760,6 +81440,15 @@ var TopologyStore = class {
80760
81440
  getAllServices() {
80761
81441
  return this.db.prepare("SELECT * FROM services ORDER BY name").all();
80762
81442
  }
81443
+ updateServiceGroup(serviceId, projectGroup) {
81444
+ this.db.prepare("UPDATE services SET project_group = ? WHERE id = ?").run(projectGroup, serviceId);
81445
+ }
81446
+ getServicesWithEndpointCounts() {
81447
+ return this.db.prepare(`
81448
+ SELECT s.*, (SELECT COUNT(*) FROM api_endpoints WHERE service_id = s.id) as endpoint_count
81449
+ FROM services s ORDER BY s.project_group NULLS LAST, s.name
81450
+ `).all();
81451
+ }
80763
81452
  deleteService(id) {
80764
81453
  this.db.prepare("DELETE FROM services WHERE id = ?").run(id);
80765
81454
  }
@@ -81038,13 +81727,29 @@ var TopologyStore = class {
81038
81727
  "SELECT * FROM client_calls WHERE matched_endpoint_id IS NULL"
81039
81728
  ).all();
81040
81729
  const endpoints = this.getAllEndpoints();
81730
+ const services = this.getAllServices();
81731
+ const serviceGroup = /* @__PURE__ */ new Map();
81732
+ for (const svc of services) {
81733
+ serviceGroup.set(svc.id, svc.project_group ?? null);
81734
+ }
81735
+ const repoGroup = /* @__PURE__ */ new Map();
81736
+ const allRepos2 = this.getAllSubprojects();
81737
+ for (const repo of allRepos2) {
81738
+ const svc = services.find((s) => s.repo_root === repo.repo_root);
81739
+ repoGroup.set(repo.id, svc?.project_group ?? null);
81740
+ }
81041
81741
  let linked = 0;
81042
81742
  const updateStmt = this.db.prepare(
81043
81743
  "UPDATE client_calls SET matched_endpoint_id = ?, target_repo_id = ?, confidence = ? WHERE id = ?"
81044
81744
  );
81045
81745
  this.db.transaction(() => {
81046
81746
  for (const call of unlinked) {
81047
- const match = findBestEndpointMatch(call.url_pattern, call.method, endpoints);
81747
+ const sourceGroup = repoGroup.get(call.source_repo_id);
81748
+ const candidateEndpoints = sourceGroup ? endpoints.filter((ep) => {
81749
+ const epGroup = serviceGroup.get(ep.service_id);
81750
+ return epGroup === sourceGroup || !epGroup || !sourceGroup;
81751
+ }) : endpoints;
81752
+ const match = findBestEndpointMatch(call.url_pattern, call.method, candidateEndpoints);
81048
81753
  if (match) {
81049
81754
  const svc = this.db.prepare("SELECT repo_root FROM services WHERE id = ?").get(match.service_id);
81050
81755
  const targetRepo = svc ? this.db.prepare("SELECT id FROM subprojects WHERE repo_root = ?").get(svc.repo_root) : void 0;
@@ -81466,7 +82171,7 @@ var DecisionStore = class {
81466
82171
  };
81467
82172
 
81468
82173
  // src/server/server.ts
81469
- var PKG_VERSION2 = true ? "1.22.0" : "0.0.0-dev";
82174
+ var PKG_VERSION2 = true ? "1.23.0" : "0.0.0-dev";
81470
82175
  function j2(value) {
81471
82176
  return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
81472
82177
  }
@@ -81891,11 +82596,11 @@ var PluginRegistry = class {
81891
82596
  // src/config.ts
81892
82597
  import { cosmiconfig } from "cosmiconfig";
81893
82598
  import { z as z15 } from "zod";
81894
- import fs55 from "fs";
82599
+ import fs56 from "fs";
81895
82600
  init_logger();
81896
82601
 
81897
82602
  // src/config-jsonc.ts
81898
- import fs54 from "fs";
82603
+ import fs55 from "fs";
81899
82604
  import { modify, applyEdits, parse as parse2 } from "jsonc-parser";
81900
82605
  init_logger();
81901
82606
 
@@ -81917,7 +82622,7 @@ var FrameworkConfigSchema = z15.object({
81917
82622
  }).optional();
81918
82623
  var AiConfigSchema = z15.object({
81919
82624
  enabled: z15.boolean().default(false),
81920
- provider: z15.enum(["onnx", "ollama", "openai"]).default("onnx"),
82625
+ provider: z15.enum(["onnx", "ollama", "openai", "anthropic", "lmstudio", "gemini", "mistral", "deepseek", "groq", "together", "xai"]).default("onnx"),
81921
82626
  base_url: z15.string().optional(),
81922
82627
  api_key: z15.string().optional(),
81923
82628
  inference_model: z15.string().optional(),
@@ -82129,9 +82834,9 @@ var TraceMcpConfigSchema = z15.object({
82129
82834
  children: z15.array(z15.string()).optional()
82130
82835
  });
82131
82836
  function loadGlobalConfigRaw() {
82132
- if (!fs55.existsSync(GLOBAL_CONFIG_PATH)) return {};
82837
+ if (!fs56.existsSync(GLOBAL_CONFIG_PATH)) return {};
82133
82838
  try {
82134
- return JSON.parse(stripJsonComments(fs55.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
82839
+ return JSON.parse(stripJsonComments(fs56.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
82135
82840
  } catch {
82136
82841
  return {};
82137
82842
  }