/**
 * BashToolbox Registry
 *
 * Central registry for all agentic tools. Enforces schema validation
 * and provides metadata for AI SDKs. Delegates execution to the executor
 * module and schema conversion to the schema-conversion module.
 */
import { z } from "zod";
import { WebFetchTool } from "../../commands/ag-web/ag-web-fetch.js";
import { WebSearchTool } from "../../commands/ag-web/ag-web-search.js";
import { sanitizeErrorMessage } from "../../fs/sanitize-error.js";
import { SemanticEngine } from "../../lsp/semantic-engine.js";
import { LspTool } from "../../lsp/LspTool.js";
import { detectDestructiveCommand } from "../../security/destructive-command-detector.js";
import { ConvertTool } from "../ConvertTool.js";
import { EditTool } from "../EditTool.js";
import { MultiReplaceTool } from "../MultiReplaceTool.js";
import { FindFilesTool, GrepTool } from "../SearchTool.js";
import { ExplainTool, FindSymbolTool, HoverTool } from "../SemanticTool.js";
import { TodoTool } from "../TodoTool.js";
import { buildTool } from "../Tool.js";
import { ToolSearchEngine } from "../ToolSearchEngine.js";
import { executeTool } from "./executor.js";
import { jsonSchemaToZod, zodToJsonSchema, } from "./schema-conversion.js";
/**
 * Defaults for all tools (fail-closed where it matters).
 */
const _TOOL_DEFAULTS = {
    isReadOnly: () => false,
    isDestructive: () => false,
    isConcurrencySafe: () => false,
    checkPermissions: async (_bash) => ({
        behavior: "allow",
    }),
    validateInput: async () => ({ result: true }),
};
/** Null-prototype empty shape for Zod schemas with no parameters. */
const EMPTY_SHAPE = Object.create(null);
/**
 * Normalizes quotes to prevent matching failures caused by LLM-generated smart quotes.
 */
function normalizeQuotes(text) {
    return text.replace(/[""]/g, '"').replace(/['']/g, "'");
}
/**
 * BashToolbox - Central registry for all agentic tools.
 *
 * Enforces schema validation and provides metadata for AI SDKs.
 */
export class BashToolbox {
    tools = new Map();
    constructor() {
        this.registerCoreTools();
    }
    registerCoreTools() {
        this.registerTool(buildTool({
            name: "read_file",
            description: "Read the contents of a file from the virtual filesystem.",
            parameters: z.object({
                path: z.string().describe("Absolute path to the file to read."),
            }),
            isReadOnly: true,
            execute: async (bash, { path }) => {
                try {
                    const content = await bash.fs.readFile(path, "utf-8");
                    bash.updateFileState(path, { content });
                    return content;
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    return `Error reading file: ${sanitizeErrorMessage(message)}`;
                }
            },
        }));
        this.registerTool(buildTool({
            name: "write_file",
            description: "Create or overwrite a file in the virtual filesystem.",
            parameters: z.object({
                path: z.string().describe("Absolute path to the file to write."),
                content: z.string().describe("The content to write to the file."),
            }),
            isDestructive: true,
            execute: async (bash, { path, content }) => {
                try {
                    await bash.fs.mkdir("/.ag-bash", { recursive: true });
                    await bash.writeFileDirect(path, content);
                    await bash.indexer.indexFile(path);
                    await bash.saveIndex();
                    await bash.lsp.notifyDidChange(path, content);
                    return `Successfully wrote to ${path}.`;
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    return `Error writing file ${path}: ${sanitizeErrorMessage(message)}`;
                }
            },
        }));
        this.registerTool(buildTool({
            name: "list_dir",
            description: "List contents of a directory.",
            parameters: z.object({
                path: z.string().describe("Absolute path to the directory to list."),
            }),
            isReadOnly: true,
            execute: async (bash, { path }) => {
                try {
                    const files = await bash.listDirDirect(path);
                    return files.join("\n");
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    return `Error listing directory ${path}: ${sanitizeErrorMessage(message)}`;
                }
            },
        }));
        this.registerTool(buildTool({
            name: "edit_file",
            description: "Apply a text patch to a file (simple find and replace).",
            parameters: z.object({
                path: z.string().describe("Absolute path to the file to edit."),
                target: z.string().describe("The exact text block to be replaced."),
                replacement: z.string().describe("The new text to insert instead."),
                replace_all: z
                    .boolean()
                    .optional()
                    .describe("If true, replaces all occurrences instead of just the first one."),
            }),
            isDestructive: true,
            execute: async (bash, { path, target, replacement, replace_all, }) => {
                try {
                    // Staleness check
                    const state = bash.getFileState(path);
                    const currentContent = await bash.readFileDirect(path);
                    if (state && state.content !== currentContent) {
                        return `Stale Edit Error: The file ${path} has changed since you last read it. Please read it again before applying edits.`;
                    }
                    const normalizedContent = normalizeQuotes(currentContent);
                    const normalizedTarget = normalizeQuotes(target);
                    if (!normalizedContent.includes(normalizedTarget)) {
                        return `Error: target content not found in ${path}. Make sure it matches exactly (ignoring smart quotes).`;
                    }
                    let newContent;
                    if (replace_all) {
                        const escapedTarget = normalizedTarget.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
                        const regex = new RegExp(escapedTarget, "g");
                        const matches = [...normalizedContent.matchAll(regex)];
                        newContent = currentContent;
                        let offset = 0;
                        for (const match of matches) {
                            const start = match.index + offset;
                            const end = start + normalizedTarget.length;
                            newContent =
                                newContent.substring(0, start) +
                                    replacement +
                                    newContent.substring(end);
                            offset += replacement.length - normalizedTarget.length;
                        }
                    }
                    else {
                        const index = normalizedContent.indexOf(normalizedTarget);
                        newContent =
                            currentContent.substring(0, index) +
                                replacement +
                                currentContent.substring(index + normalizedTarget.length);
                    }
                    await bash.fs.mkdir("/.ag-bash", { recursive: true });
                    await bash.writeFileDirect(path, newContent);
                    await bash.indexer.indexFile(path);
                    await bash.saveIndex();
                    return `Successfully edited ${path}.`;
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    return `Error editing file ${path}: ${sanitizeErrorMessage(message)}`;
                }
            },
        }));
        this.registerTool(buildTool({
            name: "analyze_code",
            description: "Perform semantic analysis on a source file.",
            parameters: z.object({
                path: z.string().describe("Absolute path to the file to analyze."),
            }),
            execute: async (bash, { path }) => {
                try {
                    const content = await bash.readFileDirect(path);
                    const { parse } = await import("../../parser/parser.js");
                    const ast = parse(content);
                    // parse() returns a ScriptNode-compatible structure
                    const engine = new SemanticEngine(ast);
                    return {
                        type: "shell",
                        symbols: engine.getAllSymbols(),
                    };
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    return `Error analyzing file ${path}: ${sanitizeErrorMessage(message)}`;
                }
            },
        }));
        this.registerTool(buildTool({
            name: "find_symbols",
            description: "Search for symbols (functions, variables) across the workspace.",
            parameters: z.object({
                query: z
                    .string()
                    .optional()
                    .describe("Query to filter symbol names."),
            }),
            execute: async (bash, { query }) => {
                return await bash.indexer.findSymbols(query);
            },
        }));
        this.registerTool(buildTool({
            name: "run_command",
            description: "Execute a shell command in the sandbox.",
            parameters: z.object({
                command: z.string().describe("The shell command to execute."),
            }),
            isReadOnly: (args) => {
                const cmd = args.command.trim().split(/\s+/)[0];
                const readOnlyCommands = [
                    "ls",
                    "cat",
                    "grep",
                    "find",
                    "pwd",
                    "printenv",
                    "echo",
                    "id",
                    "whoami",
                    "stat",
                    "df",
                    "du",
                    "ls-R",
                    "tree",
                    "ag-hover",
                    "ag-explain",
                    "ag-find-symbol",
                ];
                return (readOnlyCommands.includes(cmd) &&
                    !args.command.includes(">") &&
                    !args.command.includes("|"));
            },
            isDestructive: (args) => {
                const cmd = args.command.trim().split(/\s+/)[0];
                const destructiveCommands = [
                    "rm",
                    "mv",
                    "mkdir",
                    "touch",
                    "chmod",
                    "chown",
                    "truncate",
                    "dd",
                    "cp",
                ];
                return (destructiveCommands.includes(cmd) || args.command.includes(">"));
            },
            execute: async (bash, { command }) => {
                const result = await bash.exec(command);
                return result;
            },
        }));
        this.registerTool(buildTool({
            name: "grep_search",
            description: "Search for a text pattern across multiple files.",
            parameters: z.object({
                path: z
                    .string()
                    .describe("Absolute path to the directory to search in."),
                query: z
                    .string()
                    .describe("The text or regex pattern to search for."),
            }),
            execute: async (bash, { path, query }) => {
                try {
                    const result = await bash.exec(`grep -r "${query}" ${path}`);
                    return result.stdout || result.stderr || "No matches found.";
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    return `Error searching in ${path}: ${sanitizeErrorMessage(message)}`;
                }
            },
        }));
        this.registerTool(buildTool({
            name: "index_workspace",
            description: "Trigger a full scan and indexing of all supported files in the workspace.",
            parameters: z.object({
                path: z
                    .string()
                    .optional()
                    .describe("Root directory to scan. Defaults to /."),
            }),
            execute: async (bash, { path }) => {
                try {
                    await bash.indexer.fullScan(path || "/");
                    await bash.saveIndex();
                    return "Successfully indexed the workspace.";
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    return `Error indexing workspace: ${sanitizeErrorMessage(message)}`;
                }
            },
        }));
        this.registerTool(buildTool({
            name: "get_references",
            description: "Find all references to a function or variable.",
            parameters: z.object({
                name: z
                    .string()
                    .optional()
                    .describe("The name of the symbol to find"),
                path: z
                    .string()
                    .optional()
                    .describe("Path to the file where the symbol is referenced"),
                line: z.number().optional().describe("1-based line number"),
                character: z
                    .number()
                    .optional()
                    .describe("1-based character position"),
            }),
            execute: async (bash, { name, path, line, character, }) => {
                let symbolName = name;
                if (!symbolName &&
                    path &&
                    line !== undefined &&
                    character !== undefined) {
                    const content = await bash.readFileDirect(path);
                    const lines = content.split("\n");
                    const lineText = lines[line - 1] || "";
                    const symbolPattern = /[\w$!]+/g;
                    let match;
                    while ((match = symbolPattern.exec(lineText)) !== null) {
                        if (character - 1 >= match.index &&
                            character - 1 < match.index + match[0].length) {
                            symbolName = match[0];
                            break;
                        }
                    }
                }
                if (!symbolName)
                    return "Symbol not found at position.";
                const occurrences = bash.semanticEngine.getOccurrences(symbolName);
                if (occurrences.length === 0) {
                    return `No references found for '${symbolName}'.`;
                }
                const refs = occurrences
                    .filter((o) => !o.isDefinition)
                    .map((o) => `  Line ${o.line}:0 [${o.scope}]`)
                    .join("\n");
                const def = occurrences.find((o) => o.isDefinition);
                let output = `Found ${occurrences.length} occurrences of '${symbolName}':\n`;
                if (def)
                    output += `Definition: Line ${def.line}:0\n`;
                if (refs)
                    output += `References:\n${refs}`;
                return output;
            },
        }));
        this.registerTool(buildTool({
            name: "hover_info",
            description: "Get information about a symbol at a specific position.",
            parameters: z.object({
                path: z.string().describe("Path to the file"),
                line: z.number().describe("1-based line number"),
                character: z.number().describe("1-based character position"),
            }),
            execute: async (bash, { path, line, character, }) => {
                const content = await bash.readFileDirect(path);
                const lines = content.split("\n");
                const lineText = lines[line - 1] || "";
                const symbolPattern = /[\w$!]+/g;
                let symbolName;
                let match;
                while ((match = symbolPattern.exec(lineText)) !== null) {
                    if (character - 1 >= match.index &&
                        character - 1 < match.index + match[0].length) {
                        symbolName = match[0];
                        break;
                    }
                }
                if (!symbolName)
                    return "No symbol found at position.";
                const definition = bash.semanticEngine.findDefinition(symbolName);
                if (definition) {
                    return `${symbolName} (${definition.type})\nScope: ${definition.scope}\nDefined at line ${definition.line}`;
                }
                return `No info found for '${symbolName}'.`;
            },
        }));
        this.registerTool(buildTool({
            name: "query_json",
            description: "Run a jq query against a JSON file or string.",
            parameters: z.object({
                query: z.string().describe("The jq filter/query string."),
                path: z
                    .string()
                    .optional()
                    .describe("Optional path to a JSON file to query."),
                json: z
                    .string()
                    .optional()
                    .describe("Optional JSON string to query directly."),
            }),
            execute: async (bash, { query, path, json, }) => {
                let cmd = `echo '${(json || "").replace(/'/g, "'\\''")}' | jq '${query}'`;
                if (path) {
                    cmd = `jq '${query}' ${path}`;
                }
                const result = await bash.exec(cmd);
                return result.stdout || result.stderr;
            },
        }));
        this.registerTool(buildTool({
            name: "diff_files",
            description: "Generate a unified diff between two files.",
            parameters: z.object({
                file1: z.string().describe("Path to the first file."),
                file2: z.string().describe("Path to the second file."),
            }),
            execute: async (bash, { file1, file2 }) => {
                const result = await bash.exec(`diff -u ${file1} ${file2}`);
                return result.stdout || result.stderr;
            },
        }));
        this.registerTool(buildTool({
            name: "help_builtin",
            description: "Get detailed help for a shell builtin command.",
            parameters: z.object({
                command: z.string().describe("The name of the builtin command."),
            }),
            execute: async (bash, { command }) => {
                const result = await bash.exec(`help ${command}`);
                return result.stdout || result.stderr;
            },
        }));
        this.registerTool(buildTool({
            name: "find_files",
            description: "Search for files by name or glob pattern.",
            parameters: z.object({
                path: z.string().describe("The directory to start searching from."),
                pattern: z
                    .string()
                    .describe("The filename pattern or glob (e.g., '*.ts')."),
            }),
            execute: async (bash, { path, pattern }) => {
                const result = await bash.exec(`find ${path} -name "${pattern}"`);
                return result.stdout.split("\n").filter(Boolean);
            },
        }));
        this.registerTool(buildTool({
            name: "explain_command",
            description: "Parse and explain a shell command.",
            parameters: z.object({
                command: z.string().describe("The shell command to explain."),
            }),
            execute: async (_bash, { command }) => {
                const { parse } = await import("../../parser/parser.js");
                const ast = parse(command);
                return {
                    type: "explanation",
                    ast: JSON.parse(JSON.stringify(ast, (k, v) => (k === "parent" ? undefined : v))),
                };
            },
        }));
        this.registerTool(buildTool({
            name: "check_environment",
            description: "Get diagnostics about the sandboxed environment.",
            parameters: z.object(EMPTY_SHAPE),
            execute: async (bash) => {
                return {
                    cwd: bash.getCwd(),
                    env: Object.keys(bash.env),
                    limits: bash.limits,
                    version: "Ag-Bash vNext",
                };
            },
        }));
        this.registerTool(buildTool({
            name: "run_js",
            description: "Execute JavaScript code in the sandbox. For persistent state, use run_js_session.",
            parameters: z.object({
                code: z.string().describe("The JS code to execute."),
            }),
            execute: async (bash, { code }) => {
                const result = await bash.exec(`js-exec -c "${code.replace(/"/g, '\\"')}"`);
                return result;
            },
        }));
        this.registerTool(buildTool({
            name: "run_js_session",
            description: "Execute JavaScript code in a persistent session (stateful REPL). Maintains variables and modules between calls.",
            parameters: z.object({
                code: z.string().describe("The JS code to execute."),
                sessionId: z
                    .string()
                    .describe("Session identifier (e.g., 'main', 'test')."),
            }),
            execute: async (bash, { code, sessionId }) => {
                const result = await bash.exec(`js-exec --session ${sessionId} -c "${code.replace(/"/g, '\\"')}"`);
                return result;
            },
        }));
        this.registerTool(buildTool({
            name: "run_python",
            description: "Execute Python code in the sandbox. For persistent state, use run_python_session.",
            parameters: z.object({
                code: z.string().describe("The Python code to execute."),
            }),
            execute: async (bash, { code }) => {
                const result = await bash.exec(`python3 -c "${code.replace(/"/g, '\\"')}"`);
                return result;
            },
        }));
        this.registerTool(buildTool({
            name: "run_python_session",
            description: "Execute Python code in a persistent session (stateful REPL). Maintains variables and imports between calls.",
            parameters: z.object({
                code: z.string().describe("The Python code to execute."),
                sessionId: z
                    .string()
                    .describe("Session identifier (e.g., 'data-analysis')."),
            }),
            execute: async (bash, { code, sessionId }) => {
                const result = await bash.exec(`python3 --session ${sessionId} -c "${code.replace(/"/g, '\\"')}"`);
                return result;
            },
        }));
        this.registerTool(buildTool({
            name: "close_session",
            description: "Terminate a persistent session and release its resources.",
            parameters: z.object({
                sessionId: z.string().describe("The ID of the session to close."),
            }),
            execute: async (bash, { sessionId }) => {
                await bash.closeSession(sessionId);
                return `Session ${sessionId} closed.`;
            },
        }));
        this.registerTool(buildTool({
            name: "get_definition",
            description: "Find the definition of a symbol.",
            parameters: z.object({
                name: z.string().describe("The name of the symbol to find."),
            }),
            execute: async (bash, { name }) => {
                const def = bash.semanticEngine.findDefinition(name);
                return def || "Definition not found.";
            },
        }));
        this.registerTool(buildTool({
            name: "add_todo",
            description: "Add a new todo item.",
            parameters: z.object({
                task: z.string().describe("The task description."),
                status: z
                    .enum(["pending", "doing", "done"])
                    .optional()
                    .describe("Initial status."),
            }),
            execute: async (bash, { task, status }) => {
                const todosPath = "/.ag-bash/todos.json";
                await bash.fs.mkdir("/.ag-bash", { recursive: true });
                let todos = [];
                if (await bash.fs.exists(todosPath)) {
                    todos = JSON.parse(await bash.readFileDirect(todosPath));
                }
                const newTodo = {
                    id: (todos.length + 1).toString(),
                    task,
                    status: status || "pending",
                };
                todos.push(newTodo);
                await bash.writeFileDirect(todosPath, JSON.stringify(todos, null, 2));
                return { success: true, id: newTodo.id };
            },
        }));
        this.registerTool(buildTool({
            name: "list_todos",
            description: "List all todo items.",
            parameters: z.object(EMPTY_SHAPE),
            execute: async (bash) => {
                const todosPath = "/.ag-bash/todos.json";
                if (await bash.fs.exists(todosPath)) {
                    return { todos: JSON.parse(await bash.readFileDirect(todosPath)) };
                }
                return { todos: [] };
            },
        }));
        this.registerTool(buildTool({
            name: "update_todo",
            description: "Update the status of a todo item.",
            parameters: z.object({
                id: z.string().describe("The ID of the todo to update."),
                status: z.enum(["pending", "doing", "done"]).describe("New status."),
            }),
            execute: async (bash, { id, status }) => {
                const todosPath = "/.ag-bash/todos.json";
                if (!(await bash.fs.exists(todosPath))) {
                    return { error: "Todo not found." };
                }
                const todos = JSON.parse(await bash.readFileDirect(todosPath));
                const todo = todos.find((t) => t.id === id);
                if (!todo)
                    return { error: "Todo not found." };
                todo.status = status;
                await bash.writeFileDirect(todosPath, JSON.stringify(todos, null, 2));
                return { success: true };
            },
        }));
        this.registerTool(buildTool({
            name: "plan_enter",
            description: "Enter plan mode to design an approach before making changes.",
            parameters: z.object(EMPTY_SHAPE),
            execute: async (bash) => {
                bash.setMode("plan");
                return "Entered plan mode. You are now in read-only mode. Use plan_exit to return to execute mode when ready.";
            },
        }));
        this.registerTool(buildTool({
            name: "plan_exit",
            description: "Exit plan mode and return to execution mode.",
            parameters: z.object(EMPTY_SHAPE),
            execute: async (bash) => {
                bash.setMode("execute");
                return "Exited plan mode. You can now make changes to the codebase.";
            },
        }));
        this.registerTool(GrepTool);
        this.registerTool(FindFilesTool);
        this.registerTool(EditTool);
        this.registerTool(HoverTool);
        this.registerTool(FindSymbolTool);
        this.registerTool(ExplainTool);
        this.registerTool(TodoTool);
        this.registerTool(WebSearchTool);
        this.registerTool(WebFetchTool);
        this.registerTool(LspTool);
        this.registerTool(MultiReplaceTool);
        this.registerTool(ConvertTool);
        this.registerTool(buildTool({
            name: "search_tools",
            description: "Search for available tools by keyword, or select by exact name with 'select:Name1,Name2'.",
            parameters: z.object({
                query: z
                    .string()
                    .describe('Keyword query or "select:Name1,Name2" for exact lookup.'),
                limit: z.number().optional().describe("Max results (default: 10)."),
            }),
            isReadOnly: true,
            execute: async (_bash, { query, limit }) => {
                const tools = this.getTools();
                const engine = new ToolSearchEngine();
                if (query.startsWith("select:")) {
                    const selected = engine.selectByName(tools, query);
                    if (selected.length === 0)
                        return "No tools found matching those names.";
                    return selected.map((t) => ({
                        name: t.name,
                        description: t.description,
                        aliases: t.aliases,
                    }));
                }
                const results = engine.search(tools, query, limit);
                if (results.length === 0)
                    return "No tools found matching your query.";
                return results.map((r) => ({
                    name: r.tool.name,
                    description: r.tool.description,
                    score: r.score,
                    matchedOn: r.matchedOn,
                }));
            },
        }));
        this.registerTool(buildTool({
            name: "list_mcp_tools",
            description: "List all tools available via connected MCP servers.",
            parameters: z.object(EMPTY_SHAPE),
            execute: async (bash) => {
                const client = bash.services.mcpClient;
                return client.listConnections().map((c) => ({
                    server: c.id,
                    tools: c.tools.map((t) => `${c.id}__${t.name}`),
                }));
            },
        }));
        this.registerTool(buildTool({
            name: "sync_mcp_tools",
            description: "Synchronize and register all MCP tools into the central toolbox.",
            parameters: z.object(EMPTY_SHAPE),
            isReadOnly: true,
            execute: async (bash) => {
                const client = bash.services.mcpClient;
                const connections = client.listConnections();
                let count = 0;
                for (const conn of connections) {
                    for (const tool of conn.tools) {
                        const namespacedName = `mcp:${conn.id}:${tool.name}`;
                        this.registerTool(buildTool({
                            name: namespacedName,
                            description: `[MCP: ${conn.id}] ${tool.description || ""}`,
                            // MCP schemas are dynamic; use a passthrough object schema
                            parameters: z.object(Object.create(null)).passthrough(),
                            execute: async (b, args) => {
                                return await b.services.mcpClient.callTool(conn.id, tool.name, args, b);
                            },
                        }));
                        count++;
                    }
                }
                return `Successfully synchronized ${count} tools from ${connections.length} MCP servers.`;
            },
        }));
        // --- Phase 1: Task Management Tools ---
        this.registerTool(buildTool({
            name: "task_create",
            description: "Create a new tracked task with subject, description, owner, and progress text.",
            searchHint: "create a task to track work",
            parameters: z.object({
                subject: z.string().describe("Brief title for the task."),
                description: z.string().describe("What needs to be done."),
                activeForm: z
                    .string()
                    .optional()
                    .describe('Spinner text when in_progress (e.g., "Running tests").'),
                owner: z
                    .string()
                    .optional()
                    .describe("Agent ID that owns this task."),
            }),
            execute: async (bash, input) => {
                const task = bash.services.taskManager.create(input);
                return { id: task.id, subject: task.subject, status: task.status };
            },
        }));
        this.registerTool(buildTool({
            name: "task_update",
            description: "Update a task's status, subject, description, owner, or dependencies.",
            searchHint: "update task status or details",
            parameters: z.object({
                taskId: z.string().describe("The task ID to update."),
                status: z
                    .string()
                    .optional()
                    .describe("New status: pending, in_progress, completed, failed, blocked."),
                subject: z.string().optional().describe("New subject."),
                description: z.string().optional().describe("New description."),
                owner: z.string().optional().describe("New owner agent ID."),
                addBlocks: z
                    .array(z.string())
                    .optional()
                    .describe("Task IDs that this task blocks."),
                addBlockedBy: z
                    .array(z.string())
                    .optional()
                    .describe("Task IDs that block this task."),
            }),
            execute: async (bash, input) => {
                const { taskId, ...rest } = input;
                const changes = {
                    subject: rest.subject,
                    description: rest.description,
                    status: rest.status,
                    owner: rest.owner,
                    addBlocks: rest.addBlocks,
                    addBlockedBy: rest.addBlockedBy,
                };
                const task = bash.services.taskManager.update(taskId, changes);
                return { id: task.id, status: task.status, subject: task.subject };
            },
        }));
        this.registerTool(buildTool({
            name: "task_list",
            description: "List all tracked tasks, optionally filtered by status or owner.",
            searchHint: "list all tasks",
            parameters: z.object({
                status: z.string().optional().describe("Filter by status."),
                owner: z.string().optional().describe("Filter by owner agent ID."),
            }),
            isReadOnly: true,
            execute: async (bash, filter) => {
                return bash.services.taskManager.list({
                    status: filter.status,
                    owner: filter.owner,
                });
            },
        }));
        this.registerTool(buildTool({
            name: "task_get",
            description: "Get full details of a specific task by ID.",
            searchHint: "get task details",
            parameters: z.object({
                taskId: z.string().describe("The task ID."),
            }),
            isReadOnly: true,
            execute: async (bash, { taskId }) => {
                const task = bash.services.taskManager.get(taskId);
                if (!task)
                    return `Task ${taskId} not found.`;
                return task;
            },
        }));
        this.registerTool(buildTool({
            name: "task_stop",
            description: "Stop (fail) a running task.",
            searchHint: "stop a running task",
            parameters: z.object({
                taskId: z.string().describe("The task ID to stop."),
            }),
            execute: async (bash, { taskId }) => {
                const task = bash.services.taskManager.update(taskId, {
                    status: "failed",
                });
                return { id: task.id, status: task.status };
            },
        }));
        // --- Phase 1: Multi-Agent Swarm Tools ---
        this.registerTool(buildTool({
            name: "team_create",
            description: "Create a new agent team for coordinated multi-agent work.",
            searchHint: "create multi-agent team",
            parameters: z.object({
                name: z.string().describe("Team name."),
                description: z.string().optional().describe("Team purpose."),
            }),
            execute: async (bash, input) => {
                const team = bash.services.teamManager.createTeam(input);
                return { id: team.id, name: team.name };
            },
        }));
        this.registerTool(buildTool({
            name: "team_delete",
            description: "Delete an agent team.",
            searchHint: "delete agent team",
            parameters: z.object({
                name: z.string().describe("Team name or ID."),
            }),
            execute: async (bash, { name }) => {
                const deleted = bash.services.teamManager.deleteTeam(name);
                return deleted ? `Deleted team ${name}.` : `Team ${name} not found.`;
            },
        }));
        this.registerTool(buildTool({
            name: "send_message",
            description: "Send a message from one agent to another.",
            searchHint: "inter-agent messaging",
            parameters: z.object({
                from: z.string().describe("Sender agent ID."),
                to: z.string().describe("Recipient agent ID."),
                content: z.string().describe("Message content."),
            }),
            execute: async (bash, input) => {
                const msg = bash.services.teamManager.sendMessage(input.from, input.to, input.content);
                return { id: msg.id, from: msg.from, to: msg.to };
            },
        }));
        this.registerTool(buildTool({
            name: "agent_memory_read",
            description: "Read a memory entry for an agent type.",
            searchHint: "read agent memory",
            parameters: z.object({
                agentType: z.string().describe("Agent type identifier."),
                scope: z.string().describe("Memory scope: user, project, or local."),
                key: z.string().describe("Memory key."),
            }),
            isReadOnly: true,
            execute: async (bash, input) => {
                const entry = bash.services.agentMemory.read(input.agentType, input.scope, input.key);
                return (entry ||
                    `No memory found for ${input.agentType}:${input.scope}:${input.key}`);
            },
        }));
        this.registerTool(buildTool({
            name: "agent_memory_write",
            description: "Write a memory entry for an agent type.",
            searchHint: "write agent memory",
            parameters: z.object({
                agentType: z.string().describe("Agent type identifier."),
                scope: z.string().describe("Memory scope: user, project, or local."),
                key: z.string().describe("Memory key."),
                value: z.string().describe("Memory value."),
            }),
            execute: async (bash, input) => {
                const entry = bash.services.agentMemory.write(input.agentType, input.scope, input.key, input.value);
                return {
                    key: entry.key,
                    scope: entry.scope,
                    agentType: entry.agentType,
                };
            },
        }));
        // --- Phase 2: Intelligence Tools ---
        this.registerTool(buildTool({
            name: "glob_files",
            description: "Fast glob pattern matching over the filesystem. Returns matching file paths sorted by name or mtime.",
            searchHint: "find files by glob pattern",
            parameters: z.object({
                pattern: z
                    .string()
                    .describe('Glob pattern (e.g., "**/*.ts", "src/**/*.{ts,tsx}").'),
                path: z
                    .string()
                    .optional()
                    .describe("Root directory (default: cwd)."),
                sort: z.string().optional().describe('"alpha" (default) or "mtime".'),
                limit: z.number().optional().describe("Max results (default: 1000)."),
            }),
            isReadOnly: true,
            execute: async (bash, input) => {
                const args = [JSON.stringify(input.pattern)];
                if (input.path)
                    args.push("--path", JSON.stringify(input.path));
                if (input.sort)
                    args.push("--sort", input.sort);
                if (input.limit)
                    args.push("--limit", String(input.limit));
                const result = await bash.exec(`ag-glob ${args.join(" ")}`);
                return result.stdout.trim().split("\n").filter(Boolean);
            },
        }));
        this.registerTool(buildTool({
            name: "git_track",
            description: "Record and classify a git operation. Returns classification (safe/mutating/destructive) and audit entry.",
            searchHint: "track git operation for safety audit",
            parameters: z.object({
                command: z
                    .string()
                    .describe("The git command to classify and record."),
            }),
            execute: async (bash, { command }) => {
                const op = bash.services.gitTracker.recordOperation(command);
                return {
                    id: op.id,
                    classification: op.classification,
                    command: op.command,
                };
            },
        }));
        this.registerTool(buildTool({
            name: "git_audit_log",
            description: "Get the git operations audit log, optionally filtered to destructive operations only.",
            searchHint: "view git audit log",
            parameters: z.object({
                destructiveOnly: z
                    .boolean()
                    .optional()
                    .describe("If true, return only destructive operations."),
            }),
            isReadOnly: true,
            execute: async (bash, { destructiveOnly }) => {
                if (destructiveOnly)
                    return bash.services.gitTracker.getDestructiveOps();
                return bash.services.gitTracker.getLog();
            },
        }));
        this.registerTool(buildTool({
            name: "check_destructive",
            description: "Check if a command contains destructive patterns (rm -rf, DROP TABLE, git reset --hard, etc.). Returns warning or null.",
            searchHint: "check command for destructive patterns",
            parameters: z.object({
                command: z
                    .string()
                    .describe("The command to analyze for destructive patterns."),
            }),
            isReadOnly: true,
            execute: async (_bash, { command }) => {
                const warning = detectDestructiveCommand(command);
                return (warning || {
                    safe: true,
                    message: "No destructive patterns detected.",
                });
            },
        }));
        // --- Phase 3: Automation Tools ---
        this.registerTool(buildTool({
            name: "cron_create",
            description: "Schedule a recurring or one-shot prompt using a cron expression.",
            searchHint: "schedule recurring cron job",
            parameters: z.object({
                cron: z
                    .string()
                    .describe('5-field cron expression (e.g., "*/5 * * * *").'),
                prompt: z
                    .string()
                    .describe("The prompt/command to run at each fire time."),
                recurring: z
                    .boolean()
                    .optional()
                    .describe("True = repeating (default), false = one-shot."),
                durable: z
                    .boolean()
                    .optional()
                    .describe("True = persisted, false = session-only (default)."),
            }),
            execute: async (bash, input) => {
                const job = bash.services.cronScheduler.createJob(input);
                return { id: job.id, cron: job.cron, recurring: job.recurring };
            },
        }));
        this.registerTool(buildTool({
            name: "cron_delete",
            description: "Delete a scheduled cron job.",
            searchHint: "delete cron job",
            parameters: z.object({
                id: z.string().describe("The cron job ID to delete."),
            }),
            execute: async (bash, { id }) => {
                const deleted = bash.services.cronScheduler.deleteJob(id);
                return deleted ? `Deleted cron job ${id}.` : `Job ${id} not found.`;
            },
        }));
        this.registerTool(buildTool({
            name: "cron_list",
            description: "List all scheduled cron jobs.",
            searchHint: "list cron jobs",
            parameters: z.object(EMPTY_SHAPE),
            isReadOnly: true,
            execute: async (bash) => {
                return bash.services.cronScheduler.listJobs();
            },
        }));
        this.registerTool(buildTool({
            name: "enter_worktree",
            description: "Create and enter an isolated virtual worktree for independent development.",
            searchHint: "create worktree for isolation",
            parameters: z.object({
                name: z.string().describe("Worktree name."),
                branch: z
                    .string()
                    .optional()
                    .describe("Branch name (default: worktree/<name>)."),
            }),
            execute: async (bash, input) => {
                const cwd = bash.getCwd();
                let wt = bash.services.worktreeManager.getWorktree(input.name);
                if (!wt) {
                    wt = bash.services.worktreeManager.createWorktree({
                        name: input.name,
                        branch: input.branch,
                        originalCwd: cwd,
                    });
                }
                bash.services.worktreeManager.enterWorktree(input.name);
                return { id: wt.id, path: wt.path, branch: wt.branch };
            },
        }));
        this.registerTool(buildTool({
            name: "exit_worktree",
            description: "Exit the active worktree and restore the original working directory.",
            searchHint: "exit worktree isolation",
            parameters: z.object(EMPTY_SHAPE),
            execute: async (bash) => {
                const result = bash.services.worktreeManager.exitWorktree();
                if (!result)
                    return "No active worktree.";
                return { restored: result.originalCwd };
            },
        }));
    }
    // ─── Public API ───────────────────────────────────────────────────────────────
    registerTool(tool) {
        this.tools.set(tool.name, tool);
    }
    getTools() {
        return Array.from(this.tools.values()).sort((a, b) => a.name.localeCompare(b.name));
    }
    /**
     * Performs a semantic search over the registered tools using ToolSearchEngine.
     */
    async searchTools(query, limit = 3) {
        const engine = new ToolSearchEngine();
        return engine.search(this.getTools(), query, limit).map((r) => r.tool);
    }
    getTool(name) {
        return this.tools.get(name);
    }
    registerMcpTools(connectionId, tools) {
        for (const tool of tools) {
            this.registerTool(buildTool({
                name: `mcp:${connectionId}:${tool.name}`,
                description: tool.description || `MCP tool from ${connectionId}`,
                parameters: jsonSchemaToZod(tool.inputSchema),
                execute: async (bash, args) => {
                    return await bash.services.mcpClient.callTool(connectionId, tool.name, args, bash);
                },
            }));
        }
    }
    getAgenticTools(bash) {
        const result = Object.create(null);
        for (const tool of this.getTools()) {
            result[tool.name] = {
                description: tool.description,
                inputSchema: zodToJsonSchema(tool.parameters),
                effort: tool.effort,
                composeHooks: tool.composeHooks,
                execute: (args) => this.callTool(bash, tool.name, args),
            };
        }
        return result;
    }
    unregisterTool(name) {
        this.tools.delete(name);
    }
    /**
     * Orchestrates the tool execution lifecycle:
     * validation -> permissions -> execution.
     */
    async callTool(bash, toolName, args) {
        return executeTool(bash, toolName, args, this.tools);
    }
}
