UNPKG

@nomyx/assistant-cli

Version:

A javascript-based assistant that can help you with your daily tasks

231 lines (228 loc) 14.1 kB
// import fs from 'fs'; // import path from 'path'; const fs = require('fs'); const pathModule = require('path'); const cwd = process.cwd(); module.exports = { state: { modules: [{ name: 'files', description: 'File manipulation tools', version: '0.0.1', }], }, schemas: [ { "type": 'function', "function": { "name": 'get_file_tree', "description": 'Return a tree of files and folders `n` levels deep from the specified `path`.', "parameters": { "type": 'object', "properties": { "value": { "type": 'string', "description": 'The directory path from which to start the exploration.' }, n: { "type": 'number', "description": 'The depth of exploration.' } }, "required": ['path', 'n'] } } }, { "type": "function", "function": {"name": "file", "description": "Read, write, modify, and delete a file on the system. Supported operations are read, write, append, prepend, replace, insert_at, remove, delete, and copy.", "parameters": {"type": "object", "properties": {"operation": {"type": "string", "description": "The operation to perform on the file. Supported operations are read, append, prepend, replace, insert_at, remove, delete, copy, attach, list_attached, detach."}, "path": {"type": "string", "description": "The path to the file to perform the operation on."}, "match": {"type": "string", "description": "The string to match in the file. Regular expressions are supported."}, "data": {"type": "string", "description": "The data to write to the file."}, "position": {"type": "number", "description": "The position at which to perform the operation."}, "target": {"type": "string", "description": "The path to the target file."}}, "required": ["operation", "path"]}}}, { "type": "function", "function": {"name": "files", "description": "Perform batch operations on files", "parameters": {"type": "object", "properties": {"operations": {"type": "array", "description": "The operations to perform on the files.", "items": {"type": "object", "properties": {"operation": {"type": "string", "description": "The operation to perform on the file. Supported operations are read, append, prepend, replace, insert_at, remove, delete, and copy."}, "path": {"type": "string", "description": "The path to the file to perform the operation on."}, "match": {"type": "string", "description": "The string to match in the file. Regular expressions are supported."}, "data": {"type": "string", "description": "The data to write to the file."}, "position": {"type": "number", "description": "The position at which to perform the operation."}, "target": {"type": "string", "description": "The path to the target file."}}, "required": ["operation", "path"]}}}, "required": ["operations"]}}}, ], tools: { file: async function ({ operation, path, match, data, position, target }, run) { try { const p = pathModule.join(cwd, (path || '')); const t = pathModule.join(cwd, (target || '')); if (!fs.existsSync(p || t)) { return `Error: File not found at path ${p || t}`; } let text = fs.readFileSync(p, 'utf8'); switch (operation) { case 'read': return text; case 'write': text += data; break; case 'append': text += data; break; case 'prepend': text = data + text; break; case 'replace': text = text.replace(match, data); break; case 'insert_at': text = text.slice(0, position) + data + text.slice(position); break; case 'remove': text = text.replace(match, ''); break; case 'delete': fs.unlinkSync(p); break; case 'copy': fs.copyFileSync(p, t); break; case 'attach': await (async({ path }, assistant) => { path = pathModule.join(__dirname, '..', (path || '')); const extension = path.split('.').pop(); if (!fs.existsSync(path)) { return `Error: File ${path} does not exist`; } try { const supportedFormats = ['c', 'cpp', 'csv', 'docx', 'html', 'java', 'json', 'md', 'pdf', 'php', 'pptx', 'py', 'rb', 'tex', 'txt', 'css', 'jpeg', 'jpg', 'js', 'gif', 'png', 'tar', 'ts', 'xlsx', 'xml', 'zip']; if (!extension || !supportedFormats.includes(extension)) { return `Error: File ${path} has an unsupported format`; } const ret = assistant.attachFile(path); return ret && `Successfully attached file ${path} to assistant ${assistant.name}` || `Error attaching file ${path} to assistant ${assistant.name}`; } catch (err) { return `Error attaching file ${path} to assistant ${assistant.name}: ${err.message}` } })({ path }, assistant); break; case 'list_attached': await(async(_dummy, assistant) => { try { if (!assistant) { return `Error: Could not create assistant`; } const myAssistantFiles = await assistant.listFiles(); return JSON.stringify(myAssistantFiles); } catch (err) { return `Error: ${err.message}` } })(null, assistant); break; case 'detach': await(async({ path }, assistant) => { path = pathModule.join(__dirname, '..', (path || '')); if (!fs.existsSync(path)) { return `Error: File ${path} does not exist`; } try { const ret = assistant.detachFile(path); return ret && `Successfully detached file ${path} from assistant ${assistant.name}` || `Error detaching file ${path} from assistant ${assistant.name}`; } catch (err) { return `Error detaching file ${path} from assistant ${assistant.name}: ${err.message}` } })({ path }, assistant); break; default: return `Error: Unsupported operation ${operation}`; } fs.writeFileSync(p, text); return `Successfully executed ${operation} operation on file at path ${p}`; } catch (error) { return `Error: ${error.message}` } }, files: async function ({ operations }, run) { try { for (const { operation, path, match, data, position, target } of operations) { const p = pathModule.join(cwd, (path || '')); const t = pathModule.join(cwd, (target || '')); if (!fs.existsSync(p || t)) { return `Error: File not found at path ${p || t}`; } let text = fs.readFileSync(p, 'utf8'); switch (operation) { case 'read': return text; case 'append': text += data; break; case 'prepend': text = data + text; break; case 'replace': text = text.replace(match, data); break; case 'insert_at': text = text.slice(0, position) + data + text.slice(position); break; case 'remove': text = text.replace(match, ''); break; case 'delete': fs.unlinkSync(p); break; case 'copy': fs.copyFileSync(p, t); break; case 'attach': await (async({ path }, assistant) => { path = pathModule.join(__dirname, '..', (path || '')); const extension = path.split('.').pop(); if (!fs.existsSync(path)) { return `Error: File ${path} does not exist`; } try { const supportedFormats = ['c', 'cpp', 'csv', 'docx', 'html', 'java', 'json', 'md', 'pdf', 'php', 'pptx', 'py', 'rb', 'tex', 'txt', 'css', 'jpeg', 'jpg', 'js', 'gif', 'png', 'tar', 'ts', 'xlsx', 'xml', 'zip']; if (!extension || !supportedFormats.includes(extension)) { return `Error: File ${path} has an unsupported format`; } const ret = assistant.attachFile(path); return ret && `Successfully attached file ${path} to assistant ${assistant.name}` || `Error attaching file ${path} to assistant ${assistant.name}`; } catch (err) { return `Error attaching file ${path} to assistant ${assistant.name}: ${err.message}` } })({ path }, assistant); break; case 'list_attached': await(async(_dummy, assistant) => { try { if (!assistant) { return `Error: Could not create assistant`; } const myAssistantFiles = await assistant.listFiles(); return JSON.stringify(myAssistantFiles); } catch (err) { return `Error: ${err.message}` } })(null, assistant); break; case 'detach': await(async({ path }, assistant) => { path = pathModule.join(__dirname, '..', (path || '')); if (!fs.existsSync(path)) { return `Error: File ${path} does not exist`; } try { const ret = assistant.detachFile(path); return ret && `Successfully detached file ${path} from assistant ${assistant.name}` || `Error detaching file ${path} from assistant ${assistant.name}`; } catch (err) { return `Error detaching file ${path} from assistant ${assistant.name}: ${err.message}` } })({ path }, assistant); break; default: return `Error: Unsupported operation ${operation}`; } fs.writeFileSync(p, text); } return `Successfully executed batch operations on file at path ${p}`; } catch (error) { return `Error: ${error.message}` } }, get_file_tree: async ({ value, n }, run) => { const explore = (dir, depth) => { dir = pathModule.join(cwd, (dir || '')) if (depth < 0) return null; const directoryTree = { path: dir, children: [] }; try{ const fsd = fs.readdirSync(dir, { withFileTypes: true }) fsd.forEach(dirent => { const fullPath = pathModule.join(dir, dirent.name); // Use pathModule instead of path // ignore node_modules and .git directories if (dirent.isDirectory() && (dirent.name === 'node_modules' || dirent.name === '.git')) return; if (dirent.isDirectory()) { directoryTree.children.push(explore(fullPath, depth - 1)); } else { directoryTree.children.push({ path: fullPath }); } }); } catch (e) { return e.message; } try { } catch (e) { return e.message; } return directoryTree; }; return explore(value, n); } } }