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.96 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { constants } from 'node:fs'; import { access, rename, 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 executeMoveFile = async (args) => { const srcAbsPath = resolve(args.source); const destAbsPath = resolve(args.destination); await rename(srcAbsPath, destAbsPath); invalidateCache(srcAbsPath); return `File moved: ${args.source} → ${args.destination}`; }; const moveFileCoreTool = tool({ description: 'Move or rename a file. Use this instead of execute_bash with mv.', inputSchema: jsonSchema({ type: 'object', properties: { source: { type: 'string', description: 'The current relative path of the file.', }, destination: { type: 'string', description: 'The new relative path for the file.', }, }, required: ['source', 'destination'], }), needsApproval: () => { if (isNanocoderToolAlwaysAllowed('move_file')) { return false; } const mode = getCurrentMode(); return mode !== 'auto-accept'; }, execute: async (args, _options) => { return await executeMoveFile(args); }, }); const MoveFileFormatter = 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 move_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 moveFileFormatter = (args, result) => { return _jsx(MoveFileFormatter, { args: args, result: result }); }; const moveFileValidator = 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 moveFileTool = { name: 'move_file', tool: moveFileCoreTool, formatter: moveFileFormatter, validator: moveFileValidator, }; //# sourceMappingURL=move-file.js.map