UNPKG

spaider

Version:

Deterministic-first AI code assistant that crawls your codebase to implement changes using open source LLMs

326 lines 12 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.message = message; exports.system = system; exports.user = user; exports.readFile = readFile; exports.detectLanguage = detectLanguage; exports.getRelativePath = getRelativePath; exports.filterPathsWithinProject = filterPathsWithinProject; exports.sanitizePath = sanitizePath; exports.getDisplayPath = getDisplayPath; exports.ensureDirectoryExists = ensureDirectoryExists; exports.loadFileContexts = loadFileContexts; exports.formatFilePreview = formatFilePreview; exports.formatFileSemantic = formatFileSemantic; exports.formatFilePreviews = formatFilePreviews; exports.formatFileSemantics = formatFileSemantics; exports.formatChanges = formatChanges; exports.formatWriteChanges = formatWriteChanges; exports.resolveFilePath = resolveFilePath; exports.ensureDirectoryForFile = ensureDirectoryForFile; exports.normalizeFilePath = normalizeFilePath; exports.isWithinProjectRoot = isWithinProjectRoot; exports.getFileExtension = getFileExtension; exports.getFileName = getFileName; exports.getFileNameWithoutExtension = getFileNameWithoutExtension; exports.joinPaths = joinPaths; exports.makeRelativeToProject = makeRelativeToProject; exports.fileExists = fileExists; exports.isDirectory = isDirectory; exports.safeWriteFile = safeWriteFile; exports.safeDeleteFile = safeDeleteFile; const path = __importStar(require("path")); const fs_1 = require("fs"); const dedent_1 = __importDefault(require("dedent")); const const_1 = require("./const"); const logger_1 = require("../services/logger"); function message(role, content) { return { role, content: (0, dedent_1.default)(content) }; } function system(content) { return message("system", content); } function user(content) { return message("user", content); } async function readFile(filePath) { try { return await fs_1.promises.readFile(filePath, "utf8"); } catch (error) { throw new Error(`Failed to read file ${filePath}: ${error}`); } } function detectLanguage(filePath) { const ext = path.extname(filePath); switch (ext) { case ".ts": return "typescript"; case ".tsx": return "tsx"; case ".jsx": return "jsx"; case ".js": return "javascript"; default: return "javascript"; } } function getRelativePath(from, to) { return path.normalize(path.relative(from, to)).replace(/\\/g, "/"); } function filterPathsWithinProject(filePaths, excludePaths, projectRoot) { return filePaths .filter((file) => !excludePaths.includes(file)) .filter((filePath) => { const relativePath = path.relative(projectRoot, filePath); return !relativePath.startsWith("..") && !path.isAbsolute(relativePath); }); } function sanitizePath(filePath) { return path.normalize(filePath).replace(/\\/g, "/"); } function getDisplayPath(filePath, projectRoot) { if (projectRoot && filePath.startsWith(projectRoot)) { return filePath.substring(projectRoot.length).replace(/^\/+/, ""); } return sanitizePath(filePath); } async function ensureDirectoryExists(dirPath) { try { await fs_1.promises.mkdir(dirPath, { recursive: true }); } catch (error) { // Directory might already exist, ignore error } } async function loadFileContexts(filePaths) { const contexts = await Promise.all(filePaths.map(async (path) => { try { return { path, content: await readFile(path), language: detectLanguage(path), }; } catch (error) { logger_1.Logger.warn(`Failed to read discovered file ${path}`); return null; } })); return contexts.filter((c) => c !== null); } function formatFilePreview(file) { if (!file.content) { throw new Error("Missing file content"); } const lines = file.content.split("\n"); const fileContent = lines.length > const_1.MAX_FILE_PREVIEW_LINES ? lines.slice(0, const_1.MAX_FILE_PREVIEW_LINES).join("\n") + "\n...[truncated]" : file.content; return `${file.path}:\n${fileContent}`; } function formatFileSemantic(file) { if (!file.ast) { throw new Error("Missing file ast"); } return (0, dedent_1.default) `${file.path}: - Imports: ${file.ast.imports.map((i) => i.source).join(", ")} - Exports: ${file.ast.exports.map((e) => e.name).join(", ")} - Functions: ${file.ast.functions.map((f) => f.name).join(", ")} - Classes: ${file.ast.classes.map((c) => c.name).join(", ")}`; } function formatFilePreviews(files) { return files.map(formatFilePreview).join("\n\n"); } function formatFileSemantics(files) { return files.map(formatFileSemantic).join("\n"); } function formatChanges(changes) { return changes .map((change) => { let changeDescription = `${change.filePath}: ${change.operation}`; if (change.operation !== "delete_file") { changeDescription += ` (${change.modificationType})`; } if (change.modificationDescription) { changeDescription += `\n${change.modificationDescription}`; } if (change.oldCodeBlock) { changeDescription += `\nOld Code Block:\n${change.oldCodeBlock}`; } if (change.newCodeBlock) { changeDescription += `\nNew Code Block:\n${change.newCodeBlock}`; } return changeDescription; }) .join("\n\n"); } function formatWriteChanges(filePath, fileChanges) { const output = [`## File: ${filePath}", "`]; for (const change of fileChanges) { // Add operation description let description = ""; switch (change.operation) { case "new_file": description = `Create new file: ${change.modificationDescription || "New file creation"}`; break; case "delete_file": description = `Delete file: ${change.modificationDescription || "Remove this file"}`; break; case "modify_file": description = `${change.modificationType}: ${change.modificationDescription || "Modify existing file"}`; break; } output.push(`### ${description}\n`); // Add old code block if it exists if (change.oldCodeBlock && change.operation === "modify_file") { output.push("**Old Code:**\n```"); output.push(change.oldCodeBlock); output.push("```\n"); } // Add new code block if it exists if (change.newCodeBlock && change.operation !== "delete_file") { output.push("**New Code:**\n```"); output.push(change.newCodeBlock); output.push("```\n"); } // Add special instructions for different operations if (change.operation === "delete_file") { output.push("**Action:** Delete this file completely\n"); } else if (change.operation === "new_file") { output.push("**Action:** Create this file with the new code above\n"); } else { switch (change.modificationType) { case "replace_block": output.push("**Action:** Replace the old code block with the new code block above\n"); break; case "add_block": output.push("**Action:** Add the new code block to the appropriate location in the file\n"); break; case "remove_block": output.push("**Action:** Remove the old code block from the file\n"); break; } } output.push("---\n"); } return output.join("\n"); } // File Path Utilities function resolveFilePath(filePath, projectRoot) { if (path.isAbsolute(filePath)) { return filePath; } return path.resolve(projectRoot, filePath); } async function ensureDirectoryForFile(filePath) { const dirPath = path.dirname(filePath); await ensureDirectoryExists(dirPath); } function normalizeFilePath(filePath) { return path.normalize(filePath).replace(/\\/g, "/"); } function isWithinProjectRoot(filePath, projectRoot) { const resolvedPath = path.resolve(filePath); const resolvedRoot = path.resolve(projectRoot); const relativePath = path.relative(resolvedRoot, resolvedPath); return !relativePath.startsWith("..") && !path.isAbsolute(relativePath); } function getFileExtension(filePath) { return path.extname(filePath).toLowerCase(); } function getFileName(filePath) { return path.basename(filePath); } function getFileNameWithoutExtension(filePath) { return path.basename(filePath, path.extname(filePath)); } function joinPaths(...paths) { return normalizeFilePath(path.join(...paths)); } function makeRelativeToProject(filePath, projectRoot) { const resolved = resolveFilePath(filePath, projectRoot); return getRelativePath(projectRoot, resolved); } async function fileExists(filePath) { try { await fs_1.promises.access(filePath); return true; } catch { return false; } } async function isDirectory(dirPath) { try { const stats = await fs_1.promises.stat(dirPath); return stats.isDirectory(); } catch { return false; } } async function safeWriteFile(filePath, content, projectRoot) { const resolvedPath = projectRoot ? resolveFilePath(filePath, projectRoot) : filePath; // Ensure the file path is within project bounds if projectRoot is provided if (projectRoot && !isWithinProjectRoot(resolvedPath, projectRoot)) { throw new Error(`File path ${filePath} is outside project root ${projectRoot}`); } await ensureDirectoryForFile(resolvedPath); await fs_1.promises.writeFile(resolvedPath, content, "utf-8"); } async function safeDeleteFile(filePath, projectRoot) { const resolvedPath = projectRoot ? resolveFilePath(filePath, projectRoot) : filePath; // Ensure the file path is within project bounds if projectRoot is provided if (projectRoot && !isWithinProjectRoot(resolvedPath, projectRoot)) { throw new Error(`File path ${filePath} is outside project root ${projectRoot}`); } if (await fileExists(resolvedPath)) { await fs_1.promises.unlink(resolvedPath); } } //# sourceMappingURL=utils.js.map