trace-mcp 1.5.3 → 1.6.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.d.ts CHANGED
@@ -1546,6 +1546,7 @@ declare const TraceMcpConfigSchema: z.ZodObject<{
1546
1546
  enabled?: boolean | undefined;
1547
1547
  debounceMs?: number | undefined;
1548
1548
  }>>;
1549
+ children: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1549
1550
  }, "strip", z.ZodTypeAny, {
1550
1551
  root: string;
1551
1552
  db: {
@@ -1719,6 +1720,7 @@ declare const TraceMcpConfigSchema: z.ZodObject<{
1719
1720
  exclude?: string[] | undefined;
1720
1721
  descriptions?: Record<string, string | Record<string, string>> | undefined;
1721
1722
  } | undefined;
1723
+ children?: string[] | undefined;
1722
1724
  }, {
1723
1725
  root?: string | undefined;
1724
1726
  db?: {
@@ -1892,6 +1894,7 @@ declare const TraceMcpConfigSchema: z.ZodObject<{
1892
1894
  enabled?: boolean | undefined;
1893
1895
  debounceMs?: number | undefined;
1894
1896
  } | undefined;
1897
+ children?: string[] | undefined;
1895
1898
  }>;
1896
1899
  type TraceMcpConfig = z.infer<typeof TraceMcpConfigSchema>;
1897
1900
  /**
package/dist/index.js CHANGED
@@ -2498,6 +2498,22 @@ function detectWorkspaces(rootPath) {
2498
2498
  if (composerResult.length > 0) return composerResult;
2499
2499
  return [];
2500
2500
  }
2501
+ function buildMultiRootWorkspaces(parentDir, childRoots) {
2502
+ const workspaces = [];
2503
+ for (const childRoot of childRoots) {
2504
+ const relPath = path2.relative(parentDir, childRoot).replace(/\\/g, "/");
2505
+ const childName = path2.basename(childRoot);
2506
+ workspaces.push({ name: childName, path: relPath });
2507
+ const subWorkspaces = detectWorkspaces(childRoot);
2508
+ for (const sub of subWorkspaces) {
2509
+ workspaces.push({
2510
+ name: `${childName}/${sub.name}`,
2511
+ path: `${relPath}/${sub.path}`
2512
+ });
2513
+ }
2514
+ }
2515
+ return workspaces;
2516
+ }
2501
2517
  function detectPnpmWorkspaces(rootPath) {
2502
2518
  const yamlPath = path2.join(rootPath, "pnpm-workspace.yaml");
2503
2519
  if (!fs2.existsSync(yamlPath)) return [];
@@ -5902,9 +5918,14 @@ var IndexingPipeline = class _IndexingPipeline {
5902
5918
  const result = this._lock.then(async () => {
5903
5919
  this._isIncremental = false;
5904
5920
  const start = Date.now();
5905
- this.workspaces = detectWorkspaces(this.rootPath);
5906
- if (this.workspaces.length > 0) {
5907
- logger.info({ workspaces: this.workspaces.map((w) => w.name) }, "Detected workspaces");
5921
+ if (this.config.children?.length) {
5922
+ this.workspaces = buildMultiRootWorkspaces(this.rootPath, this.config.children);
5923
+ logger.info({ workspaces: this.workspaces.map((w) => w.name) }, "Multi-root workspaces");
5924
+ } else {
5925
+ this.workspaces = detectWorkspaces(this.rootPath);
5926
+ if (this.workspaces.length > 0) {
5927
+ logger.info({ workspaces: this.workspaces.map((w) => w.name) }, "Detected workspaces");
5928
+ }
5908
5929
  }
5909
5930
  const filePaths = await this.collectFiles();
5910
5931
  return this.runPipeline(filePaths, force ?? false, start);
@@ -8036,6 +8057,14 @@ function buildInstructions(detectedFrameworks, verbosity) {
8036
8057
  "Before creating new functions/classes:",
8037
8058
  '- `check_duplication` { name: "functionName", kind: "function" } \u2014 checks if similar symbols already exist. Use BEFORE writing new code to avoid reinventing existing logic.',
8038
8059
  "",
8060
+ "Bulk mechanical changes (adding async/await, updating patterns, fixing imports across many files):",
8061
+ "- `apply_codemod` { pattern, replacement, file_pattern } \u2014 regex find-and-replace across files. Dry-run by default (shows preview). Two-step workflow:",
8062
+ " 1. Call with dry_run: true (default) \u2192 review preview with context lines",
8063
+ " 2. Call with dry_run: false \u2192 apply changes. Requires confirm_large: true if >20 files affected.",
8064
+ "- Use `filter_content` to narrow scope to files containing a specific substring.",
8065
+ "- Use `multiline: true` for patterns spanning multiple lines.",
8066
+ "- NEVER use dozens of Edit calls for the same regex replacement \u2014 use apply_codemod instead.",
8067
+ "",
8039
8068
  "WHEN TO USE native tools (Read/Grep/Glob):",
8040
8069
  "- Non-code files (.md, .json, .yaml, .toml, config) \u2192 Read/Grep",
8041
8070
  "- Reading a file before editing (Edit needs full content) \u2192 Read",
@@ -16974,6 +17003,7 @@ import { z as z6 } from "zod";
16974
17003
  // src/tools/refactoring/refactor.ts
16975
17004
  import fs23 from "fs";
16976
17005
  import path35 from "path";
17006
+ import fg4 from "fast-glob";
16977
17007
  function readLines2(filePath) {
16978
17008
  return fs23.readFileSync(filePath, "utf-8").split("\n");
16979
17009
  }
@@ -17307,6 +17337,193 @@ ${functionDef}`
17307
17337
  }
17308
17338
  return result;
17309
17339
  }
17340
+ var CODEMOD_MAX_PREVIEW = 20;
17341
+ var CODEMOD_LARGE_THRESHOLD = 20;
17342
+ var CODEMOD_CONTEXT_LINES = 2;
17343
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
17344
+ ".png",
17345
+ ".jpg",
17346
+ ".jpeg",
17347
+ ".gif",
17348
+ ".bmp",
17349
+ ".ico",
17350
+ ".svg",
17351
+ ".webp",
17352
+ ".woff",
17353
+ ".woff2",
17354
+ ".ttf",
17355
+ ".eot",
17356
+ ".otf",
17357
+ ".zip",
17358
+ ".tar",
17359
+ ".gz",
17360
+ ".bz2",
17361
+ ".7z",
17362
+ ".rar",
17363
+ ".pdf",
17364
+ ".doc",
17365
+ ".docx",
17366
+ ".xls",
17367
+ ".xlsx",
17368
+ ".mp3",
17369
+ ".mp4",
17370
+ ".avi",
17371
+ ".mov",
17372
+ ".wav",
17373
+ ".exe",
17374
+ ".dll",
17375
+ ".so",
17376
+ ".dylib",
17377
+ ".o",
17378
+ ".a",
17379
+ ".wasm",
17380
+ ".pyc",
17381
+ ".class"
17382
+ ]);
17383
+ var SKIP_DIRS = ["node_modules", ".git", "dist", "build", "vendor", "__pycache__", ".next", ".nuxt"];
17384
+ function applyCodemod(projectRoot, pattern, replacement, filePattern, options) {
17385
+ const result = {
17386
+ success: false,
17387
+ tool: "apply_codemod",
17388
+ dry_run: options.dryRun,
17389
+ matches: [],
17390
+ files_modified: [],
17391
+ total_replacements: 0,
17392
+ total_files: 0,
17393
+ warnings: []
17394
+ };
17395
+ let regex;
17396
+ try {
17397
+ const flags = options.multiline ? "gms" : "gm";
17398
+ regex = new RegExp(pattern, flags);
17399
+ } catch (e) {
17400
+ result.error = `Invalid regex pattern: ${e.message}`;
17401
+ return result;
17402
+ }
17403
+ let files;
17404
+ try {
17405
+ files = fg4.sync(filePattern, {
17406
+ cwd: projectRoot,
17407
+ ignore: SKIP_DIRS.map((d) => `**/${d}/**`),
17408
+ onlyFiles: true,
17409
+ absolute: false
17410
+ });
17411
+ } catch (e) {
17412
+ result.error = `Invalid file pattern: ${e.message}`;
17413
+ return result;
17414
+ }
17415
+ files = files.filter((f) => !BINARY_EXTENSIONS.has(path35.extname(f).toLowerCase()));
17416
+ if (files.length === 0) {
17417
+ result.error = `No files matched pattern: ${filePattern}`;
17418
+ return result;
17419
+ }
17420
+ const allMatches = [];
17421
+ const filesWithMatches = /* @__PURE__ */ new Set();
17422
+ for (const relPath of files) {
17423
+ const absPath = path35.resolve(projectRoot, relPath);
17424
+ if (!fs23.existsSync(absPath)) continue;
17425
+ let content;
17426
+ try {
17427
+ content = fs23.readFileSync(absPath, "utf-8");
17428
+ } catch {
17429
+ result.warnings.push(`Could not read: ${relPath}`);
17430
+ continue;
17431
+ }
17432
+ if (options.filterContent && !content.includes(options.filterContent)) {
17433
+ continue;
17434
+ }
17435
+ const lines = content.split("\n");
17436
+ if (options.multiline) {
17437
+ regex.lastIndex = 0;
17438
+ if (!regex.test(content)) continue;
17439
+ filesWithMatches.add(relPath);
17440
+ regex.lastIndex = 0;
17441
+ let matchCount = 0;
17442
+ const matchPositions = [];
17443
+ let m;
17444
+ while ((m = regex.exec(content)) !== null) {
17445
+ matchPositions.push({ index: m.index, match: m[0] });
17446
+ matchCount++;
17447
+ if (m[0].length === 0) {
17448
+ regex.lastIndex++;
17449
+ }
17450
+ }
17451
+ for (const pos of matchPositions.slice(0, CODEMOD_MAX_PREVIEW - allMatches.length)) {
17452
+ const lineNum = content.slice(0, pos.index).split("\n").length;
17453
+ const original = pos.match;
17454
+ regex.lastIndex = 0;
17455
+ const replaced = original.replace(regex, replacement);
17456
+ allMatches.push({
17457
+ file: relPath,
17458
+ line: lineNum,
17459
+ original: original.length > 200 ? original.slice(0, 200) + "\u2026" : original,
17460
+ replaced: replaced.length > 200 ? replaced.slice(0, 200) + "\u2026" : replaced,
17461
+ context_before: lines.slice(Math.max(0, lineNum - 1 - CODEMOD_CONTEXT_LINES), lineNum - 1),
17462
+ context_after: lines.slice(lineNum, lineNum + CODEMOD_CONTEXT_LINES)
17463
+ });
17464
+ }
17465
+ result.total_replacements += matchCount;
17466
+ } else {
17467
+ let fileMatchCount = 0;
17468
+ for (let i = 0; i < lines.length; i++) {
17469
+ regex.lastIndex = 0;
17470
+ if (!regex.test(lines[i])) continue;
17471
+ filesWithMatches.add(relPath);
17472
+ fileMatchCount++;
17473
+ regex.lastIndex = 0;
17474
+ const newLine = lines[i].replace(regex, replacement);
17475
+ if (allMatches.length < CODEMOD_MAX_PREVIEW) {
17476
+ allMatches.push({
17477
+ file: relPath,
17478
+ line: i + 1,
17479
+ original: lines[i],
17480
+ replaced: newLine,
17481
+ context_before: lines.slice(Math.max(0, i - CODEMOD_CONTEXT_LINES), i),
17482
+ context_after: lines.slice(i + 1, i + 1 + CODEMOD_CONTEXT_LINES)
17483
+ });
17484
+ }
17485
+ }
17486
+ result.total_replacements += fileMatchCount;
17487
+ }
17488
+ }
17489
+ result.total_files = filesWithMatches.size;
17490
+ if (allMatches.length === 0) {
17491
+ result.error = `No matches found for pattern in ${files.length} files`;
17492
+ return result;
17493
+ }
17494
+ if (filesWithMatches.size > CODEMOD_LARGE_THRESHOLD && !options.confirmLarge) {
17495
+ result.matches = allMatches;
17496
+ result.warnings.push(
17497
+ `Affects ${filesWithMatches.size} files (>${CODEMOD_LARGE_THRESHOLD}). Re-run with confirm_large: true to proceed, or narrow file_pattern.`
17498
+ );
17499
+ result.dry_run = true;
17500
+ result.success = true;
17501
+ return result;
17502
+ }
17503
+ if (options.dryRun) {
17504
+ result.matches = allMatches;
17505
+ result.success = true;
17506
+ return result;
17507
+ }
17508
+ for (const relPath of filesWithMatches) {
17509
+ const absPath = path35.resolve(projectRoot, relPath);
17510
+ try {
17511
+ const content = fs23.readFileSync(absPath, "utf-8");
17512
+ const flags = options.multiline ? "gms" : "gm";
17513
+ const freshRegex = new RegExp(pattern, flags);
17514
+ const newContent = content.replace(freshRegex, replacement);
17515
+ if (newContent !== content) {
17516
+ fs23.writeFileSync(absPath, newContent, "utf-8");
17517
+ result.files_modified.push(relPath);
17518
+ }
17519
+ } catch (e) {
17520
+ result.warnings.push(`Failed to write ${relPath}: ${e.message}`);
17521
+ }
17522
+ }
17523
+ result.matches = allMatches;
17524
+ result.success = true;
17525
+ return result;
17526
+ }
17310
17527
  function detectLanguage2(ext) {
17311
17528
  switch (ext) {
17312
17529
  case ".ts":
@@ -17543,6 +17760,31 @@ function registerRefactoringTools(server, ctx) {
17543
17760
  return { content: [{ type: "text", text: j3(result) }] };
17544
17761
  }
17545
17762
  );
17763
+ server.tool(
17764
+ "apply_codemod",
17765
+ "Bulk regex find-and-replace across files. Dry-run by default \u2014 first call shows preview, second call with dry_run=false applies. Use for mechanical changes like adding async/await, renaming patterns, updating imports across many files.",
17766
+ {
17767
+ pattern: z6.string().min(1).max(1e3).describe("Regex pattern to match (JavaScript regex syntax)"),
17768
+ replacement: z6.string().max(1e3).describe("Replacement string ($1, $2 for capture groups)"),
17769
+ file_pattern: z6.string().min(1).max(512).describe('Glob pattern for files to scan (e.g. "tests/**/*.test.ts", "src/**/*.py")'),
17770
+ dry_run: z6.boolean().default(true).describe("Preview changes without writing (default: true). Set to false to apply."),
17771
+ confirm_large: z6.boolean().optional().describe("Required when >20 files affected. Acknowledges large-scale change."),
17772
+ filter_content: z6.string().max(500).optional().describe("Only process files containing this substring (narrows scope)"),
17773
+ multiline: z6.boolean().optional().describe("Enable multiline mode (dot matches newlines, patterns span lines)")
17774
+ },
17775
+ async ({ pattern, replacement, file_pattern, dry_run, confirm_large, filter_content, multiline }) => {
17776
+ const result = applyCodemod(projectRoot, pattern, replacement, file_pattern, {
17777
+ dryRun: dry_run,
17778
+ confirmLarge: confirm_large,
17779
+ filterContent: filter_content,
17780
+ multiline
17781
+ });
17782
+ if (!result.success) {
17783
+ return { content: [{ type: "text", text: j3(result) }], isError: true };
17784
+ }
17785
+ return { content: [{ type: "text", text: j3(result) }] };
17786
+ }
17787
+ );
17546
17788
  }
17547
17789
 
17548
17790
  // src/tools/register/advanced.ts
@@ -26515,6 +26757,8 @@ var KNOWN_PACKAGES = {
26515
26757
  "@clack/prompts": { category: "infra", priority: "medium", plugin: "clack" },
26516
26758
  "@clack/core": { category: "infra", priority: "medium", plugin: "clack" },
26517
26759
  "tree-sitter": { category: "infra", priority: "medium", plugin: "tree-sitter" },
26760
+ "web-tree-sitter": { category: "infra", priority: "medium", plugin: "tree-sitter" },
26761
+ "tree-sitter-wasms": { category: "infra", priority: "low", plugin: "tree-sitter" },
26518
26762
  "n8n-workflow": { category: "infra", priority: "high", plugin: "n8n" },
26519
26763
  // --- JavaScript / npm: Build tools (with plugin) ---
26520
26764
  "tsup": { category: "infra", priority: "low", plugin: "build-tools" },
@@ -27484,7 +27728,7 @@ function registerSessionTools(server, ctx) {
27484
27728
  }
27485
27729
 
27486
27730
  // src/server/server.ts
27487
- var PKG_VERSION = true ? "1.5.3" : "0.0.0-dev";
27731
+ var PKG_VERSION = true ? "1.6.0" : "0.0.0-dev";
27488
27732
  function j2(value) {
27489
27733
  return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
27490
27734
  }
@@ -29149,7 +29393,8 @@ var TraceMcpConfigSchema = z13.object({
29149
29393
  watch: z13.object({
29150
29394
  enabled: z13.boolean().default(true),
29151
29395
  debounceMs: z13.number().int().min(500).max(3e4).default(2e3)
29152
- }).default({})
29396
+ }).default({}),
29397
+ children: z13.array(z13.string()).optional()
29153
29398
  });
29154
29399
  function loadGlobalConfigRaw() {
29155
29400
  if (!fs37.existsSync(GLOBAL_CONFIG_PATH)) return {};