UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

126 lines 4.97 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { constants } from 'node:fs'; import { access, copyFile, stat } from 'node:fs/promises'; import { dirname, resolve } from 'node:path'; import { Box, Text } from 'ink'; import React from 'react'; import ToolMessage from '../../components/tool-message.js'; import { isNanocoderToolAlwaysAllowed } from '../../config/nanocoder-tools-config.js'; import { getCurrentMode } from '../../context/mode-context.js'; import { ThemeContext } from '../../hooks/useTheme.js'; import { jsonSchema, tool } from '../../types/core.js'; import { invalidateCache } from '../../utils/file-cache.js'; import { isValidFilePath, resolveFilePath } from '../../utils/path-validation.js'; const executeCopyFile = async (args) => { const srcAbsPath = resolve(args.source); const destAbsPath = resolve(args.destination); await copyFile(srcAbsPath, destAbsPath); invalidateCache(destAbsPath); return `File copied: ${args.source} → ${args.destination}`; }; const copyFileCoreTool = tool({ description: 'Copy a file to a new location. Use this instead of execute_bash with cp.', inputSchema: jsonSchema({ type: 'object', properties: { source: { type: 'string', description: 'The relative path of the file to copy.', }, destination: { type: 'string', description: 'The relative path for the copy.', }, }, required: ['source', 'destination'], }), needsApproval: () => { if (isNanocoderToolAlwaysAllowed('copy_file')) { return false; } const mode = getCurrentMode(); return mode !== 'auto-accept'; }, execute: async (args, _options) => { return await executeCopyFile(args); }, }); const CopyFileFormatter = React.memo(({ args, result }) => { const themeContext = React.useContext(ThemeContext); if (!themeContext) { throw new Error('ThemeContext is required'); } const { colors } = themeContext; const messageContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.tool, children: "\u2692 copy_file" }), _jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Source: " }), _jsx(Text, { color: colors.text, children: args.source })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Destination: " }), _jsx(Text, { color: colors.text, children: args.destination })] }), result && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Result: " }), _jsx(Text, { color: colors.text, children: result })] }))] })); return _jsx(ToolMessage, { message: messageContent, hideBox: true }); }); const copyFileFormatter = (args, result) => { return _jsx(CopyFileFormatter, { args: args, result: result }); }; const copyFileValidator = async (args) => { // Validate source path if (!isValidFilePath(args.source)) { return { valid: false, error: `⚒ Invalid source path: "${args.source}". Path must be relative and within the project directory.`, }; } // Validate destination path if (!isValidFilePath(args.destination)) { return { valid: false, error: `⚒ Invalid destination path: "${args.destination}". Path must be relative and within the project directory.`, }; } try { const cwd = process.cwd(); resolveFilePath(args.source, cwd); resolveFilePath(args.destination, cwd); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return { valid: false, error: `⚒ Path validation failed: ${errorMessage}`, }; } // Check source exists const srcAbsPath = resolve(args.source); try { await access(srcAbsPath, constants.F_OK); } catch { return { valid: false, error: `⚒ Source file does not exist: "${args.source}"`, }; } // Check source is a file const fileStat = await stat(srcAbsPath); if (fileStat.isDirectory()) { return { valid: false, error: `⚒ Source is a directory, not a file: "${args.source}"`, }; } // Check destination parent directory exists const destAbsPath = resolve(args.destination); const parentDir = dirname(destAbsPath); try { await access(parentDir, constants.F_OK); } catch { return { valid: false, error: `⚒ Destination parent directory does not exist: "${parentDir}"`, }; } return { valid: true }; }; export const copyFileTool = { name: 'copy_file', tool: copyFileCoreTool, formatter: copyFileFormatter, validator: copyFileValidator, }; //# sourceMappingURL=copy-file.js.map