Initial commit: SuperDesign project
This commit is contained in:
101
src/tools/write-tool.ts
Normal file
101
src/tools/write-tool.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { tool } from 'ai';
|
||||
import { z } from 'zod';
|
||||
import { ExecutionContext } from '../types/agent';
|
||||
import {
|
||||
handleToolError,
|
||||
validateWorkspacePath,
|
||||
resolveWorkspacePath,
|
||||
createSuccessResponse,
|
||||
ToolResponse
|
||||
} from './tool-utils';
|
||||
|
||||
/**
|
||||
* Write tool result with metadata
|
||||
*/
|
||||
export interface WriteToolResult {
|
||||
file_path: string;
|
||||
absolute_path: string;
|
||||
is_new_file: boolean;
|
||||
lines_written: number;
|
||||
bytes_written: number;
|
||||
}
|
||||
|
||||
// Path validation is now handled by validateWorkspacePath in tool-utils
|
||||
|
||||
/**
|
||||
* Create SuperDesign write tool with execution context
|
||||
*/
|
||||
export function createWriteTool(context: ExecutionContext) {
|
||||
return tool({
|
||||
description: 'Write content to a file in the SuperDesign workspace. Creates parent directories if needed.',
|
||||
parameters: z.object({
|
||||
file_path: z.string().describe('Path to the file to write to (relative to workspace root, or absolute path within workspace)'),
|
||||
content: z.string().describe('Content to write to the file'),
|
||||
create_dirs: z.boolean().optional().default(true).describe('Whether to create parent directories if they don\'t exist (default: true)')
|
||||
}),
|
||||
execute: async ({ file_path, content, create_dirs = true }): Promise<ToolResponse> => {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
// Validate workspace path (handles both absolute and relative paths)
|
||||
const pathError = validateWorkspacePath(file_path, context);
|
||||
if (pathError) {
|
||||
return pathError;
|
||||
}
|
||||
|
||||
// Resolve absolute path within workspace
|
||||
const absolutePath = resolveWorkspacePath(file_path, context);
|
||||
|
||||
context.outputChannel.appendLine(`[write] Writing to file: ${file_path}`);
|
||||
|
||||
// Check if target is a directory
|
||||
if (fs.existsSync(absolutePath)) {
|
||||
const stats = fs.lstatSync(absolutePath);
|
||||
if (stats.isDirectory()) {
|
||||
return handleToolError(`Target path is a directory, not a file: ${file_path}`, 'Path validation', 'validation');
|
||||
}
|
||||
}
|
||||
|
||||
// Create parent directories if needed and requested
|
||||
if (create_dirs) {
|
||||
const dirName = path.dirname(absolutePath);
|
||||
if (!fs.existsSync(dirName)) {
|
||||
fs.mkdirSync(dirName, { recursive: true });
|
||||
context.outputChannel.appendLine(`[write] Created parent directories for: ${file_path}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if this is a new file or overwrite
|
||||
const isNewFile = !fs.existsSync(absolutePath);
|
||||
|
||||
// Write the file
|
||||
fs.writeFileSync(absolutePath, content, 'utf8');
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
const lines = content.split('\n').length;
|
||||
const size = Buffer.byteLength(content, 'utf8');
|
||||
|
||||
context.outputChannel.appendLine(`[write] ${isNewFile ? 'Created' : 'Updated'} file: ${file_path} (${lines} lines, ${size} bytes) in ${duration}ms`);
|
||||
|
||||
const result: WriteToolResult = {
|
||||
file_path,
|
||||
absolute_path: absolutePath,
|
||||
is_new_file: isNewFile,
|
||||
lines_written: lines,
|
||||
bytes_written: size
|
||||
};
|
||||
|
||||
return createSuccessResponse(result);
|
||||
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime;
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
|
||||
context.outputChannel.appendLine(`[write] Error writing file: ${errorMessage} (${duration}ms)`);
|
||||
return handleToolError(error, 'Write tool execution', 'execution');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user