@evalops/maestro 0.10.38 → 0.10.39

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/cli.js CHANGED
@@ -158629,6 +158629,14 @@ var init_extract_document = __esm2(() => {
158629
158629
  }
158630
158630
  });
158631
158631
  });
158632
+ function toolInstallAbortError(config2) {
158633
+ return new Error(`${config2.name} installation aborted`);
158634
+ }
158635
+ function throwIfToolInstallAborted(config2, signal) {
158636
+ if (signal?.aborted) {
158637
+ throw toolInstallAbortError(config2);
158638
+ }
158639
+ }
158632
158640
  function commandExists2(cmd) {
158633
158641
  try {
158634
158642
  const result2 = spawnSync9(cmd, ["--version"], { stdio: "pipe" });
@@ -158650,24 +158658,38 @@ function getToolPath(tool) {
158650
158658
  }
158651
158659
  return null;
158652
158660
  }
158653
- async function fetchWithTimeout(url, init2) {
158661
+ async function fetchWithTimeout(url, init2, signal) {
158654
158662
  const controller = new AbortController();
158655
158663
  const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
158664
+ const onAbort = () => {
158665
+ controller.abort(signal?.reason);
158666
+ };
158667
+ if (signal?.aborted) {
158668
+ onAbort();
158669
+ } else {
158670
+ signal?.addEventListener("abort", onAbort, { once: true });
158671
+ }
158672
+ const clearFetchTimeout = () => clearTimeout(timeout);
158673
+ const cleanup = () => {
158674
+ clearFetchTimeout();
158675
+ signal?.removeEventListener("abort", onAbort);
158676
+ };
158656
158677
  try {
158657
158678
  const response = await fetch(url, { ...init2, signal: controller.signal });
158658
158679
  return {
158659
158680
  response,
158660
- clearTimeout: () => clearTimeout(timeout)
158681
+ clearTimeout: clearFetchTimeout,
158682
+ cleanup
158661
158683
  };
158662
158684
  } catch (error) {
158663
- clearTimeout(timeout);
158685
+ cleanup();
158664
158686
  throw error;
158665
158687
  }
158666
158688
  }
158667
- async function getLatestVersion(repo) {
158668
- const { response, clearTimeout: clearFetchTimeout } = await fetchWithTimeout(`https://api.github.com/repos/${repo}/releases/latest`, {
158689
+ async function getLatestVersion(repo, signal) {
158690
+ const { response, cleanup: cleanupFetch } = await fetchWithTimeout(`https://api.github.com/repos/${repo}/releases/latest`, {
158669
158691
  headers: { "User-Agent": "composer-coding-agent" }
158670
- });
158692
+ }, signal);
158671
158693
  try {
158672
158694
  if (!response.ok) {
158673
158695
  throw new Error(`GitHub API error: ${response.status}`);
@@ -158675,11 +158697,15 @@ async function getLatestVersion(repo) {
158675
158697
  const data = await response.json();
158676
158698
  return data.tag_name.replace(/^v/, "");
158677
158699
  } finally {
158678
- clearFetchTimeout();
158700
+ cleanupFetch();
158679
158701
  }
158680
158702
  }
158681
- async function downloadFile2(url, dest) {
158682
- const { response, clearTimeout: clearFetchTimeout } = await fetchWithTimeout(url);
158703
+ async function downloadFile2(url, dest, signal) {
158704
+ const {
158705
+ response,
158706
+ clearTimeout: clearFetchTimeout,
158707
+ cleanup: cleanupFetch
158708
+ } = await fetchWithTimeout(url, void 0, signal);
158683
158709
  try {
158684
158710
  if (!response.ok) {
158685
158711
  throw new Error(`Failed to download: ${response.status}`);
@@ -158691,6 +158717,11 @@ async function downloadFile2(url, dest) {
158691
158717
  const stream = Readable2.fromWeb(response.body);
158692
158718
  const fileStream = createWriteStream3(dest);
158693
158719
  let idleTimeoutId = null;
158720
+ const onAbort = () => {
158721
+ const error = new Error("Tool download aborted");
158722
+ stream.destroy(error);
158723
+ fileStream.destroy(error);
158724
+ };
158694
158725
  const clearIdleTimeout = () => {
158695
158726
  if (idleTimeoutId) {
158696
158727
  clearTimeout(idleTimeoutId);
@@ -158710,6 +158741,11 @@ async function downloadFile2(url, dest) {
158710
158741
  clearIdleTimeout();
158711
158742
  };
158712
158743
  resetIdleTimeout();
158744
+ if (signal?.aborted) {
158745
+ onAbort();
158746
+ } else {
158747
+ signal?.addEventListener("abort", onAbort, { once: true });
158748
+ }
158713
158749
  stream.on("data", resetIdleTimeout);
158714
158750
  stream.on("end", onStreamEnd);
158715
158751
  stream.on("close", onStreamEnd);
@@ -158721,10 +158757,11 @@ async function downloadFile2(url, dest) {
158721
158757
  stream.off("end", onStreamEnd);
158722
158758
  stream.off("close", onStreamEnd);
158723
158759
  stream.off("error", onStreamEnd);
158760
+ signal?.removeEventListener("abort", onAbort);
158724
158761
  clearIdleTimeout();
158725
158762
  }
158726
158763
  } finally {
158727
- clearFetchTimeout();
158764
+ cleanupFetch();
158728
158765
  }
158729
158766
  }
158730
158767
  function runExtractor(command, args, description) {
@@ -158760,13 +158797,15 @@ function findBinary(root, binaryName) {
158760
158797
  }
158761
158798
  return null;
158762
158799
  }
158763
- async function downloadTool(tool) {
158800
+ async function downloadTool(tool, signal) {
158764
158801
  const config2 = TOOLS[tool];
158765
158802
  if (!config2)
158766
158803
  throw new Error(`Unknown tool: ${tool}`);
158804
+ throwIfToolInstallAborted(config2, signal);
158767
158805
  const plat = platform3();
158768
158806
  const architecture = arch2();
158769
- const version3 = await getLatestVersion(config2.repo);
158807
+ const version3 = await getLatestVersion(config2.repo, signal);
158808
+ throwIfToolInstallAborted(config2, signal);
158770
158809
  const assetName = config2.getAssetName(version3, plat, architecture);
158771
158810
  if (!assetName) {
158772
158811
  throw new Error(`Unsupported platform: ${plat}/${architecture}`);
@@ -158776,7 +158815,8 @@ async function downloadTool(tool) {
158776
158815
  const archivePath = join79(TOOLS_DIR, assetName);
158777
158816
  const binaryExt = plat === "win32" ? ".exe" : "";
158778
158817
  const binaryPath = join79(TOOLS_DIR, config2.binaryName + binaryExt);
158779
- await downloadFile2(downloadUrl, archivePath);
158818
+ await downloadFile2(downloadUrl, archivePath, signal);
158819
+ throwIfToolInstallAborted(config2, signal);
158780
158820
  const extractDir = mkdtempSync2(join79(TOOLS_DIR, "extract-"));
158781
158821
  try {
158782
158822
  if (assetName.endsWith(".tar.gz")) {
@@ -158800,24 +158840,28 @@ async function downloadTool(tool) {
158800
158840
  }
158801
158841
  return binaryPath;
158802
158842
  }
158803
- async function ensureTool(tool, silent = false) {
158843
+ async function ensureTool(tool, silent = false, signal) {
158844
+ const config2 = TOOLS[tool];
158845
+ if (!config2)
158846
+ return null;
158847
+ throwIfToolInstallAborted(config2, signal);
158804
158848
  const existingPath = getToolPath(tool);
158805
158849
  if (existingPath) {
158806
158850
  return existingPath;
158807
158851
  }
158808
- const config2 = TOOLS[tool];
158809
- if (!config2)
158810
- return null;
158811
158852
  if (!silent) {
158812
158853
  console.log(chalk62.dim(`${config2.name} not found. Downloading...`));
158813
158854
  }
158814
158855
  try {
158815
- const path3 = await downloadTool(tool);
158856
+ const path3 = await downloadTool(tool, signal);
158816
158857
  if (!silent) {
158817
158858
  console.log(chalk62.dim(`${config2.name} installed to ${path3}`));
158818
158859
  }
158819
158860
  return path3;
158820
158861
  } catch (e2) {
158862
+ if (signal?.aborted) {
158863
+ throw toolInstallAbortError(config2);
158864
+ }
158821
158865
  if (!silent) {
158822
158866
  console.log(chalk62.yellow(`Failed to download ${config2.name}: ${e2 instanceof Error ? e2.message : e2}`));
158823
158867
  }
@@ -159122,7 +159166,7 @@ var init_find = __esm2(() => {
159122
159166
  throw new Error("Operation aborted");
159123
159167
  }
159124
159168
  const { pattern, path: searchDir, limit, includeHidden = true } = params;
159125
- const fdPath = await ensureTool("fd", true);
159169
+ const fdPath = await ensureTool("fd", true, signal);
159126
159170
  if (!fdPath) {
159127
159171
  return respond.error("fd is not available and could not be downloaded").detail({
159128
159172
  command: "fd",
@@ -160329,36 +160373,81 @@ function toArray(value) {
160329
160373
  function ripgrepAbortError() {
160330
160374
  return new Error("ripgrep search aborted before start");
160331
160375
  }
160332
- async function resolveRipgrepExecutable() {
160333
- ripgrepExecutablePromise ??= ensureTool("rg", true);
160334
- const executable = await ripgrepExecutablePromise;
160335
- if (!executable) {
160336
- ripgrepExecutablePromise = null;
160337
- throw new Error("ripgrep is not available and could not be downloaded");
160376
+ function resetRipgrepExecutablePromise() {
160377
+ ripgrepExecutablePromise = null;
160378
+ ripgrepInstallController = null;
160379
+ }
160380
+ function getRipgrepExecutablePromise() {
160381
+ if (!ripgrepExecutablePromise) {
160382
+ const controller = new AbortController();
160383
+ ripgrepInstallController = controller;
160384
+ const promise = ensureTool("rg", true, controller.signal).then((executable) => {
160385
+ if (ripgrepInstallController === controller) {
160386
+ ripgrepInstallController = null;
160387
+ }
160388
+ if (ripgrepExecutablePromise === promise && !executable) {
160389
+ ripgrepExecutablePromise = null;
160390
+ }
160391
+ return executable;
160392
+ }, (error) => {
160393
+ if (ripgrepInstallController === controller) {
160394
+ ripgrepInstallController = null;
160395
+ }
160396
+ if (ripgrepExecutablePromise === promise) {
160397
+ ripgrepExecutablePromise = null;
160398
+ }
160399
+ throw error;
160400
+ });
160401
+ ripgrepExecutablePromise = promise;
160338
160402
  }
160339
- return executable;
160403
+ return ripgrepExecutablePromise;
160340
160404
  }
160341
- function throwIfRipgrepAborted(signal) {
160342
- if (signal?.aborted) {
160343
- throw ripgrepAbortError();
160405
+ function releaseRipgrepExecutableWaiter(signal) {
160406
+ ripgrepExecutableWaiters = Math.max(0, ripgrepExecutableWaiters - 1);
160407
+ if (signal?.aborted && ripgrepExecutableWaiters === 0) {
160408
+ const controller = ripgrepInstallController;
160409
+ resetRipgrepExecutablePromise();
160410
+ controller?.abort(signal.reason);
160344
160411
  }
160345
160412
  }
160346
- async function resolveRipgrepExecutableWithAbort(signal) {
160413
+ async function waitForRipgrepExecutableWithAbort(promise, signal) {
160347
160414
  throwIfRipgrepAborted(signal);
160348
- if (!signal) {
160349
- return await resolveRipgrepExecutable();
160350
- }
160351
160415
  return await new Promise((resolve532, reject) => {
160352
160416
  const onAbort = () => {
160353
160417
  signal.removeEventListener("abort", onAbort);
160354
160418
  reject(ripgrepAbortError());
160355
160419
  };
160356
160420
  signal.addEventListener("abort", onAbort, { once: true });
160357
- resolveRipgrepExecutable().then(resolve532, reject).finally(() => {
160421
+ promise.then(resolve532, reject).finally(() => {
160358
160422
  signal.removeEventListener("abort", onAbort);
160359
160423
  });
160360
160424
  });
160361
160425
  }
160426
+ async function resolveRipgrepExecutable(signal) {
160427
+ ripgrepExecutableWaiters += 1;
160428
+ try {
160429
+ const promise = getRipgrepExecutablePromise();
160430
+ const executable = signal ? await waitForRipgrepExecutableWithAbort(promise, signal) : await promise;
160431
+ if (!executable) {
160432
+ throw new Error("ripgrep is not available and could not be downloaded");
160433
+ }
160434
+ return executable;
160435
+ } finally {
160436
+ releaseRipgrepExecutableWaiter(signal);
160437
+ }
160438
+ }
160439
+ function throwIfRipgrepAborted(signal) {
160440
+ if (signal?.aborted) {
160441
+ throw ripgrepAbortError();
160442
+ }
160443
+ }
160444
+ async function resolveRipgrepExecutableWithAbort(signal) {
160445
+ throwIfRipgrepAborted(signal);
160446
+ if (!signal) {
160447
+ return await resolveRipgrepExecutable();
160448
+ }
160449
+ return await resolveRipgrepExecutable(signal);
160450
+ }
160362
160451
  function shellQuoteArg(value) {
160363
160452
  if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {
160364
160453
  return value;
@@ -160438,6 +160527,8 @@ var pathSchema;
160438
160527
  var globSchema;
160439
160528
  var MAX_RIPGREP_OUTPUT_BYTES = 2e6;
160440
160529
  var ripgrepExecutablePromise = null;
160530
+ var ripgrepInstallController = null;
160531
+ var ripgrepExecutableWaiters = 0;
160441
160532
  var init_ripgrep_utils = __esm2(() => {
160442
160533
  init_json();
160443
160534
  init_tools_manager();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evalops/contracts",
3
- "version": "0.10.38",
3
+ "version": "0.10.39",
4
4
  "description": "Shared Maestro contracts for frontend/backend integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evalops/tui",
3
- "version": "0.10.38",
3
+ "version": "0.10.39",
4
4
  "description": "Terminal UI library with differential rendering for Maestro",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -308,7 +308,7 @@ export const findTool = createTool({
308
308
  throw new Error("Operation aborted");
309
309
  }
310
310
  const { pattern, path: searchDir, limit, includeHidden = true } = params;
311
- const fdPath = await ensureTool("fd", true);
311
+ const fdPath = await ensureTool("fd", true, signal);
312
312
  if (!fdPath) {
313
313
  return respond
314
314
  .error("fd is not available and could not be downloaded")
@@ -1 +1 @@
1
- {"version":3,"file":"find.js","sourceRoot":"","sources":["../../src/tools/find.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Find Tool - Fast File Discovery with fd\n *\n * This module provides a file search tool that uses `fd` (a fast alternative\n * to `find`) for discovering files by glob pattern. It respects .gitignore\n * files and falls back to glob when fd returns no results.\n *\n * ## Features\n *\n * - **Fast search**: Uses fd for performance on large codebases\n * - **Glob patterns**: Standard glob syntax (*.ts, **\\/*.json, etc.)\n * - **Git-aware**: Automatically respects .gitignore files\n * - **Hidden files**: Optional inclusion of dotfiles\n * - **Path handling**: Supports nested patterns with path separators\n * - **Auto-download**: Automatically downloads fd if not available\n *\n * ## Pattern Examples\n *\n * | Pattern | Matches |\n * |-------------------|----------------------------------|\n * | `*.ts` | TypeScript files in current dir |\n * | `**\\/*.spec.ts` | Test files anywhere |\n * | `src/**\\/*.json` | JSON files under src/ |\n *\n * ## Fallback Behavior\n *\n * If fd returns no results (which can happen with certain patterns on\n * some platforms), the tool falls back to Node.js glob matching to\n * ensure results are returned.\n *\n * ## Limits\n *\n * - Default limit: 1000 results\n * - Maximum buffer: 10MB\n * - Truncation indicator when limit is reached\n *\n * ## Example\n *\n * ```typescript\n * // Find all TypeScript test files\n * findTool.execute('call-id', {\n * pattern: '**\\/*.spec.ts',\n * path: 'src',\n * limit: 100,\n * });\n * ```\n *\n * @module tools/find\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport {\n\tdirname,\n\tisAbsolute,\n\trelative,\n\tresolve as resolvePath,\n\tsep,\n} from \"node:path\";\nimport { Type } from \"@sinclair/typebox\";\nimport { globSync } from \"glob\";\nimport { minimatch } from \"minimatch\";\nimport { getGitRoot } from \"../utils/git.js\";\nimport { expandTildePath } from \"../utils/path-expansion.js\";\nimport { createTool } from \"./tool-dsl.js\";\nimport { ensureTool } from \"./tools-manager.js\";\n\nconst findSchema = Type.Object({\n\tpattern: Type.String({\n\t\tdescription:\n\t\t\t\"Glob pattern to match files, e.g. '*.ts', '**/*.json', or 'src/**/*.spec.ts'\",\n\t}),\n\tpath: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Directory to search in (default: current directory)\",\n\t\t}),\n\t),\n\tlimit: Type.Optional(\n\t\tType.Number({\n\t\t\tdescription: \"Maximum number of results (default: 1000)\",\n\t\t}),\n\t),\n\tincludeHidden: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Include hidden files (default: true)\",\n\t\t}),\n\t),\n});\n\nconst DEFAULT_LIMIT = 1000;\nconst FD_IGNORE_FILE_NAMES = [\".gitignore\", \".ignore\", \".fdignore\"] as const;\n\ntype FindToolDetails = {\n\tcommand: string;\n\tcwd: string;\n\tfileCount: number;\n\ttruncated: boolean;\n};\n\n/** @internal */\nexport type GitignoreRule = {\n\tpattern: string;\n\tnegated: boolean;\n\tdirectoryOnly: boolean;\n\tmatchPrefix?: string;\n};\n\nfunction collectGitignoreFiles(searchPath: string): string[] {\n\tconst gitignoreFiles = new Set<string>();\n\tconst ancestorDirs: string[] = [];\n\tlet currentDir = searchPath;\n\twhile (true) {\n\t\tancestorDirs.push(currentDir);\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\tbreak;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n\n\tfor (const dir of ancestorDirs.reverse()) {\n\t\tfor (const fileName of FD_IGNORE_FILE_NAMES) {\n\t\t\tconst ignoreFile = resolvePath(dir, fileName);\n\t\t\tif (existsSync(ignoreFile)) {\n\t\t\t\tgitignoreFiles.add(ignoreFile);\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\tfor (const fileName of FD_IGNORE_FILE_NAMES) {\n\t\t\tconst nestedGitignores = globSync(`**/${fileName}`, {\n\t\t\t\tcwd: searchPath,\n\t\t\t\tdot: true,\n\t\t\t\tabsolute: true,\n\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t});\n\t\t\tfor (const file of nestedGitignores) {\n\t\t\t\tgitignoreFiles.add(file);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ignore glob errors\n\t}\n\n\treturn [...gitignoreFiles];\n}\n\nfunction toGlobPath(path: string): string {\n\treturn path.split(sep).join(\"/\");\n}\n\nfunction relativePathStaysInside(path: string): boolean {\n\treturn (\n\t\tpath === \"\" ||\n\t\t(path !== \"..\" && !path.startsWith(`..${sep}`) && !isAbsolute(path))\n\t);\n}\n\nfunction scopeGitignorePattern(\n\tsearchPath: string,\n\tignoreDir: string,\n\tpattern: string,\n\tanchored: boolean,\n): { matchPrefix?: string; pattern: string } | undefined {\n\tconst ignoreDirFromSearch = relative(searchPath, ignoreDir);\n\tif (relativePathStaysInside(ignoreDirFromSearch)) {\n\t\tconst relativeDir = toGlobPath(ignoreDirFromSearch);\n\t\tconst prefix = relativeDir && relativeDir !== \".\" ? `${relativeDir}/` : \"\";\n\t\treturn { pattern: `${prefix}${pattern}` };\n\t}\n\n\tconst searchPathFromIgnoreDir = relative(ignoreDir, searchPath);\n\tif (!relativePathStaysInside(searchPathFromIgnoreDir)) {\n\t\treturn undefined;\n\t}\n\n\tif (!anchored && !pattern.includes(\"/\")) {\n\t\treturn { pattern: `**/${pattern}` };\n\t}\n\tif (!anchored && pattern.startsWith(\"**/\")) {\n\t\treturn { pattern };\n\t}\n\n\tconst searchPrefix = toGlobPath(searchPathFromIgnoreDir);\n\tif (!searchPrefix || searchPrefix === \".\") {\n\t\treturn { pattern };\n\t}\n\tconst prefix = `${searchPrefix}/`;\n\treturn pattern.startsWith(prefix)\n\t\t? { pattern: pattern.slice(prefix.length) }\n\t\t: { matchPrefix: searchPrefix, pattern };\n}\n\nfunction stripUnescapedTrailingWhitespace(line: string): string {\n\tlet end = line.length;\n\twhile (end > 0 && /\\s/.test(line[end - 1] ?? \"\")) {\n\t\tlet slashCount = 0;\n\t\tfor (let index = end - 2; index >= 0 && line[index] === \"\\\\\"; index -= 1) {\n\t\t\tslashCount += 1;\n\t\t}\n\t\tif (slashCount % 2 === 1) {\n\t\t\tbreak;\n\t\t}\n\t\tend -= 1;\n\t}\n\treturn line.slice(0, end);\n}\n\nfunction unescapeGitignorePattern(pattern: string): string {\n\treturn pattern.replace(/\\\\([#!\\t ])/g, \"$1\");\n}\n\nfunction collectGitignoreRules(\n\tsearchPath: string,\n\tgitignoreFiles: string[],\n): GitignoreRule[] {\n\tconst rules: GitignoreRule[] = [];\n\n\tfor (const gitignorePath of gitignoreFiles) {\n\t\tconst ignoreDir = dirname(gitignorePath);\n\n\t\tlet contents: string;\n\t\ttry {\n\t\t\tcontents = readFileSync(gitignorePath, \"utf8\");\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const rawLine of contents.split(/\\r?\\n/)) {\n\t\t\tlet line = stripUnescapedTrailingWhitespace(rawLine);\n\t\t\tif (!line || line.startsWith(\"#\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst negated = line.startsWith(\"!\");\n\t\t\tif (negated) {\n\t\t\t\tline = line.slice(1);\n\t\t\t\tif (!line) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst anchored = line.startsWith(\"/\");\n\t\t\tconst directoryOnly = line.endsWith(\"/\");\n\t\t\tlet pattern = unescapeGitignorePattern(\n\t\t\t\tline.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\"),\n\t\t\t);\n\t\t\tif (!pattern) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!anchored && !pattern.includes(\"/\")) {\n\t\t\t\tpattern = `**/${pattern}`;\n\t\t\t}\n\n\t\t\tconst scopedPattern = scopeGitignorePattern(\n\t\t\t\tsearchPath,\n\t\t\t\tignoreDir,\n\t\t\t\tpattern,\n\t\t\t\tanchored,\n\t\t\t);\n\t\t\tif (!scopedPattern) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\trules.push({\n\t\t\t\tpattern: scopedPattern.pattern,\n\t\t\t\tmatchPrefix: scopedPattern.matchPrefix,\n\t\t\t\tnegated,\n\t\t\t\tdirectoryOnly,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn rules;\n}\n\nfunction pathIsDirectory(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction pathAncestors(path: string): string[] {\n\tconst parts = path.split(\"/\").filter(Boolean);\n\tconst ancestors: string[] = [];\n\tfor (let index = 1; index < parts.length; index += 1) {\n\t\tancestors.push(parts.slice(0, index).join(\"/\"));\n\t}\n\treturn ancestors;\n}\n\nfunction gitignoreRuleMatchPath(path: string, rule: GitignoreRule): string {\n\treturn rule.matchPrefix\n\t\t? path === \".\"\n\t\t\t? rule.matchPrefix\n\t\t\t: `${rule.matchPrefix}/${path}`\n\t\t: path;\n}\n\nfunction matchesGitignoreSubject(\n\tpath: string,\n\trule: GitignoreRule,\n\tisDirectory: boolean,\n): boolean {\n\tconst matchPath = gitignoreRuleMatchPath(path, rule);\n\tconst options = { dot: true, nonegate: true };\n\tif (rule.directoryOnly) {\n\t\treturn isDirectory && minimatch(matchPath, rule.pattern, options);\n\t}\n\treturn minimatch(matchPath, rule.pattern, options);\n}\n\nfunction evaluateGitignoreSubject(\n\tpath: string,\n\trules: GitignoreRule[],\n\tisDirectory: boolean,\n): boolean {\n\tlet ignored = false;\n\tfor (const rule of rules) {\n\t\tif (matchesGitignoreSubject(path, rule, isDirectory)) {\n\t\t\tignored = !rule.negated;\n\t\t}\n\t}\n\treturn ignored;\n}\n\n/** @internal */\nexport function isIgnoredByGitignoreRules(\n\trelativePath: string,\n\trules: GitignoreRule[],\n\tisDirectory = false,\n): boolean {\n\tconst normalizedPath = toGlobPath(relativePath);\n\tfor (const ancestor of pathAncestors(normalizedPath)) {\n\t\tif (evaluateGitignoreSubject(ancestor, rules, true)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn evaluateGitignoreSubject(normalizedPath, rules, isDirectory);\n}\n\nfunction constrainGlobMatches(\n\tmatches: string[],\n\tsearchPath: string,\n\trules: GitignoreRule[],\n): string[] {\n\tconst searchRoot = searchPath.endsWith(sep)\n\t\t? searchPath\n\t\t: `${searchPath}${sep}`;\n\tconst constrained: string[] = [];\n\tfor (const match of matches) {\n\t\tconst resolved = resolvePath(match);\n\t\tif (resolved !== searchPath && !resolved.startsWith(searchRoot)) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst relativeMatch = relative(searchPath, resolved) || \".\";\n\t\tif (\n\t\t\tisIgnoredByGitignoreRules(relativeMatch, rules, pathIsDirectory(resolved))\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tconstrained.push(resolved);\n\t}\n\treturn constrained;\n}\n\n/** @internal */\nexport function getFindGlobPatterns(pattern: string): string[] {\n\tconst patterns = [pattern];\n\tconst rootEquivalent = pattern.match(/^\\*\\*[\\\\/](.+)$/u)?.[1];\n\tif (rootEquivalent && rootEquivalent !== pattern) {\n\t\tpatterns.push(rootEquivalent);\n\t}\n\treturn patterns;\n}\n\nfunction findGlobMatches(\n\tpattern: string,\n\tsearchPath: string,\n\tincludeHidden: boolean,\n): string[] {\n\tconst matches = new Set<string>();\n\tfor (const globPattern of getFindGlobPatterns(pattern)) {\n\t\tfor (const match of globSync(globPattern, {\n\t\t\tcwd: searchPath,\n\t\t\tdot: includeHidden,\n\t\t\tnodir: false,\n\t\t\tabsolute: true,\n\t\t})) {\n\t\t\tmatches.add(resolvePath(match));\n\t\t}\n\t}\n\treturn [...matches];\n}\n\nexport const findTool = createTool<typeof findSchema, FindToolDetails>({\n\tname: \"find\",\n\tlabel: \"find\",\n\tdescription:\n\t\t\"Search for files by glob pattern using fd. Returns matching file paths relative to the search directory. Respects .gitignore. Use this for fast file discovery across large codebases.\",\n\tschema: findSchema,\n\tasync run(params, { signal, respond }) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Operation aborted\");\n\t\t}\n\n\t\tconst { pattern, path: searchDir, limit, includeHidden = true } = params;\n\n\t\tconst fdPath = await ensureTool(\"fd\", true);\n\t\tif (!fdPath) {\n\t\t\treturn respond\n\t\t\t\t.error(\"fd is not available and could not be downloaded\")\n\t\t\t\t.detail({\n\t\t\t\t\tcommand: \"fd\",\n\t\t\t\t\tcwd: process.cwd(),\n\t\t\t\t\tfileCount: 0,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t});\n\t\t}\n\n\t\tconst searchPath = resolvePath(expandTildePath(searchDir || \".\"));\n\t\tconst effectiveLimit = limit ?? DEFAULT_LIMIT;\n\n\t\tconst args: string[] = [\n\t\t\t\"--glob\",\n\t\t\t\"--color=never\",\n\t\t\t\"--max-results\",\n\t\t\tString(effectiveLimit),\n\t\t];\n\n\t\t// If pattern includes path separators, match against the full path so nested globs work.\n\t\tif (pattern.includes(\"/\") || pattern.includes(\"\\\\\")) {\n\t\t\targs.push(\"--full-path\");\n\t\t}\n\n\t\tif (includeHidden) {\n\t\t\targs.push(\"--hidden\");\n\t\t}\n\n\t\tconst gitRoot = getGitRoot(searchPath);\n\t\tconst gitignoreFiles = gitRoot ? [] : collectGitignoreFiles(searchPath);\n\t\tconst gitignoreRules = collectGitignoreRules(searchPath, gitignoreFiles);\n\t\tif (gitRoot) {\n\t\t\t// Force fd to honor repo-native ignore rules even if user config disables\n\t\t\t// VCS ignores, while keeping anchored patterns scoped to the repo root.\n\t\t\targs.push(\"--ignore-vcs\");\n\t\t} else {\n\t\t\t// Let fd discover .gitignore files in their native directories instead of\n\t\t\t// re-scoping them as current-directory --ignore-file inputs.\n\t\t\targs.push(\"--no-require-git\");\n\t\t}\n\n\t\targs.push(pattern);\n\n\t\tconst command = [fdPath, ...args].join(\" \");\n\n\t\tconst result = spawnSync(fdPath, args, {\n\t\t\tencoding: \"utf-8\",\n\t\t\tmaxBuffer: 10 * 1024 * 1024,\n\t\t\tcwd: searchPath,\n\t\t});\n\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Operation aborted\");\n\t\t}\n\n\t\tif (result.error) {\n\t\t\treturn respond\n\t\t\t\t.error(`Failed to run fd: ${result.error.message}`)\n\t\t\t\t.detail({ command, cwd: searchPath, fileCount: 0, truncated: false });\n\t\t}\n\n\t\tlet output = result.stdout ?? \"\";\n\n\t\tif (result.status !== 0 && !output) {\n\t\t\tconst errorMsg =\n\t\t\t\tresult.stderr?.trim() || `fd exited with code ${result.status}`;\n\t\t\treturn respond\n\t\t\t\t.error(errorMsg)\n\t\t\t\t.detail({ command, cwd: searchPath, fileCount: 0, truncated: false });\n\t\t}\n\n\t\tif (!output) {\n\t\t\t// Fallback to globbing when fd returns nothing (handles patterns with subdirectories on some platforms)\n\t\t\tconst globMatches = findGlobMatches(pattern, searchPath, includeHidden);\n\t\t\tconst constrained = constrainGlobMatches(\n\t\t\t\tglobMatches,\n\t\t\t\tsearchPath,\n\t\t\t\tgitignoreRules,\n\t\t\t);\n\n\t\t\tif (constrained.length > 0) {\n\t\t\t\tconst limited = constrained.slice(0, effectiveLimit);\n\t\t\t\tconst truncated = constrained.length > effectiveLimit;\n\t\t\t\tconst text = limited\n\t\t\t\t\t.map((abs) => relative(searchPath, abs) || \".\")\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn respond\n\t\t\t\t\t.text(\n\t\t\t\t\t\ttruncated\n\t\t\t\t\t\t\t? `${text}\\n\\n(truncated, ${effectiveLimit} results shown)`\n\t\t\t\t\t\t\t: text,\n\t\t\t\t\t)\n\t\t\t\t\t.detail({\n\t\t\t\t\t\tcommand,\n\t\t\t\t\t\tcwd: searchPath,\n\t\t\t\t\t\tfileCount: constrained.length,\n\t\t\t\t\t\ttruncated,\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn respond\n\t\t\t\t.text(\"No files found matching pattern\")\n\t\t\t\t.detail({ command, cwd: searchPath, fileCount: 0, truncated: false });\n\t\t}\n\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst relativized: string[] = [];\n\n\t\tfor (const rawLine of lines) {\n\t\t\tconst line = rawLine.replace(/\\r$/, \"\");\n\t\t\tif (!line) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet relativePath = line;\n\t\t\tif (line.endsWith(\"\\\\\")) {\n\t\t\t\t// Normalize Windows-style trailing backslash to a single forward slash\n\t\t\t\trelativePath = `${line.slice(0, -1)}/`;\n\t\t\t}\n\n\t\t\tif (relativePath) {\n\t\t\t\tif (\n\t\t\t\t\t!gitRoot &&\n\t\t\t\t\tisIgnoredByGitignoreRules(\n\t\t\t\t\t\trelativePath,\n\t\t\t\t\t\tgitignoreRules,\n\t\t\t\t\t\tpathIsDirectory(resolvePath(searchPath, relativePath)),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\trelativized.push(relativePath);\n\t\t\t}\n\t\t}\n\n\t\tif (!gitRoot && pattern.includes(\"**/\")) {\n\t\t\tconst searchRoot = searchPath.endsWith(sep)\n\t\t\t\t? searchPath\n\t\t\t\t: `${searchPath}${sep}`;\n\t\t\tconst seen = new Set(relativized);\n\t\t\tfor (const match of findGlobMatches(pattern, searchPath, includeHidden)) {\n\t\t\t\tconst resolved = resolvePath(match);\n\t\t\t\tif (resolved !== searchPath && !resolved.startsWith(searchRoot)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst relativeMatch = relative(searchPath, resolved) || \".\";\n\t\t\t\tif (\n\t\t\t\t\tisIgnoredByGitignoreRules(\n\t\t\t\t\t\trelativeMatch,\n\t\t\t\t\t\tgitignoreRules,\n\t\t\t\t\t\tpathIsDirectory(resolved),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!seen.has(relativeMatch)) {\n\t\t\t\t\tseen.add(relativeMatch);\n\t\t\t\t\trelativized.push(relativeMatch);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trelativized.sort();\n\t\toutput = relativized.slice(0, effectiveLimit).join(\"\\n\");\n\t\tconst count = relativized.length;\n\t\tconst truncated = count >= effectiveLimit;\n\t\tif (truncated) {\n\t\t\toutput += `\\n\\n(truncated, ${effectiveLimit} results shown)`;\n\t\t}\n\n\t\treturn respond\n\t\t\t.text(output)\n\t\t\t.detail({ command, cwd: searchPath, fileCount: count, truncated });\n\t},\n});\n"]}
1
+ {"version":3,"file":"find.js","sourceRoot":"","sources":["../../src/tools/find.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Find Tool - Fast File Discovery with fd\n *\n * This module provides a file search tool that uses `fd` (a fast alternative\n * to `find`) for discovering files by glob pattern. It respects .gitignore\n * files and falls back to glob when fd returns no results.\n *\n * ## Features\n *\n * - **Fast search**: Uses fd for performance on large codebases\n * - **Glob patterns**: Standard glob syntax (*.ts, **\\/*.json, etc.)\n * - **Git-aware**: Automatically respects .gitignore files\n * - **Hidden files**: Optional inclusion of dotfiles\n * - **Path handling**: Supports nested patterns with path separators\n * - **Auto-download**: Automatically downloads fd if not available\n *\n * ## Pattern Examples\n *\n * | Pattern | Matches |\n * |-------------------|----------------------------------|\n * | `*.ts` | TypeScript files in current dir |\n * | `**\\/*.spec.ts` | Test files anywhere |\n * | `src/**\\/*.json` | JSON files under src/ |\n *\n * ## Fallback Behavior\n *\n * If fd returns no results (which can happen with certain patterns on\n * some platforms), the tool falls back to Node.js glob matching to\n * ensure results are returned.\n *\n * ## Limits\n *\n * - Default limit: 1000 results\n * - Maximum buffer: 10MB\n * - Truncation indicator when limit is reached\n *\n * ## Example\n *\n * ```typescript\n * // Find all TypeScript test files\n * findTool.execute('call-id', {\n * pattern: '**\\/*.spec.ts',\n * path: 'src',\n * limit: 100,\n * });\n * ```\n *\n * @module tools/find\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport {\n\tdirname,\n\tisAbsolute,\n\trelative,\n\tresolve as resolvePath,\n\tsep,\n} from \"node:path\";\nimport { Type } from \"@sinclair/typebox\";\nimport { globSync } from \"glob\";\nimport { minimatch } from \"minimatch\";\nimport { getGitRoot } from \"../utils/git.js\";\nimport { expandTildePath } from \"../utils/path-expansion.js\";\nimport { createTool } from \"./tool-dsl.js\";\nimport { ensureTool } from \"./tools-manager.js\";\n\nconst findSchema = Type.Object({\n\tpattern: Type.String({\n\t\tdescription:\n\t\t\t\"Glob pattern to match files, e.g. '*.ts', '**/*.json', or 'src/**/*.spec.ts'\",\n\t}),\n\tpath: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Directory to search in (default: current directory)\",\n\t\t}),\n\t),\n\tlimit: Type.Optional(\n\t\tType.Number({\n\t\t\tdescription: \"Maximum number of results (default: 1000)\",\n\t\t}),\n\t),\n\tincludeHidden: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Include hidden files (default: true)\",\n\t\t}),\n\t),\n});\n\nconst DEFAULT_LIMIT = 1000;\nconst FD_IGNORE_FILE_NAMES = [\".gitignore\", \".ignore\", \".fdignore\"] as const;\n\ntype FindToolDetails = {\n\tcommand: string;\n\tcwd: string;\n\tfileCount: number;\n\ttruncated: boolean;\n};\n\n/** @internal */\nexport type GitignoreRule = {\n\tpattern: string;\n\tnegated: boolean;\n\tdirectoryOnly: boolean;\n\tmatchPrefix?: string;\n};\n\nfunction collectGitignoreFiles(searchPath: string): string[] {\n\tconst gitignoreFiles = new Set<string>();\n\tconst ancestorDirs: string[] = [];\n\tlet currentDir = searchPath;\n\twhile (true) {\n\t\tancestorDirs.push(currentDir);\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\tbreak;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n\n\tfor (const dir of ancestorDirs.reverse()) {\n\t\tfor (const fileName of FD_IGNORE_FILE_NAMES) {\n\t\t\tconst ignoreFile = resolvePath(dir, fileName);\n\t\t\tif (existsSync(ignoreFile)) {\n\t\t\t\tgitignoreFiles.add(ignoreFile);\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\tfor (const fileName of FD_IGNORE_FILE_NAMES) {\n\t\t\tconst nestedGitignores = globSync(`**/${fileName}`, {\n\t\t\t\tcwd: searchPath,\n\t\t\t\tdot: true,\n\t\t\t\tabsolute: true,\n\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t});\n\t\t\tfor (const file of nestedGitignores) {\n\t\t\t\tgitignoreFiles.add(file);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ignore glob errors\n\t}\n\n\treturn [...gitignoreFiles];\n}\n\nfunction toGlobPath(path: string): string {\n\treturn path.split(sep).join(\"/\");\n}\n\nfunction relativePathStaysInside(path: string): boolean {\n\treturn (\n\t\tpath === \"\" ||\n\t\t(path !== \"..\" && !path.startsWith(`..${sep}`) && !isAbsolute(path))\n\t);\n}\n\nfunction scopeGitignorePattern(\n\tsearchPath: string,\n\tignoreDir: string,\n\tpattern: string,\n\tanchored: boolean,\n): { matchPrefix?: string; pattern: string } | undefined {\n\tconst ignoreDirFromSearch = relative(searchPath, ignoreDir);\n\tif (relativePathStaysInside(ignoreDirFromSearch)) {\n\t\tconst relativeDir = toGlobPath(ignoreDirFromSearch);\n\t\tconst prefix = relativeDir && relativeDir !== \".\" ? `${relativeDir}/` : \"\";\n\t\treturn { pattern: `${prefix}${pattern}` };\n\t}\n\n\tconst searchPathFromIgnoreDir = relative(ignoreDir, searchPath);\n\tif (!relativePathStaysInside(searchPathFromIgnoreDir)) {\n\t\treturn undefined;\n\t}\n\n\tif (!anchored && !pattern.includes(\"/\")) {\n\t\treturn { pattern: `**/${pattern}` };\n\t}\n\tif (!anchored && pattern.startsWith(\"**/\")) {\n\t\treturn { pattern };\n\t}\n\n\tconst searchPrefix = toGlobPath(searchPathFromIgnoreDir);\n\tif (!searchPrefix || searchPrefix === \".\") {\n\t\treturn { pattern };\n\t}\n\tconst prefix = `${searchPrefix}/`;\n\treturn pattern.startsWith(prefix)\n\t\t? { pattern: pattern.slice(prefix.length) }\n\t\t: { matchPrefix: searchPrefix, pattern };\n}\n\nfunction stripUnescapedTrailingWhitespace(line: string): string {\n\tlet end = line.length;\n\twhile (end > 0 && /\\s/.test(line[end - 1] ?? \"\")) {\n\t\tlet slashCount = 0;\n\t\tfor (let index = end - 2; index >= 0 && line[index] === \"\\\\\"; index -= 1) {\n\t\t\tslashCount += 1;\n\t\t}\n\t\tif (slashCount % 2 === 1) {\n\t\t\tbreak;\n\t\t}\n\t\tend -= 1;\n\t}\n\treturn line.slice(0, end);\n}\n\nfunction unescapeGitignorePattern(pattern: string): string {\n\treturn pattern.replace(/\\\\([#!\\t ])/g, \"$1\");\n}\n\nfunction collectGitignoreRules(\n\tsearchPath: string,\n\tgitignoreFiles: string[],\n): GitignoreRule[] {\n\tconst rules: GitignoreRule[] = [];\n\n\tfor (const gitignorePath of gitignoreFiles) {\n\t\tconst ignoreDir = dirname(gitignorePath);\n\n\t\tlet contents: string;\n\t\ttry {\n\t\t\tcontents = readFileSync(gitignorePath, \"utf8\");\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const rawLine of contents.split(/\\r?\\n/)) {\n\t\t\tlet line = stripUnescapedTrailingWhitespace(rawLine);\n\t\t\tif (!line || line.startsWith(\"#\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst negated = line.startsWith(\"!\");\n\t\t\tif (negated) {\n\t\t\t\tline = line.slice(1);\n\t\t\t\tif (!line) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst anchored = line.startsWith(\"/\");\n\t\t\tconst directoryOnly = line.endsWith(\"/\");\n\t\t\tlet pattern = unescapeGitignorePattern(\n\t\t\t\tline.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\"),\n\t\t\t);\n\t\t\tif (!pattern) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!anchored && !pattern.includes(\"/\")) {\n\t\t\t\tpattern = `**/${pattern}`;\n\t\t\t}\n\n\t\t\tconst scopedPattern = scopeGitignorePattern(\n\t\t\t\tsearchPath,\n\t\t\t\tignoreDir,\n\t\t\t\tpattern,\n\t\t\t\tanchored,\n\t\t\t);\n\t\t\tif (!scopedPattern) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\trules.push({\n\t\t\t\tpattern: scopedPattern.pattern,\n\t\t\t\tmatchPrefix: scopedPattern.matchPrefix,\n\t\t\t\tnegated,\n\t\t\t\tdirectoryOnly,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn rules;\n}\n\nfunction pathIsDirectory(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction pathAncestors(path: string): string[] {\n\tconst parts = path.split(\"/\").filter(Boolean);\n\tconst ancestors: string[] = [];\n\tfor (let index = 1; index < parts.length; index += 1) {\n\t\tancestors.push(parts.slice(0, index).join(\"/\"));\n\t}\n\treturn ancestors;\n}\n\nfunction gitignoreRuleMatchPath(path: string, rule: GitignoreRule): string {\n\treturn rule.matchPrefix\n\t\t? path === \".\"\n\t\t\t? rule.matchPrefix\n\t\t\t: `${rule.matchPrefix}/${path}`\n\t\t: path;\n}\n\nfunction matchesGitignoreSubject(\n\tpath: string,\n\trule: GitignoreRule,\n\tisDirectory: boolean,\n): boolean {\n\tconst matchPath = gitignoreRuleMatchPath(path, rule);\n\tconst options = { dot: true, nonegate: true };\n\tif (rule.directoryOnly) {\n\t\treturn isDirectory && minimatch(matchPath, rule.pattern, options);\n\t}\n\treturn minimatch(matchPath, rule.pattern, options);\n}\n\nfunction evaluateGitignoreSubject(\n\tpath: string,\n\trules: GitignoreRule[],\n\tisDirectory: boolean,\n): boolean {\n\tlet ignored = false;\n\tfor (const rule of rules) {\n\t\tif (matchesGitignoreSubject(path, rule, isDirectory)) {\n\t\t\tignored = !rule.negated;\n\t\t}\n\t}\n\treturn ignored;\n}\n\n/** @internal */\nexport function isIgnoredByGitignoreRules(\n\trelativePath: string,\n\trules: GitignoreRule[],\n\tisDirectory = false,\n): boolean {\n\tconst normalizedPath = toGlobPath(relativePath);\n\tfor (const ancestor of pathAncestors(normalizedPath)) {\n\t\tif (evaluateGitignoreSubject(ancestor, rules, true)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn evaluateGitignoreSubject(normalizedPath, rules, isDirectory);\n}\n\nfunction constrainGlobMatches(\n\tmatches: string[],\n\tsearchPath: string,\n\trules: GitignoreRule[],\n): string[] {\n\tconst searchRoot = searchPath.endsWith(sep)\n\t\t? searchPath\n\t\t: `${searchPath}${sep}`;\n\tconst constrained: string[] = [];\n\tfor (const match of matches) {\n\t\tconst resolved = resolvePath(match);\n\t\tif (resolved !== searchPath && !resolved.startsWith(searchRoot)) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst relativeMatch = relative(searchPath, resolved) || \".\";\n\t\tif (\n\t\t\tisIgnoredByGitignoreRules(relativeMatch, rules, pathIsDirectory(resolved))\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tconstrained.push(resolved);\n\t}\n\treturn constrained;\n}\n\n/** @internal */\nexport function getFindGlobPatterns(pattern: string): string[] {\n\tconst patterns = [pattern];\n\tconst rootEquivalent = pattern.match(/^\\*\\*[\\\\/](.+)$/u)?.[1];\n\tif (rootEquivalent && rootEquivalent !== pattern) {\n\t\tpatterns.push(rootEquivalent);\n\t}\n\treturn patterns;\n}\n\nfunction findGlobMatches(\n\tpattern: string,\n\tsearchPath: string,\n\tincludeHidden: boolean,\n): string[] {\n\tconst matches = new Set<string>();\n\tfor (const globPattern of getFindGlobPatterns(pattern)) {\n\t\tfor (const match of globSync(globPattern, {\n\t\t\tcwd: searchPath,\n\t\t\tdot: includeHidden,\n\t\t\tnodir: false,\n\t\t\tabsolute: true,\n\t\t})) {\n\t\t\tmatches.add(resolvePath(match));\n\t\t}\n\t}\n\treturn [...matches];\n}\n\nexport const findTool = createTool<typeof findSchema, FindToolDetails>({\n\tname: \"find\",\n\tlabel: \"find\",\n\tdescription:\n\t\t\"Search for files by glob pattern using fd. Returns matching file paths relative to the search directory. Respects .gitignore. Use this for fast file discovery across large codebases.\",\n\tschema: findSchema,\n\tasync run(params, { signal, respond }) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Operation aborted\");\n\t\t}\n\n\t\tconst { pattern, path: searchDir, limit, includeHidden = true } = params;\n\n\t\tconst fdPath = await ensureTool(\"fd\", true, signal);\n\t\tif (!fdPath) {\n\t\t\treturn respond\n\t\t\t\t.error(\"fd is not available and could not be downloaded\")\n\t\t\t\t.detail({\n\t\t\t\t\tcommand: \"fd\",\n\t\t\t\t\tcwd: process.cwd(),\n\t\t\t\t\tfileCount: 0,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t});\n\t\t}\n\n\t\tconst searchPath = resolvePath(expandTildePath(searchDir || \".\"));\n\t\tconst effectiveLimit = limit ?? DEFAULT_LIMIT;\n\n\t\tconst args: string[] = [\n\t\t\t\"--glob\",\n\t\t\t\"--color=never\",\n\t\t\t\"--max-results\",\n\t\t\tString(effectiveLimit),\n\t\t];\n\n\t\t// If pattern includes path separators, match against the full path so nested globs work.\n\t\tif (pattern.includes(\"/\") || pattern.includes(\"\\\\\")) {\n\t\t\targs.push(\"--full-path\");\n\t\t}\n\n\t\tif (includeHidden) {\n\t\t\targs.push(\"--hidden\");\n\t\t}\n\n\t\tconst gitRoot = getGitRoot(searchPath);\n\t\tconst gitignoreFiles = gitRoot ? [] : collectGitignoreFiles(searchPath);\n\t\tconst gitignoreRules = collectGitignoreRules(searchPath, gitignoreFiles);\n\t\tif (gitRoot) {\n\t\t\t// Force fd to honor repo-native ignore rules even if user config disables\n\t\t\t// VCS ignores, while keeping anchored patterns scoped to the repo root.\n\t\t\targs.push(\"--ignore-vcs\");\n\t\t} else {\n\t\t\t// Let fd discover .gitignore files in their native directories instead of\n\t\t\t// re-scoping them as current-directory --ignore-file inputs.\n\t\t\targs.push(\"--no-require-git\");\n\t\t}\n\n\t\targs.push(pattern);\n\n\t\tconst command = [fdPath, ...args].join(\" \");\n\n\t\tconst result = spawnSync(fdPath, args, {\n\t\t\tencoding: \"utf-8\",\n\t\t\tmaxBuffer: 10 * 1024 * 1024,\n\t\t\tcwd: searchPath,\n\t\t});\n\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Operation aborted\");\n\t\t}\n\n\t\tif (result.error) {\n\t\t\treturn respond\n\t\t\t\t.error(`Failed to run fd: ${result.error.message}`)\n\t\t\t\t.detail({ command, cwd: searchPath, fileCount: 0, truncated: false });\n\t\t}\n\n\t\tlet output = result.stdout ?? \"\";\n\n\t\tif (result.status !== 0 && !output) {\n\t\t\tconst errorMsg =\n\t\t\t\tresult.stderr?.trim() || `fd exited with code ${result.status}`;\n\t\t\treturn respond\n\t\t\t\t.error(errorMsg)\n\t\t\t\t.detail({ command, cwd: searchPath, fileCount: 0, truncated: false });\n\t\t}\n\n\t\tif (!output) {\n\t\t\t// Fallback to globbing when fd returns nothing (handles patterns with subdirectories on some platforms)\n\t\t\tconst globMatches = findGlobMatches(pattern, searchPath, includeHidden);\n\t\t\tconst constrained = constrainGlobMatches(\n\t\t\t\tglobMatches,\n\t\t\t\tsearchPath,\n\t\t\t\tgitignoreRules,\n\t\t\t);\n\n\t\t\tif (constrained.length > 0) {\n\t\t\t\tconst limited = constrained.slice(0, effectiveLimit);\n\t\t\t\tconst truncated = constrained.length > effectiveLimit;\n\t\t\t\tconst text = limited\n\t\t\t\t\t.map((abs) => relative(searchPath, abs) || \".\")\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn respond\n\t\t\t\t\t.text(\n\t\t\t\t\t\ttruncated\n\t\t\t\t\t\t\t? `${text}\\n\\n(truncated, ${effectiveLimit} results shown)`\n\t\t\t\t\t\t\t: text,\n\t\t\t\t\t)\n\t\t\t\t\t.detail({\n\t\t\t\t\t\tcommand,\n\t\t\t\t\t\tcwd: searchPath,\n\t\t\t\t\t\tfileCount: constrained.length,\n\t\t\t\t\t\ttruncated,\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn respond\n\t\t\t\t.text(\"No files found matching pattern\")\n\t\t\t\t.detail({ command, cwd: searchPath, fileCount: 0, truncated: false });\n\t\t}\n\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst relativized: string[] = [];\n\n\t\tfor (const rawLine of lines) {\n\t\t\tconst line = rawLine.replace(/\\r$/, \"\");\n\t\t\tif (!line) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet relativePath = line;\n\t\t\tif (line.endsWith(\"\\\\\")) {\n\t\t\t\t// Normalize Windows-style trailing backslash to a single forward slash\n\t\t\t\trelativePath = `${line.slice(0, -1)}/`;\n\t\t\t}\n\n\t\t\tif (relativePath) {\n\t\t\t\tif (\n\t\t\t\t\t!gitRoot &&\n\t\t\t\t\tisIgnoredByGitignoreRules(\n\t\t\t\t\t\trelativePath,\n\t\t\t\t\t\tgitignoreRules,\n\t\t\t\t\t\tpathIsDirectory(resolvePath(searchPath, relativePath)),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\trelativized.push(relativePath);\n\t\t\t}\n\t\t}\n\n\t\tif (!gitRoot && pattern.includes(\"**/\")) {\n\t\t\tconst searchRoot = searchPath.endsWith(sep)\n\t\t\t\t? searchPath\n\t\t\t\t: `${searchPath}${sep}`;\n\t\t\tconst seen = new Set(relativized);\n\t\t\tfor (const match of findGlobMatches(pattern, searchPath, includeHidden)) {\n\t\t\t\tconst resolved = resolvePath(match);\n\t\t\t\tif (resolved !== searchPath && !resolved.startsWith(searchRoot)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst relativeMatch = relative(searchPath, resolved) || \".\";\n\t\t\t\tif (\n\t\t\t\t\tisIgnoredByGitignoreRules(\n\t\t\t\t\t\trelativeMatch,\n\t\t\t\t\t\tgitignoreRules,\n\t\t\t\t\t\tpathIsDirectory(resolved),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!seen.has(relativeMatch)) {\n\t\t\t\t\tseen.add(relativeMatch);\n\t\t\t\t\trelativized.push(relativeMatch);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trelativized.sort();\n\t\toutput = relativized.slice(0, effectiveLimit).join(\"\\n\");\n\t\tconst count = relativized.length;\n\t\tconst truncated = count >= effectiveLimit;\n\t\tif (truncated) {\n\t\t\toutput += `\\n\\n(truncated, ${effectiveLimit} results shown)`;\n\t\t}\n\n\t\treturn respond\n\t\t\t.text(output)\n\t\t\t.detail({ command, cwd: searchPath, fileCount: count, truncated });\n\t},\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ripgrep-utils.d.ts","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,2LActB,CAAC;AAEF,eAAO,MAAM,UAAU,2LActB,CAAC;AAEF,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,CAAC,EAAE,CAK1D;AAuDD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED,wBAAsB,UAAU,CAC/B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,GAAG,CAAC,EAAE,MAAM,GACV,OAAO,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACnB,CAAC,CAoDD;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAmC/D"}
1
+ {"version":3,"file":"ripgrep-utils.d.ts","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,2LActB,CAAC;AAEF,eAAO,MAAM,UAAU,2LActB,CAAC;AAEF,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,CAAC,EAAE,CAK1D;AAgHD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED,wBAAsB,UAAU,CAC/B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,GAAG,CAAC,EAAE,MAAM,GACV,OAAO,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACnB,CAAC,CAoDD;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAmC/D"}
@@ -30,41 +30,89 @@ export function toArray(value) {
30
30
  }
31
31
  const MAX_RIPGREP_OUTPUT_BYTES = 2_000_000; // ~2MB safeguard
32
32
  let ripgrepExecutablePromise = null;
33
+ let ripgrepInstallController = null;
34
+ let ripgrepExecutableWaiters = 0;
33
35
  function ripgrepAbortError() {
34
36
  return new Error("ripgrep search aborted before start");
35
37
  }
36
- async function resolveRipgrepExecutable() {
37
- ripgrepExecutablePromise ??= ensureTool("rg", true);
38
- const executable = await ripgrepExecutablePromise;
39
- if (!executable) {
40
- ripgrepExecutablePromise = null;
41
- throw new Error("ripgrep is not available and could not be downloaded");
38
+ function resetRipgrepExecutablePromise() {
39
+ ripgrepExecutablePromise = null;
40
+ ripgrepInstallController = null;
41
+ }
42
+ function getRipgrepExecutablePromise() {
43
+ if (!ripgrepExecutablePromise) {
44
+ const controller = new AbortController();
45
+ ripgrepInstallController = controller;
46
+ const promise = ensureTool("rg", true, controller.signal).then((executable) => {
47
+ if (ripgrepInstallController === controller) {
48
+ ripgrepInstallController = null;
49
+ }
50
+ if (ripgrepExecutablePromise === promise && !executable) {
51
+ ripgrepExecutablePromise = null;
52
+ }
53
+ return executable;
54
+ }, (error) => {
55
+ if (ripgrepInstallController === controller) {
56
+ ripgrepInstallController = null;
57
+ }
58
+ if (ripgrepExecutablePromise === promise) {
59
+ ripgrepExecutablePromise = null;
60
+ }
61
+ throw error;
62
+ });
63
+ ripgrepExecutablePromise = promise;
42
64
  }
43
- return executable;
65
+ return ripgrepExecutablePromise;
44
66
  }
45
- function throwIfRipgrepAborted(signal) {
46
- if (signal?.aborted) {
47
- throw ripgrepAbortError();
67
+ function releaseRipgrepExecutableWaiter(signal) {
68
+ ripgrepExecutableWaiters = Math.max(0, ripgrepExecutableWaiters - 1);
69
+ if (signal?.aborted && ripgrepExecutableWaiters === 0) {
70
+ const controller = ripgrepInstallController;
71
+ resetRipgrepExecutablePromise();
72
+ controller?.abort(signal.reason);
48
73
  }
49
74
  }
50
- async function resolveRipgrepExecutableWithAbort(signal) {
75
+ async function waitForRipgrepExecutableWithAbort(promise, signal) {
51
76
  throwIfRipgrepAborted(signal);
52
- if (!signal) {
53
- return await resolveRipgrepExecutable();
54
- }
55
77
  return await new Promise((resolve, reject) => {
56
78
  const onAbort = () => {
57
79
  signal.removeEventListener("abort", onAbort);
58
80
  reject(ripgrepAbortError());
59
81
  };
60
82
  signal.addEventListener("abort", onAbort, { once: true });
61
- resolveRipgrepExecutable()
62
- .then(resolve, reject)
63
- .finally(() => {
83
+ promise.then(resolve, reject).finally(() => {
64
84
  signal.removeEventListener("abort", onAbort);
65
85
  });
66
86
  });
67
87
  }
88
+ async function resolveRipgrepExecutable(signal) {
89
+ ripgrepExecutableWaiters += 1;
90
+ try {
91
+ const promise = getRipgrepExecutablePromise();
92
+ const executable = signal
93
+ ? await waitForRipgrepExecutableWithAbort(promise, signal)
94
+ : await promise;
95
+ if (!executable) {
96
+ throw new Error("ripgrep is not available and could not be downloaded");
97
+ }
98
+ return executable;
99
+ }
100
+ finally {
101
+ releaseRipgrepExecutableWaiter(signal);
102
+ }
103
+ }
104
+ function throwIfRipgrepAborted(signal) {
105
+ if (signal?.aborted) {
106
+ throw ripgrepAbortError();
107
+ }
108
+ }
109
+ async function resolveRipgrepExecutableWithAbort(signal) {
110
+ throwIfRipgrepAborted(signal);
111
+ if (!signal) {
112
+ return await resolveRipgrepExecutable();
113
+ }
114
+ return await resolveRipgrepExecutable(signal);
115
+ }
68
116
  function shellQuoteArg(value) {
69
117
  if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {
70
118
  return value;
@@ -1 +1 @@
1
- {"version":3,"file":"ripgrep-utils.js","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { Type } from \"@sinclair/typebox\";\nimport { safeJsonParse } from \"../utils/json.js\";\nimport { ensureTool } from \"./tools-manager.js\";\n\nexport const pathSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Directory or file to search\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple directories or files to search\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport const globSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Glob pattern passed to ripgrep\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple glob patterns\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport function toArray<T>(value: T | T[] | undefined): T[] {\n\tif (value === undefined) {\n\t\treturn [];\n\t}\n\treturn Array.isArray(value) ? value : [value];\n}\n\nconst MAX_RIPGREP_OUTPUT_BYTES = 2_000_000; // ~2MB safeguard\nlet ripgrepExecutablePromise: Promise<string | null> | null = null;\n\nfunction ripgrepAbortError(): Error {\n\treturn new Error(\"ripgrep search aborted before start\");\n}\n\nasync function resolveRipgrepExecutable(): Promise<string> {\n\tripgrepExecutablePromise ??= ensureTool(\"rg\", true);\n\tconst executable = await ripgrepExecutablePromise;\n\tif (!executable) {\n\t\tripgrepExecutablePromise = null;\n\t\tthrow new Error(\"ripgrep is not available and could not be downloaded\");\n\t}\n\treturn executable;\n}\n\nfunction throwIfRipgrepAborted(signal?: AbortSignal): void {\n\tif (signal?.aborted) {\n\t\tthrow ripgrepAbortError();\n\t}\n}\n\nasync function resolveRipgrepExecutableWithAbort(\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tthrowIfRipgrepAborted(signal);\n\tif (!signal) {\n\t\treturn await resolveRipgrepExecutable();\n\t}\n\n\treturn await new Promise<string>((resolve, reject) => {\n\t\tconst onAbort = (): void => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\treject(ripgrepAbortError());\n\t\t};\n\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tresolveRipgrepExecutable()\n\t\t\t.then(resolve, reject)\n\t\t\t.finally(() => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t});\n\t});\n}\n\nfunction shellQuoteArg(value: string): string {\n\tif (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {\n\t\treturn value;\n\t}\n\treturn `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport function formatRipgrepCommand(args: string[]): string {\n\treturn [\"rg\", ...args].map(shellQuoteArg).join(\" \");\n}\n\nexport async function runRipgrep(\n\targs: string[],\n\tsignal?: AbortSignal,\n\tcwd?: string,\n): Promise<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number;\n\ttruncated: boolean;\n}> {\n\tconst executable = await resolveRipgrepExecutableWithAbort(signal);\n\tthrowIfRipgrepAborted(signal);\n\tconst child = spawn(executable, args, {\n\t\tcwd: cwd ?? process.cwd(),\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tsignal,\n\t});\n\n\treturn await new Promise((resolve, reject) => {\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet truncated = false;\n\n\t\tchild.stdout.setEncoding(\"utf-8\");\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tif (stdout.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstdout += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stdout.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstdout += chunk;\n\t\t});\n\n\t\tchild.stderr.setEncoding(\"utf-8\");\n\t\tchild.stderr.on(\"data\", (chunk) => {\n\t\t\tif (stderr.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstderr += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stderr.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstderr += chunk;\n\t\t});\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\treject(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? new Error(`Failed to start ripgrep: ${error.message}`)\n\t\t\t\t\t: new Error(`Failed to start ripgrep: ${String(error)}`),\n\t\t\t);\n\t\t});\n\n\t\tchild.once(\"close\", (code) => {\n\t\t\tresolve({ stdout, stderr, exitCode: code ?? 0, truncated });\n\t\t});\n\t});\n}\n\nexport type RipgrepMatch = {\n\tfile: string;\n\tline: number;\n\tcolumn: number;\n\tmatch: string;\n\tlines: string;\n};\n\nexport function parseRipgrepJson(output: string): RipgrepMatch[] {\n\tconst matches: RipgrepMatch[] = [];\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tif (!line.trim()) continue;\n\t\tconst parsed = safeJsonParse<unknown>(line, \"ripgrep output\");\n\t\tif (!parsed.success) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst event = parsed.data as {\n\t\t\ttype?: string;\n\t\t\tdata?: {\n\t\t\t\tpath?: { text?: string };\n\t\t\t\tline_number?: number;\n\t\t\t\tlines?: { text?: string };\n\t\t\t\tsubmatches?: Array<{\n\t\t\t\t\tstart?: number;\n\t\t\t\t\tmatch?: { text?: string };\n\t\t\t\t}>;\n\t\t\t};\n\t\t};\n\t\tif (event.type !== \"match\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst pathText = event.data?.path?.text ?? \"\";\n\t\tfor (const submatch of event.data?.submatches ?? []) {\n\t\t\tmatches.push({\n\t\t\t\tfile: pathText,\n\t\t\t\tline: event.data?.line_number ?? 0,\n\t\t\t\tcolumn: submatch.start ?? 0,\n\t\t\t\tmatch: submatch.match?.text ?? \"\",\n\t\t\t\tlines: event.data?.lines?.text ?? \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn matches;\n}\n"]}
1
+ {"version":3,"file":"ripgrep-utils.js","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { Type } from \"@sinclair/typebox\";\nimport { safeJsonParse } from \"../utils/json.js\";\nimport { ensureTool } from \"./tools-manager.js\";\n\nexport const pathSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Directory or file to search\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple directories or files to search\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport const globSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Glob pattern passed to ripgrep\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple glob patterns\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport function toArray<T>(value: T | T[] | undefined): T[] {\n\tif (value === undefined) {\n\t\treturn [];\n\t}\n\treturn Array.isArray(value) ? value : [value];\n}\n\nconst MAX_RIPGREP_OUTPUT_BYTES = 2_000_000; // ~2MB safeguard\nlet ripgrepExecutablePromise: Promise<string | null> | null = null;\nlet ripgrepInstallController: AbortController | null = null;\nlet ripgrepExecutableWaiters = 0;\n\nfunction ripgrepAbortError(): Error {\n\treturn new Error(\"ripgrep search aborted before start\");\n}\n\nfunction resetRipgrepExecutablePromise(): void {\n\tripgrepExecutablePromise = null;\n\tripgrepInstallController = null;\n}\n\nfunction getRipgrepExecutablePromise(): Promise<string | null> {\n\tif (!ripgrepExecutablePromise) {\n\t\tconst controller = new AbortController();\n\t\tripgrepInstallController = controller;\n\t\tconst promise = ensureTool(\"rg\", true, controller.signal).then(\n\t\t\t(executable) => {\n\t\t\t\tif (ripgrepInstallController === controller) {\n\t\t\t\t\tripgrepInstallController = null;\n\t\t\t\t}\n\t\t\t\tif (ripgrepExecutablePromise === promise && !executable) {\n\t\t\t\t\tripgrepExecutablePromise = null;\n\t\t\t\t}\n\t\t\t\treturn executable;\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\tif (ripgrepInstallController === controller) {\n\t\t\t\t\tripgrepInstallController = null;\n\t\t\t\t}\n\t\t\t\tif (ripgrepExecutablePromise === promise) {\n\t\t\t\t\tripgrepExecutablePromise = null;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t},\n\t\t);\n\t\tripgrepExecutablePromise = promise;\n\t}\n\treturn ripgrepExecutablePromise;\n}\n\nfunction releaseRipgrepExecutableWaiter(signal?: AbortSignal): void {\n\tripgrepExecutableWaiters = Math.max(0, ripgrepExecutableWaiters - 1);\n\tif (signal?.aborted && ripgrepExecutableWaiters === 0) {\n\t\tconst controller = ripgrepInstallController;\n\t\tresetRipgrepExecutablePromise();\n\t\tcontroller?.abort(signal.reason);\n\t}\n}\n\nasync function waitForRipgrepExecutableWithAbort(\n\tpromise: Promise<string | null>,\n\tsignal: AbortSignal,\n): Promise<string | null> {\n\tthrowIfRipgrepAborted(signal);\n\treturn await new Promise<string | null>((resolve, reject) => {\n\t\tconst onAbort = (): void => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\treject(ripgrepAbortError());\n\t\t};\n\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tpromise.then(resolve, reject).finally(() => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t});\n\t});\n}\n\nasync function resolveRipgrepExecutable(signal?: AbortSignal): Promise<string> {\n\tripgrepExecutableWaiters += 1;\n\ttry {\n\t\tconst promise = getRipgrepExecutablePromise();\n\t\tconst executable = signal\n\t\t\t? await waitForRipgrepExecutableWithAbort(promise, signal)\n\t\t\t: await promise;\n\t\tif (!executable) {\n\t\t\tthrow new Error(\"ripgrep is not available and could not be downloaded\");\n\t\t}\n\t\treturn executable;\n\t} finally {\n\t\treleaseRipgrepExecutableWaiter(signal);\n\t}\n}\n\nfunction throwIfRipgrepAborted(signal?: AbortSignal): void {\n\tif (signal?.aborted) {\n\t\tthrow ripgrepAbortError();\n\t}\n}\n\nasync function resolveRipgrepExecutableWithAbort(\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tthrowIfRipgrepAborted(signal);\n\tif (!signal) {\n\t\treturn await resolveRipgrepExecutable();\n\t}\n\n\treturn await resolveRipgrepExecutable(signal);\n}\n\nfunction shellQuoteArg(value: string): string {\n\tif (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {\n\t\treturn value;\n\t}\n\treturn `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport function formatRipgrepCommand(args: string[]): string {\n\treturn [\"rg\", ...args].map(shellQuoteArg).join(\" \");\n}\n\nexport async function runRipgrep(\n\targs: string[],\n\tsignal?: AbortSignal,\n\tcwd?: string,\n): Promise<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number;\n\ttruncated: boolean;\n}> {\n\tconst executable = await resolveRipgrepExecutableWithAbort(signal);\n\tthrowIfRipgrepAborted(signal);\n\tconst child = spawn(executable, args, {\n\t\tcwd: cwd ?? process.cwd(),\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tsignal,\n\t});\n\n\treturn await new Promise((resolve, reject) => {\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet truncated = false;\n\n\t\tchild.stdout.setEncoding(\"utf-8\");\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tif (stdout.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstdout += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stdout.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstdout += chunk;\n\t\t});\n\n\t\tchild.stderr.setEncoding(\"utf-8\");\n\t\tchild.stderr.on(\"data\", (chunk) => {\n\t\t\tif (stderr.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstderr += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stderr.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstderr += chunk;\n\t\t});\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\treject(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? new Error(`Failed to start ripgrep: ${error.message}`)\n\t\t\t\t\t: new Error(`Failed to start ripgrep: ${String(error)}`),\n\t\t\t);\n\t\t});\n\n\t\tchild.once(\"close\", (code) => {\n\t\t\tresolve({ stdout, stderr, exitCode: code ?? 0, truncated });\n\t\t});\n\t});\n}\n\nexport type RipgrepMatch = {\n\tfile: string;\n\tline: number;\n\tcolumn: number;\n\tmatch: string;\n\tlines: string;\n};\n\nexport function parseRipgrepJson(output: string): RipgrepMatch[] {\n\tconst matches: RipgrepMatch[] = [];\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tif (!line.trim()) continue;\n\t\tconst parsed = safeJsonParse<unknown>(line, \"ripgrep output\");\n\t\tif (!parsed.success) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst event = parsed.data as {\n\t\t\ttype?: string;\n\t\t\tdata?: {\n\t\t\t\tpath?: { text?: string };\n\t\t\t\tline_number?: number;\n\t\t\t\tlines?: { text?: string };\n\t\t\t\tsubmatches?: Array<{\n\t\t\t\t\tstart?: number;\n\t\t\t\t\tmatch?: { text?: string };\n\t\t\t\t}>;\n\t\t\t};\n\t\t};\n\t\tif (event.type !== \"match\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst pathText = event.data?.path?.text ?? \"\";\n\t\tfor (const submatch of event.data?.submatches ?? []) {\n\t\t\tmatches.push({\n\t\t\t\tfile: pathText,\n\t\t\t\tline: event.data?.line_number ?? 0,\n\t\t\t\tcolumn: submatch.start ?? 0,\n\t\t\t\tmatch: submatch.match?.text ?? \"\",\n\t\t\t\tlines: event.data?.lines?.text ?? \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn matches;\n}\n"]}
@@ -39,5 +39,5 @@ export declare function getToolPath(tool: "fd" | "rg"): string | null;
39
39
  * @param silent - If true, suppress console output
40
40
  * @returns Path to the tool binary, or null if installation failed
41
41
  */
42
- export declare function ensureTool(tool: "fd" | "rg", silent?: boolean): Promise<string | null>;
42
+ export declare function ensureTool(tool: "fd" | "rg", silent?: boolean, signal?: AbortSignal): Promise<string | null>;
43
43
  //# sourceMappingURL=tools-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools-manager.d.ts","sourceRoot":"","sources":["../../src/tools/tools-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAmHH;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAmB5D;AAmPD;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAC/B,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,MAAM,UAAQ,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCxB"}
1
+ {"version":3,"file":"tools-manager.d.ts","sourceRoot":"","sources":["../../src/tools/tools-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgIH;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAmB5D;AAkSD;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAC/B,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,MAAM,UAAQ,EACd,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoCxB"}
@@ -28,6 +28,14 @@ import { PATHS } from "../config/constants.js";
28
28
  const TOOLS_DIR = PATHS.TOOLS_DIR;
29
29
  const FETCH_TIMEOUT_MS = 30_000;
30
30
  const DOWNLOAD_IDLE_TIMEOUT_MS = 60_000;
31
+ function toolInstallAbortError(config) {
32
+ return new Error(`${config.name} installation aborted`);
33
+ }
34
+ function throwIfToolInstallAborted(config, signal) {
35
+ if (signal?.aborted) {
36
+ throw toolInstallAbortError(config);
37
+ }
38
+ }
31
39
  /**
32
40
  * Registry of supported external tools and their configurations.
33
41
  * Each tool defines platform-specific asset name patterns for GitHub releases.
@@ -126,25 +134,40 @@ export function getToolPath(tool) {
126
134
  * @param repo - Repository in owner/repo format
127
135
  * @returns Version string (without "v" prefix)
128
136
  */
129
- async function fetchWithTimeout(url, init) {
137
+ async function fetchWithTimeout(url, init, signal) {
130
138
  const controller = new AbortController();
131
139
  const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
140
+ const onAbort = () => {
141
+ controller.abort(signal?.reason);
142
+ };
143
+ if (signal?.aborted) {
144
+ onAbort();
145
+ }
146
+ else {
147
+ signal?.addEventListener("abort", onAbort, { once: true });
148
+ }
149
+ const clearFetchTimeout = () => clearTimeout(timeout);
150
+ const cleanup = () => {
151
+ clearFetchTimeout();
152
+ signal?.removeEventListener("abort", onAbort);
153
+ };
132
154
  try {
133
155
  const response = await fetch(url, { ...init, signal: controller.signal });
134
156
  return {
135
157
  response,
136
- clearTimeout: () => clearTimeout(timeout),
158
+ clearTimeout: clearFetchTimeout,
159
+ cleanup,
137
160
  };
138
161
  }
139
162
  catch (error) {
140
- clearTimeout(timeout);
163
+ cleanup();
141
164
  throw error;
142
165
  }
143
166
  }
144
- async function getLatestVersion(repo) {
145
- const { response, clearTimeout: clearFetchTimeout } = await fetchWithTimeout(`https://api.github.com/repos/${repo}/releases/latest`, {
167
+ async function getLatestVersion(repo, signal) {
168
+ const { response, cleanup: cleanupFetch } = await fetchWithTimeout(`https://api.github.com/repos/${repo}/releases/latest`, {
146
169
  headers: { "User-Agent": "composer-coding-agent" },
147
- });
170
+ }, signal);
148
171
  try {
149
172
  if (!response.ok) {
150
173
  throw new Error(`GitHub API error: ${response.status}`);
@@ -154,15 +177,15 @@ async function getLatestVersion(repo) {
154
177
  return data.tag_name.replace(/^v/, "");
155
178
  }
156
179
  finally {
157
- clearFetchTimeout();
180
+ cleanupFetch();
158
181
  }
159
182
  }
160
183
  /**
161
184
  * Download a file from a URL to a local path.
162
185
  * Uses streaming to handle large files efficiently.
163
186
  */
164
- async function downloadFile(url, dest) {
165
- const { response, clearTimeout: clearFetchTimeout } = await fetchWithTimeout(url);
187
+ async function downloadFile(url, dest, signal) {
188
+ const { response, clearTimeout: clearFetchTimeout, cleanup: cleanupFetch, } = await fetchWithTimeout(url, undefined, signal);
166
189
  try {
167
190
  if (!response.ok) {
168
191
  throw new Error(`Failed to download: ${response.status}`);
@@ -175,6 +198,11 @@ async function downloadFile(url, dest) {
175
198
  const stream = Readable.fromWeb(response.body);
176
199
  const fileStream = createWriteStream(dest);
177
200
  let idleTimeoutId = null;
201
+ const onAbort = () => {
202
+ const error = new Error("Tool download aborted");
203
+ stream.destroy(error);
204
+ fileStream.destroy(error);
205
+ };
178
206
  const clearIdleTimeout = () => {
179
207
  if (idleTimeoutId) {
180
208
  clearTimeout(idleTimeoutId);
@@ -194,6 +222,12 @@ async function downloadFile(url, dest) {
194
222
  clearIdleTimeout();
195
223
  };
196
224
  resetIdleTimeout();
225
+ if (signal?.aborted) {
226
+ onAbort();
227
+ }
228
+ else {
229
+ signal?.addEventListener("abort", onAbort, { once: true });
230
+ }
197
231
  stream.on("data", resetIdleTimeout);
198
232
  stream.on("end", onStreamEnd);
199
233
  stream.on("close", onStreamEnd);
@@ -206,11 +240,12 @@ async function downloadFile(url, dest) {
206
240
  stream.off("end", onStreamEnd);
207
241
  stream.off("close", onStreamEnd);
208
242
  stream.off("error", onStreamEnd);
243
+ signal?.removeEventListener("abort", onAbort);
209
244
  clearIdleTimeout();
210
245
  }
211
246
  }
212
247
  finally {
213
- clearFetchTimeout();
248
+ cleanupFetch();
214
249
  }
215
250
  }
216
251
  function runExtractor(command, args, description) {
@@ -259,14 +294,16 @@ function findBinary(root, binaryName) {
259
294
  * @param tool - The tool identifier ("fd" or "rg")
260
295
  * @returns Path to the installed binary
261
296
  */
262
- async function downloadTool(tool) {
297
+ async function downloadTool(tool, signal) {
263
298
  const config = TOOLS[tool];
264
299
  if (!config)
265
300
  throw new Error(`Unknown tool: ${tool}`);
301
+ throwIfToolInstallAborted(config, signal);
266
302
  const plat = platform();
267
303
  const architecture = arch();
268
304
  // Get the latest version from GitHub
269
- const version = await getLatestVersion(config.repo);
305
+ const version = await getLatestVersion(config.repo, signal);
306
+ throwIfToolInstallAborted(config, signal);
270
307
  // Determine the correct asset for this platform
271
308
  const assetName = config.getAssetName(version, plat, architecture);
272
309
  if (!assetName) {
@@ -280,7 +317,8 @@ async function downloadTool(tool) {
280
317
  const binaryExt = plat === "win32" ? ".exe" : "";
281
318
  const binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);
282
319
  // Download the release archive
283
- await downloadFile(downloadUrl, archivePath);
320
+ await downloadFile(downloadUrl, archivePath, signal);
321
+ throwIfToolInstallAborted(config, signal);
284
322
  // Create temp directory for extraction
285
323
  const extractDir = mkdtempSync(join(TOOLS_DIR, "extract-"));
286
324
  try {
@@ -325,27 +363,31 @@ async function downloadTool(tool) {
325
363
  * @param silent - If true, suppress console output
326
364
  * @returns Path to the tool binary, or null if installation failed
327
365
  */
328
- export async function ensureTool(tool, silent = false) {
366
+ export async function ensureTool(tool, silent = false, signal) {
367
+ const config = TOOLS[tool];
368
+ if (!config)
369
+ return null;
370
+ throwIfToolInstallAborted(config, signal);
329
371
  // Check if already installed
330
372
  const existingPath = getToolPath(tool);
331
373
  if (existingPath) {
332
374
  return existingPath;
333
375
  }
334
- const config = TOOLS[tool];
335
- if (!config)
336
- return null;
337
376
  // Notify user about download (unless silent)
338
377
  if (!silent) {
339
378
  console.log(chalk.dim(`${config.name} not found. Downloading...`));
340
379
  }
341
380
  try {
342
- const path = await downloadTool(tool);
381
+ const path = await downloadTool(tool, signal);
343
382
  if (!silent) {
344
383
  console.log(chalk.dim(`${config.name} installed to ${path}`));
345
384
  }
346
385
  return path;
347
386
  }
348
387
  catch (e) {
388
+ if (signal?.aborted) {
389
+ throw toolInstallAbortError(config);
390
+ }
349
391
  // Download failed - log and return null (tool is optional)
350
392
  if (!silent) {
351
393
  console.log(chalk.yellow(`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`));
@@ -1 +1 @@
1
- {"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/tools/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * External Tools Manager\n *\n * This module manages optional external command-line tools that enhance\n * the agent's capabilities. Currently supported tools:\n *\n * - fd: Fast file finder (sharkdp/fd)\n * - rg: Ripgrep for fast content search (BurntSushi/ripgrep)\n *\n * The manager handles:\n * 1. Detection of existing installations (system PATH or local)\n * 2. Automatic download from GitHub releases\n * 3. Platform-specific binary selection (macOS, Linux, Windows)\n * 4. Architecture support (x86_64, arm64)\n *\n * Tools are installed to ~/.maestro/tools/ to avoid requiring\n * system-wide permissions or polluting the system PATH.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport {\n\tchmodSync,\n\tcreateWriteStream,\n\texistsSync,\n\tmkdirSync,\n\tmkdtempSync,\n\treaddirSync,\n\trenameSync,\n\trmSync,\n} from \"node:fs\";\nimport { arch, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { finished } from \"node:stream/promises\";\nimport chalk from \"chalk\";\nimport { PATHS } from \"../config/constants.js\";\n\n/** Directory where downloaded tools are installed */\nconst TOOLS_DIR = PATHS.TOOLS_DIR;\nconst FETCH_TIMEOUT_MS = 30_000;\nconst DOWNLOAD_IDLE_TIMEOUT_MS = 60_000;\n\n/**\n * Configuration for an external tool.\n * Defines how to find, download, and install the tool.\n */\ninterface ToolConfig {\n\t/** Human-readable name for display */\n\tname: string;\n\t/** GitHub repository in owner/repo format */\n\trepo: string;\n\t/** Name of the binary executable */\n\tbinaryName: string;\n\t/** Prefix for version tags (e.g., \"v\" for \"v1.0.0\") */\n\ttagPrefix: string;\n\t/** Function to determine the release asset name for a platform */\n\tgetAssetName: (\n\t\tversion: string,\n\t\tplat: string,\n\t\tarchitecture: string,\n\t) => string | null;\n}\n\n/**\n * Registry of supported external tools and their configurations.\n * Each tool defines platform-specific asset name patterns for GitHub releases.\n */\nconst TOOLS: Record<string, ToolConfig> = {\n\t// fd: A simple, fast and user-friendly alternative to 'find'\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\ttagPrefix: \"v\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\t// Map Node.js arch names to Rust target names\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-unknown-linux-gnu.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null; // Unsupported platform\n\t\t},\n\t},\n\n\t// ripgrep: A line-oriented search tool (faster than grep)\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\", // ripgrep uses bare version numbers (e.g., \"14.0.0\")\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"linux\") {\n\t\t\t\t// Linux x86_64 uses musl for better compatibility\n\t\t\t\tif (architecture === \"arm64\") {\n\t\t\t\t\treturn `ripgrep-${version}-aarch64-unknown-linux-gnu.tar.gz`;\n\t\t\t\t}\n\t\t\t\treturn `ripgrep-${version}-x86_64-unknown-linux-musl.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null; // Unsupported platform\n\t\t},\n\t},\n};\n\n/**\n * Check if a command exists on the system PATH.\n * Uses --version flag as a safe probe that most tools support.\n */\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\" });\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Get the path to a tool binary, checking local install first then system PATH.\n *\n * Priority order:\n * 1. Local installation in ~/.maestro/tools/\n * 2. System PATH\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @returns Path to the binary, or null if not found\n */\nexport function getToolPath(tool: \"fd\" | \"rg\"): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first (preferred - known version)\n\tconst localPath = join(\n\t\tTOOLS_DIR,\n\t\tconfig.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"),\n\t);\n\tif (existsSync(localPath)) {\n\t\treturn localPath;\n\t}\n\n\t// Fall back to system PATH (may be different version)\n\tif (commandExists(config.binaryName)) {\n\t\treturn config.binaryName;\n\t}\n\n\treturn null;\n}\n\n/**\n * Fetch the latest version number from GitHub releases API.\n *\n * @param repo - Repository in owner/repo format\n * @returns Version string (without \"v\" prefix)\n */\nasync function fetchWithTimeout(\n\turl: string,\n\tinit?: RequestInit,\n): Promise<{ response: Response; clearTimeout: () => void }> {\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\ttry {\n\t\tconst response = await fetch(url, { ...init, signal: controller.signal });\n\t\treturn {\n\t\t\tresponse,\n\t\t\tclearTimeout: () => clearTimeout(timeout),\n\t\t};\n\t} catch (error) {\n\t\tclearTimeout(timeout);\n\t\tthrow error;\n\t}\n}\n\nasync function getLatestVersion(repo: string): Promise<string> {\n\tconst { response, clearTimeout: clearFetchTimeout } = await fetchWithTimeout(\n\t\t`https://api.github.com/repos/${repo}/releases/latest`,\n\t\t{\n\t\t\theaders: { \"User-Agent\": \"composer-coding-agent\" },\n\t\t},\n\t);\n\ttry {\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`GitHub API error: ${response.status}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as { tag_name: string };\n\t\t// Strip \"v\" prefix if present for consistent version handling\n\t\treturn data.tag_name.replace(/^v/, \"\");\n\t} finally {\n\t\tclearFetchTimeout();\n\t}\n}\n\n/**\n * Download a file from a URL to a local path.\n * Uses streaming to handle large files efficiently.\n */\nasync function downloadFile(url: string, dest: string): Promise<void> {\n\tconst { response, clearTimeout: clearFetchTimeout } =\n\t\tawait fetchWithTimeout(url);\n\ttry {\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to download: ${response.status}`);\n\t\t}\n\n\t\tif (!response.body) {\n\t\t\tthrow new Error(\"No response body\");\n\t\t}\n\n\t\tclearFetchTimeout();\n\n\t\t// Stream the response directly to disk\n\t\tconst stream = Readable.fromWeb(\n\t\t\tresponse.body as Parameters<typeof Readable.fromWeb>[0],\n\t\t);\n\t\tconst fileStream = createWriteStream(dest);\n\t\tlet idleTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\t\tconst clearIdleTimeout = (): void => {\n\t\t\tif (idleTimeoutId) {\n\t\t\t\tclearTimeout(idleTimeoutId);\n\t\t\t\tidleTimeoutId = null;\n\t\t\t}\n\t\t};\n\n\t\tconst resetIdleTimeout = (): void => {\n\t\t\tclearIdleTimeout();\n\t\t\tidleTimeoutId = setTimeout(() => {\n\t\t\t\tconst error = new Error(\n\t\t\t\t\t`Tool download idle timeout after ${Math.round(DOWNLOAD_IDLE_TIMEOUT_MS / 1000)}s`,\n\t\t\t\t);\n\t\t\t\tstream.destroy();\n\t\t\t\tfileStream.destroy();\n\t\t\t\tfileStream.emit(\"error\", error);\n\t\t\t}, DOWNLOAD_IDLE_TIMEOUT_MS);\n\t\t};\n\n\t\tconst onStreamEnd = (): void => {\n\t\t\tclearIdleTimeout();\n\t\t};\n\n\t\tresetIdleTimeout();\n\t\tstream.on(\"data\", resetIdleTimeout);\n\t\tstream.on(\"end\", onStreamEnd);\n\t\tstream.on(\"close\", onStreamEnd);\n\t\tstream.on(\"error\", onStreamEnd);\n\n\t\ttry {\n\t\t\tawait finished(stream.pipe(fileStream));\n\t\t} finally {\n\t\t\tstream.off(\"data\", resetIdleTimeout);\n\t\t\tstream.off(\"end\", onStreamEnd);\n\t\t\tstream.off(\"close\", onStreamEnd);\n\t\t\tstream.off(\"error\", onStreamEnd);\n\t\t\tclearIdleTimeout();\n\t\t}\n\t} finally {\n\t\tclearFetchTimeout();\n\t}\n}\n\nfunction runExtractor(\n\tcommand: string,\n\targs: string[],\n\tdescription: string,\n): void {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\" });\n\tif (result.error) {\n\t\tthrow new Error(`${description} failed: ${result.error.message}`);\n\t}\n\tif (result.status !== 0) {\n\t\tconst stderr = result.stderr?.toString().trim();\n\t\tthrow new Error(\n\t\t\t`${description} exited with code ${result.status}${\n\t\t\t\tstderr ? `: ${stderr}` : \"\"\n\t\t\t}`,\n\t\t);\n\t}\n}\n\nfunction findBinary(root: string, binaryName: string): string | null {\n\tconst stack = [root];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop();\n\t\tif (!current) continue;\n\t\tconst entries = readdirSync(current, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst entryPath = join(current, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(entryPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (entry.isFile() && entry.name === binaryName) {\n\t\t\t\treturn entryPath;\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Download and install a tool from GitHub releases.\n *\n * Process:\n * 1. Fetch latest version from GitHub API\n * 2. Determine platform-specific asset name\n * 3. Download the release archive\n * 4. Extract the binary\n * 5. Move to tools directory and set permissions\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @returns Path to the installed binary\n */\nasync function downloadTool(tool: \"fd\" | \"rg\"): Promise<string> {\n\tconst config = TOOLS[tool];\n\tif (!config) throw new Error(`Unknown tool: ${tool}`);\n\n\tconst plat = platform();\n\tconst architecture = arch();\n\n\t// Get the latest version from GitHub\n\tconst version = await getLatestVersion(config.repo);\n\n\t// Determine the correct asset for this platform\n\tconst assetName = config.getAssetName(version, plat, architecture);\n\tif (!assetName) {\n\t\tthrow new Error(`Unsupported platform: ${plat}/${architecture}`);\n\t}\n\n\t// Ensure tools directory exists\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\t// Build paths\n\tconst downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n\tconst archivePath = join(TOOLS_DIR, assetName);\n\tconst binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n\tconst binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n\t// Download the release archive\n\tawait downloadFile(downloadUrl, archivePath);\n\n\t// Create temp directory for extraction\n\tconst extractDir = mkdtempSync(join(TOOLS_DIR, \"extract-\"));\n\n\ttry {\n\t\t// Extract based on archive type\n\t\tif (assetName.endsWith(\".tar.gz\")) {\n\t\t\trunExtractor(\n\t\t\t\t\"tar\",\n\t\t\t\t[\"-xzf\", archivePath, \"-C\", extractDir],\n\t\t\t\t\"tar extract\",\n\t\t\t);\n\t\t} else if (assetName.endsWith(\".zip\")) {\n\t\t\trunExtractor(\n\t\t\t\t\"unzip\",\n\t\t\t\t[\"-o\", archivePath, \"-d\", extractDir],\n\t\t\t\t\"unzip extract\",\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\tconst extractedBinary =\n\t\t\tfindBinary(extractDir, config.binaryName + binaryExt) ??\n\t\t\t(() => {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Binary not found in archive: ${config.binaryName}${binaryExt}`,\n\t\t\t\t);\n\t\t\t})();\n\n\t\t// Move binary to final location\n\t\trmSync(binaryPath, { force: true });\n\t\trenameSync(extractedBinary, binaryPath);\n\n\t\t// Make executable on Unix systems\n\t\tif (plat !== \"win32\") {\n\t\t\tchmodSync(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Clean up downloaded archive and temp directory\n\t\trmSync(archivePath, { force: true });\n\t\trmSync(extractDir, { recursive: true, force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n/**\n * Ensure a tool is available, downloading it if necessary.\n *\n * This is the main entry point for tool management. It:\n * 1. Checks if the tool already exists (local or system)\n * 2. If not, downloads and installs it\n * 3. Returns the path to use, or null if unavailable\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @param silent - If true, suppress console output\n * @returns Path to the tool binary, or null if installation failed\n */\nexport async function ensureTool(\n\ttool: \"fd\" | \"rg\",\n\tsilent = false,\n): Promise<string | null> {\n\t// Check if already installed\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Notify user about download (unless silent)\n\tif (!silent) {\n\t\tconsole.log(chalk.dim(`${config.name} not found. Downloading...`));\n\t}\n\n\ttry {\n\t\tconst path = await downloadTool(tool);\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.dim(`${config.name} installed to ${path}`));\n\t\t}\n\t\treturn path;\n\t} catch (e) {\n\t\t// Download failed - log and return null (tool is optional)\n\t\tif (!silent) {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(\n\t\t\t\t\t`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n}\n"]}
1
+ {"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/tools/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * External Tools Manager\n *\n * This module manages optional external command-line tools that enhance\n * the agent's capabilities. Currently supported tools:\n *\n * - fd: Fast file finder (sharkdp/fd)\n * - rg: Ripgrep for fast content search (BurntSushi/ripgrep)\n *\n * The manager handles:\n * 1. Detection of existing installations (system PATH or local)\n * 2. Automatic download from GitHub releases\n * 3. Platform-specific binary selection (macOS, Linux, Windows)\n * 4. Architecture support (x86_64, arm64)\n *\n * Tools are installed to ~/.maestro/tools/ to avoid requiring\n * system-wide permissions or polluting the system PATH.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport {\n\tchmodSync,\n\tcreateWriteStream,\n\texistsSync,\n\tmkdirSync,\n\tmkdtempSync,\n\treaddirSync,\n\trenameSync,\n\trmSync,\n} from \"node:fs\";\nimport { arch, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { finished } from \"node:stream/promises\";\nimport chalk from \"chalk\";\nimport { PATHS } from \"../config/constants.js\";\n\n/** Directory where downloaded tools are installed */\nconst TOOLS_DIR = PATHS.TOOLS_DIR;\nconst FETCH_TIMEOUT_MS = 30_000;\nconst DOWNLOAD_IDLE_TIMEOUT_MS = 60_000;\n\n/**\n * Configuration for an external tool.\n * Defines how to find, download, and install the tool.\n */\ninterface ToolConfig {\n\t/** Human-readable name for display */\n\tname: string;\n\t/** GitHub repository in owner/repo format */\n\trepo: string;\n\t/** Name of the binary executable */\n\tbinaryName: string;\n\t/** Prefix for version tags (e.g., \"v\" for \"v1.0.0\") */\n\ttagPrefix: string;\n\t/** Function to determine the release asset name for a platform */\n\tgetAssetName: (\n\t\tversion: string,\n\t\tplat: string,\n\t\tarchitecture: string,\n\t) => string | null;\n}\n\nfunction toolInstallAbortError(config: ToolConfig): Error {\n\treturn new Error(`${config.name} installation aborted`);\n}\n\nfunction throwIfToolInstallAborted(\n\tconfig: ToolConfig,\n\tsignal?: AbortSignal,\n): void {\n\tif (signal?.aborted) {\n\t\tthrow toolInstallAbortError(config);\n\t}\n}\n\n/**\n * Registry of supported external tools and their configurations.\n * Each tool defines platform-specific asset name patterns for GitHub releases.\n */\nconst TOOLS: Record<string, ToolConfig> = {\n\t// fd: A simple, fast and user-friendly alternative to 'find'\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\ttagPrefix: \"v\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\t// Map Node.js arch names to Rust target names\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-unknown-linux-gnu.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null; // Unsupported platform\n\t\t},\n\t},\n\n\t// ripgrep: A line-oriented search tool (faster than grep)\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\", // ripgrep uses bare version numbers (e.g., \"14.0.0\")\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"linux\") {\n\t\t\t\t// Linux x86_64 uses musl for better compatibility\n\t\t\t\tif (architecture === \"arm64\") {\n\t\t\t\t\treturn `ripgrep-${version}-aarch64-unknown-linux-gnu.tar.gz`;\n\t\t\t\t}\n\t\t\t\treturn `ripgrep-${version}-x86_64-unknown-linux-musl.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null; // Unsupported platform\n\t\t},\n\t},\n};\n\n/**\n * Check if a command exists on the system PATH.\n * Uses --version flag as a safe probe that most tools support.\n */\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\" });\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Get the path to a tool binary, checking local install first then system PATH.\n *\n * Priority order:\n * 1. Local installation in ~/.maestro/tools/\n * 2. System PATH\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @returns Path to the binary, or null if not found\n */\nexport function getToolPath(tool: \"fd\" | \"rg\"): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first (preferred - known version)\n\tconst localPath = join(\n\t\tTOOLS_DIR,\n\t\tconfig.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"),\n\t);\n\tif (existsSync(localPath)) {\n\t\treturn localPath;\n\t}\n\n\t// Fall back to system PATH (may be different version)\n\tif (commandExists(config.binaryName)) {\n\t\treturn config.binaryName;\n\t}\n\n\treturn null;\n}\n\n/**\n * Fetch the latest version number from GitHub releases API.\n *\n * @param repo - Repository in owner/repo format\n * @returns Version string (without \"v\" prefix)\n */\nasync function fetchWithTimeout(\n\turl: string,\n\tinit?: RequestInit,\n\tsignal?: AbortSignal,\n): Promise<{\n\tresponse: Response;\n\tclearTimeout: () => void;\n\tcleanup: () => void;\n}> {\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\tconst onAbort = (): void => {\n\t\tcontroller.abort(signal?.reason);\n\t};\n\tif (signal?.aborted) {\n\t\tonAbort();\n\t} else {\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t}\n\tconst clearFetchTimeout = (): void => clearTimeout(timeout);\n\tconst cleanup = (): void => {\n\t\tclearFetchTimeout();\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t};\n\ttry {\n\t\tconst response = await fetch(url, { ...init, signal: controller.signal });\n\t\treturn {\n\t\t\tresponse,\n\t\t\tclearTimeout: clearFetchTimeout,\n\t\t\tcleanup,\n\t\t};\n\t} catch (error) {\n\t\tcleanup();\n\t\tthrow error;\n\t}\n}\n\nasync function getLatestVersion(\n\trepo: string,\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tconst { response, cleanup: cleanupFetch } = await fetchWithTimeout(\n\t\t`https://api.github.com/repos/${repo}/releases/latest`,\n\t\t{\n\t\t\theaders: { \"User-Agent\": \"composer-coding-agent\" },\n\t\t},\n\t\tsignal,\n\t);\n\ttry {\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`GitHub API error: ${response.status}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as { tag_name: string };\n\t\t// Strip \"v\" prefix if present for consistent version handling\n\t\treturn data.tag_name.replace(/^v/, \"\");\n\t} finally {\n\t\tcleanupFetch();\n\t}\n}\n\n/**\n * Download a file from a URL to a local path.\n * Uses streaming to handle large files efficiently.\n */\nasync function downloadFile(\n\turl: string,\n\tdest: string,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst {\n\t\tresponse,\n\t\tclearTimeout: clearFetchTimeout,\n\t\tcleanup: cleanupFetch,\n\t} = await fetchWithTimeout(url, undefined, signal);\n\ttry {\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to download: ${response.status}`);\n\t\t}\n\n\t\tif (!response.body) {\n\t\t\tthrow new Error(\"No response body\");\n\t\t}\n\n\t\tclearFetchTimeout();\n\n\t\t// Stream the response directly to disk\n\t\tconst stream = Readable.fromWeb(\n\t\t\tresponse.body as Parameters<typeof Readable.fromWeb>[0],\n\t\t);\n\t\tconst fileStream = createWriteStream(dest);\n\t\tlet idleTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\t\tconst onAbort = (): void => {\n\t\t\tconst error = new Error(\"Tool download aborted\");\n\t\t\tstream.destroy(error);\n\t\t\tfileStream.destroy(error);\n\t\t};\n\n\t\tconst clearIdleTimeout = (): void => {\n\t\t\tif (idleTimeoutId) {\n\t\t\t\tclearTimeout(idleTimeoutId);\n\t\t\t\tidleTimeoutId = null;\n\t\t\t}\n\t\t};\n\n\t\tconst resetIdleTimeout = (): void => {\n\t\t\tclearIdleTimeout();\n\t\t\tidleTimeoutId = setTimeout(() => {\n\t\t\t\tconst error = new Error(\n\t\t\t\t\t`Tool download idle timeout after ${Math.round(DOWNLOAD_IDLE_TIMEOUT_MS / 1000)}s`,\n\t\t\t\t);\n\t\t\t\tstream.destroy();\n\t\t\t\tfileStream.destroy();\n\t\t\t\tfileStream.emit(\"error\", error);\n\t\t\t}, DOWNLOAD_IDLE_TIMEOUT_MS);\n\t\t};\n\n\t\tconst onStreamEnd = (): void => {\n\t\t\tclearIdleTimeout();\n\t\t};\n\n\t\tresetIdleTimeout();\n\t\tif (signal?.aborted) {\n\t\t\tonAbort();\n\t\t} else {\n\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\tstream.on(\"data\", resetIdleTimeout);\n\t\tstream.on(\"end\", onStreamEnd);\n\t\tstream.on(\"close\", onStreamEnd);\n\t\tstream.on(\"error\", onStreamEnd);\n\n\t\ttry {\n\t\t\tawait finished(stream.pipe(fileStream));\n\t\t} finally {\n\t\t\tstream.off(\"data\", resetIdleTimeout);\n\t\t\tstream.off(\"end\", onStreamEnd);\n\t\t\tstream.off(\"close\", onStreamEnd);\n\t\t\tstream.off(\"error\", onStreamEnd);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tclearIdleTimeout();\n\t\t}\n\t} finally {\n\t\tcleanupFetch();\n\t}\n}\n\nfunction runExtractor(\n\tcommand: string,\n\targs: string[],\n\tdescription: string,\n): void {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\" });\n\tif (result.error) {\n\t\tthrow new Error(`${description} failed: ${result.error.message}`);\n\t}\n\tif (result.status !== 0) {\n\t\tconst stderr = result.stderr?.toString().trim();\n\t\tthrow new Error(\n\t\t\t`${description} exited with code ${result.status}${\n\t\t\t\tstderr ? `: ${stderr}` : \"\"\n\t\t\t}`,\n\t\t);\n\t}\n}\n\nfunction findBinary(root: string, binaryName: string): string | null {\n\tconst stack = [root];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop();\n\t\tif (!current) continue;\n\t\tconst entries = readdirSync(current, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst entryPath = join(current, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(entryPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (entry.isFile() && entry.name === binaryName) {\n\t\t\t\treturn entryPath;\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Download and install a tool from GitHub releases.\n *\n * Process:\n * 1. Fetch latest version from GitHub API\n * 2. Determine platform-specific asset name\n * 3. Download the release archive\n * 4. Extract the binary\n * 5. Move to tools directory and set permissions\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @returns Path to the installed binary\n */\nasync function downloadTool(\n\ttool: \"fd\" | \"rg\",\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tconst config = TOOLS[tool];\n\tif (!config) throw new Error(`Unknown tool: ${tool}`);\n\tthrowIfToolInstallAborted(config, signal);\n\n\tconst plat = platform();\n\tconst architecture = arch();\n\n\t// Get the latest version from GitHub\n\tconst version = await getLatestVersion(config.repo, signal);\n\tthrowIfToolInstallAborted(config, signal);\n\n\t// Determine the correct asset for this platform\n\tconst assetName = config.getAssetName(version, plat, architecture);\n\tif (!assetName) {\n\t\tthrow new Error(`Unsupported platform: ${plat}/${architecture}`);\n\t}\n\n\t// Ensure tools directory exists\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\t// Build paths\n\tconst downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n\tconst archivePath = join(TOOLS_DIR, assetName);\n\tconst binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n\tconst binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n\t// Download the release archive\n\tawait downloadFile(downloadUrl, archivePath, signal);\n\tthrowIfToolInstallAborted(config, signal);\n\n\t// Create temp directory for extraction\n\tconst extractDir = mkdtempSync(join(TOOLS_DIR, \"extract-\"));\n\n\ttry {\n\t\t// Extract based on archive type\n\t\tif (assetName.endsWith(\".tar.gz\")) {\n\t\t\trunExtractor(\n\t\t\t\t\"tar\",\n\t\t\t\t[\"-xzf\", archivePath, \"-C\", extractDir],\n\t\t\t\t\"tar extract\",\n\t\t\t);\n\t\t} else if (assetName.endsWith(\".zip\")) {\n\t\t\trunExtractor(\n\t\t\t\t\"unzip\",\n\t\t\t\t[\"-o\", archivePath, \"-d\", extractDir],\n\t\t\t\t\"unzip extract\",\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\tconst extractedBinary =\n\t\t\tfindBinary(extractDir, config.binaryName + binaryExt) ??\n\t\t\t(() => {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Binary not found in archive: ${config.binaryName}${binaryExt}`,\n\t\t\t\t);\n\t\t\t})();\n\n\t\t// Move binary to final location\n\t\trmSync(binaryPath, { force: true });\n\t\trenameSync(extractedBinary, binaryPath);\n\n\t\t// Make executable on Unix systems\n\t\tif (plat !== \"win32\") {\n\t\t\tchmodSync(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Clean up downloaded archive and temp directory\n\t\trmSync(archivePath, { force: true });\n\t\trmSync(extractDir, { recursive: true, force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n/**\n * Ensure a tool is available, downloading it if necessary.\n *\n * This is the main entry point for tool management. It:\n * 1. Checks if the tool already exists (local or system)\n * 2. If not, downloads and installs it\n * 3. Returns the path to use, or null if unavailable\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @param silent - If true, suppress console output\n * @returns Path to the tool binary, or null if installation failed\n */\nexport async function ensureTool(\n\ttool: \"fd\" | \"rg\",\n\tsilent = false,\n\tsignal?: AbortSignal,\n): Promise<string | null> {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\tthrowIfToolInstallAborted(config, signal);\n\n\t// Check if already installed\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\t// Notify user about download (unless silent)\n\tif (!silent) {\n\t\tconsole.log(chalk.dim(`${config.name} not found. Downloading...`));\n\t}\n\n\ttry {\n\t\tconst path = await downloadTool(tool, signal);\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.dim(`${config.name} installed to ${path}`));\n\t\t}\n\t\treturn path;\n\t} catch (e) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow toolInstallAbortError(config);\n\t\t}\n\t\t// Download failed - log and return null (tool is optional)\n\t\tif (!silent) {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(\n\t\t\t\t\t`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n}\n"]}
package/dist/version.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "0.10.38",
3
- "notes": "Maestro by EvalOps - Deterministic coding agent with TUI/CLI and Web UI for AI-assisted development v0.10.38 is now available."
2
+ "version": "0.10.39",
3
+ "notes": "Maestro by EvalOps - Deterministic coding agent with TUI/CLI and Web UI for AI-assisted development v0.10.39 is now available."
4
4
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@evalops/maestro",
3
3
  "description": "Maestro by EvalOps - Deterministic coding agent with TUI/CLI and Web UI for AI-assisted development",
4
- "version": "0.10.38",
4
+ "version": "0.10.39",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "workspaces": [