e66d71f6b9
- Consolidated directory listing functionality from dedicated `ls` tool into the `read` tool. - Removed the `ls` tool and all related exports, types, and configurations from the public API. - Updated `read` tool to return formatted directory listings with modification times instead of redirecting to `ls` tool. - Removed `bashInterceptor.simpleLs` setting and related interception logic from bash tool. - Removed `ls.enabled` setting and related tool registration logic. - Removed test cases for the `ls` tool and updated tool type definitions to remove `LsToolResultEvent`.
106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
/**
|
|
* Bash intent interceptor - redirects common shell patterns to proper tools.
|
|
*
|
|
* When an LLM calls bash with patterns like `grep`, `cat`, `find`, etc.,
|
|
* this interceptor provides helpful error messages directing them to use
|
|
* the specialized tools instead.
|
|
*/
|
|
import type { BashInterceptorRule } from "../config/settings-schema";
|
|
|
|
export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
|
|
{
|
|
pattern: "^\\s*(cat|head|tail|less|more)\\s+",
|
|
tool: "read",
|
|
message: "Use the `read` tool instead of cat/head/tail. It provides better context and handles binary files.",
|
|
},
|
|
{
|
|
pattern: "^\\s*(grep|rg|ripgrep|ag|ack)\\s+",
|
|
tool: "grep",
|
|
message: "Use the `grep` tool instead of grep/rg. It respects .gitignore and provides structured output.",
|
|
},
|
|
{
|
|
pattern: "^\\s*(find|fd|locate)\\s+.*(-name|-iname|-type|--type|-glob)",
|
|
tool: "find",
|
|
message: "Use the `find` tool instead of find/fd. It respects .gitignore and is faster for glob patterns.",
|
|
},
|
|
{
|
|
pattern: "^\\s*sed\\s+(-i|--in-place)",
|
|
tool: "edit",
|
|
message: "Use the `edit` tool instead of sed -i. It provides diff preview and fuzzy matching.",
|
|
},
|
|
{
|
|
pattern: "^\\s*perl\\s+.*-[pn]?i",
|
|
tool: "edit",
|
|
message: "Use the `edit` tool instead of perl -i. It provides diff preview and fuzzy matching.",
|
|
},
|
|
{
|
|
pattern: "^\\s*awk\\s+.*-i\\s+inplace",
|
|
tool: "edit",
|
|
message: "Use the `edit` tool instead of awk -i inplace. It provides diff preview and fuzzy matching.",
|
|
},
|
|
{
|
|
pattern: "^\\s*(echo|printf|cat\\s*<<)\\s+.*[^|]>\\s*\\S",
|
|
tool: "write",
|
|
message: "Use the `write` tool instead of echo/cat redirection. It handles encoding and provides confirmation.",
|
|
},
|
|
];
|
|
|
|
export interface InterceptionResult {
|
|
/** If true, the bash command should be blocked */
|
|
block: boolean;
|
|
/** Error message to return instead of executing */
|
|
message?: string;
|
|
/** Suggested tool to use instead */
|
|
suggestedTool?: string;
|
|
}
|
|
|
|
/**
|
|
* Compile bash interceptor rules into regexes, skipping invalid patterns.
|
|
*/
|
|
function compileRules(rules: BashInterceptorRule[]): Array<{ rule: BashInterceptorRule; regex: RegExp }> {
|
|
const compiled: Array<{ rule: BashInterceptorRule; regex: RegExp }> = [];
|
|
for (const rule of rules) {
|
|
const flags = rule.flags ?? "";
|
|
try {
|
|
compiled.push({ rule, regex: new RegExp(rule.pattern, flags) });
|
|
} catch {
|
|
// Skip invalid regex patterns
|
|
}
|
|
}
|
|
return compiled;
|
|
}
|
|
|
|
/**
|
|
* Check if a bash command should be intercepted.
|
|
*
|
|
* @param command The bash command to check
|
|
* @param availableTools Set of tool names that are available
|
|
* @returns InterceptionResult indicating if the command should be blocked
|
|
*/
|
|
export function checkBashInterception(
|
|
command: string,
|
|
availableTools: string[],
|
|
rules: BashInterceptorRule[] = DEFAULT_BASH_INTERCEPTOR_RULES,
|
|
): InterceptionResult {
|
|
// Normalize command for pattern matching
|
|
const normalizedCommand = command.trim();
|
|
const compiled = compileRules(rules);
|
|
|
|
for (const { rule, regex } of compiled) {
|
|
// Only block if the suggested tool is actually available
|
|
if (!availableTools.includes(rule.tool)) {
|
|
continue;
|
|
}
|
|
|
|
if (regex.test(normalizedCommand)) {
|
|
return {
|
|
block: true,
|
|
message: `Blocked: ${rule.message}\n\nOriginal command: ${command}`,
|
|
suggestedTool: rule.tool,
|
|
};
|
|
}
|
|
}
|
|
|
|
return { block: false };
|
|
}
|