@kolisachint/hoocode-agent 0.4.144 → 0.4.146
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +12 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +6 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/search/context-assembler.d.ts.map +1 -1
- package/dist/core/search/context-assembler.js +22 -12
- package/dist/core/search/context-assembler.js.map +1 -1
- package/dist/core/search/hybrid-search.d.ts.map +1 -1
- package/dist/core/search/hybrid-search.js +4 -1
- package/dist/core/search/hybrid-search.js.map +1 -1
- package/dist/core/search/lexical-retriever.d.ts.map +1 -1
- package/dist/core/search/lexical-retriever.js +14 -1
- package/dist/core/search/lexical-retriever.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +1 -1
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +2 -2
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +7 -5
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +44 -6
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/native-search.d.ts.map +1 -1
- package/dist/core/tools/native-search.js +49 -3
- package/dist/core/tools/native-search.js.map +1 -1
- package/dist/core/tools/search.d.ts.map +1 -1
- package/dist/core/tools/search.js +6 -3
- package/dist/core/tools/search.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +6 -4
- package/dist/main.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lexical-retriever.d.ts","sourceRoot":"","sources":["../../../src/core/search/lexical-retriever.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAWhD,MAAM,WAAW,gBAAgB;IAChC,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAgBjF;AAED,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAUD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAiBD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"lexical-retriever.d.ts","sourceRoot":"","sources":["../../../src/core/search/lexical-retriever.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAWhD,MAAM,WAAW,gBAAgB;IAChC,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAgBjF;AAED,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAUD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAiBD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAyG5F","sourcesContent":["/**\n * Internal lexical retriever for hybrid search.\n *\n * This is not the grep *tool* — it is the lexical recall backend: it turns a\n * natural query into a ripgrep pattern, streams matches, and returns bare\n * `rel:line` hits for the grep→chunk adapter. rg drives the fast path; the\n * pure-JS nativeGrep fallback keeps restricted environments working, same as\n * the grep tool.\n */\n\nimport { createInterface } from \"node:readline\";\nimport { spawn } from \"child_process\";\nimport { readFileSync } from \"fs\";\nimport path from \"path\";\nimport { ensureTool } from \"../../utils/tools-manager.js\";\nimport { isNativeSearchForced, nativeGrep } from \"../tools/native-search.js\";\nimport type { GrepLineHit } from \"./adapter.js\";\n\n/** Terms considered per query (longest first) when building the pattern. */\nconst MAX_TERMS = 4;\n/** Minimum token length worth matching on. */\nconst MIN_TERM_LENGTH = 3;\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport interface LexicalQueryPlan {\n\t/** rg-ready regex pattern. */\n\tpattern: string;\n\t/** Raw (unescaped, lowercased) terms, for per-line term attribution. */\n\tterms: string[];\n}\n\n/**\n * Build the retrieval plan for a query: a quoted segment is searched\n * verbatim; otherwise the longest few identifier-ish tokens are OR-ed\n * together. Returns undefined when the query yields nothing searchable.\n */\nexport function buildLexicalQueryPlan(query: string): LexicalQueryPlan | undefined {\n\tconst quoted = [...query.matchAll(/[\"'`]([^\"'`]+)[\"'`]/g)]\n\t\t.map((m) => m[1].trim())\n\t\t.filter((s) => s.length > 0)\n\t\t.sort((a, b) => b.length - a.length)[0];\n\tif (quoted) return { pattern: escapeRegExp(quoted), terms: [quoted.toLowerCase()] };\n\n\tconst tokens = [...new Set(query.match(/[A-Za-z0-9_$][\\w$.-]*/g) ?? [])]\n\t\t.filter((t) => t.length >= MIN_TERM_LENGTH)\n\t\t.sort((a, b) => b.length - a.length || a.localeCompare(b))\n\t\t.slice(0, MAX_TERMS);\n\tif (tokens.length === 0) {\n\t\tconst trimmed = query.trim();\n\t\treturn trimmed ? { pattern: escapeRegExp(trimmed), terms: [trimmed.toLowerCase()] } : undefined;\n\t}\n\treturn { pattern: tokens.map(escapeRegExp).join(\"|\"), terms: tokens.map((t) => t.toLowerCase()) };\n}\n\n/** Pattern-only view of {@link buildLexicalQueryPlan}. */\nexport function buildLexicalPattern(query: string): string | undefined {\n\treturn buildLexicalQueryPlan(query)?.pattern;\n}\n\n/** Which plan terms appear on a matched line (retrieval is case-insensitive,\n * so attribution is too). */\nfunction termsOnLine(plan: LexicalQueryPlan, lineText: string | undefined): string[] {\n\tif (!lineText) return [];\n\tconst lower = lineText.toLowerCase();\n\treturn plan.terms.filter((t) => lower.includes(t));\n}\n\n/**\n * Run lexical retrieval over `cwd`, returning up to `limit` line-hits in\n * output order with POSIX repo-relative paths.\n */\nexport interface RunLexicalOptions {\n\tcwd: string;\n\tquery: string;\n\tlimit: number;\n\tglob?: string;\n\tsignal?: AbortSignal;\n}\n\n/**\n * Run the lexical retriever for the search tool. Optional glob filter scopes\n * file paths (slashless matches basename anywhere, slash patterns match the\n * full repo-relative path).\n */\nfunction normalizeSearchGlob(glob: string | undefined): string | undefined {\n\tif (!glob) return undefined;\n\t// Match fd/rg semantics: a slash-containing glob is anchored anywhere in\n\t// the tree, so prepend \"**/\" unless it already starts with a slash or \"**/\".\n\tif (glob.includes(\"/\") && !glob.startsWith(\"/\") && !glob.startsWith(\"**/\")) {\n\t\treturn `**/${glob}`;\n\t}\n\treturn glob;\n}\n\nexport async function runLexicalRetriever(options: RunLexicalOptions): Promise<GrepLineHit[]> {\n\tconst { cwd, query, limit, glob: rawGlob, signal } = options;\n\tconst glob = normalizeSearchGlob(rawGlob);\n\tconst plan = buildLexicalQueryPlan(query);\n\tif (!plan) return [];\n\tconst { pattern } = plan;\n\n\tconst toRel = (filePath: string): string => {\n\t\tconst rel = path.relative(cwd, filePath);\n\t\treturn (rel && !rel.startsWith(\"..\") ? rel : filePath).replace(/\\\\/g, \"/\");\n\t};\n\n\tconst rgPath = isNativeSearchForced() ? undefined : await ensureTool(\"rg\", true);\n\tif (!rgPath) {\n\t\tconst result = await nativeGrep(cwd, {\n\t\t\tpattern,\n\t\t\tisDirectory: true,\n\t\t\tignoreCase: true,\n\t\t\tlimit,\n\t\t\tglob,\n\t\t\tsignal,\n\t\t\treadFile: (p) => readFileSync(p, \"utf-8\"),\n\t\t});\n\t\treturn result.matches.map((m) => ({\n\t\t\trel: toRel(m.filePath),\n\t\t\tline: m.lineNumber,\n\t\t\tterms: termsOnLine(plan, m.lineText),\n\t\t}));\n\t}\n\n\treturn new Promise<GrepLineHit[]>((resolve, reject) => {\n\t\t// --sort path forces a deterministic (single-threaded) walk: with the\n\t\t// match cap truncating the stream, a parallel walk would return a\n\t\t// different hit subset per run — \"same query, different context\".\n\t\t// `!.git` because --hidden would otherwise search .git contents;\n\t\t// --no-require-git so .gitignore is honored outside git repos too.\n\t\tconst args = [\n\t\t\t\"--json\",\n\t\t\t\"--line-number\",\n\t\t\t\"--color=never\",\n\t\t\t\"--hidden\",\n\t\t\t\"--no-require-git\",\n\t\t\t\"--ignore-case\",\n\t\t\t\"--sort\",\n\t\t\t\"path\",\n\t\t\t\"--glob\",\n\t\t\t\"!**/.git/**\",\n\t\t];\n\t\tif (glob) args.push(\"--glob\", glob);\n\t\targs.push(\"--\", pattern, cwd);\n\t\tconst child = spawn(rgPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tconst rl = createInterface({ input: child.stdout });\n\t\tconst hits: GrepLineHit[] = [];\n\t\tlet stderr = \"\";\n\t\tlet killedDueToLimit = false;\n\t\tlet aborted = false;\n\n\t\tconst onAbort = () => {\n\t\t\taborted = true;\n\t\t\tif (!child.killed) child.kill();\n\t\t};\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\tstderr += chunk.toString();\n\t\t});\n\n\t\trl.on(\"line\", (line) => {\n\t\t\tif (!line.trim() || hits.length >= limit) return;\n\t\t\tlet event: any;\n\t\t\ttry {\n\t\t\t\tevent = JSON.parse(line);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event.type !== \"match\") return;\n\t\t\tconst filePath = event.data?.path?.text;\n\t\t\tconst lineNumber = event.data?.line_number;\n\t\t\tif (filePath && typeof lineNumber === \"number\") {\n\t\t\t\thits.push({ rel: toRel(filePath), line: lineNumber, terms: termsOnLine(plan, event.data?.lines?.text) });\n\t\t\t}\n\t\t\tif (hits.length >= limit && !child.killed) {\n\t\t\t\tkilledDueToLimit = true;\n\t\t\t\tchild.kill();\n\t\t\t}\n\t\t});\n\n\t\tchild.on(\"error\", (error) => {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\treject(new Error(`Failed to run ripgrep: ${error.message}`));\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\trl.close();\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tif (aborted) {\n\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// rg exits 1 on \"no matches\" — that is a valid empty result.\n\t\t\tif (!killedDueToLimit && code !== 0 && code !== 1) {\n\t\t\t\treject(new Error(stderr.trim() || `ripgrep exited with code ${code}`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolve(hits);\n\t\t});\n\t});\n}\n"]}
|
|
@@ -101,7 +101,20 @@ export async function runLexicalRetriever(options) {
|
|
|
101
101
|
// --sort path forces a deterministic (single-threaded) walk: with the
|
|
102
102
|
// match cap truncating the stream, a parallel walk would return a
|
|
103
103
|
// different hit subset per run — "same query, different context".
|
|
104
|
-
|
|
104
|
+
// `!.git` because --hidden would otherwise search .git contents;
|
|
105
|
+
// --no-require-git so .gitignore is honored outside git repos too.
|
|
106
|
+
const args = [
|
|
107
|
+
"--json",
|
|
108
|
+
"--line-number",
|
|
109
|
+
"--color=never",
|
|
110
|
+
"--hidden",
|
|
111
|
+
"--no-require-git",
|
|
112
|
+
"--ignore-case",
|
|
113
|
+
"--sort",
|
|
114
|
+
"path",
|
|
115
|
+
"--glob",
|
|
116
|
+
"!**/.git/**",
|
|
117
|
+
];
|
|
105
118
|
if (glob)
|
|
106
119
|
args.push("--glob", glob);
|
|
107
120
|
args.push("--", pattern, cwd);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lexical-retriever.js","sourceRoot":"","sources":["../../../src/core/search/lexical-retriever.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Internal lexical retriever for hybrid search.\n *\n * This is not the grep *tool* — it is the lexical recall backend: it turns a\n * natural query into a ripgrep pattern, streams matches, and returns bare\n * `rel:line` hits for the grep→chunk adapter. rg drives the fast path; the\n * pure-JS nativeGrep fallback keeps restricted environments working, same as\n * the grep tool.\n */\n\nimport { createInterface } from \"node:readline\";\nimport { spawn } from \"child_process\";\nimport { readFileSync } from \"fs\";\nimport path from \"path\";\nimport { ensureTool } from \"../../utils/tools-manager.js\";\nimport { isNativeSearchForced, nativeGrep } from \"../tools/native-search.js\";\nimport type { GrepLineHit } from \"./adapter.js\";\n\n/** Terms considered per query (longest first) when building the pattern. */\nconst MAX_TERMS = 4;\n/** Minimum token length worth matching on. */\nconst MIN_TERM_LENGTH = 3;\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport interface LexicalQueryPlan {\n\t/** rg-ready regex pattern. */\n\tpattern: string;\n\t/** Raw (unescaped, lowercased) terms, for per-line term attribution. */\n\tterms: string[];\n}\n\n/**\n * Build the retrieval plan for a query: a quoted segment is searched\n * verbatim; otherwise the longest few identifier-ish tokens are OR-ed\n * together. Returns undefined when the query yields nothing searchable.\n */\nexport function buildLexicalQueryPlan(query: string): LexicalQueryPlan | undefined {\n\tconst quoted = [...query.matchAll(/[\"'`]([^\"'`]+)[\"'`]/g)]\n\t\t.map((m) => m[1].trim())\n\t\t.filter((s) => s.length > 0)\n\t\t.sort((a, b) => b.length - a.length)[0];\n\tif (quoted) return { pattern: escapeRegExp(quoted), terms: [quoted.toLowerCase()] };\n\n\tconst tokens = [...new Set(query.match(/[A-Za-z0-9_$][\\w$.-]*/g) ?? [])]\n\t\t.filter((t) => t.length >= MIN_TERM_LENGTH)\n\t\t.sort((a, b) => b.length - a.length || a.localeCompare(b))\n\t\t.slice(0, MAX_TERMS);\n\tif (tokens.length === 0) {\n\t\tconst trimmed = query.trim();\n\t\treturn trimmed ? { pattern: escapeRegExp(trimmed), terms: [trimmed.toLowerCase()] } : undefined;\n\t}\n\treturn { pattern: tokens.map(escapeRegExp).join(\"|\"), terms: tokens.map((t) => t.toLowerCase()) };\n}\n\n/** Pattern-only view of {@link buildLexicalQueryPlan}. */\nexport function buildLexicalPattern(query: string): string | undefined {\n\treturn buildLexicalQueryPlan(query)?.pattern;\n}\n\n/** Which plan terms appear on a matched line (retrieval is case-insensitive,\n * so attribution is too). */\nfunction termsOnLine(plan: LexicalQueryPlan, lineText: string | undefined): string[] {\n\tif (!lineText) return [];\n\tconst lower = lineText.toLowerCase();\n\treturn plan.terms.filter((t) => lower.includes(t));\n}\n\n/**\n * Run lexical retrieval over `cwd`, returning up to `limit` line-hits in\n * output order with POSIX repo-relative paths.\n */\nexport interface RunLexicalOptions {\n\tcwd: string;\n\tquery: string;\n\tlimit: number;\n\tglob?: string;\n\tsignal?: AbortSignal;\n}\n\n/**\n * Run the lexical retriever for the search tool. Optional glob filter scopes\n * file paths (slashless matches basename anywhere, slash patterns match the\n * full repo-relative path).\n */\nfunction normalizeSearchGlob(glob: string | undefined): string | undefined {\n\tif (!glob) return undefined;\n\t// Match fd/rg semantics: a slash-containing glob is anchored anywhere in\n\t// the tree, so prepend \"**/\" unless it already starts with a slash or \"**/\".\n\tif (glob.includes(\"/\") && !glob.startsWith(\"/\") && !glob.startsWith(\"**/\")) {\n\t\treturn `**/${glob}`;\n\t}\n\treturn glob;\n}\n\nexport async function runLexicalRetriever(options: RunLexicalOptions): Promise<GrepLineHit[]> {\n\tconst { cwd, query, limit, glob: rawGlob, signal } = options;\n\tconst glob = normalizeSearchGlob(rawGlob);\n\tconst plan = buildLexicalQueryPlan(query);\n\tif (!plan) return [];\n\tconst { pattern } = plan;\n\n\tconst toRel = (filePath: string): string => {\n\t\tconst rel = path.relative(cwd, filePath);\n\t\treturn (rel && !rel.startsWith(\"..\") ? rel : filePath).replace(/\\\\/g, \"/\");\n\t};\n\n\tconst rgPath = isNativeSearchForced() ? undefined : await ensureTool(\"rg\", true);\n\tif (!rgPath) {\n\t\tconst result = await nativeGrep(cwd, {\n\t\t\tpattern,\n\t\t\tisDirectory: true,\n\t\t\tignoreCase: true,\n\t\t\tlimit,\n\t\t\tglob,\n\t\t\tsignal,\n\t\t\treadFile: (p) => readFileSync(p, \"utf-8\"),\n\t\t});\n\t\treturn result.matches.map((m) => ({\n\t\t\trel: toRel(m.filePath),\n\t\t\tline: m.lineNumber,\n\t\t\tterms: termsOnLine(plan, m.lineText),\n\t\t}));\n\t}\n\n\treturn new Promise<GrepLineHit[]>((resolve, reject) => {\n\t\t// --sort path forces a deterministic (single-threaded) walk: with the\n\t\t// match cap truncating the stream, a parallel walk would return a\n\t\t// different hit subset per run — \"same query, different context\".\n\t\tconst args = [\"--json\", \"--line-number\", \"--color=never\", \"--hidden\", \"--ignore-case\", \"--sort\", \"path\"];\n\t\tif (glob) args.push(\"--glob\", glob);\n\t\targs.push(\"--\", pattern, cwd);\n\t\tconst child = spawn(rgPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tconst rl = createInterface({ input: child.stdout });\n\t\tconst hits: GrepLineHit[] = [];\n\t\tlet stderr = \"\";\n\t\tlet killedDueToLimit = false;\n\t\tlet aborted = false;\n\n\t\tconst onAbort = () => {\n\t\t\taborted = true;\n\t\t\tif (!child.killed) child.kill();\n\t\t};\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\tstderr += chunk.toString();\n\t\t});\n\n\t\trl.on(\"line\", (line) => {\n\t\t\tif (!line.trim() || hits.length >= limit) return;\n\t\t\tlet event: any;\n\t\t\ttry {\n\t\t\t\tevent = JSON.parse(line);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event.type !== \"match\") return;\n\t\t\tconst filePath = event.data?.path?.text;\n\t\t\tconst lineNumber = event.data?.line_number;\n\t\t\tif (filePath && typeof lineNumber === \"number\") {\n\t\t\t\thits.push({ rel: toRel(filePath), line: lineNumber, terms: termsOnLine(plan, event.data?.lines?.text) });\n\t\t\t}\n\t\t\tif (hits.length >= limit && !child.killed) {\n\t\t\t\tkilledDueToLimit = true;\n\t\t\t\tchild.kill();\n\t\t\t}\n\t\t});\n\n\t\tchild.on(\"error\", (error) => {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\treject(new Error(`Failed to run ripgrep: ${error.message}`));\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\trl.close();\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tif (aborted) {\n\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// rg exits 1 on \"no matches\" — that is a valid empty result.\n\t\t\tif (!killedDueToLimit && code !== 0 && code !== 1) {\n\t\t\t\treject(new Error(stderr.trim() || `ripgrep exited with code ${code}`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolve(hits);\n\t\t});\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lexical-retriever.js","sourceRoot":"","sources":["../../../src/core/search/lexical-retriever.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Internal lexical retriever for hybrid search.\n *\n * This is not the grep *tool* — it is the lexical recall backend: it turns a\n * natural query into a ripgrep pattern, streams matches, and returns bare\n * `rel:line` hits for the grep→chunk adapter. rg drives the fast path; the\n * pure-JS nativeGrep fallback keeps restricted environments working, same as\n * the grep tool.\n */\n\nimport { createInterface } from \"node:readline\";\nimport { spawn } from \"child_process\";\nimport { readFileSync } from \"fs\";\nimport path from \"path\";\nimport { ensureTool } from \"../../utils/tools-manager.js\";\nimport { isNativeSearchForced, nativeGrep } from \"../tools/native-search.js\";\nimport type { GrepLineHit } from \"./adapter.js\";\n\n/** Terms considered per query (longest first) when building the pattern. */\nconst MAX_TERMS = 4;\n/** Minimum token length worth matching on. */\nconst MIN_TERM_LENGTH = 3;\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport interface LexicalQueryPlan {\n\t/** rg-ready regex pattern. */\n\tpattern: string;\n\t/** Raw (unescaped, lowercased) terms, for per-line term attribution. */\n\tterms: string[];\n}\n\n/**\n * Build the retrieval plan for a query: a quoted segment is searched\n * verbatim; otherwise the longest few identifier-ish tokens are OR-ed\n * together. Returns undefined when the query yields nothing searchable.\n */\nexport function buildLexicalQueryPlan(query: string): LexicalQueryPlan | undefined {\n\tconst quoted = [...query.matchAll(/[\"'`]([^\"'`]+)[\"'`]/g)]\n\t\t.map((m) => m[1].trim())\n\t\t.filter((s) => s.length > 0)\n\t\t.sort((a, b) => b.length - a.length)[0];\n\tif (quoted) return { pattern: escapeRegExp(quoted), terms: [quoted.toLowerCase()] };\n\n\tconst tokens = [...new Set(query.match(/[A-Za-z0-9_$][\\w$.-]*/g) ?? [])]\n\t\t.filter((t) => t.length >= MIN_TERM_LENGTH)\n\t\t.sort((a, b) => b.length - a.length || a.localeCompare(b))\n\t\t.slice(0, MAX_TERMS);\n\tif (tokens.length === 0) {\n\t\tconst trimmed = query.trim();\n\t\treturn trimmed ? { pattern: escapeRegExp(trimmed), terms: [trimmed.toLowerCase()] } : undefined;\n\t}\n\treturn { pattern: tokens.map(escapeRegExp).join(\"|\"), terms: tokens.map((t) => t.toLowerCase()) };\n}\n\n/** Pattern-only view of {@link buildLexicalQueryPlan}. */\nexport function buildLexicalPattern(query: string): string | undefined {\n\treturn buildLexicalQueryPlan(query)?.pattern;\n}\n\n/** Which plan terms appear on a matched line (retrieval is case-insensitive,\n * so attribution is too). */\nfunction termsOnLine(plan: LexicalQueryPlan, lineText: string | undefined): string[] {\n\tif (!lineText) return [];\n\tconst lower = lineText.toLowerCase();\n\treturn plan.terms.filter((t) => lower.includes(t));\n}\n\n/**\n * Run lexical retrieval over `cwd`, returning up to `limit` line-hits in\n * output order with POSIX repo-relative paths.\n */\nexport interface RunLexicalOptions {\n\tcwd: string;\n\tquery: string;\n\tlimit: number;\n\tglob?: string;\n\tsignal?: AbortSignal;\n}\n\n/**\n * Run the lexical retriever for the search tool. Optional glob filter scopes\n * file paths (slashless matches basename anywhere, slash patterns match the\n * full repo-relative path).\n */\nfunction normalizeSearchGlob(glob: string | undefined): string | undefined {\n\tif (!glob) return undefined;\n\t// Match fd/rg semantics: a slash-containing glob is anchored anywhere in\n\t// the tree, so prepend \"**/\" unless it already starts with a slash or \"**/\".\n\tif (glob.includes(\"/\") && !glob.startsWith(\"/\") && !glob.startsWith(\"**/\")) {\n\t\treturn `**/${glob}`;\n\t}\n\treturn glob;\n}\n\nexport async function runLexicalRetriever(options: RunLexicalOptions): Promise<GrepLineHit[]> {\n\tconst { cwd, query, limit, glob: rawGlob, signal } = options;\n\tconst glob = normalizeSearchGlob(rawGlob);\n\tconst plan = buildLexicalQueryPlan(query);\n\tif (!plan) return [];\n\tconst { pattern } = plan;\n\n\tconst toRel = (filePath: string): string => {\n\t\tconst rel = path.relative(cwd, filePath);\n\t\treturn (rel && !rel.startsWith(\"..\") ? rel : filePath).replace(/\\\\/g, \"/\");\n\t};\n\n\tconst rgPath = isNativeSearchForced() ? undefined : await ensureTool(\"rg\", true);\n\tif (!rgPath) {\n\t\tconst result = await nativeGrep(cwd, {\n\t\t\tpattern,\n\t\t\tisDirectory: true,\n\t\t\tignoreCase: true,\n\t\t\tlimit,\n\t\t\tglob,\n\t\t\tsignal,\n\t\t\treadFile: (p) => readFileSync(p, \"utf-8\"),\n\t\t});\n\t\treturn result.matches.map((m) => ({\n\t\t\trel: toRel(m.filePath),\n\t\t\tline: m.lineNumber,\n\t\t\tterms: termsOnLine(plan, m.lineText),\n\t\t}));\n\t}\n\n\treturn new Promise<GrepLineHit[]>((resolve, reject) => {\n\t\t// --sort path forces a deterministic (single-threaded) walk: with the\n\t\t// match cap truncating the stream, a parallel walk would return a\n\t\t// different hit subset per run — \"same query, different context\".\n\t\t// `!.git` because --hidden would otherwise search .git contents;\n\t\t// --no-require-git so .gitignore is honored outside git repos too.\n\t\tconst args = [\n\t\t\t\"--json\",\n\t\t\t\"--line-number\",\n\t\t\t\"--color=never\",\n\t\t\t\"--hidden\",\n\t\t\t\"--no-require-git\",\n\t\t\t\"--ignore-case\",\n\t\t\t\"--sort\",\n\t\t\t\"path\",\n\t\t\t\"--glob\",\n\t\t\t\"!**/.git/**\",\n\t\t];\n\t\tif (glob) args.push(\"--glob\", glob);\n\t\targs.push(\"--\", pattern, cwd);\n\t\tconst child = spawn(rgPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tconst rl = createInterface({ input: child.stdout });\n\t\tconst hits: GrepLineHit[] = [];\n\t\tlet stderr = \"\";\n\t\tlet killedDueToLimit = false;\n\t\tlet aborted = false;\n\n\t\tconst onAbort = () => {\n\t\t\taborted = true;\n\t\t\tif (!child.killed) child.kill();\n\t\t};\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\tstderr += chunk.toString();\n\t\t});\n\n\t\trl.on(\"line\", (line) => {\n\t\t\tif (!line.trim() || hits.length >= limit) return;\n\t\t\tlet event: any;\n\t\t\ttry {\n\t\t\t\tevent = JSON.parse(line);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event.type !== \"match\") return;\n\t\t\tconst filePath = event.data?.path?.text;\n\t\t\tconst lineNumber = event.data?.line_number;\n\t\t\tif (filePath && typeof lineNumber === \"number\") {\n\t\t\t\thits.push({ rel: toRel(filePath), line: lineNumber, terms: termsOnLine(plan, event.data?.lines?.text) });\n\t\t\t}\n\t\t\tif (hits.length >= limit && !child.killed) {\n\t\t\t\tkilledDueToLimit = true;\n\t\t\t\tchild.kill();\n\t\t\t}\n\t\t});\n\n\t\tchild.on(\"error\", (error) => {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\treject(new Error(`Failed to run ripgrep: ${error.message}`));\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\trl.close();\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tif (aborted) {\n\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// rg exits 1 on \"no matches\" — that is a valid empty result.\n\t\t\tif (!killedDueToLimit && code !== 0 && code !== 1) {\n\t\t\t\treject(new Error(stderr.trim() || `ripgrep exited with code ${code}`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolve(hits);\n\t\t});\n\t});\n}\n"]}
|
|
@@ -43,7 +43,7 @@ export declare const DEFAULT_SETTINGS: {
|
|
|
43
43
|
enableBrowserTools: false;
|
|
44
44
|
enableBrowserLivePreview: false;
|
|
45
45
|
enableFileTools: false;
|
|
46
|
-
enableEmbsearchTools:
|
|
46
|
+
enableEmbsearchTools: true;
|
|
47
47
|
embsearchThresholdBytes: number;
|
|
48
48
|
light: false;
|
|
49
49
|
terminal: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-defaults.d.ts","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2ET,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t\tmaxContextRatio: 0.75,\n\t},\n\ttoolOutput: {\n\t\tmaxBytes: 16 * 1024,\n\t\tmaxLines: 800,\n\t},\n\tcontextGc: {\n\t\tenabled: true,\n\t},\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: false,\n\tdeferMcpSchemas: true,\n\tenableWebTools: false,\n\tenableBrowserTools: false,\n\tenableBrowserLivePreview: false,\n\tenableFileTools: false,\n\tenableEmbsearchTools:
|
|
1
|
+
{"version":3,"file":"settings-defaults.d.ts","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2ET,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t\tmaxContextRatio: 0.75,\n\t},\n\ttoolOutput: {\n\t\tmaxBytes: 16 * 1024,\n\t\tmaxLines: 800,\n\t},\n\tcontextGc: {\n\t\tenabled: true,\n\t},\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: false,\n\tdeferMcpSchemas: true,\n\tenableWebTools: false,\n\tenableBrowserTools: false,\n\tenableBrowserLivePreview: false,\n\tenableFileTools: false,\n\tenableEmbsearchTools: true,\n\tembsearchThresholdBytes: 0,\n\tlight: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t\tchimeOnTurnComplete: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorPaddingX: 0,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
|
|
@@ -43,8 +43,8 @@ export const DEFAULT_SETTINGS = {
|
|
|
43
43
|
enableBrowserTools: false,
|
|
44
44
|
enableBrowserLivePreview: false,
|
|
45
45
|
enableFileTools: false,
|
|
46
|
-
enableEmbsearchTools:
|
|
47
|
-
embsearchThresholdBytes:
|
|
46
|
+
enableEmbsearchTools: true,
|
|
47
|
+
embsearchThresholdBytes: 0,
|
|
48
48
|
light: false,
|
|
49
49
|
terminal: {
|
|
50
50
|
showImages: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-defaults.js","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE;QACX,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,KAAK;QACpB,gBAAgB,EAAE,KAAK;QACvB,eAAe,EAAE,IAAI;KACrB;IACD,UAAU,EAAE;QACX,QAAQ,EAAE,EAAE,GAAG,IAAI;QACnB,QAAQ,EAAE,GAAG;KACb;IACD,SAAS,EAAE;QACV,OAAO,EAAE,IAAI;KACb;IACD,aAAa,EAAE;QACd,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;KACjB;IACD,KAAK,EAAE;QACN,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE;YACT,eAAe,EAAE,KAAK;SACtB;KACD;IACD,iBAAiB,EAAE,KAAK;IACxB,YAAY,EAAE,KAAK;IACnB,iBAAiB,EAAE,KAAK;IACxB,sBAAsB,EAAE,IAAI;IAC5B,mBAAmB,EAAE,IAAI;IACzB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,CAAC;IACnB,yBAAyB,EAAE,CAAC;IAC5B,eAAe,EAAE,IAAI;IACrB,iBAAiB,EAAE,KAAK;IACxB,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,KAAK;IACrB,kBAAkB,EAAE,KAAK;IACzB,wBAAwB,EAAE,KAAK;IAC/B,eAAe,EAAE,KAAK;IACtB,oBAAoB,EAAE,
|
|
1
|
+
{"version":3,"file":"settings-defaults.js","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE;QACX,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,KAAK;QACpB,gBAAgB,EAAE,KAAK;QACvB,eAAe,EAAE,IAAI;KACrB;IACD,UAAU,EAAE;QACX,QAAQ,EAAE,EAAE,GAAG,IAAI;QACnB,QAAQ,EAAE,GAAG;KACb;IACD,SAAS,EAAE;QACV,OAAO,EAAE,IAAI;KACb;IACD,aAAa,EAAE;QACd,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;KACjB;IACD,KAAK,EAAE;QACN,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE;YACT,eAAe,EAAE,KAAK;SACtB;KACD;IACD,iBAAiB,EAAE,KAAK;IACxB,YAAY,EAAE,KAAK;IACnB,iBAAiB,EAAE,KAAK;IACxB,sBAAsB,EAAE,IAAI;IAC5B,mBAAmB,EAAE,IAAI;IACzB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,CAAC;IACnB,yBAAyB,EAAE,CAAC;IAC5B,eAAe,EAAE,IAAI;IACrB,iBAAiB,EAAE,KAAK;IACxB,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,KAAK;IACrB,kBAAkB,EAAE,KAAK;IACzB,wBAAwB,EAAE,KAAK;IAC/B,eAAe,EAAE,KAAK;IACtB,oBAAoB,EAAE,IAAI;IAC1B,uBAAuB,EAAE,CAAC;IAC1B,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE;QACT,UAAU,EAAE,IAAI;QAChB,eAAe,EAAE,EAAE;QACnB,aAAa,EAAE,KAAK;QACpB,oBAAoB,EAAE,KAAK;QAC3B,mBAAmB,EAAE,KAAK;KAC1B;IACD,MAAM,EAAE;QACP,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,KAAK;KAClB;IACD,kBAAkB,EAAE,MAAM;IAC1B,cAAc,EAAE,SAAS;IACzB,cAAc,EAAE,CAAC;IACjB,sBAAsB,EAAE,CAAC;IACzB,QAAQ,EAAE;QACT,eAAe,EAAE,IAAI;KACrB;IACD,QAAQ,EAAE;QACT,mBAAmB,EAAE,IAAI;KACzB;IACD,QAAQ,EAAE,EAAE;IACZ,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,aAAa,EAAE,EAAE;IACjB,MAAM,EAAE,EAAE;CACS,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t\tmaxContextRatio: 0.75,\n\t},\n\ttoolOutput: {\n\t\tmaxBytes: 16 * 1024,\n\t\tmaxLines: 800,\n\t},\n\tcontextGc: {\n\t\tenabled: true,\n\t},\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: false,\n\tdeferMcpSchemas: true,\n\tenableWebTools: false,\n\tenableBrowserTools: false,\n\tenableBrowserLivePreview: false,\n\tenableFileTools: false,\n\tenableEmbsearchTools: true,\n\tembsearchThresholdBytes: 0,\n\tlight: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t\tchimeOnTurnComplete: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorPaddingX: 0,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-types.d.ts","sourceRoot":"","sources":["../../src/core/settings-types.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Settings schema shared across the app.\n *\n * The `Settings` interface and its nested option groups describe the on-disk\n * global/project settings.json shape. Extracted from settings-manager.ts so the\n * schema can be imported without pulling in the manager implementation.\n */\n\nimport type { Transport } from \"@kolisachint/hoocode-ai\";\n\nexport interface CompactionSettings {\n\tenabled?: boolean; // default: true\n\treserveTokens?: number; // default: 16384\n\tkeepRecentTokens?: number; // default: 20000\n\tmaxContextRatio?: number; // default: 0.75 - compact once context exceeds this fraction of the window, even before the reserveTokens rule fires (bounds transcript growth on large windows)\n}\n\nexport interface ToolOutputSettings {\n\tmaxBytes?: number; // default: 16384 (16KB) - byte cap on a single read/bash tool result before truncation\n\tmaxLines?: number; // default: 800 - line cap on a single read/bash tool result before truncation\n}\n\nexport interface ContextGcSettings {\n\tenabled?: boolean; // default: true - stub out superseded read results (file later edited/re-read) from the outgoing context\n}\n\nexport interface BranchSummarySettings {\n\treserveTokens?: number; // default: 16384 (tokens reserved for prompt + LLM response)\n\tskipPrompt?: boolean; // default: false - when true, skips \"Summarize branch?\" prompt and defaults to no summary\n}\n\nexport interface ProviderRetrySettings {\n\ttimeoutMs?: number; // SDK/provider request timeout in milliseconds\n\tmaxRetries?: number; // SDK/provider retry attempts\n\tmaxRetryDelayMs?: number; // default: 60000 (max server-requested delay before failing)\n}\n\nexport interface RetrySettings {\n\tenabled?: boolean; // default: true\n\tmaxRetries?: number; // default: 3\n\tbaseDelayMs?: number; // default: 2000 (exponential backoff: 2s, 4s, 8s)\n\tprovider?: ProviderRetrySettings;\n}\n\nexport interface TerminalSettings {\n\tshowImages?: boolean; // default: true (only relevant if terminal supports images)\n\timageWidthCells?: number; // default: 60 (preferred inline image width in terminal cells)\n\tclearOnShrink?: boolean; // default: false (clear empty rows when content shrinks)\n\tshowTerminalProgress?: boolean; // default: false (OSC 9;4 terminal progress indicators)\n\tchimeOnTurnComplete?: boolean; // default: false (ring the terminal bell when a long turn finishes or the agent asks for input)\n}\n\nexport interface ImageSettings {\n\tautoResize?: boolean; // default: true (resize images to 2000x2000 max for better model compatibility)\n\tblockImages?: boolean; // default: false - when true, prevents all images from being sent to LLM providers\n}\n\nexport interface ThinkingBudgetsSettings {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\nexport interface MarkdownSettings {\n\tcodeBlockIndent?: string; // default: \" \"\n}\n\nexport interface WarningSettings {\n\tanthropicExtraUsage?: boolean; // default: true\n}\n\n/**\n * Model categories for subagent model selection.\n * Categories map to explicit model IDs (e.g., \"<provider>/<model-id>\").\n *\n * A field set here always wins. When a tier is left unset, it does NOT become a\n * no-op: it resolves to a default *derived* from the user's available models\n * (nothing is hardcoded, so the feature stays provider-neutral). The derivation,\n * implemented in `deriveDefaultModelCategories` (core/model-categories.ts), is:\n * - `capable` = the user's primary model — the configured default\n * (`defaultProvider`/`defaultModel`) when available, else the most capable\n * available model (highest combined input+output token price as a proxy).\n * - `fast` / `standard` = the cheapest / upper-median of every available model\n * priced at or below `capable`, ordered cheapest-first. Clamping to\n * `capable`'s price keeps tiers monotonic (`fast` <= `standard` <= `capable`).\n * Ties break on a fixed key (context window, then id), so identical inputs always\n * yield the same mapping. When no models are available, tiers stay unresolved and\n * the agent's or parent's default model is used.\n */\nexport interface ModelCategories {\n\t/** Quick, cheap models for read-only exploration (grep, find, file discovery) */\n\tfast?: string;\n\t/** Balanced models for general work (planning, moderate complexity) */\n\tstandard?: string;\n\t/** Most capable models for complex reasoning (multi-file refactors) */\n\tcapable?: string;\n}\n\nexport type TransportSetting = Transport;\n\n/**\n * Package source for npm/git packages.\n * - String form: load all resources from the package\n * - Object form: filter which resources to load\n */\nexport type PackageSource =\n\t| string\n\t| {\n\t\t\tsource: string;\n\t\t\textensions?: string[];\n\t\t\tskills?: string[];\n\t\t\tprompts?: string[];\n\t\t\tthemes?: string[];\n\t };\n\nexport interface Settings {\n\tlastChangelogVersion?: string;\n\tdefaultProvider?: string;\n\tdefaultModel?: string;\n\tdefaultThinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\tmodelCategories?: ModelCategories; // Model categories for subagent model selection (fast, standard, capable)\n\ttransport?: TransportSetting; // default: \"auto\"\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\ttheme?: string;\n\tcompaction?: CompactionSettings;\n\ttoolOutput?: ToolOutputSettings; // caps on a single read/bash result (bounds per-turn transcript growth)\n\tcontextGc?: ContextGcSettings; // garbage-collect superseded read results from the outgoing context\n\tbranchSummary?: BranchSummarySettings;\n\tretry?: RetrySettings;\n\thideThinkingBlock?: boolean;\n\tshellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows)\n\tquietStartup?: boolean;\n\tshellCommandPrefix?: string; // Prefix prepended to every bash command (e.g., \"shopt -s expand_aliases\" for alias support)\n\tnpmCommand?: string[]; // Command used for npm package lookup/install operations, argv-style (e.g., [\"mise\", \"exec\", \"node@20\", \"--\", \"npm\"])\n\tcollapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full)\n\tenableInstallTelemetry?: boolean; // default: true - anonymous version/update ping after changelog-detected updates\n\tpackages?: PackageSource[]; // Array of npm/git package sources (string or object with filtering)\n\textensions?: string[]; // Array of local extension file paths or directories\n\tskills?: string[]; // Array of local skill file paths or directories\n\tprompts?: string[]; // Array of local prompt template paths or directories\n\tslashCommands?: string[]; // Array of local slash-command paths or directories\n\tthemes?: string[]; // Array of local theme file paths or directories\n\tenableSkillCommands?: boolean; // default: true - register skills as /skill:name commands\n\tenableSubagent?: boolean; // default: true - enable the subagent tool (delegate tasks to isolated agent loops); set false to disable\n\twarmSubagents?: boolean; // default: false - dispatch eligible subagents on reused warm RPC workers (experimental)\n\tmaxSubagentDepth?: number; // default: 2 - tree-wide subagent nesting cap (2 = a subagent may spawn one more level; 1 = no nesting)\n\tnestedSubagentConcurrency?: number; // default: 2 - max concurrent subagents per pool at nesting depth >= 1\n\tenableTodoWrite?: boolean; // default: true - enable the TodoWrite tool (maintain a live todo list in the task panel)\n\tenablePluginTools?: boolean; // default: false - master switch for the whole autonomous plugin system: the plugin lifecycle tools (SearchPlugins, InstallPlugin, ...) and ProposePlugin on the top-level agent AND the runtime plugin-reuse nudge. Off by default; set true to opt in.\n\tsupportPlatform?: string | string[]; // Platform layout(s) hoocode targets when writing artifacts (authored plugins, /new-* scaffolds). Tokens: claude, copilot|github|gh, agents|native. Same as the --support-platform CLI flag (which overrides this).\n\tdeferMcpSchemas?: boolean; // default: true - defer MCP tool schemas (inject names only + ResolveMcpTools on demand) instead of registering every schema up front; set false to eagerly register every schema\n\tenableWebTools?: boolean; // default: false - enable the webfetch + websearch tools (network access)\n\tenableBrowserTools?: boolean; // default: false - enable the browser_run + browser_continue tools (browsertools engine)\n\tenableBrowserLivePreview?: boolean; // default: false - default the live viewer on for browser_run runs and auto-open it\n\tenableFileTools?: boolean; // default: false - enable the document tools: DocRead/DocEdit/DocWrite + DocScan/DocGrep/DocPeek (filetools binary)\n\tenableEmbsearchTools?: boolean; // default: false - enable the ranked search tool (lexical + semantic hybrid): index the repo with the embsearch binary and register the search tool. grep/find are unchanged.\n\tembsearchBinaryPath?: string; // Path to the embsearch binary. Default: resolve \"embsearch\" from PATH.\n\tembsearchThresholdBytes?: number; // default: 10485760 (10 MB) - minimum indexable source bytes before a repo is embedded; smaller repos skip embedding entirely\n\tlight?: boolean; // default: false - minimal low-token preset for small/local models: read/write/edit/bash only (short schemas), terse prompt, no subagents/TodoWrite/skills/context files/mode appendix. Same as the --light CLI flag.\n\tterminal?: TerminalSettings;\n\timages?: ImageSettings;\n\tenabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)\n\tdoubleEscapeAction?: \"fork\" | \"tree\" | \"none\"; // Action for double-escape with empty editor (default: \"tree\")\n\ttreeFilterMode?: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\"; // Default filter when opening /tree\n\tthinkingBudgets?: ThinkingBudgetsSettings; // Custom token budgets for thinking levels\n\tthinkingDisplay?: \"summarized\" | \"omitted\"; // How adaptive-thinking models return thinking content. Opus 4.8 defaults to \"omitted\" (faster tool use); set \"summarized\" to surface thinking text.\n\teditorPaddingX?: number; // Horizontal padding for input editor (default: 0)\n\tautocompleteMaxVisible?: number; // Max visible items in autocomplete dropdown (default: 5)\n\tshowHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME\n\tmarkdown?: MarkdownSettings;\n\twarnings?: WarningSettings;\n\tsessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"settings-types.d.ts","sourceRoot":"","sources":["../../src/core/settings-types.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Settings schema shared across the app.\n *\n * The `Settings` interface and its nested option groups describe the on-disk\n * global/project settings.json shape. Extracted from settings-manager.ts so the\n * schema can be imported without pulling in the manager implementation.\n */\n\nimport type { Transport } from \"@kolisachint/hoocode-ai\";\n\nexport interface CompactionSettings {\n\tenabled?: boolean; // default: true\n\treserveTokens?: number; // default: 16384\n\tkeepRecentTokens?: number; // default: 20000\n\tmaxContextRatio?: number; // default: 0.75 - compact once context exceeds this fraction of the window, even before the reserveTokens rule fires (bounds transcript growth on large windows)\n}\n\nexport interface ToolOutputSettings {\n\tmaxBytes?: number; // default: 16384 (16KB) - byte cap on a single read/bash tool result before truncation\n\tmaxLines?: number; // default: 800 - line cap on a single read/bash tool result before truncation\n}\n\nexport interface ContextGcSettings {\n\tenabled?: boolean; // default: true - stub out superseded read results (file later edited/re-read) from the outgoing context\n}\n\nexport interface BranchSummarySettings {\n\treserveTokens?: number; // default: 16384 (tokens reserved for prompt + LLM response)\n\tskipPrompt?: boolean; // default: false - when true, skips \"Summarize branch?\" prompt and defaults to no summary\n}\n\nexport interface ProviderRetrySettings {\n\ttimeoutMs?: number; // SDK/provider request timeout in milliseconds\n\tmaxRetries?: number; // SDK/provider retry attempts\n\tmaxRetryDelayMs?: number; // default: 60000 (max server-requested delay before failing)\n}\n\nexport interface RetrySettings {\n\tenabled?: boolean; // default: true\n\tmaxRetries?: number; // default: 3\n\tbaseDelayMs?: number; // default: 2000 (exponential backoff: 2s, 4s, 8s)\n\tprovider?: ProviderRetrySettings;\n}\n\nexport interface TerminalSettings {\n\tshowImages?: boolean; // default: true (only relevant if terminal supports images)\n\timageWidthCells?: number; // default: 60 (preferred inline image width in terminal cells)\n\tclearOnShrink?: boolean; // default: false (clear empty rows when content shrinks)\n\tshowTerminalProgress?: boolean; // default: false (OSC 9;4 terminal progress indicators)\n\tchimeOnTurnComplete?: boolean; // default: false (ring the terminal bell when a long turn finishes or the agent asks for input)\n}\n\nexport interface ImageSettings {\n\tautoResize?: boolean; // default: true (resize images to 2000x2000 max for better model compatibility)\n\tblockImages?: boolean; // default: false - when true, prevents all images from being sent to LLM providers\n}\n\nexport interface ThinkingBudgetsSettings {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\nexport interface MarkdownSettings {\n\tcodeBlockIndent?: string; // default: \" \"\n}\n\nexport interface WarningSettings {\n\tanthropicExtraUsage?: boolean; // default: true\n}\n\n/**\n * Model categories for subagent model selection.\n * Categories map to explicit model IDs (e.g., \"<provider>/<model-id>\").\n *\n * A field set here always wins. When a tier is left unset, it does NOT become a\n * no-op: it resolves to a default *derived* from the user's available models\n * (nothing is hardcoded, so the feature stays provider-neutral). The derivation,\n * implemented in `deriveDefaultModelCategories` (core/model-categories.ts), is:\n * - `capable` = the user's primary model — the configured default\n * (`defaultProvider`/`defaultModel`) when available, else the most capable\n * available model (highest combined input+output token price as a proxy).\n * - `fast` / `standard` = the cheapest / upper-median of every available model\n * priced at or below `capable`, ordered cheapest-first. Clamping to\n * `capable`'s price keeps tiers monotonic (`fast` <= `standard` <= `capable`).\n * Ties break on a fixed key (context window, then id), so identical inputs always\n * yield the same mapping. When no models are available, tiers stay unresolved and\n * the agent's or parent's default model is used.\n */\nexport interface ModelCategories {\n\t/** Quick, cheap models for read-only exploration (grep, find, file discovery) */\n\tfast?: string;\n\t/** Balanced models for general work (planning, moderate complexity) */\n\tstandard?: string;\n\t/** Most capable models for complex reasoning (multi-file refactors) */\n\tcapable?: string;\n}\n\nexport type TransportSetting = Transport;\n\n/**\n * Package source for npm/git packages.\n * - String form: load all resources from the package\n * - Object form: filter which resources to load\n */\nexport type PackageSource =\n\t| string\n\t| {\n\t\t\tsource: string;\n\t\t\textensions?: string[];\n\t\t\tskills?: string[];\n\t\t\tprompts?: string[];\n\t\t\tthemes?: string[];\n\t };\n\nexport interface Settings {\n\tlastChangelogVersion?: string;\n\tdefaultProvider?: string;\n\tdefaultModel?: string;\n\tdefaultThinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\tmodelCategories?: ModelCategories; // Model categories for subagent model selection (fast, standard, capable)\n\ttransport?: TransportSetting; // default: \"auto\"\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\ttheme?: string;\n\tcompaction?: CompactionSettings;\n\ttoolOutput?: ToolOutputSettings; // caps on a single read/bash result (bounds per-turn transcript growth)\n\tcontextGc?: ContextGcSettings; // garbage-collect superseded read results from the outgoing context\n\tbranchSummary?: BranchSummarySettings;\n\tretry?: RetrySettings;\n\thideThinkingBlock?: boolean;\n\tshellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows)\n\tquietStartup?: boolean;\n\tshellCommandPrefix?: string; // Prefix prepended to every bash command (e.g., \"shopt -s expand_aliases\" for alias support)\n\tnpmCommand?: string[]; // Command used for npm package lookup/install operations, argv-style (e.g., [\"mise\", \"exec\", \"node@20\", \"--\", \"npm\"])\n\tcollapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full)\n\tenableInstallTelemetry?: boolean; // default: true - anonymous version/update ping after changelog-detected updates\n\tpackages?: PackageSource[]; // Array of npm/git package sources (string or object with filtering)\n\textensions?: string[]; // Array of local extension file paths or directories\n\tskills?: string[]; // Array of local skill file paths or directories\n\tprompts?: string[]; // Array of local prompt template paths or directories\n\tslashCommands?: string[]; // Array of local slash-command paths or directories\n\tthemes?: string[]; // Array of local theme file paths or directories\n\tenableSkillCommands?: boolean; // default: true - register skills as /skill:name commands\n\tenableSubagent?: boolean; // default: true - enable the subagent tool (delegate tasks to isolated agent loops); set false to disable\n\twarmSubagents?: boolean; // default: false - dispatch eligible subagents on reused warm RPC workers (experimental)\n\tmaxSubagentDepth?: number; // default: 2 - tree-wide subagent nesting cap (2 = a subagent may spawn one more level; 1 = no nesting)\n\tnestedSubagentConcurrency?: number; // default: 2 - max concurrent subagents per pool at nesting depth >= 1\n\tenableTodoWrite?: boolean; // default: true - enable the TodoWrite tool (maintain a live todo list in the task panel)\n\tenablePluginTools?: boolean; // default: false - master switch for the whole autonomous plugin system: the plugin lifecycle tools (SearchPlugins, InstallPlugin, ...) and ProposePlugin on the top-level agent AND the runtime plugin-reuse nudge. Off by default; set true to opt in.\n\tsupportPlatform?: string | string[]; // Platform layout(s) hoocode targets when writing artifacts (authored plugins, /new-* scaffolds). Tokens: claude, copilot|github|gh, agents|native. Same as the --support-platform CLI flag (which overrides this).\n\tdeferMcpSchemas?: boolean; // default: true - defer MCP tool schemas (inject names only + ResolveMcpTools on demand) instead of registering every schema up front; set false to eagerly register every schema\n\tenableWebTools?: boolean; // default: false - enable the webfetch + websearch tools (network access)\n\tenableBrowserTools?: boolean; // default: false - enable the browser_run + browser_continue tools (browsertools engine)\n\tenableBrowserLivePreview?: boolean; // default: false - default the live viewer on for browser_run runs and auto-open it\n\tenableFileTools?: boolean; // default: false - enable the document tools: DocRead/DocEdit/DocWrite + DocScan/DocGrep/DocPeek (filetools binary)\n\tenableEmbsearchTools?: boolean; // default: true - enable the semantic index layer for the always-on `search` tool: index the repo with the embsearch binary so search can fuse semantic hits. Set false to run search lexical-only. grep/find are unchanged.\n\tembsearchBinaryPath?: string; // Path to the embsearch binary. Default: resolve \"embsearch\" from PATH.\n\tembsearchThresholdBytes?: number; // default: 0 - minimum indexable source bytes before a repo is embedded; 0 means index every repo regardless of size. Raise it to skip embedding for repos under N bytes.\n\tlight?: boolean; // default: false - minimal low-token preset for small/local models: read/write/edit/bash only (short schemas), terse prompt, no subagents/TodoWrite/skills/context files/mode appendix. Same as the --light CLI flag.\n\tterminal?: TerminalSettings;\n\timages?: ImageSettings;\n\tenabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)\n\tdoubleEscapeAction?: \"fork\" | \"tree\" | \"none\"; // Action for double-escape with empty editor (default: \"tree\")\n\ttreeFilterMode?: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\"; // Default filter when opening /tree\n\tthinkingBudgets?: ThinkingBudgetsSettings; // Custom token budgets for thinking levels\n\tthinkingDisplay?: \"summarized\" | \"omitted\"; // How adaptive-thinking models return thinking content. Opus 4.8 defaults to \"omitted\" (faster tool use); set \"summarized\" to surface thinking text.\n\teditorPaddingX?: number; // Horizontal padding for input editor (default: 0)\n\tautocompleteMaxVisible?: number; // Max visible items in autocomplete dropdown (default: 5)\n\tshowHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME\n\tmarkdown?: MarkdownSettings;\n\twarnings?: WarningSettings;\n\tsessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-types.js","sourceRoot":"","sources":["../../src/core/settings-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * Settings schema shared across the app.\n *\n * The `Settings` interface and its nested option groups describe the on-disk\n * global/project settings.json shape. Extracted from settings-manager.ts so the\n * schema can be imported without pulling in the manager implementation.\n */\n\nimport type { Transport } from \"@kolisachint/hoocode-ai\";\n\nexport interface CompactionSettings {\n\tenabled?: boolean; // default: true\n\treserveTokens?: number; // default: 16384\n\tkeepRecentTokens?: number; // default: 20000\n\tmaxContextRatio?: number; // default: 0.75 - compact once context exceeds this fraction of the window, even before the reserveTokens rule fires (bounds transcript growth on large windows)\n}\n\nexport interface ToolOutputSettings {\n\tmaxBytes?: number; // default: 16384 (16KB) - byte cap on a single read/bash tool result before truncation\n\tmaxLines?: number; // default: 800 - line cap on a single read/bash tool result before truncation\n}\n\nexport interface ContextGcSettings {\n\tenabled?: boolean; // default: true - stub out superseded read results (file later edited/re-read) from the outgoing context\n}\n\nexport interface BranchSummarySettings {\n\treserveTokens?: number; // default: 16384 (tokens reserved for prompt + LLM response)\n\tskipPrompt?: boolean; // default: false - when true, skips \"Summarize branch?\" prompt and defaults to no summary\n}\n\nexport interface ProviderRetrySettings {\n\ttimeoutMs?: number; // SDK/provider request timeout in milliseconds\n\tmaxRetries?: number; // SDK/provider retry attempts\n\tmaxRetryDelayMs?: number; // default: 60000 (max server-requested delay before failing)\n}\n\nexport interface RetrySettings {\n\tenabled?: boolean; // default: true\n\tmaxRetries?: number; // default: 3\n\tbaseDelayMs?: number; // default: 2000 (exponential backoff: 2s, 4s, 8s)\n\tprovider?: ProviderRetrySettings;\n}\n\nexport interface TerminalSettings {\n\tshowImages?: boolean; // default: true (only relevant if terminal supports images)\n\timageWidthCells?: number; // default: 60 (preferred inline image width in terminal cells)\n\tclearOnShrink?: boolean; // default: false (clear empty rows when content shrinks)\n\tshowTerminalProgress?: boolean; // default: false (OSC 9;4 terminal progress indicators)\n\tchimeOnTurnComplete?: boolean; // default: false (ring the terminal bell when a long turn finishes or the agent asks for input)\n}\n\nexport interface ImageSettings {\n\tautoResize?: boolean; // default: true (resize images to 2000x2000 max for better model compatibility)\n\tblockImages?: boolean; // default: false - when true, prevents all images from being sent to LLM providers\n}\n\nexport interface ThinkingBudgetsSettings {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\nexport interface MarkdownSettings {\n\tcodeBlockIndent?: string; // default: \" \"\n}\n\nexport interface WarningSettings {\n\tanthropicExtraUsage?: boolean; // default: true\n}\n\n/**\n * Model categories for subagent model selection.\n * Categories map to explicit model IDs (e.g., \"<provider>/<model-id>\").\n *\n * A field set here always wins. When a tier is left unset, it does NOT become a\n * no-op: it resolves to a default *derived* from the user's available models\n * (nothing is hardcoded, so the feature stays provider-neutral). The derivation,\n * implemented in `deriveDefaultModelCategories` (core/model-categories.ts), is:\n * - `capable` = the user's primary model — the configured default\n * (`defaultProvider`/`defaultModel`) when available, else the most capable\n * available model (highest combined input+output token price as a proxy).\n * - `fast` / `standard` = the cheapest / upper-median of every available model\n * priced at or below `capable`, ordered cheapest-first. Clamping to\n * `capable`'s price keeps tiers monotonic (`fast` <= `standard` <= `capable`).\n * Ties break on a fixed key (context window, then id), so identical inputs always\n * yield the same mapping. When no models are available, tiers stay unresolved and\n * the agent's or parent's default model is used.\n */\nexport interface ModelCategories {\n\t/** Quick, cheap models for read-only exploration (grep, find, file discovery) */\n\tfast?: string;\n\t/** Balanced models for general work (planning, moderate complexity) */\n\tstandard?: string;\n\t/** Most capable models for complex reasoning (multi-file refactors) */\n\tcapable?: string;\n}\n\nexport type TransportSetting = Transport;\n\n/**\n * Package source for npm/git packages.\n * - String form: load all resources from the package\n * - Object form: filter which resources to load\n */\nexport type PackageSource =\n\t| string\n\t| {\n\t\t\tsource: string;\n\t\t\textensions?: string[];\n\t\t\tskills?: string[];\n\t\t\tprompts?: string[];\n\t\t\tthemes?: string[];\n\t };\n\nexport interface Settings {\n\tlastChangelogVersion?: string;\n\tdefaultProvider?: string;\n\tdefaultModel?: string;\n\tdefaultThinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\tmodelCategories?: ModelCategories; // Model categories for subagent model selection (fast, standard, capable)\n\ttransport?: TransportSetting; // default: \"auto\"\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\ttheme?: string;\n\tcompaction?: CompactionSettings;\n\ttoolOutput?: ToolOutputSettings; // caps on a single read/bash result (bounds per-turn transcript growth)\n\tcontextGc?: ContextGcSettings; // garbage-collect superseded read results from the outgoing context\n\tbranchSummary?: BranchSummarySettings;\n\tretry?: RetrySettings;\n\thideThinkingBlock?: boolean;\n\tshellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows)\n\tquietStartup?: boolean;\n\tshellCommandPrefix?: string; // Prefix prepended to every bash command (e.g., \"shopt -s expand_aliases\" for alias support)\n\tnpmCommand?: string[]; // Command used for npm package lookup/install operations, argv-style (e.g., [\"mise\", \"exec\", \"node@20\", \"--\", \"npm\"])\n\tcollapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full)\n\tenableInstallTelemetry?: boolean; // default: true - anonymous version/update ping after changelog-detected updates\n\tpackages?: PackageSource[]; // Array of npm/git package sources (string or object with filtering)\n\textensions?: string[]; // Array of local extension file paths or directories\n\tskills?: string[]; // Array of local skill file paths or directories\n\tprompts?: string[]; // Array of local prompt template paths or directories\n\tslashCommands?: string[]; // Array of local slash-command paths or directories\n\tthemes?: string[]; // Array of local theme file paths or directories\n\tenableSkillCommands?: boolean; // default: true - register skills as /skill:name commands\n\tenableSubagent?: boolean; // default: true - enable the subagent tool (delegate tasks to isolated agent loops); set false to disable\n\twarmSubagents?: boolean; // default: false - dispatch eligible subagents on reused warm RPC workers (experimental)\n\tmaxSubagentDepth?: number; // default: 2 - tree-wide subagent nesting cap (2 = a subagent may spawn one more level; 1 = no nesting)\n\tnestedSubagentConcurrency?: number; // default: 2 - max concurrent subagents per pool at nesting depth >= 1\n\tenableTodoWrite?: boolean; // default: true - enable the TodoWrite tool (maintain a live todo list in the task panel)\n\tenablePluginTools?: boolean; // default: false - master switch for the whole autonomous plugin system: the plugin lifecycle tools (SearchPlugins, InstallPlugin, ...) and ProposePlugin on the top-level agent AND the runtime plugin-reuse nudge. Off by default; set true to opt in.\n\tsupportPlatform?: string | string[]; // Platform layout(s) hoocode targets when writing artifacts (authored plugins, /new-* scaffolds). Tokens: claude, copilot|github|gh, agents|native. Same as the --support-platform CLI flag (which overrides this).\n\tdeferMcpSchemas?: boolean; // default: true - defer MCP tool schemas (inject names only + ResolveMcpTools on demand) instead of registering every schema up front; set false to eagerly register every schema\n\tenableWebTools?: boolean; // default: false - enable the webfetch + websearch tools (network access)\n\tenableBrowserTools?: boolean; // default: false - enable the browser_run + browser_continue tools (browsertools engine)\n\tenableBrowserLivePreview?: boolean; // default: false - default the live viewer on for browser_run runs and auto-open it\n\tenableFileTools?: boolean; // default: false - enable the document tools: DocRead/DocEdit/DocWrite + DocScan/DocGrep/DocPeek (filetools binary)\n\tenableEmbsearchTools?: boolean; // default: false - enable the ranked search tool (lexical + semantic hybrid): index the repo with the embsearch binary and register the search tool. grep/find are unchanged.\n\tembsearchBinaryPath?: string; // Path to the embsearch binary. Default: resolve \"embsearch\" from PATH.\n\tembsearchThresholdBytes?: number; // default: 10485760 (10 MB) - minimum indexable source bytes before a repo is embedded; smaller repos skip embedding entirely\n\tlight?: boolean; // default: false - minimal low-token preset for small/local models: read/write/edit/bash only (short schemas), terse prompt, no subagents/TodoWrite/skills/context files/mode appendix. Same as the --light CLI flag.\n\tterminal?: TerminalSettings;\n\timages?: ImageSettings;\n\tenabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)\n\tdoubleEscapeAction?: \"fork\" | \"tree\" | \"none\"; // Action for double-escape with empty editor (default: \"tree\")\n\ttreeFilterMode?: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\"; // Default filter when opening /tree\n\tthinkingBudgets?: ThinkingBudgetsSettings; // Custom token budgets for thinking levels\n\tthinkingDisplay?: \"summarized\" | \"omitted\"; // How adaptive-thinking models return thinking content. Opus 4.8 defaults to \"omitted\" (faster tool use); set \"summarized\" to surface thinking text.\n\teditorPaddingX?: number; // Horizontal padding for input editor (default: 0)\n\tautocompleteMaxVisible?: number; // Max visible items in autocomplete dropdown (default: 5)\n\tshowHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME\n\tmarkdown?: MarkdownSettings;\n\twarnings?: WarningSettings;\n\tsessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"settings-types.js","sourceRoot":"","sources":["../../src/core/settings-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * Settings schema shared across the app.\n *\n * The `Settings` interface and its nested option groups describe the on-disk\n * global/project settings.json shape. Extracted from settings-manager.ts so the\n * schema can be imported without pulling in the manager implementation.\n */\n\nimport type { Transport } from \"@kolisachint/hoocode-ai\";\n\nexport interface CompactionSettings {\n\tenabled?: boolean; // default: true\n\treserveTokens?: number; // default: 16384\n\tkeepRecentTokens?: number; // default: 20000\n\tmaxContextRatio?: number; // default: 0.75 - compact once context exceeds this fraction of the window, even before the reserveTokens rule fires (bounds transcript growth on large windows)\n}\n\nexport interface ToolOutputSettings {\n\tmaxBytes?: number; // default: 16384 (16KB) - byte cap on a single read/bash tool result before truncation\n\tmaxLines?: number; // default: 800 - line cap on a single read/bash tool result before truncation\n}\n\nexport interface ContextGcSettings {\n\tenabled?: boolean; // default: true - stub out superseded read results (file later edited/re-read) from the outgoing context\n}\n\nexport interface BranchSummarySettings {\n\treserveTokens?: number; // default: 16384 (tokens reserved for prompt + LLM response)\n\tskipPrompt?: boolean; // default: false - when true, skips \"Summarize branch?\" prompt and defaults to no summary\n}\n\nexport interface ProviderRetrySettings {\n\ttimeoutMs?: number; // SDK/provider request timeout in milliseconds\n\tmaxRetries?: number; // SDK/provider retry attempts\n\tmaxRetryDelayMs?: number; // default: 60000 (max server-requested delay before failing)\n}\n\nexport interface RetrySettings {\n\tenabled?: boolean; // default: true\n\tmaxRetries?: number; // default: 3\n\tbaseDelayMs?: number; // default: 2000 (exponential backoff: 2s, 4s, 8s)\n\tprovider?: ProviderRetrySettings;\n}\n\nexport interface TerminalSettings {\n\tshowImages?: boolean; // default: true (only relevant if terminal supports images)\n\timageWidthCells?: number; // default: 60 (preferred inline image width in terminal cells)\n\tclearOnShrink?: boolean; // default: false (clear empty rows when content shrinks)\n\tshowTerminalProgress?: boolean; // default: false (OSC 9;4 terminal progress indicators)\n\tchimeOnTurnComplete?: boolean; // default: false (ring the terminal bell when a long turn finishes or the agent asks for input)\n}\n\nexport interface ImageSettings {\n\tautoResize?: boolean; // default: true (resize images to 2000x2000 max for better model compatibility)\n\tblockImages?: boolean; // default: false - when true, prevents all images from being sent to LLM providers\n}\n\nexport interface ThinkingBudgetsSettings {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\nexport interface MarkdownSettings {\n\tcodeBlockIndent?: string; // default: \" \"\n}\n\nexport interface WarningSettings {\n\tanthropicExtraUsage?: boolean; // default: true\n}\n\n/**\n * Model categories for subagent model selection.\n * Categories map to explicit model IDs (e.g., \"<provider>/<model-id>\").\n *\n * A field set here always wins. When a tier is left unset, it does NOT become a\n * no-op: it resolves to a default *derived* from the user's available models\n * (nothing is hardcoded, so the feature stays provider-neutral). The derivation,\n * implemented in `deriveDefaultModelCategories` (core/model-categories.ts), is:\n * - `capable` = the user's primary model — the configured default\n * (`defaultProvider`/`defaultModel`) when available, else the most capable\n * available model (highest combined input+output token price as a proxy).\n * - `fast` / `standard` = the cheapest / upper-median of every available model\n * priced at or below `capable`, ordered cheapest-first. Clamping to\n * `capable`'s price keeps tiers monotonic (`fast` <= `standard` <= `capable`).\n * Ties break on a fixed key (context window, then id), so identical inputs always\n * yield the same mapping. When no models are available, tiers stay unresolved and\n * the agent's or parent's default model is used.\n */\nexport interface ModelCategories {\n\t/** Quick, cheap models for read-only exploration (grep, find, file discovery) */\n\tfast?: string;\n\t/** Balanced models for general work (planning, moderate complexity) */\n\tstandard?: string;\n\t/** Most capable models for complex reasoning (multi-file refactors) */\n\tcapable?: string;\n}\n\nexport type TransportSetting = Transport;\n\n/**\n * Package source for npm/git packages.\n * - String form: load all resources from the package\n * - Object form: filter which resources to load\n */\nexport type PackageSource =\n\t| string\n\t| {\n\t\t\tsource: string;\n\t\t\textensions?: string[];\n\t\t\tskills?: string[];\n\t\t\tprompts?: string[];\n\t\t\tthemes?: string[];\n\t };\n\nexport interface Settings {\n\tlastChangelogVersion?: string;\n\tdefaultProvider?: string;\n\tdefaultModel?: string;\n\tdefaultThinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\tmodelCategories?: ModelCategories; // Model categories for subagent model selection (fast, standard, capable)\n\ttransport?: TransportSetting; // default: \"auto\"\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\ttheme?: string;\n\tcompaction?: CompactionSettings;\n\ttoolOutput?: ToolOutputSettings; // caps on a single read/bash result (bounds per-turn transcript growth)\n\tcontextGc?: ContextGcSettings; // garbage-collect superseded read results from the outgoing context\n\tbranchSummary?: BranchSummarySettings;\n\tretry?: RetrySettings;\n\thideThinkingBlock?: boolean;\n\tshellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows)\n\tquietStartup?: boolean;\n\tshellCommandPrefix?: string; // Prefix prepended to every bash command (e.g., \"shopt -s expand_aliases\" for alias support)\n\tnpmCommand?: string[]; // Command used for npm package lookup/install operations, argv-style (e.g., [\"mise\", \"exec\", \"node@20\", \"--\", \"npm\"])\n\tcollapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full)\n\tenableInstallTelemetry?: boolean; // default: true - anonymous version/update ping after changelog-detected updates\n\tpackages?: PackageSource[]; // Array of npm/git package sources (string or object with filtering)\n\textensions?: string[]; // Array of local extension file paths or directories\n\tskills?: string[]; // Array of local skill file paths or directories\n\tprompts?: string[]; // Array of local prompt template paths or directories\n\tslashCommands?: string[]; // Array of local slash-command paths or directories\n\tthemes?: string[]; // Array of local theme file paths or directories\n\tenableSkillCommands?: boolean; // default: true - register skills as /skill:name commands\n\tenableSubagent?: boolean; // default: true - enable the subagent tool (delegate tasks to isolated agent loops); set false to disable\n\twarmSubagents?: boolean; // default: false - dispatch eligible subagents on reused warm RPC workers (experimental)\n\tmaxSubagentDepth?: number; // default: 2 - tree-wide subagent nesting cap (2 = a subagent may spawn one more level; 1 = no nesting)\n\tnestedSubagentConcurrency?: number; // default: 2 - max concurrent subagents per pool at nesting depth >= 1\n\tenableTodoWrite?: boolean; // default: true - enable the TodoWrite tool (maintain a live todo list in the task panel)\n\tenablePluginTools?: boolean; // default: false - master switch for the whole autonomous plugin system: the plugin lifecycle tools (SearchPlugins, InstallPlugin, ...) and ProposePlugin on the top-level agent AND the runtime plugin-reuse nudge. Off by default; set true to opt in.\n\tsupportPlatform?: string | string[]; // Platform layout(s) hoocode targets when writing artifacts (authored plugins, /new-* scaffolds). Tokens: claude, copilot|github|gh, agents|native. Same as the --support-platform CLI flag (which overrides this).\n\tdeferMcpSchemas?: boolean; // default: true - defer MCP tool schemas (inject names only + ResolveMcpTools on demand) instead of registering every schema up front; set false to eagerly register every schema\n\tenableWebTools?: boolean; // default: false - enable the webfetch + websearch tools (network access)\n\tenableBrowserTools?: boolean; // default: false - enable the browser_run + browser_continue tools (browsertools engine)\n\tenableBrowserLivePreview?: boolean; // default: false - default the live viewer on for browser_run runs and auto-open it\n\tenableFileTools?: boolean; // default: false - enable the document tools: DocRead/DocEdit/DocWrite + DocScan/DocGrep/DocPeek (filetools binary)\n\tenableEmbsearchTools?: boolean; // default: true - enable the semantic index layer for the always-on `search` tool: index the repo with the embsearch binary so search can fuse semantic hits. Set false to run search lexical-only. grep/find are unchanged.\n\tembsearchBinaryPath?: string; // Path to the embsearch binary. Default: resolve \"embsearch\" from PATH.\n\tembsearchThresholdBytes?: number; // default: 0 - minimum indexable source bytes before a repo is embedded; 0 means index every repo regardless of size. Raise it to skip embedding for repos under N bytes.\n\tlight?: boolean; // default: false - minimal low-token preset for small/local models: read/write/edit/bash only (short schemas), terse prompt, no subagents/TodoWrite/skills/context files/mode appendix. Same as the --light CLI flag.\n\tterminal?: TerminalSettings;\n\timages?: ImageSettings;\n\tenabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)\n\tdoubleEscapeAction?: \"fork\" | \"tree\" | \"none\"; // Action for double-escape with empty editor (default: \"tree\")\n\ttreeFilterMode?: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\"; // Default filter when opening /tree\n\tthinkingBudgets?: ThinkingBudgetsSettings; // Custom token budgets for thinking levels\n\tthinkingDisplay?: \"summarized\" | \"omitted\"; // How adaptive-thinking models return thinking content. Opus 4.8 defaults to \"omitted\" (faster tool use); set \"summarized\" to surface thinking text.\n\teditorPaddingX?: number; // Horizontal padding for input editor (default: 0)\n\tautocompleteMaxVisible?: number; // Max visible items in autocomplete dropdown (default: 5)\n\tshowHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME\n\tmarkdown?: MarkdownSettings;\n\twarnings?: WarningSettings;\n\tsessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)\n}\n"]}
|
|
@@ -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,iCAAiC,CAAC;AAKjE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAMtF,OAAO,EAAiC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAEnG,QAAA,MAAM,UAAU;;;;;;;;EAqBd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAKtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,2BAA2B;IAC3B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,yFAAyF;IACzF,IAAI,EAAE,CACL,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACvD,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CAClC;AAQD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAoID,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAiPhE;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 \"@kolisachint/hoocode-agent-core\";\nimport { Text } from \"@kolisachint/hoocode-tui\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.js\";\nimport { ensureTool } from \"../../utils/tools-manager.js\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { applyFdGlobPattern, relativizeFdLine, toPosixPath } from \"./fd-utils.js\";\nimport { isNativeSearchForced, nativeFind } from \"./native-search.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from \"./truncate.js\";\n\nconst findSchema = Type.Object({\n\tpattern: Type.Union([Type.String(), Type.Array(Type.String())], {\n\t\tdescription:\n\t\t\t\"Glob pattern(s) to match files. Pass one pattern or an array for OR logic, e.g. '*.ts', 'src/**/*.spec.ts', or ['src/**/*.ts', 'test/**/*.ts'].\",\n\t}),\n\tpath: Type.Optional(Type.String({ description: \"Directory to search in (default: current directory)\" })),\n\texclude: Type.Optional(\n\t\tType.Union([Type.String(), Type.Array(Type.String())], {\n\t\t\tdescription: \"Additional exclusion glob(s), e.g. '**/*.test.ts' or ['**/dist/**', '**/build/**'].\",\n\t\t}),\n\t),\n\ttype: Type.Optional(\n\t\tType.Union([Type.Literal(\"f\"), Type.Literal(\"d\"), Type.Literal(\"l\")], {\n\t\t\tdescription: \"Filter by entry type: 'f' files, 'd' directories, 'l' symlinks (default: 'f').\",\n\t\t}),\n\t),\n\tdepth: Type.Optional(Type.Number({ description: \"Maximum directory depth to search.\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results (default: 1000).\" })),\n\tcompress: Type.Optional(\n\t\tType.Boolean({ description: \"Group files in the same directory to shorten output (default: false).\" }),\n\t),\n});\n\nexport type FindToolInput = Static<typeof findSchema>;\n\nconst DEFAULT_LIMIT = 1000;\nconst NO_RESULTS_MESSAGE = \"No files found matching pattern\";\n\nexport interface FindToolDetails {\n\ttruncation?: TruncationResult;\n\tresultLimitReached?: number;\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 one or more glob patterns. Returns relative or absolute paths. */\n\tglob: (\n\t\tpatterns: string[],\n\t\tcwd: string,\n\t\toptions: { ignore: string[]; limit: number; type?: string },\n\t) => Promise<string[]> | string[];\n}\n\nconst defaultFindOperations: FindOperations = {\n\texists: existsSync,\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 fd */\n\toperations?: FindOperations;\n}\n\n/**\n * Compress a list of file paths by grouping files in the same directory.\n *\n * Compression rules:\n * - If a directory has >=3 entries all sharing the same extension: fold into `dir/{stem1,stem2,stem3}.ext`\n * - If >=3 entries with mixed extensions: sub-group by extension; fold sub-groups >=3\n * - If directory has >6 total files: summarize verbatim after sub-grouping\n * - 1-2 entries: emit verbatim\n */\nfunction compressPaths(paths: string[]): string {\n\tif (paths.length === 0) return \"\";\n\n\tconst sorted = [...paths].sort();\n\n\tconst dirMap = new Map<string, string[]>();\n\tfor (const p of sorted) {\n\t\tconst dir = path.dirname(p);\n\t\tconst base = path.basename(p);\n\t\tif (!dirMap.has(dir)) dirMap.set(dir, []);\n\t\tdirMap.get(dir)!.push(base);\n\t}\n\n\tconst result: string[] = [];\n\n\tfor (const [dir, files] of dirMap) {\n\t\tif (files.length <= 2) {\n\t\t\tfor (const f of files) result.push(dir === \".\" ? f : `${dir}/${f}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst extMap = new Map<string, string[]>();\n\t\tfor (const f of files) {\n\t\t\tconst ext = path.extname(f);\n\t\t\tconst stem = path.basename(f, ext);\n\t\t\tif (!extMap.has(ext)) extMap.set(ext, []);\n\t\t\textMap.get(ext)!.push(stem);\n\t\t}\n\n\t\tif (extMap.size === 1 && files.length >= 3) {\n\t\t\tconst [ext, stems] = extMap.entries().next().value!;\n\t\t\tconst displayDir = dir === \".\" ? \"\" : `${dir}/`;\n\t\t\tresult.push(`${displayDir}{${stems.join(\",\")}}${ext}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet anyFolded = false;\n\t\tconst subResults: string[] = [];\n\t\tfor (const [ext, stems] of extMap) {\n\t\t\tif (stems.length >= 3) {\n\t\t\t\tconst displayDir = dir === \".\" ? \"\" : `${dir}/`;\n\t\t\t\tsubResults.push(`${displayDir}{${stems.join(\",\")}}${ext}`);\n\t\t\t\tanyFolded = true;\n\t\t\t} else {\n\t\t\t\tfor (const stem of stems) subResults.push(dir === \".\" ? `${stem}${ext}` : `${dir}/${stem}${ext}`);\n\t\t\t}\n\t\t}\n\n\t\tif (anyFolded || files.length > 6) {\n\t\t\tresult.push(...subResults);\n\t\t} else {\n\t\t\tfor (const f of files) result.push(dir === \".\" ? f : `${dir}/${f}`);\n\t\t}\n\t}\n\n\treturn result.join(\"\\n\");\n}\n\nfunction formatFindCall(\n\targs: { pattern?: string | string[]; path?: string; type?: string; depth?: number; limit?: number } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst patterns = args?.pattern;\n\tconst patternStr = Array.isArray(patterns) ? patterns.join(\", \") : str(patterns);\n\tconst rawPath = str(args?.path);\n\tconst displayPath = rawPath !== null ? shortenPath(rawPath || \".\") : null;\n\tconst type = args?.type;\n\tconst depth = args?.depth;\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(patternStr === null || patternStr === \"\" ? invalidArg : theme.fg(\"accent\", patternStr)) +\n\t\ttheme.fg(\"toolOutput\", ` in ${displayPath === null ? invalidArg : displayPath}`);\n\tif (type && type !== \"f\") {\n\t\tconst typeLabel = type === \"d\" ? \"dirs\" : type === \"l\" ? \"symlinks\" : type;\n\t\ttext += theme.fg(\"toolOutput\", ` (${typeLabel})`);\n\t}\n\tif (depth !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (depth ${depth})`);\n\t}\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: typeof import(\"../../modes/interactive/theme/theme.js\").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\nexport function createFindToolDefinition(\n\tcwd: string,\n\toptions?: FindToolOptions,\n): ToolDefinition<typeof findSchema, FindToolDetails | undefined> {\n\tconst customOps = options?.operations;\n\treturn {\n\t\tname: \"find\",\n\t\tlabel: \"find\",\n\t\tdescription: `Search for files by one or more glob patterns (OR logic across an array). Optionally filter by entry type (files/dirs/symlinks), directory depth, and extra exclusions. Returns matching 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 (supports multiple patterns, respects .gitignore)\",\n\t\tparameters: findSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{\n\t\t\t\tpattern: rawPattern,\n\t\t\t\tpath: searchDir,\n\t\t\t\texclude: rawExclude,\n\t\t\t\ttype,\n\t\t\t\tdepth,\n\t\t\t\tlimit,\n\t\t\t\tcompress = false,\n\t\t\t}: {\n\t\t\t\tpattern: string | string[];\n\t\t\t\tpath?: string;\n\t\t\t\texclude?: string | string[];\n\t\t\t\ttype?: \"f\" | \"d\" | \"l\";\n\t\t\t\tdepth?: number;\n\t\t\t\tlimit?: number;\n\t\t\t\tcompress?: boolean;\n\t\t\t},\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 stopChildren: (() => 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\tstopChildren = undefined;\n\t\t\t\t\tfn();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tstopChildren?.();\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\tconst patterns = Array.isArray(rawPattern) ? rawPattern : [rawPattern];\n\t\t\t\t\t\tconst excludePatterns = rawExclude ? (Array.isArray(rawExclude) ? rawExclude : [rawExclude]) : [];\n\t\t\t\t\t\t// Always exclude node_modules and .git, plus any caller exclusions.\n\t\t\t\t\t\tconst allIgnore = [\"**/node_modules/**\", \"**/.git/**\", ...excludePatterns];\n\t\t\t\t\t\tconst typeFilter = type === \"d\" ? \"d\" : type === \"l\" ? \"l\" : \"f\";\n\n\t\t\t\t\t\tconst emit = (relativized: string[]) => {\n\t\t\t\t\t\t\tif (relativized.length === 0) {\n\t\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: NO_RESULTS_MESSAGE }], details: undefined }),\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\tconst unique = [...new Set(relativized)].sort();\n\t\t\t\t\t\t\tconst resultLimitReached = unique.length >= effectiveLimit;\n\t\t\t\t\t\t\tconst truncated = unique.slice(0, effectiveLimit);\n\t\t\t\t\t\t\tconst rawOutput = compress ? compressPaths(truncated) : truncated.join(\"\\n\");\n\t\t\t\t\t\t\tconst truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });\n\t\t\t\t\t\t\tlet resultOutput = truncation.content;\n\t\t\t\t\t\t\tconst details: FindToolDetails = {};\n\t\t\t\t\t\t\tconst notices: string[] = [];\n\t\t\t\t\t\t\tif (resultLimitReached) {\n\t\t\t\t\t\t\t\tnotices.push(\n\t\t\t\t\t\t\t\t\t`${effectiveLimit} results limit reached. Use limit=${effectiveLimit * 2} for more, or refine pattern`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tdetails.resultLimitReached = effectiveLimit;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t\t\t\t\t\t\tdetails.truncation = truncation;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (notices.length > 0) resultOutput += `\\n\\n[${notices.join(\". \")}]`;\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: resultOutput }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(details).length > 0 ? 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\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(patterns, searchPath, {\n\t\t\t\t\t\t\t\tignore: allIgnore,\n\t\t\t\t\t\t\t\tlimit: effectiveLimit,\n\t\t\t\t\t\t\t\ttype: typeFilter,\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\temit(\n\t\t\t\t\t\t\t\tresults.map((p) => {\n\t\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\t\treturn toPosixPath(path.relative(searchPath, p));\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, with a pure-JS fallback when fd is\n\t\t\t\t\t\t// unavailable (restricted environments) or explicitly forced.\n\t\t\t\t\t\tconst fdPath = isNativeSearchForced() ? undefined : 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\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\tconst nativeResults = nativeFind(searchPath, {\n\t\t\t\t\t\t\t\tpatterns,\n\t\t\t\t\t\t\t\ttype: typeFilter,\n\t\t\t\t\t\t\t\texcludeGlobs: allIgnore,\n\t\t\t\t\t\t\t\tmaxDepth: depth,\n\t\t\t\t\t\t\t\t// find always excludes node_modules/.git; the exclude globs above\n\t\t\t\t\t\t\t\t// cover them too, but skipping the dirs avoids descending them.\n\t\t\t\t\t\t\t\talwaysSkipDirs: new Set([\".git\", \"node_modules\"]),\n\t\t\t\t\t\t\t\tsignal,\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\temit(nativeResults);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst children: ChildProcess[] = [];\n\t\t\t\t\t\tstopChildren = () => {\n\t\t\t\t\t\t\tfor (const child of children) if (!child.killed) child.kill();\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Run each pattern with fd. fd errors (e.g. an invalid glob) surface as a\n\t\t\t\t\t\t// rejection when the pattern produced no output, matching the shell's\n\t\t\t\t\t\t// behavior; a non-zero exit that still produced matches is tolerated.\n\t\t\t\t\t\tconst runPattern = (pattern: string): Promise<string[]> =>\n\t\t\t\t\t\t\tnew Promise<string[]>((res, rej) => {\n\t\t\t\t\t\t\t\tconst args: string[] = [\n\t\t\t\t\t\t\t\t\t\"--glob\",\n\t\t\t\t\t\t\t\t\t\"--color=never\",\n\t\t\t\t\t\t\t\t\t\"--hidden\",\n\t\t\t\t\t\t\t\t\t\"--no-require-git\",\n\t\t\t\t\t\t\t\t\t\"--type\",\n\t\t\t\t\t\t\t\t\ttypeFilter,\n\t\t\t\t\t\t\t\t\t\"--max-results\",\n\t\t\t\t\t\t\t\t\tString(effectiveLimit),\n\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\tfor (const excl of allIgnore) args.push(\"--exclude\", excl);\n\t\t\t\t\t\t\t\tif (depth !== undefined) args.push(\"--max-depth\", String(depth));\n\t\t\t\t\t\t\t\tconst effectivePattern = applyFdGlobPattern(args, pattern);\n\t\t\t\t\t\t\t\targs.push(\"--\", effectivePattern, searchPath);\n\n\t\t\t\t\t\t\t\tconst child = spawn(fdPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\t\t\t\t\t\tchildren.push(child);\n\t\t\t\t\t\t\t\tconst rl = createInterface({ input: child.stdout! });\n\t\t\t\t\t\t\t\tlet stderr = \"\";\n\t\t\t\t\t\t\t\tconst lines: string[] = [];\n\t\t\t\t\t\t\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\t\t\t\t\t\t\tstderr += chunk.toString();\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\trl.on(\"line\", (line) => lines.push(line));\n\t\t\t\t\t\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t\t\t\trej(new Error(`Failed to run fd: ${error.message}`));\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t\t\t\tif (code !== 0 && lines.length === 0) {\n\t\t\t\t\t\t\t\t\t\trej(new Error(stderr.trim() || `fd exited with code ${code}`));\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tres(lines);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst patternResults = await Promise.all(patterns.map(runPattern));\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\n\t\t\t\t\t\tconst relativized: string[] = [];\n\t\t\t\t\t\tfor (const results of patternResults) {\n\t\t\t\t\t\t\tfor (const rawLine of results) {\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\trelativized.push(relativizeFdLine(line, searchPath));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\temit(relativized);\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));\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,iCAAiC,CAAC;AAKjE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAMtF,OAAO,EAAiC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAEnG,QAAA,MAAM,UAAU;;;;;;;;EAqBd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAKtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,2BAA2B;IAC3B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,yFAAyF;IACzF,IAAI,EAAE,CACL,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACvD,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CAClC;AAQD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAoID,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAmPhE;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 \"@kolisachint/hoocode-agent-core\";\nimport { Text } from \"@kolisachint/hoocode-tui\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.js\";\nimport { ensureTool } from \"../../utils/tools-manager.js\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { applyFdGlobPattern, relativizeFdLine, toPosixPath } from \"./fd-utils.js\";\nimport { isNativeSearchForced, nativeFind } from \"./native-search.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from \"./truncate.js\";\n\nconst findSchema = Type.Object({\n\tpattern: Type.Union([Type.String(), Type.Array(Type.String())], {\n\t\tdescription:\n\t\t\t\"Glob pattern(s) to match files. Pass one pattern or an array for OR logic, e.g. '*.ts', 'src/**/*.spec.ts', or ['src/**/*.ts', 'test/**/*.ts'].\",\n\t}),\n\tpath: Type.Optional(Type.String({ description: \"Directory to search in (default: current directory)\" })),\n\texclude: Type.Optional(\n\t\tType.Union([Type.String(), Type.Array(Type.String())], {\n\t\t\tdescription: \"Additional exclusion glob(s), e.g. '**/*.test.ts' or ['**/dist/**', '**/build/**'].\",\n\t\t}),\n\t),\n\ttype: Type.Optional(\n\t\tType.Union([Type.Literal(\"f\"), Type.Literal(\"d\"), Type.Literal(\"l\")], {\n\t\t\tdescription: \"Filter by entry type: 'f' files, 'd' directories, 'l' symlinks (default: 'f').\",\n\t\t}),\n\t),\n\tdepth: Type.Optional(Type.Number({ description: \"Maximum directory depth to search.\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results (default: 1000).\" })),\n\tcompress: Type.Optional(\n\t\tType.Boolean({ description: \"Group files in the same directory to shorten output (default: false).\" }),\n\t),\n});\n\nexport type FindToolInput = Static<typeof findSchema>;\n\nconst DEFAULT_LIMIT = 1000;\nconst NO_RESULTS_MESSAGE = \"No files found matching pattern\";\n\nexport interface FindToolDetails {\n\ttruncation?: TruncationResult;\n\tresultLimitReached?: number;\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 one or more glob patterns. Returns relative or absolute paths. */\n\tglob: (\n\t\tpatterns: string[],\n\t\tcwd: string,\n\t\toptions: { ignore: string[]; limit: number; type?: string },\n\t) => Promise<string[]> | string[];\n}\n\nconst defaultFindOperations: FindOperations = {\n\texists: existsSync,\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 fd */\n\toperations?: FindOperations;\n}\n\n/**\n * Compress a list of file paths by grouping files in the same directory.\n *\n * Compression rules:\n * - If a directory has >=3 entries all sharing the same extension: fold into `dir/{stem1,stem2,stem3}.ext`\n * - If >=3 entries with mixed extensions: sub-group by extension; fold sub-groups >=3\n * - If directory has >6 total files: summarize verbatim after sub-grouping\n * - 1-2 entries: emit verbatim\n */\nfunction compressPaths(paths: string[]): string {\n\tif (paths.length === 0) return \"\";\n\n\tconst sorted = [...paths].sort();\n\n\tconst dirMap = new Map<string, string[]>();\n\tfor (const p of sorted) {\n\t\tconst dir = path.dirname(p);\n\t\tconst base = path.basename(p);\n\t\tif (!dirMap.has(dir)) dirMap.set(dir, []);\n\t\tdirMap.get(dir)!.push(base);\n\t}\n\n\tconst result: string[] = [];\n\n\tfor (const [dir, files] of dirMap) {\n\t\tif (files.length <= 2) {\n\t\t\tfor (const f of files) result.push(dir === \".\" ? f : `${dir}/${f}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst extMap = new Map<string, string[]>();\n\t\tfor (const f of files) {\n\t\t\tconst ext = path.extname(f);\n\t\t\tconst stem = path.basename(f, ext);\n\t\t\tif (!extMap.has(ext)) extMap.set(ext, []);\n\t\t\textMap.get(ext)!.push(stem);\n\t\t}\n\n\t\tif (extMap.size === 1 && files.length >= 3) {\n\t\t\tconst [ext, stems] = extMap.entries().next().value!;\n\t\t\tconst displayDir = dir === \".\" ? \"\" : `${dir}/`;\n\t\t\tresult.push(`${displayDir}{${stems.join(\",\")}}${ext}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet anyFolded = false;\n\t\tconst subResults: string[] = [];\n\t\tfor (const [ext, stems] of extMap) {\n\t\t\tif (stems.length >= 3) {\n\t\t\t\tconst displayDir = dir === \".\" ? \"\" : `${dir}/`;\n\t\t\t\tsubResults.push(`${displayDir}{${stems.join(\",\")}}${ext}`);\n\t\t\t\tanyFolded = true;\n\t\t\t} else {\n\t\t\t\tfor (const stem of stems) subResults.push(dir === \".\" ? `${stem}${ext}` : `${dir}/${stem}${ext}`);\n\t\t\t}\n\t\t}\n\n\t\tif (anyFolded || files.length > 6) {\n\t\t\tresult.push(...subResults);\n\t\t} else {\n\t\t\tfor (const f of files) result.push(dir === \".\" ? f : `${dir}/${f}`);\n\t\t}\n\t}\n\n\treturn result.join(\"\\n\");\n}\n\nfunction formatFindCall(\n\targs: { pattern?: string | string[]; path?: string; type?: string; depth?: number; limit?: number } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst patterns = args?.pattern;\n\tconst patternStr = Array.isArray(patterns) ? patterns.join(\", \") : str(patterns);\n\tconst rawPath = str(args?.path);\n\tconst displayPath = rawPath !== null ? shortenPath(rawPath || \".\") : null;\n\tconst type = args?.type;\n\tconst depth = args?.depth;\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(patternStr === null || patternStr === \"\" ? invalidArg : theme.fg(\"accent\", patternStr)) +\n\t\ttheme.fg(\"toolOutput\", ` in ${displayPath === null ? invalidArg : displayPath}`);\n\tif (type && type !== \"f\") {\n\t\tconst typeLabel = type === \"d\" ? \"dirs\" : type === \"l\" ? \"symlinks\" : type;\n\t\ttext += theme.fg(\"toolOutput\", ` (${typeLabel})`);\n\t}\n\tif (depth !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (depth ${depth})`);\n\t}\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: typeof import(\"../../modes/interactive/theme/theme.js\").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\nexport function createFindToolDefinition(\n\tcwd: string,\n\toptions?: FindToolOptions,\n): ToolDefinition<typeof findSchema, FindToolDetails | undefined> {\n\tconst customOps = options?.operations;\n\treturn {\n\t\tname: \"find\",\n\t\tlabel: \"find\",\n\t\tdescription: `Search for files by one or more glob patterns (OR logic across an array). Optionally filter by entry type (files/dirs/symlinks), directory depth, and extra exclusions. Returns matching 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 (supports multiple patterns, respects .gitignore)\",\n\t\tparameters: findSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{\n\t\t\t\tpattern: rawPattern,\n\t\t\t\tpath: searchDir,\n\t\t\t\texclude: rawExclude,\n\t\t\t\ttype,\n\t\t\t\tdepth,\n\t\t\t\tlimit,\n\t\t\t\tcompress = false,\n\t\t\t}: {\n\t\t\t\tpattern: string | string[];\n\t\t\t\tpath?: string;\n\t\t\t\texclude?: string | string[];\n\t\t\t\ttype?: \"f\" | \"d\" | \"l\";\n\t\t\t\tdepth?: number;\n\t\t\t\tlimit?: number;\n\t\t\t\tcompress?: boolean;\n\t\t\t},\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 stopChildren: (() => 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\tstopChildren = undefined;\n\t\t\t\t\tfn();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tstopChildren?.();\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 = Math.max(1, limit ?? DEFAULT_LIMIT);\n\t\t\t\t\t\tconst ops = customOps ?? defaultFindOperations;\n\n\t\t\t\t\t\tconst patterns = Array.isArray(rawPattern) ? rawPattern : [rawPattern];\n\t\t\t\t\t\tconst excludePatterns = rawExclude ? (Array.isArray(rawExclude) ? rawExclude : [rawExclude]) : [];\n\t\t\t\t\t\t// Always exclude node_modules and .git, plus any caller exclusions.\n\t\t\t\t\t\tconst allIgnore = [\"**/node_modules/**\", \"**/.git/**\", ...excludePatterns];\n\t\t\t\t\t\tconst typeFilter = type === \"d\" ? \"d\" : type === \"l\" ? \"l\" : \"f\";\n\n\t\t\t\t\t\tconst emit = (relativized: string[]) => {\n\t\t\t\t\t\t\tif (relativized.length === 0) {\n\t\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: NO_RESULTS_MESSAGE }], details: undefined }),\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\t// Sources over-fetch by one so exactly-limit result sets are not\n\t\t\t\t\t\t\t// misreported as truncated.\n\t\t\t\t\t\t\tconst unique = [...new Set(relativized)].sort();\n\t\t\t\t\t\t\tconst resultLimitReached = unique.length > effectiveLimit;\n\t\t\t\t\t\t\tconst truncated = unique.slice(0, effectiveLimit);\n\t\t\t\t\t\t\tconst rawOutput = compress ? compressPaths(truncated) : truncated.join(\"\\n\");\n\t\t\t\t\t\t\tconst truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });\n\t\t\t\t\t\t\tlet resultOutput = truncation.content;\n\t\t\t\t\t\t\tconst details: FindToolDetails = {};\n\t\t\t\t\t\t\tconst notices: string[] = [];\n\t\t\t\t\t\t\tif (resultLimitReached) {\n\t\t\t\t\t\t\t\tnotices.push(\n\t\t\t\t\t\t\t\t\t`${effectiveLimit} result${effectiveLimit === 1 ? \"\" : \"s\"} limit reached. Use limit=${effectiveLimit * 2} for more, or refine pattern`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tdetails.resultLimitReached = effectiveLimit;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t\t\t\t\t\t\tdetails.truncation = truncation;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (notices.length > 0) resultOutput += `\\n\\n[${notices.join(\". \")}]`;\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: resultOutput }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(details).length > 0 ? 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\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(patterns, searchPath, {\n\t\t\t\t\t\t\t\tignore: allIgnore,\n\t\t\t\t\t\t\t\tlimit: effectiveLimit + 1,\n\t\t\t\t\t\t\t\ttype: typeFilter,\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\temit(\n\t\t\t\t\t\t\t\tresults.map((p) => {\n\t\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\t\treturn toPosixPath(path.relative(searchPath, p));\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, with a pure-JS fallback when fd is\n\t\t\t\t\t\t// unavailable (restricted environments) or explicitly forced.\n\t\t\t\t\t\tconst fdPath = isNativeSearchForced() ? undefined : 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\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\tconst nativeResults = nativeFind(searchPath, {\n\t\t\t\t\t\t\t\tpatterns,\n\t\t\t\t\t\t\t\ttype: typeFilter,\n\t\t\t\t\t\t\t\texcludeGlobs: allIgnore,\n\t\t\t\t\t\t\t\tmaxDepth: depth,\n\t\t\t\t\t\t\t\t// find always excludes node_modules/.git; the exclude globs above\n\t\t\t\t\t\t\t\t// cover them too, but skipping the dirs avoids descending them.\n\t\t\t\t\t\t\t\talwaysSkipDirs: new Set([\".git\", \"node_modules\"]),\n\t\t\t\t\t\t\t\tsignal,\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\temit(nativeResults);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst children: ChildProcess[] = [];\n\t\t\t\t\t\tstopChildren = () => {\n\t\t\t\t\t\t\tfor (const child of children) if (!child.killed) child.kill();\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Run each pattern with fd. fd errors (e.g. an invalid glob) surface as a\n\t\t\t\t\t\t// rejection when the pattern produced no output, matching the shell's\n\t\t\t\t\t\t// behavior; a non-zero exit that still produced matches is tolerated.\n\t\t\t\t\t\tconst runPattern = (pattern: string): Promise<string[]> =>\n\t\t\t\t\t\t\tnew Promise<string[]>((res, rej) => {\n\t\t\t\t\t\t\t\tconst args: string[] = [\n\t\t\t\t\t\t\t\t\t\"--glob\",\n\t\t\t\t\t\t\t\t\t\"--color=never\",\n\t\t\t\t\t\t\t\t\t\"--hidden\",\n\t\t\t\t\t\t\t\t\t\"--no-require-git\",\n\t\t\t\t\t\t\t\t\t\"--type\",\n\t\t\t\t\t\t\t\t\ttypeFilter,\n\t\t\t\t\t\t\t\t\t\"--max-results\",\n\t\t\t\t\t\t\t\t\tString(effectiveLimit + 1),\n\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\tfor (const excl of allIgnore) args.push(\"--exclude\", excl);\n\t\t\t\t\t\t\t\tif (depth !== undefined) args.push(\"--max-depth\", String(depth));\n\t\t\t\t\t\t\t\tconst effectivePattern = applyFdGlobPattern(args, pattern);\n\t\t\t\t\t\t\t\targs.push(\"--\", effectivePattern, searchPath);\n\n\t\t\t\t\t\t\t\tconst child = spawn(fdPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\t\t\t\t\t\tchildren.push(child);\n\t\t\t\t\t\t\t\tconst rl = createInterface({ input: child.stdout! });\n\t\t\t\t\t\t\t\tlet stderr = \"\";\n\t\t\t\t\t\t\t\tconst lines: string[] = [];\n\t\t\t\t\t\t\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\t\t\t\t\t\t\tstderr += chunk.toString();\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\trl.on(\"line\", (line) => lines.push(line));\n\t\t\t\t\t\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t\t\t\trej(new Error(`Failed to run fd: ${error.message}`));\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t\t\t\tif (code !== 0 && lines.length === 0) {\n\t\t\t\t\t\t\t\t\t\trej(new Error(stderr.trim() || `fd exited with code ${code}`));\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tres(lines);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst patternResults = await Promise.all(patterns.map(runPattern));\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\n\t\t\t\t\t\tconst relativized: string[] = [];\n\t\t\t\t\t\tfor (const results of patternResults) {\n\t\t\t\t\t\t\tfor (const rawLine of results) {\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\trelativized.push(relativizeFdLine(line, searchPath));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\temit(relativized);\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));\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"]}
|
package/dist/core/tools/find.js
CHANGED
|
@@ -181,7 +181,7 @@ export function createFindToolDefinition(cwd, options) {
|
|
|
181
181
|
(async () => {
|
|
182
182
|
try {
|
|
183
183
|
const searchPath = resolveToCwd(searchDir || ".", cwd);
|
|
184
|
-
const effectiveLimit = limit ?? DEFAULT_LIMIT;
|
|
184
|
+
const effectiveLimit = Math.max(1, limit ?? DEFAULT_LIMIT);
|
|
185
185
|
const ops = customOps ?? defaultFindOperations;
|
|
186
186
|
const patterns = Array.isArray(rawPattern) ? rawPattern : [rawPattern];
|
|
187
187
|
const excludePatterns = rawExclude ? (Array.isArray(rawExclude) ? rawExclude : [rawExclude]) : [];
|
|
@@ -193,8 +193,10 @@ export function createFindToolDefinition(cwd, options) {
|
|
|
193
193
|
settle(() => resolve({ content: [{ type: "text", text: NO_RESULTS_MESSAGE }], details: undefined }));
|
|
194
194
|
return;
|
|
195
195
|
}
|
|
196
|
+
// Sources over-fetch by one so exactly-limit result sets are not
|
|
197
|
+
// misreported as truncated.
|
|
196
198
|
const unique = [...new Set(relativized)].sort();
|
|
197
|
-
const resultLimitReached = unique.length
|
|
199
|
+
const resultLimitReached = unique.length > effectiveLimit;
|
|
198
200
|
const truncated = unique.slice(0, effectiveLimit);
|
|
199
201
|
const rawOutput = compress ? compressPaths(truncated) : truncated.join("\n");
|
|
200
202
|
const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
|
|
@@ -202,7 +204,7 @@ export function createFindToolDefinition(cwd, options) {
|
|
|
202
204
|
const details = {};
|
|
203
205
|
const notices = [];
|
|
204
206
|
if (resultLimitReached) {
|
|
205
|
-
notices.push(`${effectiveLimit}
|
|
207
|
+
notices.push(`${effectiveLimit} result${effectiveLimit === 1 ? "" : "s"} limit reached. Use limit=${effectiveLimit * 2} for more, or refine pattern`);
|
|
206
208
|
details.resultLimitReached = effectiveLimit;
|
|
207
209
|
}
|
|
208
210
|
if (truncation.truncated) {
|
|
@@ -228,7 +230,7 @@ export function createFindToolDefinition(cwd, options) {
|
|
|
228
230
|
}
|
|
229
231
|
const results = await ops.glob(patterns, searchPath, {
|
|
230
232
|
ignore: allIgnore,
|
|
231
|
-
limit: effectiveLimit,
|
|
233
|
+
limit: effectiveLimit + 1,
|
|
232
234
|
type: typeFilter,
|
|
233
235
|
});
|
|
234
236
|
if (signal?.aborted) {
|
|
@@ -289,7 +291,7 @@ export function createFindToolDefinition(cwd, options) {
|
|
|
289
291
|
"--type",
|
|
290
292
|
typeFilter,
|
|
291
293
|
"--max-results",
|
|
292
|
-
String(effectiveLimit),
|
|
294
|
+
String(effectiveLimit + 1),
|
|
293
295
|
];
|
|
294
296
|
for (const excl of allIgnore)
|
|
295
297
|
args.push("--exclude", excl);
|