"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.tools = exports.batchPluginAgentTools = exports.batchNocodeLayerTools = void 0;
exports.narrowToolGroup = narrowToolGroup;
exports.readAllPluginFilesByPluginName = readAllPluginFilesByPluginName;
const zod_1 = require("zod");
const path_1 = __importDefault(require("path"));
const axios_1 = __importDefault(require("axios"));
const utils_1 = require("../../../utils");
const graphqlschema_1 = require("./toolImplementations/graphqlschema");
const renderFileTree_1 = require("./toolImplementations/renderFileTree");
const fileOps_1 = require("./toolImplementations/fileOps");
const iconList_1 = require("./toolImplementations/iconList");
const cliconfig_1 = require("../../../database/cliconfig");
const promises_1 = require("fs/promises");
const js_sha256_1 = require("js-sha256");
const compiler_1 = require("../../compiler");
const chalk_1 = __importDefault(require("chalk"));
const utils_2 = require("./utils");
const supabase_1 = require("./supabase");
// import { applyPatchToFile } from "./filePatch";
const promises_2 = require("fs/promises");
const toolGroups = zod_1.z.enum([
    "graphqlSchema",
    "projectCodeRead",
    "projectCodeWrite",
    "codeExecution",
    "dashboardRead",
    "dashboardWrite",
    "promptPluginAgent",
    "projectCodeReadForPlanner",
    "supabaseTools",
    "supabaseReadTools",
    "batchedTool",
    "batchedPluginAgentTools"
]);
function narrowToolGroup(groupName) {
    return toolGroups.options.includes(groupName);
}
function handleFrontendTools(inputVars) {
    return __awaiter(this, void 0, void 0, function* () {
        return {
            content: [
                {
                    type: "text",
                    text: "user_response_required"
                },
                {
                    type: "text",
                    text: JSON.stringify(inputVars)
                }
            ]
        };
    });
}
const exploreSchema = (0, utils_2.defineToolSpec)({
    name: "explore_shopify_graphql_schema",
    description: "Enumerate the fields for an object in the graphql schema inside the query field. Mutations are not available throught this tool. To get the entire set of top level fields in the queries section pass path as a blank string i.e. ''",
    inputSchema: {
        path: zod_1.z.string().describe("A dot separated string specifying the field in the schema whose fields are to be enumerated. If a blank string is passed the top level of the schema will be enumerated."),
        includeDescriptions: zod_1.z.boolean().describe("Include the descriptions for the fields in the result"),
        includeArgumentsInfo: zod_1.z.boolean().describe("Include all available information about the arguments applicable to the fields being returned. Set this to true sparingly to save tokens.")
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            const result = yield (0, graphqlschema_1.enumerateFields)(inputVars.path, inputVars.includeDescriptions, inputVars.includeArgumentsInfo);
            return {
                content: [{
                        type: "text",
                        text: JSON.stringify(result)
                    }]
            };
        });
    }
});
const getHunkFromFile = (0, utils_2.defineToolSpec)({
    name: "get_lines_from_file",
    description: `Returns lines in a set of inclusive ranges from the given file.`,
    inputSchema: {
        file_path: zod_1.z.string().describe("Path to the file relative to the plugin's directory, from which the lines should be read."),
        line_ranges: zod_1.z.array(zod_1.z.object({
            first_line: zod_1.z.number().describe("The starting line number"),
            last_line: zod_1.z.number().describe("The ending line number")
        }).describe("A tuple with the starting and ending line numbers")).describe("Line ranges to get from the file"),
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            if (!((_a = inputVars.apptile_context) === null || _a === void 0 ? void 0 : _a.plugins_dir)) {
                return {
                    content: [{
                            type: "text",
                            text: "Error: apptile context did not have the plugins directory! Something went wrong."
                        }]
                };
            }
            else {
                const pluginName = inputVars.apptile_context.plugin_root.substring(inputVars.apptile_context.plugins_dir.length + 1);
                const fullPath = path_1.default.resolve(inputVars.apptile_context.plugins_dir, pluginName, inputVars.file_path);
                const fullContent = yield (0, promises_2.readFile)(fullPath, "utf8");
                const lines = fullContent.split(/\r?\n/);
                let hunks = "";
                inputVars.line_ranges.forEach(range => {
                    const text = lines.slice(range.first_line - 1, range.last_line).join("\n");
                    hunks += `
---------------------------------
*first_line*: ${range.first_line} 
*last_line*: ${range.last_line}
---------------------------------
\`\`\`
${(0, utils_2.prefixWithLineNumbers)(text, range.first_line)}
\`\`\`
`;
                });
                return {
                    content: [{
                            type: "text",
                            text: hunks
                        }]
                };
            }
        });
    }
});
const readPluginFiles = (0, utils_2.defineToolSpec)({
    name: "read_plugin_files",
    description: `Recursively read contents of all files within a <strong>directory</strong> for a plugin. 
  The file contents have line numbers prepended to each line for your reference, but 
  they are not part of the file. Each line is prefixed with the line number in the format: 
  <line_number>:<space>.`,
    inputSchema: {
        root_path: zod_1.z.string().describe("The path of the directory whose files should be read, relative to the plugin's root directory. This SHOULD NOT be a file path, it should always be a directory path.").optional().default("source"),
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            utils_2.logger.info("Running tool read_plugin_files: " + JSON.stringify(inputVars, null, 2));
            if (!inputVars.root_path) {
                inputVars.root_path = "source";
            }
            if (inputVars.apptile_context) {
                const files = yield (0, renderFileTree_1.getFileContents)(path_1.default.resolve(inputVars.apptile_context.plugin_root, inputVars.root_path), []);
                if (files.length === 0) {
                    return {
                        content: [{
                                type: "text",
                                text: "No files found!"
                            }]
                    };
                }
                else {
                    const response = {
                        content: [{
                                type: "text",
                                text: ""
                            }]
                    };
                    let concatenatedFiles = "";
                    for (let i = 0; i < files.length; ++i) {
                        const filePath = files[i].path;
                        const fileContents = files[i].content;
                        const contentWithLineNums = (0, utils_2.prefixWithLineNumbers)(fileContents);
                        const pluginName = inputVars.apptile_context.plugin_root.substring(inputVars.apptile_context.plugins_dir.length + 1);
                        const truncatedFilePath = filePath.substring(filePath.indexOf(pluginName) + pluginName.length + 1);
                        concatenatedFiles += `
<file path="${truncatedFilePath}">
${contentWithLineNums}
</file>
`;
                    }
                    utils_2.logger.info("Returning response: " + JSON.stringify(response));
                    response.content[0].text = concatenatedFiles;
                    return response;
                }
            }
            else {
                throw new Error("Error: Apptile context not provided!");
            }
        });
    }
});
function readAllPluginFilesByPluginName(pluginsDir, pluginName) {
    return __awaiter(this, void 0, void 0, function* () {
        const rootPath = path_1.default.resolve(pluginsDir, pluginName);
        const files = yield (0, renderFileTree_1.getFileContents)(rootPath, []);
        if (files.length === 0) {
            utils_2.logger.error("No files were found for: " + rootPath);
            return ({
                content: [{
                        type: "text",
                        text: "Error: No files found!"
                    }]
            });
        }
        else {
            const response = {
                content: []
            };
            let fullResult = '';
            for (let i = 0; i < files.length; ++i) {
                const filePath = files[i].path;
                const fileContents = files[i].content;
                const truncatedFilePath = filePath.substring(filePath.indexOf(pluginName));
                const contentWithLineNums = (0, utils_2.prefixWithLineNumbers)(fileContents);
                // fullResult += 'File path: ' + filePath + '\nFile contents: \n```\n' + contentWithLineNums + '\n```\n\n';
                fullResult += `
<file path="${truncatedFilePath}">
${contentWithLineNums}
</file>`;
            }
            response.content.push({
                type: "text",
                text: fullResult
            });
            utils_2.logger.info("Returning response: " + JSON.stringify(response));
            return response;
        }
    });
}
const readPluginFilesByPluginName = (0, utils_2.defineToolSpec)({
    name: "read_plugin_files_by_plugin_name",
    description: "Recursively read contents of all files within the root directory of a plugin. Each line in the file is prefixed with its line number before returning for your reference, but they are not part of the file contents. These line numbers are provided to help with patch generation. Each line is prefixed with the line number in the format: <line_number>:<space>.",
    inputSchema: {
        plugin_name: zod_1.z.string().describe("name of the plugin whose files should be read"),
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            utils_2.logger.info("Running tool read_plugin_files_by_plugin_name: " + JSON.stringify(inputVars, null, 2));
            if (inputVars.apptile_context) {
                return readAllPluginFilesByPluginName(inputVars.apptile_context.plugins_dir, inputVars.plugin_name);
            }
            else {
                utils_2.logger.error("Failed to provide tool response as no context was found");
                throw new Error("Apptile context not provided!");
            }
        });
    }
});
const writePluginFiles = (0, utils_2.defineToolSpec)({
    name: "apply_file_ops",
    description: "Overwrite/Delete/Move a file inside a plugin's directory",
    inputSchema: {
        file_ops: zod_1.z.array(fileOps_1.FileOperationSchema),
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            utils_2.logger.info("Running tool apply_file_ops: " + ((_a = inputVars.apptile_context) === null || _a === void 0 ? void 0 : _a.plugin_root));
            if (!((_b = inputVars.apptile_context) === null || _b === void 0 ? void 0 : _b.plugin_root)) {
                utils_2.logger.error("No apptile context was provided. Bailing.");
                throw new Error("Apptile context was not provided!");
            }
            let isLLMTryingToModifyWidgetJSX = false;
            let file_paths_copy = [];
            let was_subdirectory_fix_applied = false;
            for (let i = 0; i < inputVars.file_ops.length; ++i) {
                let filePath = inputVars.file_ops[i].path;
                if (!filePath.startsWith("source")) {
                    utils_2.logger.info("Looks like the LLM has fucked up AGAIN!!! It is trying to make recursive directories. Lets try to trim it down!");
                    // Ensure there is only one path part before source, otherwise we bail
                    const parts = filePath.split("/");
                    if (parts.length > 1 && parts[1] === "source") {
                        filePath = parts.slice(1).join("/");
                        utils_2.logger.info("We have updated the filepaths to not create an incorrect subfolder");
                        was_subdirectory_fix_applied = true;
                    }
                }
                file_paths_copy.push(filePath);
                inputVars.file_ops[i].path = path_1.default.resolve(inputVars.apptile_context.plugin_root, filePath);
                if (inputVars.file_ops[i].path.endsWith("widget.jsx")) {
                    isLLMTryingToModifyWidgetJSX = true;
                    break;
                }
            }
            utils_2.logger.info("Finalized fileops: " + JSON.stringify(file_paths_copy));
            if (isLLMTryingToModifyWidgetJSX) {
                utils_2.logger.error("LLM tried to write to readonly file! Returning error");
                return {
                    content: [{
                            type: "text",
                            text: "Error: You cannot modify widget.jsx! This is a readonly file \
          and your changes will be overwritten during the build. You should treat \
          component.jsx as your entry point. As long as you provide the correct\
          exports from there everything should work."
                        }]
                };
            }
            else {
                try {
                    yield (0, fileOps_1.applyFileOps)(inputVars.file_ops);
                }
                catch (err) {
                    utils_2.logger.error("Error when applying fileops" + (err === null || err === void 0 ? void 0 : err.toString()));
                }
                const directoryStructure = (0, renderFileTree_1.makeAsciiDirectoryStructure)(inputVars.apptile_context.plugin_root, file_paths_copy);
                let toolResult = `file_ops completed successfully! These files are now written:
${directoryStructure}.`;
                if (was_subdirectory_fix_applied) {
                    toolResult += `\n Note that you tried to pass in the file path including the plugin's own directory. The tool has made sure that the files were written relative to the plugin's directory but no duplicate subdirectories of the same name as the plugin were created.`;
                }
                utils_2.logger.info(toolResult);
                return {
                    content: [{
                            type: "text",
                            text: toolResult
                        }]
                };
            }
        });
    }
});
const lookupAvailableIcons = (0, utils_2.defineToolSpec)({
    name: "lookup_available_icons",
    description: "Search the list of available icons in the platform. The value returned will contain the values for iconType and name.",
    inputSchema: {
        search_strings: zod_1.z.array(zod_1.z.string()).describe("Perform a fuzzy search for each of the supplied terms to look for icons."),
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            utils_2.logger.info("Running tool lookup_available_icons");
            const icons = (0, iconList_1.searchIcons)(inputVars.search_strings);
            const toolResult = JSON.stringify(icons, null, 2);
            utils_2.logger.info(toolResult);
            return {
                content: [{
                        type: "text",
                        text: toolResult
                    }]
            };
        });
    }
});
const lookupImagesFromPexels = (0, utils_2.defineToolSpec)({
    name: "lookup_images_from_pexels",
    description: "This tool can be used to search for nice looking images along with good descriptions about what is in the image. When using these assets always attribute the photographer on the image according to pexel guidelines. You should use the medium or small size images only. The bigger images make the app slow and should only be used in rare instances.",
    inputSchema: {
        query: zod_1.z.string().describe("Search for images by searchwords."),
        per_page: zod_1.z.number().describe("Number of results to fetch per page."),
        page: zod_1.z.number().describe("The page number to fetch"),
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            utils_2.logger.info("Running tool lookup_images_from_pexels");
            const API_KEY = (_a = (0, cliconfig_1.getAPIKey)("pexels")) === null || _a === void 0 ? void 0 : _a.apikey;
            const url = `https://api.pexels.com/v1/search`;
            if (API_KEY) {
                try {
                    const response = yield axios_1.default.get(url, {
                        headers: { Authorization: API_KEY },
                        params: {
                            query: inputVars.query,
                            per_page: inputVars.per_page,
                            page: inputVars.page
                        }
                    });
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify(response.data)
                            }]
                    };
                }
                catch (error) {
                    // Optionally log error details
                    utils_2.logger.error('Pexels API error:' + (((_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data) || (error === null || error === void 0 ? void 0 : error.message)));
                    return {
                        content: [{
                                type: "text",
                                text: "Error: " + error
                            }]
                    };
                }
            }
            else {
                return {
                    content: [{
                            type: "text",
                            text: "Error: Pexels API key is either not present or expired!"
                        }]
                };
            }
        });
    }
});
const runCurl = (0, utils_2.defineToolSpec)({
    name: "run_curl",
    description: "Run a curl command and get the result.",
    inputSchema: {
        curl_string: zod_1.z.string().describe("The curl to be run as a string"),
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            utils_2.logger.info("Running curl: " + inputVars.curl_string);
            try {
                const { stdout, stderr } = yield (0, utils_1.exec)(inputVars.curl_string);
                return {
                    content: [
                        {
                            type: "text",
                            text: `
              stdout: ${stdout}
              -----------------
              stderr: ${stderr}
            `
                        }
                    ]
                };
            }
            catch (err) {
                utils_2.logger.error("Failed to execute curl: " + err);
                return {
                    content: [
                        {
                            type: "text",
                            text: "Error: Unexpected error ocurred during tool execution." + err
                        }
                    ]
                };
            }
        });
    }
});
const runHttpRequest = (0, utils_2.defineToolSpec)({
    name: "http_request",
    description: "Make an HTTP request to inspect API responses. This tool is useful when integrating apis if enough information doesn't exist in the context to know what the response format would be. This tool uses axios to run the calls.",
    inputSchema: {
        url: zod_1.z.string().url().describe("The full URL to make a request to"),
        method: zod_1.z.enum(["GET", "POST", "PUT", "DELETE"]).default("GET"),
        headers: zod_1.z.record(zod_1.z.string()).optional(),
        body: zod_1.z.union([zod_1.z.string(), zod_1.z.record(zod_1.z.any())]).optional()
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            utils_2.logger.info("Running tool http_request");
            const { url, method, headers, body } = inputVars;
            utils_2.logger.info(`LLM wants to run: ${method} ${url}`);
            try {
                const requestPayload = {
                    url,
                    method,
                    headers,
                };
                if (body) {
                    requestPayload.data = body;
                }
                const response = yield axios_1.default.request(requestPayload);
                return {
                    content: [
                        {
                            type: "text",
                            text: `Status: ${response.status}\n\nResponse:\n${JSON.stringify(response.data, null, 2)}`,
                        },
                    ],
                };
            }
            catch (error) {
                utils_2.logger.error("Failure in running http_request: " + (error === null || error === void 0 ? void 0 : error.message));
                return {
                    content: [
                        {
                            type: "text",
                            text: `Request failed:\n${error.message}\n\n${error.response
                                ? `Status: ${error.response.status}\n${JSON.stringify(error.response.data, null, 2)}`
                                : ""}`,
                        },
                    ],
                };
            }
        });
    }
});
const compileCode = (0, utils_2.defineToolSpec)({
    name: "compile_generated_code",
    description: "This tool will compile all the code generated so far that is supposed to run in the apptile platform and return the compile errors if any.",
    inputSchema: {
        apptile_context: utils_1.apptileContextZodSchema,
    },
    cb(inputVars) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            utils_2.logger.info("Running tool compile_generated_code");
            const app_id = (_a = inputVars.apptile_context) === null || _a === void 0 ? void 0 : _a.app_id;
            let toolResult = "Tool not executed";
            if (!app_id) {
                toolResult = "This tool is currently not functioning!";
            }
            else {
                const openedApp = yield (0, cliconfig_1.getOpenApp)(app_id);
                if (openedApp) {
                    const pluginsDir = path_1.default.resolve(openedApp === null || openedApp === void 0 ? void 0 : openedApp.repoPath, "remoteCode/plugins");
                    const entries = yield (0, promises_1.readdir)(pluginsDir, { withFileTypes: true });
                    const plugins = entries
                        .filter((it) => it.isDirectory())
                        .map((it) => it.name);
                    const joinedPlugins = plugins.join("_");
                    utils_2.logger.info("Running compilation for plugins: ", joinedPlugins);
                    const compiledDirectoryName = (0, js_sha256_1.sha256)(joinedPlugins).toString();
                    try {
                        const result = yield (0, compiler_1.compileMultiplePlugins)(path_1.default.resolve(openedApp.repoPath, "remoteCode"), plugins, compiledDirectoryName);
                        toolResult = JSON.stringify(result);
                        utils_2.logger.info("Tool result: " + toolResult);
                    }
                    catch (err) {
                        if (err && err.message && Array.isArray(err.errors)) {
                            utils_2.logger.info(chalk_1.default.red("Failed to compile: ") + JSON.stringify(err, null, 2));
                            toolResult = JSON.stringify(err);
                        }
                        else {
                            console.error("Compilation proces error: ", err.message);
                            toolResult = "Unknown error during compilation: " + err;
                        }
                    }
                }
                else {
                    toolResult = "This tool is currently not functioning!";
                }
            }
            utils_2.logger.info(toolResult);
            return {
                content: [{
                        type: "text",
                        text: toolResult
                    }]
            };
        });
    }
});
const createGlobalPlugin = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_create_global_plugin",
    description: "Create a globalPlugin in the apptile nocode platform",
    inputSchema: {
        name: zod_1.z.string().describe("name of the globalPlugin. Must be a valid javascript name."),
        pluginType: zod_1.z.enum(["LocalStoragePlugin", "StatePlugin"])
            .describe("The type of globalPlugin. StatePlugin stores values ephemerally, i.e. values are lost once the app is closed. LocalStoragePlugin automatically persists its values in LocalStorage and restores from there on startup."),
        value: zod_1.z.string().describe("The inital value exposed by the global plugin. This can be any valid json embedded inside double curly braces and single parenthesis. For example {{( {todos: []} )}}. The platform will compile the value inside double curly braces as a javascript expression. This is why an object must be wrapped inside parentheses to make it a valid javascript expression.")
    },
    cb: handleFrontendTools
});
const readCurrentScreens = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_read_current_screens",
    description: "Get the current @react-navigation tree that is setup in the app. It returns a tree representing the navigation tree and the plugins in each screen.",
    inputSchema: {
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb: handleFrontendTools
});
const readGlobalPlugin = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_read_global_plugin_value",
    description: "Read the initial value and the current value for a global plugin.",
    inputSchema: {
        plugin_name: zod_1.z.string().describe("Name of the global plugin whose value should be read")
    },
    cb: handleFrontendTools
});
const listGlobalPlugins = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_list_global_plugins",
    description: "Get the list of global plugins that exist in the app",
    inputSchema: {
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb: handleFrontendTools
});
const createScreen = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_create_screen",
    description: "Create a @react-navigation screen in the nocode platform. All screens are added in a stack navigator.",
    inputSchema: {
        name: zod_1.z.string().describe("Name of the screen to be created.")
    },
    cb: handleFrontendTools
});
const createPlugin = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_create_plugin",
    description: "Create a plugin in the apptile nocode platform that can be dragged and dropped into a screen.",
    inputSchema: {
        name: zod_1.z.string().describe("Name of the plugin. This must be a single word with all lowercase letters and no numbers."),
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb: handleFrontendTools
});
const generatePluginCode = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_generate_code_for_plugin",
    description: "Genrate the code for a plugin in the nocode platform using an agent.",
    inputSchema: {
        name: zod_1.z.string().describe("name of the plugin for which react-native code has to be generated"),
        screen: zod_1.z.string().describe("The nocode platform will navigate to this screen after the code generation is finished. Specify the screen where you added this plugin"),
        prompt: zod_1.z.string().describe("The prompt to send to the agent for generating the code for the plugin."),
        inlcude_image: zod_1.z.boolean().describe("If set to true, all the images in the context will be forwarded to the agent."),
        apptile_context: utils_1.apptileContextZodSchema
    },
    cb: handleFrontendTools
});
const addPluginToScreen = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_add_plugin_to_screen",
    description: "This will drag and drop the specified plugin at the bottom of the specified screen.",
    inputSchema: {
        screen_name: zod_1.z.string().describe("Name of the screen to which the plugin should be added."),
        plugin_name: zod_1.z.string().describe("Name of plugin that should be added to the screen.")
    },
    cb: handleFrontendTools
});
const updateGlobalPluginConfig = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_update_global_plugin_defaultvalue",
    description: "This will update the default value of a global plugin that already exists.",
    inputSchema: {
        plugin_name: zod_1.z.string().describe("Name of the global plugin whose value should be updated"),
        new_value: zod_1.z.string().describe("The new value for the global plugin. This can be any valid json embedded inside double curly braces and single parenthesis. For example {{( {todos: []} )}}. The platform will compile the value inside double curly braces as a javascript expression. This is why an object must be wrapped inside parentheses to make it a valid javascript expression.")
    },
    cb: handleFrontendTools
});
// const inspectNavContainer = defineToolSpec({
//   name: "nocodelayer_get_current_route",
//   description: `Get the result of calling getCurrentRoute on the root navigation container. This can be used to determine, which
//   screen is currently visible in the web preview of the app.`,
//   inputSchema: {
//   },
//   cb: handleFrontendTools
// });
// const consoleTool = defineToolSpec({
//   name: "nocodelayer_inspect_dom",
//   description: `A code execution tool that lets you run a callback in the browser console and obtain its result.
//   This should only be used for inspection and debugging purposes and not for performing any effects that are intended
//   to happen in the final product. The function has the signature: 
//   function codeToExecute(appCanvas?: HTMLElement, navContainerRef?: NavigationContainer): string {...}`,
//   inputSchema: {
//     callback: z.string().describe(`A javascript function provided as a string that will be evaled and called.`),
//     pass_app_canvas: z.boolean().describe(`If set to true, the function will be passed
//     the dom-element with the id #app-canvas that contains the web preivew of the app. All
//     elements in the plugins that have nativeID's can be found with document.querySelector within
//     this element.`),
//     pass_navigation_ref: z.boolean().describe(`If set to true a reference to navigationContainer 
//       is passed to the function which can be used to inspect the currentRoute, rootState etc. as well
//       as trigger navigation so that elements in that screen can be viewed.`)
//   },
//   cb: handleFrontendTools
// });
exports.batchNocodeLayerTools = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_batch_tools",
    description: "Use this to run multiple nocodelayer tools and get all the results. This will only batch tools that run inside the apptile platform and are therefore prefixed with nocodelayer. The tools will be run sequentially one after the other. So it is safe to assume that the effect of first tool can be assumed to have been completed before the second tool executes. This can be useful, for example, when creating a screen, a plugin and dropping the plugin into the screen. As long as the order of operations is correct in the array a single call with all three commands will do the job just fine.",
    inputSchema: {
        calls: zod_1.z.array(zod_1.z.union([
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_read_current_screens")
            }).describe("Get the current @react-navigation tree that is setup in the app. It returns a tree representing the navigation tree and the plugins in each screen."),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_read_global_plugin_value"),
                plugin_name: zod_1.z.string().describe("Name of the global plugin")
            }).describe("Read the initial value and the current value for a global plugin."),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_list_global_plugins")
            }).describe("Get the list of global plugins that exist in the app"),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_create_screen"),
                name: zod_1.z.string().describe("Name of the screen to be created.")
            }).describe("Create a @react-navigation screen in the nocode platform. All screens are added in a stack navigator."),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_create_plugin"),
                name: zod_1.z.string().describe("Name of the plugin. This must be a single word with all lowercase letters and no numbers.")
            }).describe("Create a plugin in the apptile nocode platform that can be dragged and dropped into a screen."),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_add_plugin_to_screen"),
                screen_name: zod_1.z.string().describe("Name of the screen to which the plugin should be added."),
                plugin_name: zod_1.z.string().describe("Name of plugin that should be added to the screen.")
            }).describe("This will drag and drop the specified plugin at the bottom of the specified screen."),
            // z.object({
            //   tool_name: z.literal("nocodelayer_generate_code_for_plugin"),
            //   name: z.string().describe("name of the plugin for which react-native code has to be generated"),
            //   screen: z.string().describe("The nocode platform will navigate to this screen after the code generation is finished. Specify the screen where you added this plugin"),
            //   prompt: z.string().describe("The prompt to send to the agent for generating the code for the plugin."),
            //   inlcude_image: z.boolean().describe("If set to true, all the images in the context will be forwarded to the agent.")
            // }),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_update_global_plugin_defaultvalue"),
                plugin_name: zod_1.z.string().describe("Name of the global plugin whose value should be updated"),
                new_value: zod_1.z.string().describe("The new value for the global plugin. This can be any valid json embedded inside double curly braces and single parenthesis. For example {{( {todos: []} )}}. The platform will compile the value inside double curly braces as a javascript expression. This is why an object must be wrapped inside parentheses to make it a valid javascript expression.")
            }).describe("This will update the default value of a global plugin that already exists."),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_create_global_plugin"),
                name: zod_1.z.string().describe("name of the globalPlugin. Must be a valid javascript name."),
                pluginType: zod_1.z.enum(["LocalStoragePlugin", "StatePlugin"])
                    .describe("The type of globalPlugin. StatePlugin stores values ephemerally, i.e. values are lost once the app is closed. LocalStoragePlugin automatically persists its values in LocalStorage and restores from there on startup."),
                value: zod_1.z.string().describe("The inital value exposed by the global plugin. This can be any valid json embedded inside double curly braces and single parenthesis. For example {{( {todos: []} )}}. The platform will compile the value inside double curly braces as a javascript expression. This is why an object must be wrapped inside parentheses to make it a valid javascript expression.")
            }).describe("Create a globalPlugin in the apptile nocode platform")
        ]))
    },
    cb: handleFrontendTools
});
exports.batchPluginAgentTools = (0, utils_2.defineToolSpec)({
    name: "nocodelayer_batch_tools",
    description: "Use this to run multiple nocodelayer tools and get all the results. This will only batch tools that run inside the apptile platform and are therefore prefixed with nocodelayer.",
    inputSchema: {
        calls: zod_1.z.array(zod_1.z.union([
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_read_current_screens")
            }).describe("Get the current @react-navigation tree that is setup in the app. It returns a tree representing the navigation tree and the plugins in each screen."),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_read_global_plugin_value"),
                plugin_name: zod_1.z.string().describe("Name of the global plugin")
            }).describe("Read the initial value and the current value for a global plugin."),
            zod_1.z.object({
                tool_name: zod_1.z.literal("nocodelayer_list_global_plugins")
            }).describe("Get the list of global plugins that exist in the app"),
        ]))
    },
    cb: handleFrontendTools
});
exports.tools = {
    graphqlSchema: [
        exploreSchema
    ],
    projectCodeRead: [
        readPluginFiles,
        getHunkFromFile
    ],
    projectCodeReadForPlanner: [
        readPluginFilesByPluginName,
        // readCurrentScreens,
        // listGlobalPlugins,
    ],
    projectCodeWrite: [
        writePluginFiles,
        // applyPatchToFile
    ],
    codeExecution: [
        // runHttpRequest,
        runCurl,
        compileCode,
        lookupImagesFromPexels
    ],
    dashboardRead: [
        lookupAvailableIcons,
        // readGlobalPlugin,
    ],
    dashboardWrite: [
        createGlobalPlugin,
        createScreen,
        createPlugin,
        addPluginToScreen,
        updateGlobalPluginConfig
    ],
    supabaseTools: supabase_1.supabaseTools,
    supabaseReadTools: supabase_1.supabaseReadTools,
    promptPluginAgent: [
        generatePluginCode
    ],
    batchedTool: [
        exports.batchNocodeLayerTools,
    ],
    batchedPluginAgentTools: [
        exports.batchPluginAgentTools
    ]
};
