@caupulican/pi-adaptative 0.80.99 → 0.80.102

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.
Files changed (129) hide show
  1. package/CHANGELOG.md +203 -0
  2. package/dist/core/agent-session.d.ts +72 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +437 -46
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/autonomy/envelope-enforcement.d.ts +5 -0
  7. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  8. package/dist/core/autonomy/envelope-enforcement.js +28 -4
  9. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  10. package/dist/core/autonomy/path-scope.d.ts.map +1 -1
  11. package/dist/core/autonomy/path-scope.js +33 -2
  12. package/dist/core/autonomy/path-scope.js.map +1 -1
  13. package/dist/core/bash-executor.d.ts.map +1 -1
  14. package/dist/core/bash-executor.js +15 -7
  15. package/dist/core/bash-executor.js.map +1 -1
  16. package/dist/core/context/brain-curator.d.ts +10 -0
  17. package/dist/core/context/brain-curator.d.ts.map +1 -1
  18. package/dist/core/context/brain-curator.js +19 -2
  19. package/dist/core/context/brain-curator.js.map +1 -1
  20. package/dist/core/context-gc.d.ts +8 -3
  21. package/dist/core/context-gc.d.ts.map +1 -1
  22. package/dist/core/context-gc.js +5 -0
  23. package/dist/core/context-gc.js.map +1 -1
  24. package/dist/core/delegation/worker-actions.d.ts +0 -10
  25. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  26. package/dist/core/delegation/worker-actions.js +5 -7
  27. package/dist/core/delegation/worker-actions.js.map +1 -1
  28. package/dist/core/delegation/worker-result.d.ts +6 -0
  29. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  30. package/dist/core/delegation/worker-result.js +10 -4
  31. package/dist/core/delegation/worker-result.js.map +1 -1
  32. package/dist/core/delegation/worker-runner.d.ts +3 -0
  33. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  34. package/dist/core/delegation/worker-runner.js +6 -1
  35. package/dist/core/delegation/worker-runner.js.map +1 -1
  36. package/dist/core/doctor.d.ts +66 -0
  37. package/dist/core/doctor.d.ts.map +1 -0
  38. package/dist/core/doctor.js +144 -0
  39. package/dist/core/doctor.js.map +1 -0
  40. package/dist/core/learning/learning-audit.d.ts +13 -1
  41. package/dist/core/learning/learning-audit.d.ts.map +1 -1
  42. package/dist/core/learning/learning-audit.js +22 -1
  43. package/dist/core/learning/learning-audit.js.map +1 -1
  44. package/dist/core/learning/learning-gate.d.ts +3 -0
  45. package/dist/core/learning/learning-gate.d.ts.map +1 -1
  46. package/dist/core/learning/learning-gate.js +13 -4
  47. package/dist/core/learning/learning-gate.js.map +1 -1
  48. package/dist/core/models/default-model-suggestions.d.ts +34 -0
  49. package/dist/core/models/default-model-suggestions.d.ts.map +1 -0
  50. package/dist/core/models/default-model-suggestions.js +58 -0
  51. package/dist/core/models/default-model-suggestions.js.map +1 -0
  52. package/dist/core/models/local-registration.d.ts +2 -0
  53. package/dist/core/models/local-registration.d.ts.map +1 -1
  54. package/dist/core/models/local-registration.js +2 -1
  55. package/dist/core/models/local-registration.js.map +1 -1
  56. package/dist/core/models/local-runtime.d.ts +76 -2
  57. package/dist/core/models/local-runtime.d.ts.map +1 -1
  58. package/dist/core/models/local-runtime.js +233 -21
  59. package/dist/core/models/local-runtime.js.map +1 -1
  60. package/dist/core/profile-resource-selection.d.ts +15 -3
  61. package/dist/core/profile-resource-selection.d.ts.map +1 -1
  62. package/dist/core/profile-resource-selection.js +19 -9
  63. package/dist/core/profile-resource-selection.js.map +1 -1
  64. package/dist/core/resource-loader.d.ts +20 -0
  65. package/dist/core/resource-loader.d.ts.map +1 -1
  66. package/dist/core/resource-loader.js +42 -16
  67. package/dist/core/resource-loader.js.map +1 -1
  68. package/dist/core/settings-manager.d.ts +30 -0
  69. package/dist/core/settings-manager.d.ts.map +1 -1
  70. package/dist/core/settings-manager.js +65 -3
  71. package/dist/core/settings-manager.js.map +1 -1
  72. package/dist/core/skills.d.ts +14 -0
  73. package/dist/core/skills.d.ts.map +1 -1
  74. package/dist/core/skills.js +8 -0
  75. package/dist/core/skills.js.map +1 -1
  76. package/dist/core/slash-commands.d.ts.map +1 -1
  77. package/dist/core/slash-commands.js +1 -1
  78. package/dist/core/slash-commands.js.map +1 -1
  79. package/dist/core/tools/fff-search-backend.d.ts +34 -1
  80. package/dist/core/tools/fff-search-backend.d.ts.map +1 -1
  81. package/dist/core/tools/fff-search-backend.js +43 -4
  82. package/dist/core/tools/fff-search-backend.js.map +1 -1
  83. package/dist/core/tools/find.d.ts +16 -0
  84. package/dist/core/tools/find.d.ts.map +1 -1
  85. package/dist/core/tools/find.js +17 -3
  86. package/dist/core/tools/find.js.map +1 -1
  87. package/dist/core/tools/grep.d.ts +20 -0
  88. package/dist/core/tools/grep.d.ts.map +1 -1
  89. package/dist/core/tools/grep.js +15 -3
  90. package/dist/core/tools/grep.js.map +1 -1
  91. package/dist/main.d.ts.map +1 -1
  92. package/dist/main.js +17 -0
  93. package/dist/main.js.map +1 -1
  94. package/dist/modes/interactive/components/fitness-role-selector.d.ts +6 -2
  95. package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -1
  96. package/dist/modes/interactive/components/fitness-role-selector.js +55 -45
  97. package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -1
  98. package/dist/modes/interactive/components/model-suggestion-selector.d.ts +13 -0
  99. package/dist/modes/interactive/components/model-suggestion-selector.d.ts.map +1 -0
  100. package/dist/modes/interactive/components/model-suggestion-selector.js +40 -0
  101. package/dist/modes/interactive/components/model-suggestion-selector.js.map +1 -0
  102. package/dist/modes/interactive/components/profile-resource-editor.d.ts +3 -0
  103. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  104. package/dist/modes/interactive/components/profile-resource-editor.js +16 -2
  105. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  106. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/settings-selector.js +98 -0
  108. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  109. package/dist/modes/interactive/interactive-mode.d.ts +8 -1
  110. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  111. package/dist/modes/interactive/interactive-mode.js +57 -10
  112. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  113. package/dist/utils/safe-write-stream.d.ts +8 -0
  114. package/dist/utils/safe-write-stream.d.ts.map +1 -1
  115. package/dist/utils/safe-write-stream.js +32 -0
  116. package/dist/utils/safe-write-stream.js.map +1 -1
  117. package/dist/utils/tools-manager.d.ts +92 -2
  118. package/dist/utils/tools-manager.d.ts.map +1 -1
  119. package/dist/utils/tools-manager.js +172 -32
  120. package/dist/utils/tools-manager.js.map +1 -1
  121. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  122. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  123. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  124. package/examples/extensions/sandbox/package-lock.json +2 -2
  125. package/examples/extensions/sandbox/package.json +1 -1
  126. package/examples/extensions/with-deps/package-lock.json +2 -2
  127. package/examples/extensions/with-deps/package.json +1 -1
  128. package/npm-shrinkwrap.json +12 -36
  129. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"fff-search-backend.js","sourceRoot":"","sources":["../../../src/core/tools/fff-search-backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { ensureFffNodePackage, loadAvailableFffNodePackage } from \"../../utils/tools-manager.ts\";\n\nexport type FffResult<T> = { ok: true; value: T } | { ok: false; error: string };\nexport type FffGrepMode = \"plain\" | \"regex\" | \"fuzzy\";\n\nexport interface FffFileItem {\n\trelativePath: string;\n\tfileName: string;\n\tsize: number;\n\tmodified: number;\n\tgitStatus: string;\n\taccessFrecencyScore?: number;\n\tmodificationFrecencyScore?: number;\n\ttotalFrecencyScore?: number;\n}\n\nexport interface FffScore {\n\ttotal: number;\n\tbaseScore: number;\n\tmatchType: string;\n}\n\nexport interface FffSearchResult {\n\titems: FffFileItem[];\n\tscores: FffScore[];\n\ttotalMatched: number;\n\ttotalFiles: number;\n}\n\nexport interface FffSearchOptions {\n\tpageIndex?: number;\n\tpageSize?: number;\n}\n\nexport interface FffGlobOptions {\n\tpageIndex?: number;\n\tpageSize?: number;\n}\n\nexport interface FffGrepOptions {\n\tmaxMatchesPerFile?: number;\n\tsmartCase?: boolean;\n\tmode?: FffGrepMode;\n\tbeforeContext?: number;\n\tafterContext?: number;\n\tpageSize?: number;\n}\n\nexport interface FffGrepMatch {\n\trelativePath: string;\n\tfileName: string;\n\tgitStatus: string;\n\tsize: number;\n\tmodified: number;\n\tisBinary: boolean;\n\ttotalFrecencyScore: number;\n\taccessFrecencyScore: number;\n\tmodificationFrecencyScore: number;\n\tlineNumber: number;\n\tcol: number;\n\tbyteOffset: number;\n\tlineContent: string;\n\tmatchRanges: [number, number][];\n\tcontextBefore?: string[];\n\tcontextAfter?: string[];\n}\n\nexport interface FffGrepResult {\n\titems: FffGrepMatch[];\n\ttotalMatched: number;\n\ttotalFilesSearched: number;\n\ttotalFiles: number;\n\tfilteredFileCount: number;\n\tnextCursor: unknown | null;\n\tregexFallbackError?: string;\n}\n\nexport interface FffFileFinder {\n\treadonly isDestroyed: boolean;\n\tdestroy(): void;\n\tfileSearch(query: string, options?: FffSearchOptions): FffResult<FffSearchResult>;\n\tglob(pattern: string, options?: FffGlobOptions): FffResult<FffSearchResult>;\n\tgrep(query: string, options?: FffGrepOptions): FffResult<FffGrepResult>;\n\twaitForScan(timeoutMs?: number): Promise<FffResult<boolean>>;\n}\n\ninterface FffInitOptions {\n\tbasePath: string;\n\taiMode?: boolean;\n\tenableHomeDirScanning?: boolean;\n\tenableFsRootScanning?: boolean;\n}\n\ninterface FffFileFinderConstructor {\n\tcreate(options: FffInitOptions): FffResult<FffFileFinder>;\n\tisAvailable?: () => boolean;\n}\n\ninterface FffModule {\n\tFileFinder: FffFileFinderConstructor;\n}\n\nexport interface FffSearchBackend {\n\tgetFinder(basePath: string): Promise<FffFileFinder | undefined>;\n}\n\ntype ModuleRequire = (id: string) => unknown;\n\nconst DEFAULT_WAIT_FOR_SCAN_MS = 15_000;\nconst MAX_FINDER_CACHE_SIZE = 8;\nconst FFF_GITIGNORE_SKIP_DIRS = new Set([\".git\", \"node_modules\"]);\n\nlet loadedFffModule: FffModule | null | undefined;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\";\n}\n\nfunction hasProperties(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && (typeof value === \"object\" || typeof value === \"function\");\n}\n\nfunction isFffResult<T>(value: unknown): value is FffResult<T> {\n\tif (!isRecord(value)) return false;\n\treturn value.ok === true || value.ok === false;\n}\n\nfunction isFffModule(value: unknown): value is FffModule {\n\tif (!isRecord(value)) return false;\n\tconst fileFinder = value.FileFinder;\n\treturn hasProperties(fileFinder) && typeof fileFinder.create === \"function\";\n}\n\nexport function loadFffModule(requires?: readonly ModuleRequire[]): FffModule | null {\n\tif (requires) {\n\t\tfor (const requireFff of requires) {\n\t\t\ttry {\n\t\t\t\tconst loaded = requireFff(\"@ff-labs/fff-node\");\n\t\t\t\tif (isFffModule(loaded)) return loaded;\n\t\t\t} catch {\n\t\t\t\t// Try the next resolution root.\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tif (loadedFffModule !== undefined) return loadedFffModule;\n\tconst loaded = loadAvailableFffNodePackage();\n\tloadedFffModule = isFffModule(loaded) ? loaded : null;\n\treturn loadedFffModule;\n}\n\nasync function ensureFffModule(): Promise<FffModule | null> {\n\tconst loaded = loadFffModule();\n\tif (loaded) return loaded;\n\tconst installed = await ensureFffNodePackage(true);\n\tloadedFffModule = isFffModule(installed) ? installed : null;\n\treturn loadedFffModule;\n}\n\nfunction isFffRuntimeDisabled(): boolean {\n\tconst value = process.env.PI_FFF_DISABLED ?? process.env.PI_SEARCH_BACKEND;\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"disabled\";\n}\n\nfunction isRootScanningEnabled(): boolean {\n\tconst value = process.env.PI_FFF_ENABLE_ROOT_SCAN;\n\treturn value === \"1\" || value?.toLowerCase() === \"true\";\n}\n\nfunction destroyFinder(finder: FffFileFinder | undefined): void {\n\tif (finder && !finder.isDestroyed) {\n\t\tfinder.destroy();\n\t}\n}\n\nexport function relativePathInside(basePath: string, targetPath: string): string | undefined {\n\tconst relative = path.relative(path.resolve(basePath), path.resolve(targetPath));\n\tif (relative === \"\") return \"\";\n\tif (relative.startsWith(\"..\") || path.isAbsolute(relative)) return undefined;\n\treturn relative.split(path.sep).join(\"/\");\n}\n\nexport async function hasGitignoreInTree(rootPath: string): Promise<boolean> {\n\tconst stack = [path.resolve(rootPath)];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop();\n\t\tif (!current) continue;\n\n\t\tlet entries: Dirent[];\n\t\ttry {\n\t\t\tentries = await readdir(current, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isFile() && entry.name === \".gitignore\") return true;\n\t\t\tif (entry.isDirectory() && !FFF_GITIGNORE_SKIP_DIRS.has(entry.name)) {\n\t\t\t\tstack.push(path.join(current, entry.name));\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nclass DefaultFffSearchBackend implements FffSearchBackend {\n\tprivate readonly finders = new Map<string, Promise<FffFileFinder | undefined>>();\n\n\tasync getFinder(basePath: string): Promise<FffFileFinder | undefined> {\n\t\tif (isFffRuntimeDisabled()) return undefined;\n\n\t\tconst normalizedBasePath = path.resolve(basePath);\n\t\tconst cached = this.finders.get(normalizedBasePath);\n\t\tif (cached) return cached;\n\n\t\tconst created = this.createFinder(normalizedBasePath);\n\t\tthis.finders.set(normalizedBasePath, created);\n\t\tthis.evictIfNeeded();\n\t\treturn created;\n\t}\n\n\tprivate evictIfNeeded(): void {\n\t\twhile (this.finders.size > MAX_FINDER_CACHE_SIZE) {\n\t\t\tconst firstKey = this.finders.keys().next().value;\n\t\t\tif (!firstKey) return;\n\t\t\tconst first = this.finders.get(firstKey);\n\t\t\tthis.finders.delete(firstKey);\n\t\t\tvoid first?.then(destroyFinder, () => undefined);\n\t\t}\n\t}\n\n\tprivate async createFinder(basePath: string): Promise<FffFileFinder | undefined> {\n\t\tlet fff = await ensureFffModule();\n\t\tif (!fff) return undefined;\n\t\tif (fff.FileFinder.isAvailable && !fff.FileFinder.isAvailable()) {\n\t\t\tconst installed = await ensureFffNodePackage(true, true);\n\t\t\tloadedFffModule = isFffModule(installed) ? installed : null;\n\t\t\tfff = loadedFffModule;\n\t\t\tif (!fff || (fff.FileFinder.isAvailable && !fff.FileFinder.isAvailable())) return undefined;\n\t\t}\n\n\t\tconst created = fff.FileFinder.create({\n\t\t\tbasePath,\n\t\t\taiMode: true,\n\t\t\tenableHomeDirScanning: true,\n\t\t\tenableFsRootScanning: isRootScanningEnabled(),\n\t\t});\n\t\tif (!isFffResult<FffFileFinder>(created) || !created.ok) return undefined;\n\n\t\tconst scan = await created.value.waitForScan(DEFAULT_WAIT_FOR_SCAN_MS);\n\t\tif (!scan.ok) {\n\t\t\tdestroyFinder(created.value);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn created.value;\n\t}\n}\n\nexport const defaultFffSearchBackend: FffSearchBackend = new DefaultFffSearchBackend();\n"]}
1
+ {"version":3,"file":"fff-search-backend.js","sourceRoot":"","sources":["../../../src/core/tools/fff-search-backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { ensureFffNodePackage, isFffInstallRetryable, loadAvailableFffNodePackage } from \"../../utils/tools-manager.ts\";\n\nexport type FffResult<T> = { ok: true; value: T } | { ok: false; error: string };\nexport type FffGrepMode = \"plain\" | \"regex\" | \"fuzzy\";\n\nexport interface FffFileItem {\n\trelativePath: string;\n\tfileName: string;\n\tsize: number;\n\tmodified: number;\n\tgitStatus: string;\n\taccessFrecencyScore?: number;\n\tmodificationFrecencyScore?: number;\n\ttotalFrecencyScore?: number;\n}\n\nexport interface FffScore {\n\ttotal: number;\n\tbaseScore: number;\n\tmatchType: string;\n}\n\nexport interface FffSearchResult {\n\titems: FffFileItem[];\n\tscores: FffScore[];\n\ttotalMatched: number;\n\ttotalFiles: number;\n}\n\nexport interface FffSearchOptions {\n\tpageIndex?: number;\n\tpageSize?: number;\n}\n\nexport interface FffGlobOptions {\n\tpageIndex?: number;\n\tpageSize?: number;\n}\n\nexport interface FffGrepOptions {\n\tmaxMatchesPerFile?: number;\n\tsmartCase?: boolean;\n\tmode?: FffGrepMode;\n\tbeforeContext?: number;\n\tafterContext?: number;\n\tpageSize?: number;\n}\n\nexport interface FffGrepMatch {\n\trelativePath: string;\n\tfileName: string;\n\tgitStatus: string;\n\tsize: number;\n\tmodified: number;\n\tisBinary: boolean;\n\ttotalFrecencyScore: number;\n\taccessFrecencyScore: number;\n\tmodificationFrecencyScore: number;\n\tlineNumber: number;\n\tcol: number;\n\tbyteOffset: number;\n\tlineContent: string;\n\tmatchRanges: [number, number][];\n\tcontextBefore?: string[];\n\tcontextAfter?: string[];\n}\n\nexport interface FffGrepResult {\n\titems: FffGrepMatch[];\n\ttotalMatched: number;\n\ttotalFilesSearched: number;\n\ttotalFiles: number;\n\tfilteredFileCount: number;\n\tnextCursor: unknown | null;\n\tregexFallbackError?: string;\n}\n\nexport interface FffFileFinder {\n\treadonly isDestroyed: boolean;\n\tdestroy(): void;\n\tfileSearch(query: string, options?: FffSearchOptions): FffResult<FffSearchResult>;\n\tglob(pattern: string, options?: FffGlobOptions): FffResult<FffSearchResult>;\n\tgrep(query: string, options?: FffGrepOptions): FffResult<FffGrepResult>;\n\twaitForScan(timeoutMs?: number): Promise<FffResult<boolean>>;\n}\n\ninterface FffInitOptions {\n\tbasePath: string;\n\taiMode?: boolean;\n\tenableHomeDirScanning?: boolean;\n\tenableFsRootScanning?: boolean;\n}\n\ninterface FffFileFinderConstructor {\n\tcreate(options: FffInitOptions): FffResult<FffFileFinder>;\n\tisAvailable?: () => boolean;\n}\n\nexport interface FffModule {\n\tFileFinder: FffFileFinderConstructor;\n}\n\nexport interface FffSearchBackend {\n\tgetFinder(basePath: string): Promise<FffFileFinder | undefined>;\n}\n\n/**\n * Calls backend.getFinder(cwd) and guarantees the returned promise can never\n * reject -- not even if a non-conforming backend's getFinder throws\n * synchronously instead of returning a rejected promise (the FffSearchBackend\n * contract is also implemented by extension-supplied backends, e.g. an SSH\n * remote search, which aren't guaranteed to be `async`-declared). find.ts and\n * grep.ts call this unconditionally, before routing decides whether this call\n * even wants FFF, specifically so a broken/throwing backend degrades to\n * \"unavailable for this call\" (graceful fd/rg fallback) instead of failing\n * the whole tool call or risking an unhandled rejection.\n */\nexport async function safeGetFinder(backend: FffSearchBackend, cwd: string): Promise<FffFileFinder | undefined> {\n\ttry {\n\t\treturn await backend.getFinder(cwd);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\ntype ModuleRequire = (id: string) => unknown;\n\nconst DEFAULT_WAIT_FOR_SCAN_MS = 15_000;\nconst MAX_FINDER_CACHE_SIZE = 8;\nconst FFF_GITIGNORE_SKIP_DIRS = new Set([\".git\", \"node_modules\"]);\n\nlet loadedFffModule: FffModule | null | undefined;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\";\n}\n\nfunction hasProperties(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && (typeof value === \"object\" || typeof value === \"function\");\n}\n\nfunction isFffResult<T>(value: unknown): value is FffResult<T> {\n\tif (!isRecord(value)) return false;\n\treturn value.ok === true || value.ok === false;\n}\n\nfunction isFffModule(value: unknown): value is FffModule {\n\tif (!isRecord(value)) return false;\n\tconst fileFinder = value.FileFinder;\n\treturn hasProperties(fileFinder) && typeof fileFinder.create === \"function\";\n}\n\nexport function loadFffModule(requires?: readonly ModuleRequire[]): FffModule | null {\n\tif (requires) {\n\t\tfor (const requireFff of requires) {\n\t\t\ttry {\n\t\t\t\tconst loaded = requireFff(\"@ff-labs/fff-node\");\n\t\t\t\tif (isFffModule(loaded)) return loaded;\n\t\t\t} catch {\n\t\t\t\t// Try the next resolution root.\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tif (loadedFffModule !== undefined) return loadedFffModule;\n\tconst loaded = loadAvailableFffNodePackage();\n\tloadedFffModule = isFffModule(loaded) ? loaded : null;\n\treturn loadedFffModule;\n}\n\nasync function ensureFffModule(): Promise<FffModule | null> {\n\tconst loaded = loadFffModule();\n\tif (loaded) return loaded;\n\tconst installed = await ensureFffNodePackage(true);\n\tloadedFffModule = isFffModule(installed) ? installed : null;\n\treturn loadedFffModule;\n}\n\nfunction isFffRuntimeDisabled(): boolean {\n\tconst value = process.env.PI_FFF_DISABLED ?? process.env.PI_SEARCH_BACKEND;\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"disabled\";\n}\n\nfunction isRootScanningEnabled(): boolean {\n\tconst value = process.env.PI_FFF_ENABLE_ROOT_SCAN;\n\treturn value === \"1\" || value?.toLowerCase() === \"true\";\n}\n\nfunction destroyFinder(finder: FffFileFinder | undefined): void {\n\tif (finder && !finder.isDestroyed) {\n\t\tfinder.destroy();\n\t}\n}\n\nexport function relativePathInside(basePath: string, targetPath: string): string | undefined {\n\tconst relative = path.relative(path.resolve(basePath), path.resolve(targetPath));\n\tif (relative === \"\") return \"\";\n\tif (relative.startsWith(\"..\") || path.isAbsolute(relative)) return undefined;\n\treturn relative.split(path.sep).join(\"/\");\n}\n\nexport async function hasGitignoreInTree(rootPath: string): Promise<boolean> {\n\tconst stack = [path.resolve(rootPath)];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop();\n\t\tif (!current) continue;\n\n\t\tlet entries: Dirent[];\n\t\ttry {\n\t\t\tentries = await readdir(current, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isFile() && entry.name === \".gitignore\") return true;\n\t\t\tif (entry.isDirectory() && !FFF_GITIGNORE_SKIP_DIRS.has(entry.name)) {\n\t\t\t\tstack.push(path.join(current, entry.name));\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * The two calls DefaultFffSearchBackend.createFinder makes to lazily load and,\n * if needed, install `@ff-labs/fff-node`. Pulled out as an injectable seam\n * (mirroring loadFffModule's optional `requires` param) so tests can simulate\n * a fresh machine -- and a faked install succeeding or failing -- without\n * touching the real module-global caches or spawning a real npm install.\n */\nexport interface FffFinderDeps {\n\tensureFffModule: () => Promise<FffModule | null>;\n\tensureFffNodePackage: (silent: boolean, forceManagedInstall?: boolean) => Promise<unknown | undefined>;\n\t/** Whether the last install outcome was a genuine failure worth retrying. */\n\tisInstallRetryable: () => boolean;\n}\n\nconst realFffFinderDeps: FffFinderDeps = {\n\tensureFffModule,\n\tensureFffNodePackage,\n\tisInstallRetryable: isFffInstallRetryable,\n};\n\nexport class DefaultFffSearchBackend implements FffSearchBackend {\n\tprivate readonly finders = new Map<string, Promise<FffFileFinder | undefined>>();\n\tprivate readonly deps: FffFinderDeps;\n\n\tconstructor(deps: FffFinderDeps = realFffFinderDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync getFinder(basePath: string): Promise<FffFileFinder | undefined> {\n\t\tif (isFffRuntimeDisabled()) return undefined;\n\n\t\tconst normalizedBasePath = path.resolve(basePath);\n\t\tconst cached = this.finders.get(normalizedBasePath);\n\t\tif (cached) return cached;\n\n\t\tconst created = this.createFinder(normalizedBasePath);\n\t\tvoid created.then(\n\t\t\t(finder) => {\n\t\t\t\t// A genuine install failure (network hiccup, registry blip, ...) must\n\t\t\t\t// not permanently gate FFF out of this basePath for the rest of the\n\t\t\t\t// process: drop the cache entry so the NEXT search retries instead of\n\t\t\t\t// being silently stuck on the fd/rg fallback forever. A stable\n\t\t\t\t// \"not applicable\" outcome (offline mode, unsupported platform) is left\n\t\t\t\t// cached, since retrying it would just repeat the same answer.\n\t\t\t\tif (!finder && this.deps.isInstallRetryable() && this.finders.get(normalizedBasePath) === created) {\n\t\t\t\t\tthis.finders.delete(normalizedBasePath);\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => undefined,\n\t\t);\n\t\tthis.finders.set(normalizedBasePath, created);\n\t\tthis.evictIfNeeded();\n\t\treturn created;\n\t}\n\n\tprivate evictIfNeeded(): void {\n\t\twhile (this.finders.size > MAX_FINDER_CACHE_SIZE) {\n\t\t\tconst firstKey = this.finders.keys().next().value;\n\t\t\tif (!firstKey) return;\n\t\t\tconst first = this.finders.get(firstKey);\n\t\t\tthis.finders.delete(firstKey);\n\t\t\tvoid first?.then(destroyFinder, () => undefined);\n\t\t}\n\t}\n\n\tprivate async createFinder(basePath: string): Promise<FffFileFinder | undefined> {\n\t\tlet fff = await this.deps.ensureFffModule();\n\t\tif (!fff) return undefined;\n\t\tif (fff.FileFinder.isAvailable && !fff.FileFinder.isAvailable()) {\n\t\t\tconst installed = await this.deps.ensureFffNodePackage(true, true);\n\t\t\tloadedFffModule = isFffModule(installed) ? installed : null;\n\t\t\tfff = loadedFffModule;\n\t\t\tif (!fff || (fff.FileFinder.isAvailable && !fff.FileFinder.isAvailable())) return undefined;\n\t\t}\n\n\t\tconst created = fff.FileFinder.create({\n\t\t\tbasePath,\n\t\t\taiMode: true,\n\t\t\tenableHomeDirScanning: true,\n\t\t\tenableFsRootScanning: isRootScanningEnabled(),\n\t\t});\n\t\tif (!isFffResult<FffFileFinder>(created) || !created.ok) return undefined;\n\n\t\tconst scan = await created.value.waitForScan(DEFAULT_WAIT_FOR_SCAN_MS);\n\t\tif (!scan.ok) {\n\t\t\tdestroyFinder(created.value);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn created.value;\n\t}\n}\n\nexport const defaultFffSearchBackend: FffSearchBackend = new DefaultFffSearchBackend();\n"]}
@@ -50,6 +50,22 @@ export interface FindToolOptions {
50
50
  /** Opt-in tracker for repeated-broad-query "do not repeat" signals. Also default-off. */
51
51
  broadQueryTracker?: BroadQueryTracker;
52
52
  }
53
+ export declare function tryFffFind(options: {
54
+ backend: FffSearchBackend;
55
+ router: SearchRouter;
56
+ cwd: string;
57
+ searchPath: string;
58
+ pattern: string;
59
+ ignoreCase?: boolean;
60
+ effectiveLimit: number;
61
+ toolCallId: string;
62
+ artifactStore?: ArtifactStore;
63
+ broadQueryTracker?: BroadQueryTracker;
64
+ rawPath?: string;
65
+ }): Promise<{
66
+ text: string;
67
+ details: FindToolDetails;
68
+ } | undefined>;
53
69
  export declare function createFindToolDefinition(cwd: string, options?: FindToolOptions): ToolDefinition<typeof findSchema, FindToolDetails | undefined>;
54
70
  export declare function createFindTool(cwd: string, options?: FindToolOptions): AgentTool<typeof findSchema>;
55
71
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/core/tools/find.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAI3D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EACN,KAAK,iBAAiB,EAKtB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAEN,KAAK,gBAAgB,EAIrB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE5E,OAAO,EAAiC,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMrF,QAAA,MAAM,UAAU;;;;;EAQd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAItD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,2BAA2B;IAC3B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,4EAA4E;IAC5E,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CACnH;AAQD,MAAM,WAAW,eAAe;IAC/B,sFAAsF;IACtF,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,+EAA+E;IAC/E,GAAG,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAC;IAC/B,qFAAqF;IACrF,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,yFAAyF;IACzF,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACtC;AA6RD,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAuQhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import { createInterface } from \"node:readline\";\nimport type { AgentTool } from \"@caupulican/pi-agent-core\";\nimport { Text } from \"@caupulican/pi-tui\";\nimport { spawn } from \"child_process\";\nimport path from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { ensureTool } from \"../../utils/tools-manager.ts\";\nimport type { ArtifactStore } from \"../context/context-artifacts.ts\";\nimport {\n\ttype BroadQueryTracker,\n\tbroadQueryInvalidationNote,\n\tformatArtifactNotice,\n\tnormalizeBroadQueryKey,\n\tpackToolOutput,\n} from \"../context/tool-output-packer.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tdefaultFffSearchBackend,\n\ttype FffSearchBackend,\n\ttype FffSearchResult,\n\thasGitignoreInTree,\n\trelativePathInside,\n} from \"./fff-search-backend.ts\";\nimport { pathExists, resolveToCwd } from \"./path-utils.ts\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.ts\";\nimport { defaultSearchRouter, type SearchRouter } from \"./search-router.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nfunction toPosixPath(value: string): string {\n\treturn value.split(path.sep).join(\"/\");\n}\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'. Use '.' to match all files.\",\n\t}),\n\tpath: Type.Optional(Type.String({ description: \"Directory to search in (default: current directory)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results (default: 1000)\" })),\n\tignoreCase: Type.Optional(Type.Boolean({ description: \"Case-insensitive matching (default: false)\" })),\n});\n\nexport type FindToolInput = Static<typeof findSchema>;\n\nconst DEFAULT_LIMIT = 1000;\n\nexport interface FindToolDetails {\n\ttruncation?: TruncationResult;\n\tresultLimitReached?: number;\n\t/** Set only when output was packed to an artifact; see tool-output-packer.ts. */\n\tartifactId?: string;\n\t/** Set when this exact query has repeatedly produced broad/truncated results. */\n\tinvalidationCandidate?: boolean;\n}\n\n/**\n * Pluggable operations for the find tool.\n * Override these to delegate file search to remote systems (for example SSH).\n */\nexport interface FindOperations {\n\t/** Check if path exists */\n\texists: (absolutePath: string) => Promise<boolean> | boolean;\n\t/** Find files matching glob pattern. Returns relative or absolute paths. */\n\tglob: (pattern: string, cwd: string, options: { ignore: string[]; limit: number }) => Promise<string[]> | string[];\n}\n\nconst defaultFindOperations: FindOperations = {\n\texists: pathExists,\n\t// This is a placeholder. Actual fd execution happens in execute() when no custom glob is provided.\n\tglob: () => [],\n};\n\nexport interface FindToolOptions {\n\t/** Custom operations for find. Default: local filesystem plus routed FFF/fd search */\n\toperations?: FindOperations;\n\t/** FFF backend for resident indexed search. Set false to force fd fallback. */\n\tfff?: FffSearchBackend | false;\n\t/** Pure router that selects FFF or fd from request filters and environment facts. */\n\tsearchRouter?: SearchRouter;\n\t/**\n\t * Opt-in artifact store for first-capture-then-bound output packing (Phase 3). When\n\t * omitted (the default), behavior is byte-for-byte unchanged from before this option\n\t * existed: output is truncated the same way, just never artifact-backed.\n\t */\n\tartifactStore?: ArtifactStore;\n\t/** Opt-in tracker for repeated-broad-query \"do not repeat\" signals. Also default-off. */\n\tbroadQueryTracker?: BroadQueryTracker;\n}\n\nfunction formatFindCall(\n\targs: { pattern: string; path?: string; limit?: number } | undefined,\n\ttheme: Theme,\n\tcwd: string,\n): string {\n\tconst pattern = str(args?.pattern);\n\tconst rawPath = str(args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath || \".\", cwd) : null;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text =\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"find\")) +\n\t\t\" \" +\n\t\t(pattern === null ? invalidArg : theme.fg(\"accent\", pattern || \"\")) +\n\t\ttheme.fg(\"toolOutput\", ` in ${path === null ? invalidArg : path}`);\n\tif (limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (limit ${limit})`);\n\t}\n\treturn text;\n}\n\nfunction formatFindResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: FindToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tshowImages: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tlet text = \"\";\n\tif (output) {\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst maxLines = options.expanded ? lines.length : 20;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t}\n\t}\n\n\tconst resultLimit = result.details?.resultLimitReached;\n\tconst truncation = result.details?.truncation;\n\tif (resultLimit || truncation?.truncated) {\n\t\tconst warnings: string[] = [];\n\t\tif (resultLimit) warnings.push(`${resultLimit} results limit`);\n\t\tif (truncation?.truncated) warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);\n\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${warnings.join(\", \")}]`)}`;\n\t}\n\treturn text;\n}\n\nfunction hasGlobSyntax(pattern: string): boolean {\n\treturn pattern === \".\" || /[*?[{]/.test(pattern);\n}\n\nfunction fffQueryParts(parts: string[]): string {\n\treturn parts.filter(Boolean).join(\" \");\n}\n\nfunction toSearchRelative(repoRelativePath: string, searchPathRelativeToCwd: string): string | undefined {\n\tif (!searchPathRelativeToCwd) return repoRelativePath;\n\tconst prefix = `${searchPathRelativeToCwd}/`;\n\tif (!repoRelativePath.startsWith(prefix)) return undefined;\n\treturn repoRelativePath.slice(prefix.length);\n}\n\nfunction fffGlobPattern(pattern: string, searchPathRelativeToCwd: string): string {\n\tconst effectivePattern = pattern === \".\" ? \"**/*\" : pattern;\n\tif (!searchPathRelativeToCwd) {\n\t\tif (effectivePattern.includes(\"/\") || effectivePattern.startsWith(\"**/\")) return effectivePattern;\n\t\treturn `**/${effectivePattern}`;\n\t}\n\tif (effectivePattern === \"**\" || effectivePattern === \"**/*\") return `${searchPathRelativeToCwd}/**/*`;\n\tif (effectivePattern.includes(\"/\")) return `${searchPathRelativeToCwd}/${effectivePattern}`;\n\treturn `${searchPathRelativeToCwd}/**/${effectivePattern}`;\n}\n\ninterface FindPackingOptions {\n\ttoolCallId: string;\n\tartifactStore?: ArtifactStore;\n\tbroadQueryTracker?: BroadQueryTracker;\n\tpattern: string;\n\trawPath?: string;\n}\n\nfunction fffSearchOutput(\n\tresult: FffSearchResult,\n\tsearchPathRelativeToCwd: string,\n\teffectiveLimit: number,\n\tpacking: FindPackingOptions,\n) {\n\tconst relativized = result.items\n\t\t.map((item) => toSearchRelative(item.relativePath, searchPathRelativeToCwd))\n\t\t.filter((item): item is string => Boolean(item));\n\treturn formatFindResults(relativized, effectiveLimit, packing);\n}\n\nasync function tryFffFind(options: {\n\tbackend: FffSearchBackend;\n\trouter: SearchRouter;\n\tcwd: string;\n\tsearchPath: string;\n\tpattern: string;\n\tignoreCase?: boolean;\n\teffectiveLimit: number;\n\ttoolCallId: string;\n\tartifactStore?: ArtifactStore;\n\tbroadQueryTracker?: BroadQueryTracker;\n\trawPath?: string;\n}): Promise<{ text: string; details: FindToolDetails } | undefined> {\n\tif (!(await pathExists(options.searchPath))) return undefined;\n\n\tconst searchPathRelativeToCwd = relativePathInside(options.cwd, options.searchPath);\n\tconst glob = hasGlobSyntax(options.pattern);\n\tconst baseRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: true,\n\t\tpathResolvable: searchPathRelativeToCwd !== undefined,\n\t\tgitignoreInTree: false,\n\t});\n\tif (baseRoute.backend !== \"fff\") return undefined;\n\tif (searchPathRelativeToCwd === undefined) return undefined;\n\n\tconst gitignoreInTree = await hasGitignoreInTree(options.searchPath);\n\tconst semanticRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: true,\n\t\tpathResolvable: true,\n\t\tgitignoreInTree,\n\t});\n\tif (semanticRoute.backend !== \"fff\") return undefined;\n\n\tconst finder = await options.backend.getFinder(options.cwd);\n\tconst finderRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: Boolean(finder),\n\t\tpathResolvable: true,\n\t\tgitignoreInTree: false,\n\t});\n\tif (!finder || finderRoute.backend !== \"fff\") return undefined;\n\n\tconst packing: FindPackingOptions = {\n\t\ttoolCallId: options.toolCallId,\n\t\tartifactStore: options.artifactStore,\n\t\tbroadQueryTracker: options.broadQueryTracker,\n\t\tpattern: options.pattern,\n\t\trawPath: options.rawPath,\n\t};\n\n\tif (glob) {\n\t\tconst result = finder.glob(fffGlobPattern(options.pattern, searchPathRelativeToCwd), {\n\t\t\tpageSize: options.effectiveLimit,\n\t\t});\n\t\treturn result.ok\n\t\t\t? fffSearchOutput(result.value, searchPathRelativeToCwd, options.effectiveLimit, packing)\n\t\t\t: undefined;\n\t}\n\n\tconst pathConstraint = searchPathRelativeToCwd ? `${searchPathRelativeToCwd}/` : \"\";\n\tconst result = finder.fileSearch(fffQueryParts([pathConstraint, options.pattern]), {\n\t\tpageSize: options.effectiveLimit,\n\t});\n\treturn result.ok\n\t\t? fffSearchOutput(result.value, searchPathRelativeToCwd, options.effectiveLimit, packing)\n\t\t: undefined;\n}\n\nfunction formatFindResults(\n\trelativized: string[],\n\teffectiveLimit: number,\n\tpacking: FindPackingOptions,\n): { text: string; details: FindToolDetails } {\n\tif (relativized.length === 0) {\n\t\treturn { text: \"No files found matching pattern\", details: {} };\n\t}\n\n\tconst dirGroups = new Map<string, string[]>();\n\tconst extCounts = new Map<string, number>();\n\n\tfor (const p of relativized) {\n\t\tconst dir = path.dirname(p);\n\t\tconst base = path.basename(p);\n\t\tconst dirKey = dir === \".\" ? \"./\" : `${dir}/`;\n\t\tif (!dirGroups.has(dirKey)) {\n\t\t\tdirGroups.set(dirKey, []);\n\t\t}\n\t\tdirGroups.get(dirKey)!.push(base);\n\n\t\tconst ext = path.extname(p).toLowerCase() || \"(no extension)\";\n\t\textCounts.set(ext, (extCounts.get(ext) || 0) + 1);\n\t}\n\n\tconst sortedDirs = Array.from(dirGroups.keys()).sort((a, b) => a.localeCompare(b));\n\tconst formattedLines: string[] = [];\n\tfor (const dir of sortedDirs) {\n\t\tformattedLines.push(dir);\n\t\tconst files = dirGroups.get(dir)!;\n\t\tfiles.sort((a, b) => a.localeCompare(b));\n\t\tfor (const file of files) {\n\t\t\tformattedLines.push(` ${file}`);\n\t\t}\n\t}\n\n\tconst extSummaryParts = Array.from(extCounts.entries())\n\t\t.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\t\t.map(([ext, count]) => `${ext}: ${count}`);\n\tconst extSummary = `Extensions: ${extSummaryParts.join(\", \")}`;\n\n\tconst resultLimitReached = relativized.length >= effectiveLimit;\n\tconst rawOutput = formattedLines.join(\"\\n\");\n\t// Measure -> pack (artifact-backed if oversized and a store was provided) -> notices.\n\tconst packed = packToolOutput(\n\t\t{\n\t\t\ttoolName: \"find\",\n\t\t\tpath: packing.rawPath,\n\t\t\trawContent: rawOutput,\n\t\t\t// No line limit here because the result limit already caps rows; only the byte\n\t\t\t// cap should apply, matching the pre-Slice-B truncateHead call exactly.\n\t\t\ttruncation: { maxLines: Number.MAX_SAFE_INTEGER },\n\t\t},\n\t\tpacking.artifactStore,\n\t\tpacking.toolCallId,\n\t);\n\tlet resultOutput = packed.content;\n\tconst details: FindToolDetails = {};\n\tconst notices: string[] = [];\n\tif (packed.artifactId) {\n\t\tnotices.push(formatArtifactNotice(packed.artifactId));\n\t\tdetails.artifactId = packed.artifactId;\n\t}\n\tif (resultLimitReached) {\n\t\tnotices.push(\n\t\t\t`${effectiveLimit} results limit reached. Use limit=${effectiveLimit * 2} for more, or narrow path/pattern`,\n\t\t);\n\t\tdetails.resultLimitReached = effectiveLimit;\n\t}\n\tif (packed.truncation.truncated) {\n\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t// Drop the duplicated bounded-preview text: it's already in the message's own\n\t\t// content, and re-including it here can push `details` past\n\t\t// MAX_RETAINED_TOOL_RESULT_DETAILS_BYTES (message-retention.ts), which replaces\n\t\t// the *entire* details object with a stub -- silently losing artifactId and every\n\t\t// other field alongside it. This is load-bearing beyond just the retention budget:\n\t\t// agent-session.ts's _releaseGcPackedArtifactReferences() reads artifactId back off\n\t\t// this same canonical message at eviction time (potentially many turns later), so\n\t\t// keeping `details` small here is what keeps that release path working at all. If\n\t\t// this field ever grows a large addition again, add a regression proving artifactId\n\t\t// survives compactToolResultDetailsForRetention (see\n\t\t// test/suite/agent-session-artifact-lifecycle.test.ts), not just a details-size check.\n\t\tdetails.truncation = { ...packed.truncation, content: \"\" };\n\t}\n\tif (resultLimitReached || packed.truncation.truncated) {\n\t\tconst note = broadQueryInvalidationNote(\n\t\t\tpacking.broadQueryTracker,\n\t\t\tnormalizeBroadQueryKey({ toolName: \"find\", pattern: packing.pattern, path: packing.rawPath }),\n\t\t\t`find \"${packing.pattern}\" in ${packing.rawPath ?? \".\"}`,\n\t\t);\n\t\tif (note) {\n\t\t\tnotices.push(note);\n\t\t\tdetails.invalidationCandidate = true;\n\t\t}\n\t}\n\tif (relativized.length > 0) {\n\t\tresultOutput += `\\n\\n[Summary - ${extSummary}]`;\n\t}\n\tif (notices.length > 0) {\n\t\tresultOutput += `\\n\\n[${notices.join(\". \")}]`;\n\t}\n\treturn { text: resultOutput, details };\n}\n\nexport function createFindToolDefinition(\n\tcwd: string,\n\toptions?: FindToolOptions,\n): ToolDefinition<typeof findSchema, FindToolDetails | undefined> {\n\tconst customOps = options?.operations;\n\tconst fffBackend = options?.fff === false ? undefined : (options?.fff ?? defaultFffSearchBackend);\n\tconst searchRouter = options?.searchRouter ?? defaultSearchRouter;\n\tconst artifactStore = options?.artifactStore;\n\tconst broadQueryTracker = options?.broadQueryTracker;\n\treturn {\n\t\tname: \"find\",\n\t\tlabel: \"find\",\n\t\tdescription: `Search for files by glob pattern. Returns matching file paths relative to the search directory. Respects .gitignore. Output is truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first).`,\n\t\tpromptSnippet: \"Find files by glob pattern (respects .gitignore)\",\n\t\tparameters: findSchema,\n\t\ttoolGroup: \"explore\",\n\t\tasync execute(\n\t\t\ttoolCallId,\n\t\t\t{\n\t\t\t\tpattern,\n\t\t\t\tpath: searchDir,\n\t\t\t\tlimit,\n\t\t\t\tignoreCase,\n\t\t\t}: { pattern: string; path?: string; limit?: number; ignoreCase?: boolean },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet settled = false;\n\t\t\t\tlet stopChild: (() => void) | undefined;\n\t\t\t\tconst settle = (fn: () => void) => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tstopChild = undefined;\n\t\t\t\t\tfn();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tstopChild?.();\n\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t};\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst searchPath = resolveToCwd(searchDir || \".\", cwd);\n\t\t\t\t\t\tconst effectiveLimit = limit ?? DEFAULT_LIMIT;\n\t\t\t\t\t\tconst ops = customOps ?? defaultFindOperations;\n\n\t\t\t\t\t\tlet effectivePattern = pattern;\n\t\t\t\t\t\tif (pattern === \".\") {\n\t\t\t\t\t\t\teffectivePattern = \"**/*\";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!customOps && fffBackend) {\n\t\t\t\t\t\t\tconst fffResult = await tryFffFind({\n\t\t\t\t\t\t\t\tbackend: fffBackend,\n\t\t\t\t\t\t\t\trouter: searchRouter,\n\t\t\t\t\t\t\t\tcwd,\n\t\t\t\t\t\t\t\tsearchPath,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\tignoreCase,\n\t\t\t\t\t\t\t\teffectiveLimit,\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (fffResult) {\n\t\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: fffResult.text }],\n\t\t\t\t\t\t\t\t\t\tdetails: Object.keys(fffResult.details).length > 0 ? fffResult.details : undefined,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If custom operations provide glob(), use that instead of fd.\n\t\t\t\t\t\tif (customOps?.glob) {\n\t\t\t\t\t\t\tif (!(await ops.exists(searchPath))) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(`Path not found: ${searchPath}`)));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst results = await ops.glob(effectivePattern, searchPath, {\n\t\t\t\t\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t\t\t\t\t\tlimit: effectiveLimit,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Relativize paths against the search root for stable output.\n\t\t\t\t\t\t\tconst relativized = results.map((p) => {\n\t\t\t\t\t\t\t\tif (p.startsWith(searchPath)) return toPosixPath(p.slice(searchPath.length + 1));\n\t\t\t\t\t\t\t\treturn toPosixPath(path.relative(searchPath, p));\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst formatted = formatFindResults(relativized, effectiveLimit, {\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: formatted.text }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(formatted.details).length > 0 ? formatted.details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default implementation uses fd.\n\t\t\t\t\t\tconst fdPath = await ensureTool(\"fd\", true);\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!fdPath) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"fd is not available and could not be downloaded\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Build fd arguments. --no-require-git makes fd apply hierarchical .gitignore\n\t\t\t\t\t\t// semantics whether or not the search path is inside a git repository, without\n\t\t\t\t\t\t// leaking sibling-directory rules the way --ignore-file (a global source) would.\n\t\t\t\t\t\tconst args: string[] = [\n\t\t\t\t\t\t\t\"--glob\",\n\t\t\t\t\t\t\t\"--color=never\",\n\t\t\t\t\t\t\t\"--hidden\",\n\t\t\t\t\t\t\t\"--no-require-git\",\n\t\t\t\t\t\t\t\"--max-results\",\n\t\t\t\t\t\t\tString(effectiveLimit),\n\t\t\t\t\t\t];\n\t\t\t\t\t\tif (ignoreCase) {\n\t\t\t\t\t\t\targs.push(\"--ignore-case\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// fd --glob matches against the basename unless --full-path is set; in --full-path\n\t\t\t\t\t\t// mode it matches against the absolute candidate path, so a path-containing\n\t\t\t\t\t\t// pattern like 'src/**/*.spec.ts' needs a leading '**/' to match anything.\n\t\t\t\t\t\tlet finalPattern = effectivePattern;\n\t\t\t\t\t\tif (effectivePattern.includes(\"/\")) {\n\t\t\t\t\t\t\targs.push(\"--full-path\");\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!effectivePattern.startsWith(\"/\") &&\n\t\t\t\t\t\t\t\t!effectivePattern.startsWith(\"**/\") &&\n\t\t\t\t\t\t\t\teffectivePattern !== \"**\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tfinalPattern = `**/${effectivePattern}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\targs.push(\"--\", finalPattern, searchPath);\n\n\t\t\t\t\t\tconst child = spawn(fdPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\t\t\t\tconst rl = createInterface({ input: child.stdout });\n\t\t\t\t\t\tlet stderr = \"\";\n\t\t\t\t\t\tconst lines: string[] = [];\n\n\t\t\t\t\t\tstopChild = () => {\n\t\t\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\t\t\tchild.kill();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst cleanup = () => {\n\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\t\t\t\t\tstderr += chunk.toString();\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\trl.on(\"line\", (line) => {\n\t\t\t\t\t\t\tlines.push(line);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\tsettle(() => reject(new Error(`Failed to run fd: ${error.message}`)));\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst output = lines.join(\"\\n\");\n\t\t\t\t\t\t\tif (code !== 0) {\n\t\t\t\t\t\t\t\tconst errorMsg = stderr.trim() || `fd exited with code ${code}`;\n\t\t\t\t\t\t\t\tif (!output) {\n\t\t\t\t\t\t\t\t\tsettle(() => reject(new Error(errorMsg)));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst relativized: string[] = [];\n\t\t\t\t\t\t\tfor (const rawLine of lines) {\n\t\t\t\t\t\t\t\tconst line = rawLine.replace(/\\r$/, \"\").trim();\n\t\t\t\t\t\t\t\tif (!line) continue;\n\t\t\t\t\t\t\t\tconst hadTrailingSlash = line.endsWith(\"/\") || line.endsWith(\"\\\\\");\n\t\t\t\t\t\t\t\tlet relativePath = line;\n\t\t\t\t\t\t\t\tif (line.startsWith(searchPath)) {\n\t\t\t\t\t\t\t\t\trelativePath = line.slice(searchPath.length + 1);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\trelativePath = path.relative(searchPath, line);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (hadTrailingSlash && !relativePath.endsWith(\"/\")) relativePath += \"/\";\n\t\t\t\t\t\t\t\trelativized.push(toPosixPath(relativePath));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst formatted = formatFindResults(relativized, effectiveLimit, {\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: formatted.text }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(formatted.details).length > 0 ? formatted.details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst error = e instanceof Error ? e : new Error(String(e));\n\t\t\t\t\t\tsettle(() => reject(error));\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindCall(args, theme, context.cwd));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindResult(result as any, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createFindTool(cwd: string, options?: FindToolOptions): AgentTool<typeof findSchema> {\n\treturn wrapToolDefinition(createFindToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/core/tools/find.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAI3D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EACN,KAAK,iBAAiB,EAKtB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAEN,KAAK,gBAAgB,EAKrB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE5E,OAAO,EAAiC,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMrF,QAAA,MAAM,UAAU;;;;;EAQd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAItD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,2BAA2B;IAC3B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,4EAA4E;IAC5E,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CACnH;AAQD,MAAM,WAAW,eAAe;IAC/B,sFAAsF;IACtF,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,+EAA+E;IAC/E,GAAG,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAC;IAC/B,qFAAqF;IACrF,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,yFAAyF;IACzF,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACtC;AAsGD,wBAAsB,UAAU,CAAC,OAAO,EAAE;IACzC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,eAAe,CAAA;CAAE,GAAG,SAAS,CAAC,CAgFlE;AA0GD,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAuQhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import { createInterface } from \"node:readline\";\nimport type { AgentTool } from \"@caupulican/pi-agent-core\";\nimport { Text } from \"@caupulican/pi-tui\";\nimport { spawn } from \"child_process\";\nimport path from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { ensureTool } from \"../../utils/tools-manager.ts\";\nimport type { ArtifactStore } from \"../context/context-artifacts.ts\";\nimport {\n\ttype BroadQueryTracker,\n\tbroadQueryInvalidationNote,\n\tformatArtifactNotice,\n\tnormalizeBroadQueryKey,\n\tpackToolOutput,\n} from \"../context/tool-output-packer.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tdefaultFffSearchBackend,\n\ttype FffSearchBackend,\n\ttype FffSearchResult,\n\thasGitignoreInTree,\n\trelativePathInside,\n\tsafeGetFinder,\n} from \"./fff-search-backend.ts\";\nimport { pathExists, resolveToCwd } from \"./path-utils.ts\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.ts\";\nimport { defaultSearchRouter, type SearchRouter } from \"./search-router.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nfunction toPosixPath(value: string): string {\n\treturn value.split(path.sep).join(\"/\");\n}\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'. Use '.' to match all files.\",\n\t}),\n\tpath: Type.Optional(Type.String({ description: \"Directory to search in (default: current directory)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results (default: 1000)\" })),\n\tignoreCase: Type.Optional(Type.Boolean({ description: \"Case-insensitive matching (default: false)\" })),\n});\n\nexport type FindToolInput = Static<typeof findSchema>;\n\nconst DEFAULT_LIMIT = 1000;\n\nexport interface FindToolDetails {\n\ttruncation?: TruncationResult;\n\tresultLimitReached?: number;\n\t/** Set only when output was packed to an artifact; see tool-output-packer.ts. */\n\tartifactId?: string;\n\t/** Set when this exact query has repeatedly produced broad/truncated results. */\n\tinvalidationCandidate?: boolean;\n}\n\n/**\n * Pluggable operations for the find tool.\n * Override these to delegate file search to remote systems (for example SSH).\n */\nexport interface FindOperations {\n\t/** Check if path exists */\n\texists: (absolutePath: string) => Promise<boolean> | boolean;\n\t/** Find files matching glob pattern. Returns relative or absolute paths. */\n\tglob: (pattern: string, cwd: string, options: { ignore: string[]; limit: number }) => Promise<string[]> | string[];\n}\n\nconst defaultFindOperations: FindOperations = {\n\texists: pathExists,\n\t// This is a placeholder. Actual fd execution happens in execute() when no custom glob is provided.\n\tglob: () => [],\n};\n\nexport interface FindToolOptions {\n\t/** Custom operations for find. Default: local filesystem plus routed FFF/fd search */\n\toperations?: FindOperations;\n\t/** FFF backend for resident indexed search. Set false to force fd fallback. */\n\tfff?: FffSearchBackend | false;\n\t/** Pure router that selects FFF or fd from request filters and environment facts. */\n\tsearchRouter?: SearchRouter;\n\t/**\n\t * Opt-in artifact store for first-capture-then-bound output packing (Phase 3). When\n\t * omitted (the default), behavior is byte-for-byte unchanged from before this option\n\t * existed: output is truncated the same way, just never artifact-backed.\n\t */\n\tartifactStore?: ArtifactStore;\n\t/** Opt-in tracker for repeated-broad-query \"do not repeat\" signals. Also default-off. */\n\tbroadQueryTracker?: BroadQueryTracker;\n}\n\nfunction formatFindCall(\n\targs: { pattern: string; path?: string; limit?: number } | undefined,\n\ttheme: Theme,\n\tcwd: string,\n): string {\n\tconst pattern = str(args?.pattern);\n\tconst rawPath = str(args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath || \".\", cwd) : null;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text =\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"find\")) +\n\t\t\" \" +\n\t\t(pattern === null ? invalidArg : theme.fg(\"accent\", pattern || \"\")) +\n\t\ttheme.fg(\"toolOutput\", ` in ${path === null ? invalidArg : path}`);\n\tif (limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (limit ${limit})`);\n\t}\n\treturn text;\n}\n\nfunction formatFindResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: FindToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tshowImages: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tlet text = \"\";\n\tif (output) {\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst maxLines = options.expanded ? lines.length : 20;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t}\n\t}\n\n\tconst resultLimit = result.details?.resultLimitReached;\n\tconst truncation = result.details?.truncation;\n\tif (resultLimit || truncation?.truncated) {\n\t\tconst warnings: string[] = [];\n\t\tif (resultLimit) warnings.push(`${resultLimit} results limit`);\n\t\tif (truncation?.truncated) warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);\n\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${warnings.join(\", \")}]`)}`;\n\t}\n\treturn text;\n}\n\nfunction hasGlobSyntax(pattern: string): boolean {\n\treturn pattern === \".\" || /[*?[{]/.test(pattern);\n}\n\nfunction fffQueryParts(parts: string[]): string {\n\treturn parts.filter(Boolean).join(\" \");\n}\n\nfunction toSearchRelative(repoRelativePath: string, searchPathRelativeToCwd: string): string | undefined {\n\tif (!searchPathRelativeToCwd) return repoRelativePath;\n\tconst prefix = `${searchPathRelativeToCwd}/`;\n\tif (!repoRelativePath.startsWith(prefix)) return undefined;\n\treturn repoRelativePath.slice(prefix.length);\n}\n\nfunction fffGlobPattern(pattern: string, searchPathRelativeToCwd: string): string {\n\tconst effectivePattern = pattern === \".\" ? \"**/*\" : pattern;\n\tif (!searchPathRelativeToCwd) {\n\t\tif (effectivePattern.includes(\"/\") || effectivePattern.startsWith(\"**/\")) return effectivePattern;\n\t\treturn `**/${effectivePattern}`;\n\t}\n\tif (effectivePattern === \"**\" || effectivePattern === \"**/*\") return `${searchPathRelativeToCwd}/**/*`;\n\tif (effectivePattern.includes(\"/\")) return `${searchPathRelativeToCwd}/${effectivePattern}`;\n\treturn `${searchPathRelativeToCwd}/**/${effectivePattern}`;\n}\n\ninterface FindPackingOptions {\n\ttoolCallId: string;\n\tartifactStore?: ArtifactStore;\n\tbroadQueryTracker?: BroadQueryTracker;\n\tpattern: string;\n\trawPath?: string;\n}\n\nfunction fffSearchOutput(\n\tresult: FffSearchResult,\n\tsearchPathRelativeToCwd: string,\n\teffectiveLimit: number,\n\tpacking: FindPackingOptions,\n) {\n\tconst relativized = result.items\n\t\t.map((item) => toSearchRelative(item.relativePath, searchPathRelativeToCwd))\n\t\t.filter((item): item is string => Boolean(item));\n\treturn formatFindResults(relativized, effectiveLimit, packing);\n}\n\nexport async function tryFffFind(options: {\n\tbackend: FffSearchBackend;\n\trouter: SearchRouter;\n\tcwd: string;\n\tsearchPath: string;\n\tpattern: string;\n\tignoreCase?: boolean;\n\teffectiveLimit: number;\n\ttoolCallId: string;\n\tartifactStore?: ArtifactStore;\n\tbroadQueryTracker?: BroadQueryTracker;\n\trawPath?: string;\n}): Promise<{ text: string; details: FindToolDetails } | undefined> {\n\tif (!(await pathExists(options.searchPath))) return undefined;\n\n\t// Kick off the FFF finder -- and, on a machine where fff-node isn't provisioned\n\t// yet, its lazy managed install -- unconditionally and as early as possible.\n\t// Routing below can still send THIS call to the fd fallback for reasons that\n\t// have nothing to do with tool availability (most commonly: the default result\n\t// limit is well above the FFF top-N threshold, see search-router.ts). That\n\t// outcome must not gate the install itself, or a machine that only ever issues\n\t// default-limit searches would never provision fff-node at all. getFinder() is\n\t// cached per basePath, so this costs nothing beyond the first call for a given\n\t// cwd -- the `await` further down, when this call does want FFF, reuses this\n\t// same in-flight/resolved promise instead of triggering the work twice.\n\t// safeGetFinder() guarantees this can never reject (a non-conforming custom\n\t// backend that throws synchronously still degrades to \"unavailable\"), so it\n\t// can neither produce an unhandled rejection nor fail this tool call outright.\n\tconst finderPromise = safeGetFinder(options.backend, options.cwd);\n\n\tconst searchPathRelativeToCwd = relativePathInside(options.cwd, options.searchPath);\n\tconst glob = hasGlobSyntax(options.pattern);\n\tconst baseRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: true,\n\t\tpathResolvable: searchPathRelativeToCwd !== undefined,\n\t\tgitignoreInTree: false,\n\t});\n\tif (baseRoute.backend !== \"fff\") return undefined;\n\tif (searchPathRelativeToCwd === undefined) return undefined;\n\n\tconst gitignoreInTree = await hasGitignoreInTree(options.searchPath);\n\tconst semanticRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: true,\n\t\tpathResolvable: true,\n\t\tgitignoreInTree,\n\t});\n\tif (semanticRoute.backend !== \"fff\") return undefined;\n\n\tconst finder = await finderPromise;\n\tconst finderRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: Boolean(finder),\n\t\tpathResolvable: true,\n\t\tgitignoreInTree: false,\n\t});\n\tif (!finder || finderRoute.backend !== \"fff\") return undefined;\n\n\tconst packing: FindPackingOptions = {\n\t\ttoolCallId: options.toolCallId,\n\t\tartifactStore: options.artifactStore,\n\t\tbroadQueryTracker: options.broadQueryTracker,\n\t\tpattern: options.pattern,\n\t\trawPath: options.rawPath,\n\t};\n\n\tif (glob) {\n\t\tconst result = finder.glob(fffGlobPattern(options.pattern, searchPathRelativeToCwd), {\n\t\t\tpageSize: options.effectiveLimit,\n\t\t});\n\t\treturn result.ok\n\t\t\t? fffSearchOutput(result.value, searchPathRelativeToCwd, options.effectiveLimit, packing)\n\t\t\t: undefined;\n\t}\n\n\tconst pathConstraint = searchPathRelativeToCwd ? `${searchPathRelativeToCwd}/` : \"\";\n\tconst result = finder.fileSearch(fffQueryParts([pathConstraint, options.pattern]), {\n\t\tpageSize: options.effectiveLimit,\n\t});\n\treturn result.ok\n\t\t? fffSearchOutput(result.value, searchPathRelativeToCwd, options.effectiveLimit, packing)\n\t\t: undefined;\n}\n\nfunction formatFindResults(\n\trelativized: string[],\n\teffectiveLimit: number,\n\tpacking: FindPackingOptions,\n): { text: string; details: FindToolDetails } {\n\tif (relativized.length === 0) {\n\t\treturn { text: \"No files found matching pattern\", details: {} };\n\t}\n\n\tconst dirGroups = new Map<string, string[]>();\n\tconst extCounts = new Map<string, number>();\n\n\tfor (const p of relativized) {\n\t\tconst dir = path.dirname(p);\n\t\tconst base = path.basename(p);\n\t\tconst dirKey = dir === \".\" ? \"./\" : `${dir}/`;\n\t\tif (!dirGroups.has(dirKey)) {\n\t\t\tdirGroups.set(dirKey, []);\n\t\t}\n\t\tdirGroups.get(dirKey)!.push(base);\n\n\t\tconst ext = path.extname(p).toLowerCase() || \"(no extension)\";\n\t\textCounts.set(ext, (extCounts.get(ext) || 0) + 1);\n\t}\n\n\tconst sortedDirs = Array.from(dirGroups.keys()).sort((a, b) => a.localeCompare(b));\n\tconst formattedLines: string[] = [];\n\tfor (const dir of sortedDirs) {\n\t\tformattedLines.push(dir);\n\t\tconst files = dirGroups.get(dir)!;\n\t\tfiles.sort((a, b) => a.localeCompare(b));\n\t\tfor (const file of files) {\n\t\t\tformattedLines.push(` ${file}`);\n\t\t}\n\t}\n\n\tconst extSummaryParts = Array.from(extCounts.entries())\n\t\t.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\t\t.map(([ext, count]) => `${ext}: ${count}`);\n\tconst extSummary = `Extensions: ${extSummaryParts.join(\", \")}`;\n\n\tconst resultLimitReached = relativized.length >= effectiveLimit;\n\tconst rawOutput = formattedLines.join(\"\\n\");\n\t// Measure -> pack (artifact-backed if oversized and a store was provided) -> notices.\n\tconst packed = packToolOutput(\n\t\t{\n\t\t\ttoolName: \"find\",\n\t\t\tpath: packing.rawPath,\n\t\t\trawContent: rawOutput,\n\t\t\t// No line limit here because the result limit already caps rows; only the byte\n\t\t\t// cap should apply, matching the pre-Slice-B truncateHead call exactly.\n\t\t\ttruncation: { maxLines: Number.MAX_SAFE_INTEGER },\n\t\t},\n\t\tpacking.artifactStore,\n\t\tpacking.toolCallId,\n\t);\n\tlet resultOutput = packed.content;\n\tconst details: FindToolDetails = {};\n\tconst notices: string[] = [];\n\tif (packed.artifactId) {\n\t\tnotices.push(formatArtifactNotice(packed.artifactId));\n\t\tdetails.artifactId = packed.artifactId;\n\t}\n\tif (resultLimitReached) {\n\t\tnotices.push(\n\t\t\t`${effectiveLimit} results limit reached. Use limit=${effectiveLimit * 2} for more, or narrow path/pattern`,\n\t\t);\n\t\tdetails.resultLimitReached = effectiveLimit;\n\t}\n\tif (packed.truncation.truncated) {\n\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t// Drop the duplicated bounded-preview text: it's already in the message's own\n\t\t// content, and re-including it here can push `details` past\n\t\t// MAX_RETAINED_TOOL_RESULT_DETAILS_BYTES (message-retention.ts), which replaces\n\t\t// the *entire* details object with a stub -- silently losing artifactId and every\n\t\t// other field alongside it. This is load-bearing beyond just the retention budget:\n\t\t// agent-session.ts's _releaseGcPackedArtifactReferences() reads artifactId back off\n\t\t// this same canonical message at eviction time (potentially many turns later), so\n\t\t// keeping `details` small here is what keeps that release path working at all. If\n\t\t// this field ever grows a large addition again, add a regression proving artifactId\n\t\t// survives compactToolResultDetailsForRetention (see\n\t\t// test/suite/agent-session-artifact-lifecycle.test.ts), not just a details-size check.\n\t\tdetails.truncation = { ...packed.truncation, content: \"\" };\n\t}\n\tif (resultLimitReached || packed.truncation.truncated) {\n\t\tconst note = broadQueryInvalidationNote(\n\t\t\tpacking.broadQueryTracker,\n\t\t\tnormalizeBroadQueryKey({ toolName: \"find\", pattern: packing.pattern, path: packing.rawPath }),\n\t\t\t`find \"${packing.pattern}\" in ${packing.rawPath ?? \".\"}`,\n\t\t);\n\t\tif (note) {\n\t\t\tnotices.push(note);\n\t\t\tdetails.invalidationCandidate = true;\n\t\t}\n\t}\n\tif (relativized.length > 0) {\n\t\tresultOutput += `\\n\\n[Summary - ${extSummary}]`;\n\t}\n\tif (notices.length > 0) {\n\t\tresultOutput += `\\n\\n[${notices.join(\". \")}]`;\n\t}\n\treturn { text: resultOutput, details };\n}\n\nexport function createFindToolDefinition(\n\tcwd: string,\n\toptions?: FindToolOptions,\n): ToolDefinition<typeof findSchema, FindToolDetails | undefined> {\n\tconst customOps = options?.operations;\n\tconst fffBackend = options?.fff === false ? undefined : (options?.fff ?? defaultFffSearchBackend);\n\tconst searchRouter = options?.searchRouter ?? defaultSearchRouter;\n\tconst artifactStore = options?.artifactStore;\n\tconst broadQueryTracker = options?.broadQueryTracker;\n\treturn {\n\t\tname: \"find\",\n\t\tlabel: \"find\",\n\t\tdescription: `Search for files by glob pattern. Returns matching file paths relative to the search directory. Respects .gitignore. Output is truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first).`,\n\t\tpromptSnippet: \"Find files by glob pattern (respects .gitignore)\",\n\t\tparameters: findSchema,\n\t\ttoolGroup: \"explore\",\n\t\tasync execute(\n\t\t\ttoolCallId,\n\t\t\t{\n\t\t\t\tpattern,\n\t\t\t\tpath: searchDir,\n\t\t\t\tlimit,\n\t\t\t\tignoreCase,\n\t\t\t}: { pattern: string; path?: string; limit?: number; ignoreCase?: boolean },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet settled = false;\n\t\t\t\tlet stopChild: (() => void) | undefined;\n\t\t\t\tconst settle = (fn: () => void) => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tstopChild = undefined;\n\t\t\t\t\tfn();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tstopChild?.();\n\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t};\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst searchPath = resolveToCwd(searchDir || \".\", cwd);\n\t\t\t\t\t\tconst effectiveLimit = limit ?? DEFAULT_LIMIT;\n\t\t\t\t\t\tconst ops = customOps ?? defaultFindOperations;\n\n\t\t\t\t\t\tlet effectivePattern = pattern;\n\t\t\t\t\t\tif (pattern === \".\") {\n\t\t\t\t\t\t\teffectivePattern = \"**/*\";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!customOps && fffBackend) {\n\t\t\t\t\t\t\tconst fffResult = await tryFffFind({\n\t\t\t\t\t\t\t\tbackend: fffBackend,\n\t\t\t\t\t\t\t\trouter: searchRouter,\n\t\t\t\t\t\t\t\tcwd,\n\t\t\t\t\t\t\t\tsearchPath,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\tignoreCase,\n\t\t\t\t\t\t\t\teffectiveLimit,\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (fffResult) {\n\t\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: fffResult.text }],\n\t\t\t\t\t\t\t\t\t\tdetails: Object.keys(fffResult.details).length > 0 ? fffResult.details : undefined,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If custom operations provide glob(), use that instead of fd.\n\t\t\t\t\t\tif (customOps?.glob) {\n\t\t\t\t\t\t\tif (!(await ops.exists(searchPath))) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(`Path not found: ${searchPath}`)));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst results = await ops.glob(effectivePattern, searchPath, {\n\t\t\t\t\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t\t\t\t\t\tlimit: effectiveLimit,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Relativize paths against the search root for stable output.\n\t\t\t\t\t\t\tconst relativized = results.map((p) => {\n\t\t\t\t\t\t\t\tif (p.startsWith(searchPath)) return toPosixPath(p.slice(searchPath.length + 1));\n\t\t\t\t\t\t\t\treturn toPosixPath(path.relative(searchPath, p));\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst formatted = formatFindResults(relativized, effectiveLimit, {\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: formatted.text }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(formatted.details).length > 0 ? formatted.details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default implementation uses fd.\n\t\t\t\t\t\tconst fdPath = await ensureTool(\"fd\", true);\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!fdPath) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"fd is not available and could not be downloaded\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Build fd arguments. --no-require-git makes fd apply hierarchical .gitignore\n\t\t\t\t\t\t// semantics whether or not the search path is inside a git repository, without\n\t\t\t\t\t\t// leaking sibling-directory rules the way --ignore-file (a global source) would.\n\t\t\t\t\t\tconst args: string[] = [\n\t\t\t\t\t\t\t\"--glob\",\n\t\t\t\t\t\t\t\"--color=never\",\n\t\t\t\t\t\t\t\"--hidden\",\n\t\t\t\t\t\t\t\"--no-require-git\",\n\t\t\t\t\t\t\t\"--max-results\",\n\t\t\t\t\t\t\tString(effectiveLimit),\n\t\t\t\t\t\t];\n\t\t\t\t\t\tif (ignoreCase) {\n\t\t\t\t\t\t\targs.push(\"--ignore-case\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// fd --glob matches against the basename unless --full-path is set; in --full-path\n\t\t\t\t\t\t// mode it matches against the absolute candidate path, so a path-containing\n\t\t\t\t\t\t// pattern like 'src/**/*.spec.ts' needs a leading '**/' to match anything.\n\t\t\t\t\t\tlet finalPattern = effectivePattern;\n\t\t\t\t\t\tif (effectivePattern.includes(\"/\")) {\n\t\t\t\t\t\t\targs.push(\"--full-path\");\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!effectivePattern.startsWith(\"/\") &&\n\t\t\t\t\t\t\t\t!effectivePattern.startsWith(\"**/\") &&\n\t\t\t\t\t\t\t\teffectivePattern !== \"**\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tfinalPattern = `**/${effectivePattern}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\targs.push(\"--\", finalPattern, searchPath);\n\n\t\t\t\t\t\tconst child = spawn(fdPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\t\t\t\tconst rl = createInterface({ input: child.stdout });\n\t\t\t\t\t\tlet stderr = \"\";\n\t\t\t\t\t\tconst lines: string[] = [];\n\n\t\t\t\t\t\tstopChild = () => {\n\t\t\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\t\t\tchild.kill();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst cleanup = () => {\n\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\t\t\t\t\tstderr += chunk.toString();\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\trl.on(\"line\", (line) => {\n\t\t\t\t\t\t\tlines.push(line);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\tsettle(() => reject(new Error(`Failed to run fd: ${error.message}`)));\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst output = lines.join(\"\\n\");\n\t\t\t\t\t\t\tif (code !== 0) {\n\t\t\t\t\t\t\t\tconst errorMsg = stderr.trim() || `fd exited with code ${code}`;\n\t\t\t\t\t\t\t\tif (!output) {\n\t\t\t\t\t\t\t\t\tsettle(() => reject(new Error(errorMsg)));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst relativized: string[] = [];\n\t\t\t\t\t\t\tfor (const rawLine of lines) {\n\t\t\t\t\t\t\t\tconst line = rawLine.replace(/\\r$/, \"\").trim();\n\t\t\t\t\t\t\t\tif (!line) continue;\n\t\t\t\t\t\t\t\tconst hadTrailingSlash = line.endsWith(\"/\") || line.endsWith(\"\\\\\");\n\t\t\t\t\t\t\t\tlet relativePath = line;\n\t\t\t\t\t\t\t\tif (line.startsWith(searchPath)) {\n\t\t\t\t\t\t\t\t\trelativePath = line.slice(searchPath.length + 1);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\trelativePath = path.relative(searchPath, line);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (hadTrailingSlash && !relativePath.endsWith(\"/\")) relativePath += \"/\";\n\t\t\t\t\t\t\t\trelativized.push(toPosixPath(relativePath));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst formatted = formatFindResults(relativized, effectiveLimit, {\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: formatted.text }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(formatted.details).length > 0 ? formatted.details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst error = e instanceof Error ? e : new Error(String(e));\n\t\t\t\t\t\tsettle(() => reject(error));\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindCall(args, theme, context.cwd));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindResult(result as any, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createFindTool(cwd: string, options?: FindToolOptions): AgentTool<typeof findSchema> {\n\treturn wrapToolDefinition(createFindToolDefinition(cwd, options));\n}\n"]}
@@ -6,7 +6,7 @@ import { Type } from "typebox";
6
6
  import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
7
7
  import { ensureTool } from "../../utils/tools-manager.js";
8
8
  import { broadQueryInvalidationNote, formatArtifactNotice, normalizeBroadQueryKey, packToolOutput, } from "../context/tool-output-packer.js";
9
- import { defaultFffSearchBackend, hasGitignoreInTree, relativePathInside, } from "./fff-search-backend.js";
9
+ import { defaultFffSearchBackend, hasGitignoreInTree, relativePathInside, safeGetFinder, } from "./fff-search-backend.js";
10
10
  import { pathExists, resolveToCwd } from "./path-utils.js";
11
11
  import { getTextOutput, invalidArgText, shortenPath, str } from "./render-utils.js";
12
12
  import { defaultSearchRouter } from "./search-router.js";
@@ -102,9 +102,23 @@ function fffSearchOutput(result, searchPathRelativeToCwd, effectiveLimit, packin
102
102
  .filter((item) => Boolean(item));
103
103
  return formatFindResults(relativized, effectiveLimit, packing);
104
104
  }
105
- async function tryFffFind(options) {
105
+ export async function tryFffFind(options) {
106
106
  if (!(await pathExists(options.searchPath)))
107
107
  return undefined;
108
+ // Kick off the FFF finder -- and, on a machine where fff-node isn't provisioned
109
+ // yet, its lazy managed install -- unconditionally and as early as possible.
110
+ // Routing below can still send THIS call to the fd fallback for reasons that
111
+ // have nothing to do with tool availability (most commonly: the default result
112
+ // limit is well above the FFF top-N threshold, see search-router.ts). That
113
+ // outcome must not gate the install itself, or a machine that only ever issues
114
+ // default-limit searches would never provision fff-node at all. getFinder() is
115
+ // cached per basePath, so this costs nothing beyond the first call for a given
116
+ // cwd -- the `await` further down, when this call does want FFF, reuses this
117
+ // same in-flight/resolved promise instead of triggering the work twice.
118
+ // safeGetFinder() guarantees this can never reject (a non-conforming custom
119
+ // backend that throws synchronously still degrades to "unavailable"), so it
120
+ // can neither produce an unhandled rejection nor fail this tool call outright.
121
+ const finderPromise = safeGetFinder(options.backend, options.cwd);
108
122
  const searchPathRelativeToCwd = relativePathInside(options.cwd, options.searchPath);
109
123
  const glob = hasGlobSyntax(options.pattern);
110
124
  const baseRoute = options.router.route({
@@ -132,7 +146,7 @@ async function tryFffFind(options) {
132
146
  });
133
147
  if (semanticRoute.backend !== "fff")
134
148
  return undefined;
135
- const finder = await options.backend.getFinder(options.cwd);
149
+ const finder = await finderPromise;
136
150
  const finderRoute = options.router.route({
137
151
  tool: "find",
138
152
  glob,
@@ -1 +1 @@
1
- {"version":3,"file":"find.js","sourceRoot":"","sources":["../../../src/core/tools/find.ts"],"names":[],"mappings":"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","sourcesContent":["import { createInterface } from \"node:readline\";\nimport type { AgentTool } from \"@caupulican/pi-agent-core\";\nimport { Text } from \"@caupulican/pi-tui\";\nimport { spawn } from \"child_process\";\nimport path from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { ensureTool } from \"../../utils/tools-manager.ts\";\nimport type { ArtifactStore } from \"../context/context-artifacts.ts\";\nimport {\n\ttype BroadQueryTracker,\n\tbroadQueryInvalidationNote,\n\tformatArtifactNotice,\n\tnormalizeBroadQueryKey,\n\tpackToolOutput,\n} from \"../context/tool-output-packer.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tdefaultFffSearchBackend,\n\ttype FffSearchBackend,\n\ttype FffSearchResult,\n\thasGitignoreInTree,\n\trelativePathInside,\n} from \"./fff-search-backend.ts\";\nimport { pathExists, resolveToCwd } from \"./path-utils.ts\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.ts\";\nimport { defaultSearchRouter, type SearchRouter } from \"./search-router.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nfunction toPosixPath(value: string): string {\n\treturn value.split(path.sep).join(\"/\");\n}\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'. Use '.' to match all files.\",\n\t}),\n\tpath: Type.Optional(Type.String({ description: \"Directory to search in (default: current directory)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results (default: 1000)\" })),\n\tignoreCase: Type.Optional(Type.Boolean({ description: \"Case-insensitive matching (default: false)\" })),\n});\n\nexport type FindToolInput = Static<typeof findSchema>;\n\nconst DEFAULT_LIMIT = 1000;\n\nexport interface FindToolDetails {\n\ttruncation?: TruncationResult;\n\tresultLimitReached?: number;\n\t/** Set only when output was packed to an artifact; see tool-output-packer.ts. */\n\tartifactId?: string;\n\t/** Set when this exact query has repeatedly produced broad/truncated results. */\n\tinvalidationCandidate?: boolean;\n}\n\n/**\n * Pluggable operations for the find tool.\n * Override these to delegate file search to remote systems (for example SSH).\n */\nexport interface FindOperations {\n\t/** Check if path exists */\n\texists: (absolutePath: string) => Promise<boolean> | boolean;\n\t/** Find files matching glob pattern. Returns relative or absolute paths. */\n\tglob: (pattern: string, cwd: string, options: { ignore: string[]; limit: number }) => Promise<string[]> | string[];\n}\n\nconst defaultFindOperations: FindOperations = {\n\texists: pathExists,\n\t// This is a placeholder. Actual fd execution happens in execute() when no custom glob is provided.\n\tglob: () => [],\n};\n\nexport interface FindToolOptions {\n\t/** Custom operations for find. Default: local filesystem plus routed FFF/fd search */\n\toperations?: FindOperations;\n\t/** FFF backend for resident indexed search. Set false to force fd fallback. */\n\tfff?: FffSearchBackend | false;\n\t/** Pure router that selects FFF or fd from request filters and environment facts. */\n\tsearchRouter?: SearchRouter;\n\t/**\n\t * Opt-in artifact store for first-capture-then-bound output packing (Phase 3). When\n\t * omitted (the default), behavior is byte-for-byte unchanged from before this option\n\t * existed: output is truncated the same way, just never artifact-backed.\n\t */\n\tartifactStore?: ArtifactStore;\n\t/** Opt-in tracker for repeated-broad-query \"do not repeat\" signals. Also default-off. */\n\tbroadQueryTracker?: BroadQueryTracker;\n}\n\nfunction formatFindCall(\n\targs: { pattern: string; path?: string; limit?: number } | undefined,\n\ttheme: Theme,\n\tcwd: string,\n): string {\n\tconst pattern = str(args?.pattern);\n\tconst rawPath = str(args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath || \".\", cwd) : null;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text =\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"find\")) +\n\t\t\" \" +\n\t\t(pattern === null ? invalidArg : theme.fg(\"accent\", pattern || \"\")) +\n\t\ttheme.fg(\"toolOutput\", ` in ${path === null ? invalidArg : path}`);\n\tif (limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (limit ${limit})`);\n\t}\n\treturn text;\n}\n\nfunction formatFindResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: FindToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tshowImages: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tlet text = \"\";\n\tif (output) {\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst maxLines = options.expanded ? lines.length : 20;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t}\n\t}\n\n\tconst resultLimit = result.details?.resultLimitReached;\n\tconst truncation = result.details?.truncation;\n\tif (resultLimit || truncation?.truncated) {\n\t\tconst warnings: string[] = [];\n\t\tif (resultLimit) warnings.push(`${resultLimit} results limit`);\n\t\tif (truncation?.truncated) warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);\n\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${warnings.join(\", \")}]`)}`;\n\t}\n\treturn text;\n}\n\nfunction hasGlobSyntax(pattern: string): boolean {\n\treturn pattern === \".\" || /[*?[{]/.test(pattern);\n}\n\nfunction fffQueryParts(parts: string[]): string {\n\treturn parts.filter(Boolean).join(\" \");\n}\n\nfunction toSearchRelative(repoRelativePath: string, searchPathRelativeToCwd: string): string | undefined {\n\tif (!searchPathRelativeToCwd) return repoRelativePath;\n\tconst prefix = `${searchPathRelativeToCwd}/`;\n\tif (!repoRelativePath.startsWith(prefix)) return undefined;\n\treturn repoRelativePath.slice(prefix.length);\n}\n\nfunction fffGlobPattern(pattern: string, searchPathRelativeToCwd: string): string {\n\tconst effectivePattern = pattern === \".\" ? \"**/*\" : pattern;\n\tif (!searchPathRelativeToCwd) {\n\t\tif (effectivePattern.includes(\"/\") || effectivePattern.startsWith(\"**/\")) return effectivePattern;\n\t\treturn `**/${effectivePattern}`;\n\t}\n\tif (effectivePattern === \"**\" || effectivePattern === \"**/*\") return `${searchPathRelativeToCwd}/**/*`;\n\tif (effectivePattern.includes(\"/\")) return `${searchPathRelativeToCwd}/${effectivePattern}`;\n\treturn `${searchPathRelativeToCwd}/**/${effectivePattern}`;\n}\n\ninterface FindPackingOptions {\n\ttoolCallId: string;\n\tartifactStore?: ArtifactStore;\n\tbroadQueryTracker?: BroadQueryTracker;\n\tpattern: string;\n\trawPath?: string;\n}\n\nfunction fffSearchOutput(\n\tresult: FffSearchResult,\n\tsearchPathRelativeToCwd: string,\n\teffectiveLimit: number,\n\tpacking: FindPackingOptions,\n) {\n\tconst relativized = result.items\n\t\t.map((item) => toSearchRelative(item.relativePath, searchPathRelativeToCwd))\n\t\t.filter((item): item is string => Boolean(item));\n\treturn formatFindResults(relativized, effectiveLimit, packing);\n}\n\nasync function tryFffFind(options: {\n\tbackend: FffSearchBackend;\n\trouter: SearchRouter;\n\tcwd: string;\n\tsearchPath: string;\n\tpattern: string;\n\tignoreCase?: boolean;\n\teffectiveLimit: number;\n\ttoolCallId: string;\n\tartifactStore?: ArtifactStore;\n\tbroadQueryTracker?: BroadQueryTracker;\n\trawPath?: string;\n}): Promise<{ text: string; details: FindToolDetails } | undefined> {\n\tif (!(await pathExists(options.searchPath))) return undefined;\n\n\tconst searchPathRelativeToCwd = relativePathInside(options.cwd, options.searchPath);\n\tconst glob = hasGlobSyntax(options.pattern);\n\tconst baseRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: true,\n\t\tpathResolvable: searchPathRelativeToCwd !== undefined,\n\t\tgitignoreInTree: false,\n\t});\n\tif (baseRoute.backend !== \"fff\") return undefined;\n\tif (searchPathRelativeToCwd === undefined) return undefined;\n\n\tconst gitignoreInTree = await hasGitignoreInTree(options.searchPath);\n\tconst semanticRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: true,\n\t\tpathResolvable: true,\n\t\tgitignoreInTree,\n\t});\n\tif (semanticRoute.backend !== \"fff\") return undefined;\n\n\tconst finder = await options.backend.getFinder(options.cwd);\n\tconst finderRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: Boolean(finder),\n\t\tpathResolvable: true,\n\t\tgitignoreInTree: false,\n\t});\n\tif (!finder || finderRoute.backend !== \"fff\") return undefined;\n\n\tconst packing: FindPackingOptions = {\n\t\ttoolCallId: options.toolCallId,\n\t\tartifactStore: options.artifactStore,\n\t\tbroadQueryTracker: options.broadQueryTracker,\n\t\tpattern: options.pattern,\n\t\trawPath: options.rawPath,\n\t};\n\n\tif (glob) {\n\t\tconst result = finder.glob(fffGlobPattern(options.pattern, searchPathRelativeToCwd), {\n\t\t\tpageSize: options.effectiveLimit,\n\t\t});\n\t\treturn result.ok\n\t\t\t? fffSearchOutput(result.value, searchPathRelativeToCwd, options.effectiveLimit, packing)\n\t\t\t: undefined;\n\t}\n\n\tconst pathConstraint = searchPathRelativeToCwd ? `${searchPathRelativeToCwd}/` : \"\";\n\tconst result = finder.fileSearch(fffQueryParts([pathConstraint, options.pattern]), {\n\t\tpageSize: options.effectiveLimit,\n\t});\n\treturn result.ok\n\t\t? fffSearchOutput(result.value, searchPathRelativeToCwd, options.effectiveLimit, packing)\n\t\t: undefined;\n}\n\nfunction formatFindResults(\n\trelativized: string[],\n\teffectiveLimit: number,\n\tpacking: FindPackingOptions,\n): { text: string; details: FindToolDetails } {\n\tif (relativized.length === 0) {\n\t\treturn { text: \"No files found matching pattern\", details: {} };\n\t}\n\n\tconst dirGroups = new Map<string, string[]>();\n\tconst extCounts = new Map<string, number>();\n\n\tfor (const p of relativized) {\n\t\tconst dir = path.dirname(p);\n\t\tconst base = path.basename(p);\n\t\tconst dirKey = dir === \".\" ? \"./\" : `${dir}/`;\n\t\tif (!dirGroups.has(dirKey)) {\n\t\t\tdirGroups.set(dirKey, []);\n\t\t}\n\t\tdirGroups.get(dirKey)!.push(base);\n\n\t\tconst ext = path.extname(p).toLowerCase() || \"(no extension)\";\n\t\textCounts.set(ext, (extCounts.get(ext) || 0) + 1);\n\t}\n\n\tconst sortedDirs = Array.from(dirGroups.keys()).sort((a, b) => a.localeCompare(b));\n\tconst formattedLines: string[] = [];\n\tfor (const dir of sortedDirs) {\n\t\tformattedLines.push(dir);\n\t\tconst files = dirGroups.get(dir)!;\n\t\tfiles.sort((a, b) => a.localeCompare(b));\n\t\tfor (const file of files) {\n\t\t\tformattedLines.push(` ${file}`);\n\t\t}\n\t}\n\n\tconst extSummaryParts = Array.from(extCounts.entries())\n\t\t.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\t\t.map(([ext, count]) => `${ext}: ${count}`);\n\tconst extSummary = `Extensions: ${extSummaryParts.join(\", \")}`;\n\n\tconst resultLimitReached = relativized.length >= effectiveLimit;\n\tconst rawOutput = formattedLines.join(\"\\n\");\n\t// Measure -> pack (artifact-backed if oversized and a store was provided) -> notices.\n\tconst packed = packToolOutput(\n\t\t{\n\t\t\ttoolName: \"find\",\n\t\t\tpath: packing.rawPath,\n\t\t\trawContent: rawOutput,\n\t\t\t// No line limit here because the result limit already caps rows; only the byte\n\t\t\t// cap should apply, matching the pre-Slice-B truncateHead call exactly.\n\t\t\ttruncation: { maxLines: Number.MAX_SAFE_INTEGER },\n\t\t},\n\t\tpacking.artifactStore,\n\t\tpacking.toolCallId,\n\t);\n\tlet resultOutput = packed.content;\n\tconst details: FindToolDetails = {};\n\tconst notices: string[] = [];\n\tif (packed.artifactId) {\n\t\tnotices.push(formatArtifactNotice(packed.artifactId));\n\t\tdetails.artifactId = packed.artifactId;\n\t}\n\tif (resultLimitReached) {\n\t\tnotices.push(\n\t\t\t`${effectiveLimit} results limit reached. Use limit=${effectiveLimit * 2} for more, or narrow path/pattern`,\n\t\t);\n\t\tdetails.resultLimitReached = effectiveLimit;\n\t}\n\tif (packed.truncation.truncated) {\n\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t// Drop the duplicated bounded-preview text: it's already in the message's own\n\t\t// content, and re-including it here can push `details` past\n\t\t// MAX_RETAINED_TOOL_RESULT_DETAILS_BYTES (message-retention.ts), which replaces\n\t\t// the *entire* details object with a stub -- silently losing artifactId and every\n\t\t// other field alongside it. This is load-bearing beyond just the retention budget:\n\t\t// agent-session.ts's _releaseGcPackedArtifactReferences() reads artifactId back off\n\t\t// this same canonical message at eviction time (potentially many turns later), so\n\t\t// keeping `details` small here is what keeps that release path working at all. If\n\t\t// this field ever grows a large addition again, add a regression proving artifactId\n\t\t// survives compactToolResultDetailsForRetention (see\n\t\t// test/suite/agent-session-artifact-lifecycle.test.ts), not just a details-size check.\n\t\tdetails.truncation = { ...packed.truncation, content: \"\" };\n\t}\n\tif (resultLimitReached || packed.truncation.truncated) {\n\t\tconst note = broadQueryInvalidationNote(\n\t\t\tpacking.broadQueryTracker,\n\t\t\tnormalizeBroadQueryKey({ toolName: \"find\", pattern: packing.pattern, path: packing.rawPath }),\n\t\t\t`find \"${packing.pattern}\" in ${packing.rawPath ?? \".\"}`,\n\t\t);\n\t\tif (note) {\n\t\t\tnotices.push(note);\n\t\t\tdetails.invalidationCandidate = true;\n\t\t}\n\t}\n\tif (relativized.length > 0) {\n\t\tresultOutput += `\\n\\n[Summary - ${extSummary}]`;\n\t}\n\tif (notices.length > 0) {\n\t\tresultOutput += `\\n\\n[${notices.join(\". \")}]`;\n\t}\n\treturn { text: resultOutput, details };\n}\n\nexport function createFindToolDefinition(\n\tcwd: string,\n\toptions?: FindToolOptions,\n): ToolDefinition<typeof findSchema, FindToolDetails | undefined> {\n\tconst customOps = options?.operations;\n\tconst fffBackend = options?.fff === false ? undefined : (options?.fff ?? defaultFffSearchBackend);\n\tconst searchRouter = options?.searchRouter ?? defaultSearchRouter;\n\tconst artifactStore = options?.artifactStore;\n\tconst broadQueryTracker = options?.broadQueryTracker;\n\treturn {\n\t\tname: \"find\",\n\t\tlabel: \"find\",\n\t\tdescription: `Search for files by glob pattern. Returns matching file paths relative to the search directory. Respects .gitignore. Output is truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first).`,\n\t\tpromptSnippet: \"Find files by glob pattern (respects .gitignore)\",\n\t\tparameters: findSchema,\n\t\ttoolGroup: \"explore\",\n\t\tasync execute(\n\t\t\ttoolCallId,\n\t\t\t{\n\t\t\t\tpattern,\n\t\t\t\tpath: searchDir,\n\t\t\t\tlimit,\n\t\t\t\tignoreCase,\n\t\t\t}: { pattern: string; path?: string; limit?: number; ignoreCase?: boolean },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet settled = false;\n\t\t\t\tlet stopChild: (() => void) | undefined;\n\t\t\t\tconst settle = (fn: () => void) => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tstopChild = undefined;\n\t\t\t\t\tfn();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tstopChild?.();\n\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t};\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst searchPath = resolveToCwd(searchDir || \".\", cwd);\n\t\t\t\t\t\tconst effectiveLimit = limit ?? DEFAULT_LIMIT;\n\t\t\t\t\t\tconst ops = customOps ?? defaultFindOperations;\n\n\t\t\t\t\t\tlet effectivePattern = pattern;\n\t\t\t\t\t\tif (pattern === \".\") {\n\t\t\t\t\t\t\teffectivePattern = \"**/*\";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!customOps && fffBackend) {\n\t\t\t\t\t\t\tconst fffResult = await tryFffFind({\n\t\t\t\t\t\t\t\tbackend: fffBackend,\n\t\t\t\t\t\t\t\trouter: searchRouter,\n\t\t\t\t\t\t\t\tcwd,\n\t\t\t\t\t\t\t\tsearchPath,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\tignoreCase,\n\t\t\t\t\t\t\t\teffectiveLimit,\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (fffResult) {\n\t\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: fffResult.text }],\n\t\t\t\t\t\t\t\t\t\tdetails: Object.keys(fffResult.details).length > 0 ? fffResult.details : undefined,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If custom operations provide glob(), use that instead of fd.\n\t\t\t\t\t\tif (customOps?.glob) {\n\t\t\t\t\t\t\tif (!(await ops.exists(searchPath))) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(`Path not found: ${searchPath}`)));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst results = await ops.glob(effectivePattern, searchPath, {\n\t\t\t\t\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t\t\t\t\t\tlimit: effectiveLimit,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Relativize paths against the search root for stable output.\n\t\t\t\t\t\t\tconst relativized = results.map((p) => {\n\t\t\t\t\t\t\t\tif (p.startsWith(searchPath)) return toPosixPath(p.slice(searchPath.length + 1));\n\t\t\t\t\t\t\t\treturn toPosixPath(path.relative(searchPath, p));\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst formatted = formatFindResults(relativized, effectiveLimit, {\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: formatted.text }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(formatted.details).length > 0 ? formatted.details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default implementation uses fd.\n\t\t\t\t\t\tconst fdPath = await ensureTool(\"fd\", true);\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!fdPath) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"fd is not available and could not be downloaded\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Build fd arguments. --no-require-git makes fd apply hierarchical .gitignore\n\t\t\t\t\t\t// semantics whether or not the search path is inside a git repository, without\n\t\t\t\t\t\t// leaking sibling-directory rules the way --ignore-file (a global source) would.\n\t\t\t\t\t\tconst args: string[] = [\n\t\t\t\t\t\t\t\"--glob\",\n\t\t\t\t\t\t\t\"--color=never\",\n\t\t\t\t\t\t\t\"--hidden\",\n\t\t\t\t\t\t\t\"--no-require-git\",\n\t\t\t\t\t\t\t\"--max-results\",\n\t\t\t\t\t\t\tString(effectiveLimit),\n\t\t\t\t\t\t];\n\t\t\t\t\t\tif (ignoreCase) {\n\t\t\t\t\t\t\targs.push(\"--ignore-case\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// fd --glob matches against the basename unless --full-path is set; in --full-path\n\t\t\t\t\t\t// mode it matches against the absolute candidate path, so a path-containing\n\t\t\t\t\t\t// pattern like 'src/**/*.spec.ts' needs a leading '**/' to match anything.\n\t\t\t\t\t\tlet finalPattern = effectivePattern;\n\t\t\t\t\t\tif (effectivePattern.includes(\"/\")) {\n\t\t\t\t\t\t\targs.push(\"--full-path\");\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!effectivePattern.startsWith(\"/\") &&\n\t\t\t\t\t\t\t\t!effectivePattern.startsWith(\"**/\") &&\n\t\t\t\t\t\t\t\teffectivePattern !== \"**\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tfinalPattern = `**/${effectivePattern}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\targs.push(\"--\", finalPattern, searchPath);\n\n\t\t\t\t\t\tconst child = spawn(fdPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\t\t\t\tconst rl = createInterface({ input: child.stdout });\n\t\t\t\t\t\tlet stderr = \"\";\n\t\t\t\t\t\tconst lines: string[] = [];\n\n\t\t\t\t\t\tstopChild = () => {\n\t\t\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\t\t\tchild.kill();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst cleanup = () => {\n\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\t\t\t\t\tstderr += chunk.toString();\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\trl.on(\"line\", (line) => {\n\t\t\t\t\t\t\tlines.push(line);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\tsettle(() => reject(new Error(`Failed to run fd: ${error.message}`)));\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst output = lines.join(\"\\n\");\n\t\t\t\t\t\t\tif (code !== 0) {\n\t\t\t\t\t\t\t\tconst errorMsg = stderr.trim() || `fd exited with code ${code}`;\n\t\t\t\t\t\t\t\tif (!output) {\n\t\t\t\t\t\t\t\t\tsettle(() => reject(new Error(errorMsg)));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst relativized: string[] = [];\n\t\t\t\t\t\t\tfor (const rawLine of lines) {\n\t\t\t\t\t\t\t\tconst line = rawLine.replace(/\\r$/, \"\").trim();\n\t\t\t\t\t\t\t\tif (!line) continue;\n\t\t\t\t\t\t\t\tconst hadTrailingSlash = line.endsWith(\"/\") || line.endsWith(\"\\\\\");\n\t\t\t\t\t\t\t\tlet relativePath = line;\n\t\t\t\t\t\t\t\tif (line.startsWith(searchPath)) {\n\t\t\t\t\t\t\t\t\trelativePath = line.slice(searchPath.length + 1);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\trelativePath = path.relative(searchPath, line);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (hadTrailingSlash && !relativePath.endsWith(\"/\")) relativePath += \"/\";\n\t\t\t\t\t\t\t\trelativized.push(toPosixPath(relativePath));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst formatted = formatFindResults(relativized, effectiveLimit, {\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: formatted.text }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(formatted.details).length > 0 ? formatted.details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst error = e instanceof Error ? e : new Error(String(e));\n\t\t\t\t\t\tsettle(() => reject(error));\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindCall(args, theme, context.cwd));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindResult(result as any, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createFindTool(cwd: string, options?: FindToolOptions): AgentTool<typeof findSchema> {\n\treturn wrapToolDefinition(createFindToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"find.js","sourceRoot":"","sources":["../../../src/core/tools/find.ts"],"names":[],"mappings":"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","sourcesContent":["import { createInterface } from \"node:readline\";\nimport type { AgentTool } from \"@caupulican/pi-agent-core\";\nimport { Text } from \"@caupulican/pi-tui\";\nimport { spawn } from \"child_process\";\nimport path from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { ensureTool } from \"../../utils/tools-manager.ts\";\nimport type { ArtifactStore } from \"../context/context-artifacts.ts\";\nimport {\n\ttype BroadQueryTracker,\n\tbroadQueryInvalidationNote,\n\tformatArtifactNotice,\n\tnormalizeBroadQueryKey,\n\tpackToolOutput,\n} from \"../context/tool-output-packer.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport {\n\tdefaultFffSearchBackend,\n\ttype FffSearchBackend,\n\ttype FffSearchResult,\n\thasGitignoreInTree,\n\trelativePathInside,\n\tsafeGetFinder,\n} from \"./fff-search-backend.ts\";\nimport { pathExists, resolveToCwd } from \"./path-utils.ts\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.ts\";\nimport { defaultSearchRouter, type SearchRouter } from \"./search-router.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nfunction toPosixPath(value: string): string {\n\treturn value.split(path.sep).join(\"/\");\n}\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'. Use '.' to match all files.\",\n\t}),\n\tpath: Type.Optional(Type.String({ description: \"Directory to search in (default: current directory)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results (default: 1000)\" })),\n\tignoreCase: Type.Optional(Type.Boolean({ description: \"Case-insensitive matching (default: false)\" })),\n});\n\nexport type FindToolInput = Static<typeof findSchema>;\n\nconst DEFAULT_LIMIT = 1000;\n\nexport interface FindToolDetails {\n\ttruncation?: TruncationResult;\n\tresultLimitReached?: number;\n\t/** Set only when output was packed to an artifact; see tool-output-packer.ts. */\n\tartifactId?: string;\n\t/** Set when this exact query has repeatedly produced broad/truncated results. */\n\tinvalidationCandidate?: boolean;\n}\n\n/**\n * Pluggable operations for the find tool.\n * Override these to delegate file search to remote systems (for example SSH).\n */\nexport interface FindOperations {\n\t/** Check if path exists */\n\texists: (absolutePath: string) => Promise<boolean> | boolean;\n\t/** Find files matching glob pattern. Returns relative or absolute paths. */\n\tglob: (pattern: string, cwd: string, options: { ignore: string[]; limit: number }) => Promise<string[]> | string[];\n}\n\nconst defaultFindOperations: FindOperations = {\n\texists: pathExists,\n\t// This is a placeholder. Actual fd execution happens in execute() when no custom glob is provided.\n\tglob: () => [],\n};\n\nexport interface FindToolOptions {\n\t/** Custom operations for find. Default: local filesystem plus routed FFF/fd search */\n\toperations?: FindOperations;\n\t/** FFF backend for resident indexed search. Set false to force fd fallback. */\n\tfff?: FffSearchBackend | false;\n\t/** Pure router that selects FFF or fd from request filters and environment facts. */\n\tsearchRouter?: SearchRouter;\n\t/**\n\t * Opt-in artifact store for first-capture-then-bound output packing (Phase 3). When\n\t * omitted (the default), behavior is byte-for-byte unchanged from before this option\n\t * existed: output is truncated the same way, just never artifact-backed.\n\t */\n\tartifactStore?: ArtifactStore;\n\t/** Opt-in tracker for repeated-broad-query \"do not repeat\" signals. Also default-off. */\n\tbroadQueryTracker?: BroadQueryTracker;\n}\n\nfunction formatFindCall(\n\targs: { pattern: string; path?: string; limit?: number } | undefined,\n\ttheme: Theme,\n\tcwd: string,\n): string {\n\tconst pattern = str(args?.pattern);\n\tconst rawPath = str(args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath || \".\", cwd) : null;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text =\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"find\")) +\n\t\t\" \" +\n\t\t(pattern === null ? invalidArg : theme.fg(\"accent\", pattern || \"\")) +\n\t\ttheme.fg(\"toolOutput\", ` in ${path === null ? invalidArg : path}`);\n\tif (limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (limit ${limit})`);\n\t}\n\treturn text;\n}\n\nfunction formatFindResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: FindToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tshowImages: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tlet text = \"\";\n\tif (output) {\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst maxLines = options.expanded ? lines.length : 20;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t}\n\t}\n\n\tconst resultLimit = result.details?.resultLimitReached;\n\tconst truncation = result.details?.truncation;\n\tif (resultLimit || truncation?.truncated) {\n\t\tconst warnings: string[] = [];\n\t\tif (resultLimit) warnings.push(`${resultLimit} results limit`);\n\t\tif (truncation?.truncated) warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);\n\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${warnings.join(\", \")}]`)}`;\n\t}\n\treturn text;\n}\n\nfunction hasGlobSyntax(pattern: string): boolean {\n\treturn pattern === \".\" || /[*?[{]/.test(pattern);\n}\n\nfunction fffQueryParts(parts: string[]): string {\n\treturn parts.filter(Boolean).join(\" \");\n}\n\nfunction toSearchRelative(repoRelativePath: string, searchPathRelativeToCwd: string): string | undefined {\n\tif (!searchPathRelativeToCwd) return repoRelativePath;\n\tconst prefix = `${searchPathRelativeToCwd}/`;\n\tif (!repoRelativePath.startsWith(prefix)) return undefined;\n\treturn repoRelativePath.slice(prefix.length);\n}\n\nfunction fffGlobPattern(pattern: string, searchPathRelativeToCwd: string): string {\n\tconst effectivePattern = pattern === \".\" ? \"**/*\" : pattern;\n\tif (!searchPathRelativeToCwd) {\n\t\tif (effectivePattern.includes(\"/\") || effectivePattern.startsWith(\"**/\")) return effectivePattern;\n\t\treturn `**/${effectivePattern}`;\n\t}\n\tif (effectivePattern === \"**\" || effectivePattern === \"**/*\") return `${searchPathRelativeToCwd}/**/*`;\n\tif (effectivePattern.includes(\"/\")) return `${searchPathRelativeToCwd}/${effectivePattern}`;\n\treturn `${searchPathRelativeToCwd}/**/${effectivePattern}`;\n}\n\ninterface FindPackingOptions {\n\ttoolCallId: string;\n\tartifactStore?: ArtifactStore;\n\tbroadQueryTracker?: BroadQueryTracker;\n\tpattern: string;\n\trawPath?: string;\n}\n\nfunction fffSearchOutput(\n\tresult: FffSearchResult,\n\tsearchPathRelativeToCwd: string,\n\teffectiveLimit: number,\n\tpacking: FindPackingOptions,\n) {\n\tconst relativized = result.items\n\t\t.map((item) => toSearchRelative(item.relativePath, searchPathRelativeToCwd))\n\t\t.filter((item): item is string => Boolean(item));\n\treturn formatFindResults(relativized, effectiveLimit, packing);\n}\n\nexport async function tryFffFind(options: {\n\tbackend: FffSearchBackend;\n\trouter: SearchRouter;\n\tcwd: string;\n\tsearchPath: string;\n\tpattern: string;\n\tignoreCase?: boolean;\n\teffectiveLimit: number;\n\ttoolCallId: string;\n\tartifactStore?: ArtifactStore;\n\tbroadQueryTracker?: BroadQueryTracker;\n\trawPath?: string;\n}): Promise<{ text: string; details: FindToolDetails } | undefined> {\n\tif (!(await pathExists(options.searchPath))) return undefined;\n\n\t// Kick off the FFF finder -- and, on a machine where fff-node isn't provisioned\n\t// yet, its lazy managed install -- unconditionally and as early as possible.\n\t// Routing below can still send THIS call to the fd fallback for reasons that\n\t// have nothing to do with tool availability (most commonly: the default result\n\t// limit is well above the FFF top-N threshold, see search-router.ts). That\n\t// outcome must not gate the install itself, or a machine that only ever issues\n\t// default-limit searches would never provision fff-node at all. getFinder() is\n\t// cached per basePath, so this costs nothing beyond the first call for a given\n\t// cwd -- the `await` further down, when this call does want FFF, reuses this\n\t// same in-flight/resolved promise instead of triggering the work twice.\n\t// safeGetFinder() guarantees this can never reject (a non-conforming custom\n\t// backend that throws synchronously still degrades to \"unavailable\"), so it\n\t// can neither produce an unhandled rejection nor fail this tool call outright.\n\tconst finderPromise = safeGetFinder(options.backend, options.cwd);\n\n\tconst searchPathRelativeToCwd = relativePathInside(options.cwd, options.searchPath);\n\tconst glob = hasGlobSyntax(options.pattern);\n\tconst baseRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: true,\n\t\tpathResolvable: searchPathRelativeToCwd !== undefined,\n\t\tgitignoreInTree: false,\n\t});\n\tif (baseRoute.backend !== \"fff\") return undefined;\n\tif (searchPathRelativeToCwd === undefined) return undefined;\n\n\tconst gitignoreInTree = await hasGitignoreInTree(options.searchPath);\n\tconst semanticRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: true,\n\t\tpathResolvable: true,\n\t\tgitignoreInTree,\n\t});\n\tif (semanticRoute.backend !== \"fff\") return undefined;\n\n\tconst finder = await finderPromise;\n\tconst finderRoute = options.router.route({\n\t\ttool: \"find\",\n\t\tglob,\n\t\tignoreCase: Boolean(options.ignoreCase),\n\t\tlimit: options.effectiveLimit,\n\t\tfinderAvailable: Boolean(finder),\n\t\tpathResolvable: true,\n\t\tgitignoreInTree: false,\n\t});\n\tif (!finder || finderRoute.backend !== \"fff\") return undefined;\n\n\tconst packing: FindPackingOptions = {\n\t\ttoolCallId: options.toolCallId,\n\t\tartifactStore: options.artifactStore,\n\t\tbroadQueryTracker: options.broadQueryTracker,\n\t\tpattern: options.pattern,\n\t\trawPath: options.rawPath,\n\t};\n\n\tif (glob) {\n\t\tconst result = finder.glob(fffGlobPattern(options.pattern, searchPathRelativeToCwd), {\n\t\t\tpageSize: options.effectiveLimit,\n\t\t});\n\t\treturn result.ok\n\t\t\t? fffSearchOutput(result.value, searchPathRelativeToCwd, options.effectiveLimit, packing)\n\t\t\t: undefined;\n\t}\n\n\tconst pathConstraint = searchPathRelativeToCwd ? `${searchPathRelativeToCwd}/` : \"\";\n\tconst result = finder.fileSearch(fffQueryParts([pathConstraint, options.pattern]), {\n\t\tpageSize: options.effectiveLimit,\n\t});\n\treturn result.ok\n\t\t? fffSearchOutput(result.value, searchPathRelativeToCwd, options.effectiveLimit, packing)\n\t\t: undefined;\n}\n\nfunction formatFindResults(\n\trelativized: string[],\n\teffectiveLimit: number,\n\tpacking: FindPackingOptions,\n): { text: string; details: FindToolDetails } {\n\tif (relativized.length === 0) {\n\t\treturn { text: \"No files found matching pattern\", details: {} };\n\t}\n\n\tconst dirGroups = new Map<string, string[]>();\n\tconst extCounts = new Map<string, number>();\n\n\tfor (const p of relativized) {\n\t\tconst dir = path.dirname(p);\n\t\tconst base = path.basename(p);\n\t\tconst dirKey = dir === \".\" ? \"./\" : `${dir}/`;\n\t\tif (!dirGroups.has(dirKey)) {\n\t\t\tdirGroups.set(dirKey, []);\n\t\t}\n\t\tdirGroups.get(dirKey)!.push(base);\n\n\t\tconst ext = path.extname(p).toLowerCase() || \"(no extension)\";\n\t\textCounts.set(ext, (extCounts.get(ext) || 0) + 1);\n\t}\n\n\tconst sortedDirs = Array.from(dirGroups.keys()).sort((a, b) => a.localeCompare(b));\n\tconst formattedLines: string[] = [];\n\tfor (const dir of sortedDirs) {\n\t\tformattedLines.push(dir);\n\t\tconst files = dirGroups.get(dir)!;\n\t\tfiles.sort((a, b) => a.localeCompare(b));\n\t\tfor (const file of files) {\n\t\t\tformattedLines.push(` ${file}`);\n\t\t}\n\t}\n\n\tconst extSummaryParts = Array.from(extCounts.entries())\n\t\t.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\t\t.map(([ext, count]) => `${ext}: ${count}`);\n\tconst extSummary = `Extensions: ${extSummaryParts.join(\", \")}`;\n\n\tconst resultLimitReached = relativized.length >= effectiveLimit;\n\tconst rawOutput = formattedLines.join(\"\\n\");\n\t// Measure -> pack (artifact-backed if oversized and a store was provided) -> notices.\n\tconst packed = packToolOutput(\n\t\t{\n\t\t\ttoolName: \"find\",\n\t\t\tpath: packing.rawPath,\n\t\t\trawContent: rawOutput,\n\t\t\t// No line limit here because the result limit already caps rows; only the byte\n\t\t\t// cap should apply, matching the pre-Slice-B truncateHead call exactly.\n\t\t\ttruncation: { maxLines: Number.MAX_SAFE_INTEGER },\n\t\t},\n\t\tpacking.artifactStore,\n\t\tpacking.toolCallId,\n\t);\n\tlet resultOutput = packed.content;\n\tconst details: FindToolDetails = {};\n\tconst notices: string[] = [];\n\tif (packed.artifactId) {\n\t\tnotices.push(formatArtifactNotice(packed.artifactId));\n\t\tdetails.artifactId = packed.artifactId;\n\t}\n\tif (resultLimitReached) {\n\t\tnotices.push(\n\t\t\t`${effectiveLimit} results limit reached. Use limit=${effectiveLimit * 2} for more, or narrow path/pattern`,\n\t\t);\n\t\tdetails.resultLimitReached = effectiveLimit;\n\t}\n\tif (packed.truncation.truncated) {\n\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t// Drop the duplicated bounded-preview text: it's already in the message's own\n\t\t// content, and re-including it here can push `details` past\n\t\t// MAX_RETAINED_TOOL_RESULT_DETAILS_BYTES (message-retention.ts), which replaces\n\t\t// the *entire* details object with a stub -- silently losing artifactId and every\n\t\t// other field alongside it. This is load-bearing beyond just the retention budget:\n\t\t// agent-session.ts's _releaseGcPackedArtifactReferences() reads artifactId back off\n\t\t// this same canonical message at eviction time (potentially many turns later), so\n\t\t// keeping `details` small here is what keeps that release path working at all. If\n\t\t// this field ever grows a large addition again, add a regression proving artifactId\n\t\t// survives compactToolResultDetailsForRetention (see\n\t\t// test/suite/agent-session-artifact-lifecycle.test.ts), not just a details-size check.\n\t\tdetails.truncation = { ...packed.truncation, content: \"\" };\n\t}\n\tif (resultLimitReached || packed.truncation.truncated) {\n\t\tconst note = broadQueryInvalidationNote(\n\t\t\tpacking.broadQueryTracker,\n\t\t\tnormalizeBroadQueryKey({ toolName: \"find\", pattern: packing.pattern, path: packing.rawPath }),\n\t\t\t`find \"${packing.pattern}\" in ${packing.rawPath ?? \".\"}`,\n\t\t);\n\t\tif (note) {\n\t\t\tnotices.push(note);\n\t\t\tdetails.invalidationCandidate = true;\n\t\t}\n\t}\n\tif (relativized.length > 0) {\n\t\tresultOutput += `\\n\\n[Summary - ${extSummary}]`;\n\t}\n\tif (notices.length > 0) {\n\t\tresultOutput += `\\n\\n[${notices.join(\". \")}]`;\n\t}\n\treturn { text: resultOutput, details };\n}\n\nexport function createFindToolDefinition(\n\tcwd: string,\n\toptions?: FindToolOptions,\n): ToolDefinition<typeof findSchema, FindToolDetails | undefined> {\n\tconst customOps = options?.operations;\n\tconst fffBackend = options?.fff === false ? undefined : (options?.fff ?? defaultFffSearchBackend);\n\tconst searchRouter = options?.searchRouter ?? defaultSearchRouter;\n\tconst artifactStore = options?.artifactStore;\n\tconst broadQueryTracker = options?.broadQueryTracker;\n\treturn {\n\t\tname: \"find\",\n\t\tlabel: \"find\",\n\t\tdescription: `Search for files by glob pattern. Returns matching file paths relative to the search directory. Respects .gitignore. Output is truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first).`,\n\t\tpromptSnippet: \"Find files by glob pattern (respects .gitignore)\",\n\t\tparameters: findSchema,\n\t\ttoolGroup: \"explore\",\n\t\tasync execute(\n\t\t\ttoolCallId,\n\t\t\t{\n\t\t\t\tpattern,\n\t\t\t\tpath: searchDir,\n\t\t\t\tlimit,\n\t\t\t\tignoreCase,\n\t\t\t}: { pattern: string; path?: string; limit?: number; ignoreCase?: boolean },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet settled = false;\n\t\t\t\tlet stopChild: (() => void) | undefined;\n\t\t\t\tconst settle = (fn: () => void) => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tstopChild = undefined;\n\t\t\t\t\tfn();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tstopChild?.();\n\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t};\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst searchPath = resolveToCwd(searchDir || \".\", cwd);\n\t\t\t\t\t\tconst effectiveLimit = limit ?? DEFAULT_LIMIT;\n\t\t\t\t\t\tconst ops = customOps ?? defaultFindOperations;\n\n\t\t\t\t\t\tlet effectivePattern = pattern;\n\t\t\t\t\t\tif (pattern === \".\") {\n\t\t\t\t\t\t\teffectivePattern = \"**/*\";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!customOps && fffBackend) {\n\t\t\t\t\t\t\tconst fffResult = await tryFffFind({\n\t\t\t\t\t\t\t\tbackend: fffBackend,\n\t\t\t\t\t\t\t\trouter: searchRouter,\n\t\t\t\t\t\t\t\tcwd,\n\t\t\t\t\t\t\t\tsearchPath,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\tignoreCase,\n\t\t\t\t\t\t\t\teffectiveLimit,\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (fffResult) {\n\t\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: fffResult.text }],\n\t\t\t\t\t\t\t\t\t\tdetails: Object.keys(fffResult.details).length > 0 ? fffResult.details : undefined,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If custom operations provide glob(), use that instead of fd.\n\t\t\t\t\t\tif (customOps?.glob) {\n\t\t\t\t\t\t\tif (!(await ops.exists(searchPath))) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(`Path not found: ${searchPath}`)));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst results = await ops.glob(effectivePattern, searchPath, {\n\t\t\t\t\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t\t\t\t\t\tlimit: effectiveLimit,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Relativize paths against the search root for stable output.\n\t\t\t\t\t\t\tconst relativized = results.map((p) => {\n\t\t\t\t\t\t\t\tif (p.startsWith(searchPath)) return toPosixPath(p.slice(searchPath.length + 1));\n\t\t\t\t\t\t\t\treturn toPosixPath(path.relative(searchPath, p));\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst formatted = formatFindResults(relativized, effectiveLimit, {\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: formatted.text }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(formatted.details).length > 0 ? formatted.details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default implementation uses fd.\n\t\t\t\t\t\tconst fdPath = await ensureTool(\"fd\", true);\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!fdPath) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"fd is not available and could not be downloaded\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Build fd arguments. --no-require-git makes fd apply hierarchical .gitignore\n\t\t\t\t\t\t// semantics whether or not the search path is inside a git repository, without\n\t\t\t\t\t\t// leaking sibling-directory rules the way --ignore-file (a global source) would.\n\t\t\t\t\t\tconst args: string[] = [\n\t\t\t\t\t\t\t\"--glob\",\n\t\t\t\t\t\t\t\"--color=never\",\n\t\t\t\t\t\t\t\"--hidden\",\n\t\t\t\t\t\t\t\"--no-require-git\",\n\t\t\t\t\t\t\t\"--max-results\",\n\t\t\t\t\t\t\tString(effectiveLimit),\n\t\t\t\t\t\t];\n\t\t\t\t\t\tif (ignoreCase) {\n\t\t\t\t\t\t\targs.push(\"--ignore-case\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// fd --glob matches against the basename unless --full-path is set; in --full-path\n\t\t\t\t\t\t// mode it matches against the absolute candidate path, so a path-containing\n\t\t\t\t\t\t// pattern like 'src/**/*.spec.ts' needs a leading '**/' to match anything.\n\t\t\t\t\t\tlet finalPattern = effectivePattern;\n\t\t\t\t\t\tif (effectivePattern.includes(\"/\")) {\n\t\t\t\t\t\t\targs.push(\"--full-path\");\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!effectivePattern.startsWith(\"/\") &&\n\t\t\t\t\t\t\t\t!effectivePattern.startsWith(\"**/\") &&\n\t\t\t\t\t\t\t\teffectivePattern !== \"**\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tfinalPattern = `**/${effectivePattern}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\targs.push(\"--\", finalPattern, searchPath);\n\n\t\t\t\t\t\tconst child = spawn(fdPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\t\t\t\tconst rl = createInterface({ input: child.stdout });\n\t\t\t\t\t\tlet stderr = \"\";\n\t\t\t\t\t\tconst lines: string[] = [];\n\n\t\t\t\t\t\tstopChild = () => {\n\t\t\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\t\t\tchild.kill();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst cleanup = () => {\n\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\t\t\t\t\tstderr += chunk.toString();\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\trl.on(\"line\", (line) => {\n\t\t\t\t\t\t\tlines.push(line);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\tsettle(() => reject(new Error(`Failed to run fd: ${error.message}`)));\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst output = lines.join(\"\\n\");\n\t\t\t\t\t\t\tif (code !== 0) {\n\t\t\t\t\t\t\t\tconst errorMsg = stderr.trim() || `fd exited with code ${code}`;\n\t\t\t\t\t\t\t\tif (!output) {\n\t\t\t\t\t\t\t\t\tsettle(() => reject(new Error(errorMsg)));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst relativized: string[] = [];\n\t\t\t\t\t\t\tfor (const rawLine of lines) {\n\t\t\t\t\t\t\t\tconst line = rawLine.replace(/\\r$/, \"\").trim();\n\t\t\t\t\t\t\t\tif (!line) continue;\n\t\t\t\t\t\t\t\tconst hadTrailingSlash = line.endsWith(\"/\") || line.endsWith(\"\\\\\");\n\t\t\t\t\t\t\t\tlet relativePath = line;\n\t\t\t\t\t\t\t\tif (line.startsWith(searchPath)) {\n\t\t\t\t\t\t\t\t\trelativePath = line.slice(searchPath.length + 1);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\trelativePath = path.relative(searchPath, line);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (hadTrailingSlash && !relativePath.endsWith(\"/\")) relativePath += \"/\";\n\t\t\t\t\t\t\t\trelativized.push(toPosixPath(relativePath));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst formatted = formatFindResults(relativized, effectiveLimit, {\n\t\t\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\t\t\tartifactStore,\n\t\t\t\t\t\t\t\tbroadQueryTracker,\n\t\t\t\t\t\t\t\tpattern: effectivePattern,\n\t\t\t\t\t\t\t\trawPath: searchDir,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: formatted.text }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(formatted.details).length > 0 ? formatted.details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst error = e instanceof Error ? e : new Error(String(e));\n\t\t\t\t\t\tsettle(() => reject(error));\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindCall(args, theme, context.cwd));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindResult(result as any, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createFindTool(cwd: string, options?: FindToolOptions): AgentTool<typeof findSchema> {\n\treturn wrapToolDefinition(createFindToolDefinition(cwd, options));\n}\n"]}
@@ -51,6 +51,26 @@ export interface GrepToolOptions {
51
51
  /** Opt-in tracker for repeated-broad-query "do not repeat" signals. Also default-off. */
52
52
  broadQueryTracker?: BroadQueryTracker;
53
53
  }
54
+ export declare function tryFffGrep(options: {
55
+ backend: FffSearchBackend;
56
+ router: SearchRouter;
57
+ cwd: string;
58
+ searchPath: string;
59
+ pattern: string;
60
+ glob?: string;
61
+ ignoreCase?: boolean;
62
+ literal?: boolean;
63
+ contextValue: number;
64
+ effectiveLimit: number;
65
+ isDirectory: boolean;
66
+ toolCallId: string;
67
+ artifactStore?: ArtifactStore;
68
+ broadQueryTracker?: BroadQueryTracker;
69
+ rawPath?: string;
70
+ }): Promise<{
71
+ text: string;
72
+ details: GrepToolDetails;
73
+ } | undefined>;
54
74
  export declare function createGrepToolDefinition(cwd: string, options?: GrepToolOptions): ToolDefinition<typeof grepSchema, GrepToolDetails | undefined>;
55
75
  export declare function createGrepTool(cwd: string, options?: GrepToolOptions): AgentTool<typeof grepSchema>;
56
76
  export {};