import z from "zod";
import { SKILL_PATH_PREFIXES, SkillLayer } from "../../skills/types.js";
import { createToolFactory, ToolCategory, ToolExecutionError, } from "../tools2/types.js";
const MAX_LIMIT_READ_FILES = 25000;
const ERROR_PREFIX = "Failed to read file:";
const fileToolConfig = {
    extractDisplayName: (parameters) => {
        const params = parameters;
        if (params.filePath && typeof params.filePath === "string") {
            const fileName = params.filePath.split("/").pop() || params.filePath;
            return `:${fileName}`;
        }
        return "";
    },
    extractMetadata: (parameters) => {
        const params = parameters;
        return {
            filePath: params.filePath,
            ranges: params.ranges,
            rangeCount: Array.isArray(params.ranges) ? params.ranges.length : 0,
        };
    },
};
/**
 * Read a file using AppShell.
 * Handles both regular files and virtual paths (APIs).
 *
 * @param filePath - Relative path to the file
 * @param services - Services containing AppShell instance
 * @returns File content as string
 */
export const readFile = async (filePath, services) => {
    return services.appShell.readFile(filePath);
};
export const buildReadFileToolFactory = createToolFactory("build_readFile", ({ services }) => {
    return {
        category: ToolCategory.FILE,
        description: `
      Read content from a file in the application. You can read the entire file or specify line ranges to read specific portions.
      - If the file is too large (>25k characters), you must read it in parts by specifying line ranges.
      - You can specify multiple ranges to read different sections of the file.
      - Only 25k characters can be read at once across all ranges.
    `,
        inputSchema: z.object({
            filePath: z.string().describe("Path to the file to read"),
            ranges: z
                .array(z.object({
                start: z
                    .number()
                    .int()
                    .positive()
                    .describe("Starting line number (1-indexed)"),
                end: z
                    .number()
                    .int()
                    .positive()
                    .describe("Ending line number (1-indexed, inclusive)"),
            }))
                .optional()
                .describe("Optional line ranges to read. If not provided, attempts to read the entire file."),
        }),
        execute: async ({ filePath, ranges }) => {
            let fileContent;
            let lines;
            let totalLines;
            try {
                fileContent = await services.appShell.readFile(filePath);
                lines = fileContent.split("\n");
                totalLines = lines.length;
                // If no ranges specified, check if entire file is too large
                if (!ranges || ranges.length === 0) {
                    // Read entire file
                    const output = `<file lines="${totalLines}">\n<content range="1-${totalLines}" lines="${totalLines}">\n${fileContent}\n</content>\n</file>`;
                    if (output.length > MAX_LIMIT_READ_FILES) {
                        throw new ToolExecutionError(`The file is too large to read all at once. Please read it in parts by specifying line ranges.`, { toolName: "", input: {}, context: {}, severity: "warn" });
                    }
                    return output;
                }
                // Extract specified ranges
                const contentParts = [];
                for (const range of ranges) {
                    const startLine = Math.max(1, range.start) - 1; // Convert to 0-indexed
                    const endLine = Math.min(totalLines, range.end); // Keep 1-indexed for display
                    const actualStartLine = Math.max(0, startLine);
                    const actualEndLine = Math.min(totalLines, endLine);
                    if (actualStartLine >= actualEndLine ||
                        actualStartLine >= lines.length) {
                        continue; // Skip invalid ranges
                    }
                    const selectedLines = lines.slice(actualStartLine, actualEndLine);
                    const rangeContent = selectedLines.join("\n");
                    const rangeLines = selectedLines.length;
                    contentParts.push({
                        range: `${range.start}-${range.end}`,
                        lines: rangeLines,
                        content: rangeContent,
                    });
                }
                if (contentParts.length === 0) {
                    throw new ToolExecutionError("No valid line ranges were provided or all ranges were invalid.", { toolName: "", input: {}, context: {}, severity: "warn" });
                }
                // Build XML output
                const contentTags = contentParts
                    .map((part) => `<content range="${part.range}" lines="${part.lines}">\n${part.content}\n</content>`)
                    .join("\n");
                const output = `<file lines="${totalLines}">\n${contentTags}\n</file>`;
                // Check total output size
                if (output.length > MAX_LIMIT_READ_FILES) {
                    throw new ToolExecutionError(`The total content across all specified ranges exceeds the 25k character limit. Please reduce the number of ranges or the size of each range.`, { toolName: "", input: {}, context: {}, severity: "warn" });
                }
                return output;
            }
            catch (error) {
                if (!(error instanceof Error)) {
                    throw new Error(`${ERROR_PREFIX} Unknown error`);
                }
                throw new ToolExecutionError(`${ERROR_PREFIX} ${error.message}`, {
                    toolName: "",
                    input: {},
                    context: {},
                    severity: "warn",
                });
            }
        },
        readOnly: true,
        getSummary: (input, output) => {
            // Check if this is a system skill file (protected from compaction)
            const isSystemSkill = input.filePath.startsWith(SKILL_PATH_PREFIXES[SkillLayer.SYSTEM]);
            // Full file read
            if (!input.ranges || input.ranges.length === 0) {
                return { output: "full read", protected: isSystemSkill };
            }
            // Extract ranges from output XML content tags
            const rangeMatches = typeof output === "string"
                ? [...output.matchAll(/<content range="(\d+-\d+)"/g)]
                : [];
            const ranges = rangeMatches.map((m) => m[1]);
            if (ranges.length === 0) {
                return { output: "full read", protected: isSystemSkill };
            }
            if (ranges.length === 1) {
                return { output: `lines ${ranges[0]}`, protected: isSystemSkill };
            }
            return {
                output: `lines ${ranges.join(", ")}`,
                protected: isSystemSkill,
            };
        },
    };
});
export { fileToolConfig };
//# sourceMappingURL=build-read-file.js.map