"use strict";
// =============================================================================
// Pure Agent - File Tool Registration
// Ported from Hermes tools/file_tools.py
// =============================================================================
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.clearFileOpsCache = clearFileOpsCache;
exports.registerFileTools = registerFileTools;
const path = __importStar(require("path"));
const tool_registry_1 = require("../tool-registry");
const result_types_1 = require("../result-types");
const file_operations_1 = require("./file-operations");
const constants_1 = require("../constants");
const file_safety_1 = require("./file-safety");
// ---------------------------------------------------------------------------
// Per-session file operations cache
// ---------------------------------------------------------------------------
const _fileOpsCache = new Map();
function getFileOps(context) {
    let ops = _fileOpsCache.get(context.sessionId);
    if (!ops) {
        ops = new file_operations_1.NativeFileOperations(context.workingDir);
        _fileOpsCache.set(context.sessionId, ops);
    }
    return ops;
}
function clearFileOpsCache(sessionId) {
    if (sessionId) {
        _fileOpsCache.delete(sessionId);
    }
    else {
        _fileOpsCache.clear();
    }
}
// ---------------------------------------------------------------------------
// read_file tool
// ---------------------------------------------------------------------------
async function readFileHandler(args, context) {
    const { path: filepath, offset = 1, limit = constants_1.DEFAULT_READ_LIMIT } = args;
    if (!filepath)
        return (0, result_types_1.toolError)('Missing required parameter: path');
    // Path traversal check
    if ((0, file_safety_1.hasPathTraversal)(filepath)) {
        return (0, result_types_1.toolError)('Path traversal detected: ".." is not allowed in file paths');
    }
    // Dedup check
    const dedup = (0, file_safety_1.checkFileDedup)(filepath, offset, limit);
    if (dedup.blocked) {
        return (0, result_types_1.toolResult)({ warning: dedup.warning, dedup: true, content: '' });
    }
    // Binary extension warning
    if ((0, file_safety_1.isBinaryExtension)(filepath)) {
        return (0, result_types_1.toolResult)({
            warning: `File "${path.basename(filepath)}" appears to be a binary file. Reading binary files as text may produce garbled output.`,
            is_binary: true,
            content: `[Binary file: ${filepath}]`,
        });
    }
    const ops = getFileOps(context);
    const result = ops.readFile(filepath, offset, Math.min(limit, 2000));
    if (result.error)
        return (0, result_types_1.toolError)(result.error);
    const response = {
        content: result.content,
        total_lines: result.totalLines,
        file_size: result.fileSize,
        truncated: result.truncated,
        hint: result.hint,
        is_binary: result.isBinary,
        is_image: result.isImage,
        base64_content: result.base64Content,
        mime_type: result.mimeType,
    };
    // Include dedup warning if applicable
    if (dedup.warning)
        response.dedup_warning = dedup.warning;
    return (0, result_types_1.toolResult)(response);
}
// ---------------------------------------------------------------------------
// write_file tool
// ---------------------------------------------------------------------------
async function writeFileHandler(args, context) {
    const { path: filepath, content } = args;
    if (!filepath)
        return (0, result_types_1.toolError)('Missing required parameter: path');
    if (content === undefined || content === null)
        return (0, result_types_1.toolError)('Missing required parameter: content');
    // Path traversal check
    if ((0, file_safety_1.hasPathTraversal)(filepath)) {
        return (0, result_types_1.toolError)('Path traversal detected: ".." is not allowed in file paths');
    }
    // Write denied check
    if ((0, file_safety_1.isWriteDenied)(filepath)) {
        return (0, result_types_1.toolError)(`Write denied: ${filepath} is a protected system path`);
    }
    // Credential file warning
    if ((0, file_safety_1.isCredentialFile)(filepath)) {
        return (0, result_types_1.toolError)(`Refusing to write to credential/sensitive file: ${filepath}. Use the terminal tool if you really need to modify this file.`);
    }
    const ops = getFileOps(context);
    const result = ops.writeFile(filepath, content);
    if (result.error)
        return (0, result_types_1.toolError)(result.error);
    // Inline lint after write
    const lint = (0, file_safety_1.lintFileContent)(filepath, content);
    const response = {
        bytes_written: result.bytesWritten,
        dirs_created: result.dirsCreated,
    };
    if (result.warning)
        response.warning = result.warning;
    if (!lint.success)
        response.lint_warnings = lint.errors;
    return (0, result_types_1.toolResult)(response);
}
// ---------------------------------------------------------------------------
// patch tool (replace mode)
// ---------------------------------------------------------------------------
async function patchHandler(args, context) {
    const { path: filepath, old_string, new_string, replace_all = false, } = args;
    if (!filepath)
        return (0, result_types_1.toolError)('Missing required parameter: path');
    if (old_string === undefined)
        return (0, result_types_1.toolError)('Missing required parameter: old_string');
    if (new_string === undefined)
        return (0, result_types_1.toolError)('Missing required parameter: new_string');
    // Security checks
    if ((0, file_safety_1.hasPathTraversal)(filepath)) {
        return (0, result_types_1.toolError)('Path traversal detected');
    }
    if ((0, file_safety_1.isWriteDenied)(filepath)) {
        return (0, result_types_1.toolError)(`Write denied: ${filepath} is a protected system path`);
    }
    if ((0, file_safety_1.isCredentialFile)(filepath)) {
        return (0, result_types_1.toolError)(`Refusing to patch credential/sensitive file: ${filepath}`);
    }
    const ops = getFileOps(context);
    const result = ops.patchReplace(filepath, old_string, new_string, replace_all);
    if (!result.success)
        return (0, result_types_1.toolError)(result.error || 'Patch failed');
    return (0, result_types_1.toolResult)({
        success: true,
        diff: result.diff,
        files_modified: result.filesModified,
    });
}
// ---------------------------------------------------------------------------
// search_files tool
// ---------------------------------------------------------------------------
async function searchHandler(args, context) {
    const { pattern, path: searchPath = '.', file_glob, limit = constants_1.DEFAULT_SEARCH_LIMIT, offset = 0, } = args;
    if (!pattern)
        return (0, result_types_1.toolError)('Missing required parameter: pattern');
    const ops = getFileOps(context);
    const result = ops.search(pattern, searchPath, file_glob, limit, offset);
    if (result.error)
        return (0, result_types_1.toolError)(result.error);
    return (0, result_types_1.toolResult)({
        matches: result.matches.map(m => ({
            path: m.path,
            line_number: m.lineNumber,
            content: m.content,
        })),
        total_count: result.totalCount,
        truncated: result.truncated,
    });
}
// ---------------------------------------------------------------------------
// Registration
// ---------------------------------------------------------------------------
function registerFileTools() {
    tool_registry_1.registry.register({
        name: 'read_file',
        toolset: 'file',
        schema: {
            description: 'Read file contents with line numbers (cat -n style). Supports pagination with offset and limit. Can also read directories to list their contents.',
            parameters: {
                type: 'object',
                properties: {
                    path: {
                        type: 'string',
                        description: 'Path to the file or directory to read',
                    },
                    offset: {
                        type: 'number',
                        description: 'Starting line number (1-based). Default: 1',
                        default: 1,
                    },
                    limit: {
                        type: 'number',
                        description: 'Maximum number of lines to read. Default: 500',
                        default: 500,
                    },
                },
                required: ['path'],
            },
        },
        handler: readFileHandler,
        isAsync: true,
        description: 'Read file contents',
        emoji: '📄',
        maxResultSizeChars: constants_1.DEFAULT_MAX_READ_CHARS,
    });
    tool_registry_1.registry.register({
        name: 'write_file',
        toolset: 'file',
        schema: {
            description: 'Write content to a file. Creates parent directories if needed. Atomically writes via temp file to prevent corruption.',
            parameters: {
                type: 'object',
                properties: {
                    path: {
                        type: 'string',
                        description: 'Path to the file to write',
                    },
                    content: {
                        type: 'string',
                        description: 'Content to write to the file',
                    },
                },
                required: ['path', 'content'],
            },
        },
        handler: writeFileHandler,
        isAsync: true,
        description: 'Write content to a file',
        emoji: '✏️',
    });
    tool_registry_1.registry.register({
        name: 'patch',
        toolset: 'file',
        schema: {
            description: 'Replace text in a file using string matching. Finds old_string and replaces with new_string. If multiple matches exist, set replace_all=true or provide a more specific match.',
            parameters: {
                type: 'object',
                properties: {
                    path: {
                        type: 'string',
                        description: 'Path to the file to patch',
                    },
                    old_string: {
                        type: 'string',
                        description: 'The exact string to find and replace',
                    },
                    new_string: {
                        type: 'string',
                        description: 'The replacement string',
                    },
                    replace_all: {
                        type: 'boolean',
                        description: 'Replace all occurrences (default: false)',
                        default: false,
                    },
                },
                required: ['path', 'old_string', 'new_string'],
            },
        },
        handler: patchHandler,
        isAsync: true,
        description: 'Replace text in a file',
        emoji: '🔧',
    });
    tool_registry_1.registry.register({
        name: 'search_files',
        toolset: 'file',
        schema: {
            description: 'Search for patterns in files. Uses ripgrep when available, falls back to Node.js implementation. Supports file glob filtering.',
            parameters: {
                type: 'object',
                properties: {
                    pattern: {
                        type: 'string',
                        description: 'Regex pattern to search for',
                    },
                    path: {
                        type: 'string',
                        description: 'Directory to search in. Default: current directory',
                        default: '.',
                    },
                    file_glob: {
                        type: 'string',
                        description: 'File glob filter (e.g., "*.ts", "*.js")',
                    },
                    limit: {
                        type: 'number',
                        description: 'Maximum number of results. Default: 50',
                        default: 50,
                    },
                    offset: {
                        type: 'number',
                        description: 'Skip first N results. Default: 0',
                        default: 0,
                    },
                },
                required: ['pattern'],
            },
        },
        handler: searchHandler,
        isAsync: true,
        description: 'Search for patterns in files',
        emoji: '🔍',
        maxResultSizeChars: constants_1.DEFAULT_MAX_READ_CHARS,
    });
}
// Auto-register when module is loaded
registerFileTools();
//# sourceMappingURL=file-tools.js.map