scai
Version:
> **AI-powered CLI for local code analysis, commit message suggestions, and natural-language queries.** 100% local, private, GDPR-friendly, made in Denmark/EU with ❤️.
87 lines (86 loc) • 3.46 kB
JavaScript
// File: src/modules/writeFileModule.ts
import fs from "fs/promises";
import chalk from "chalk";
import { normalizePath } from "../../utils/contentUtils.js";
import { logInputOutput } from "../../utils/promptLogHelper.js";
export const writeFileModule = {
name: "writeFile",
description: "Writes materialized file outputs from codeTransformModule to disk. " +
"Only writes files specified in the current plan step.",
groups: ["finalize"],
run: async (input) => {
var _a;
const context = input.context;
const mode = input.data?.mode ?? "overwrite";
if (!context) {
return {
query: input.query,
data: { writeMode: mode, writtenFiles: [], errors: ["Missing execution context"] },
};
}
const step = context.currentStep;
if (!step) {
return {
query: input.query,
data: { writeMode: mode, writtenFiles: [], errors: ["No current step in context"] },
};
}
// Determine target file(s) from the current step
const targetFiles = [];
if (step.targetFile)
targetFiles.push(step.targetFile);
if (Array.isArray(step.targetFiles))
targetFiles.push(...step.targetFiles);
if (!targetFiles.length) {
return {
query: input.query,
data: { writeMode: mode, writtenFiles: [], errors: ["No targetFile(s) specified in current step"] },
};
}
// Filter codeTransformArtifacts to only the target file(s)
const allFiles = context.execution?.codeTransformArtifacts?.files ?? [];
const filesToWrite = allFiles.filter(f => targetFiles.includes(f.filePath));
if (!filesToWrite.length) {
return {
query: input.query,
data: { writeMode: mode, writtenFiles: [], errors: ["No transformed files found for targetFile(s)"] },
};
}
const writtenFiles = [];
const errors = [];
for (const f of filesToWrite) {
const filePath = normalizePath(f.filePath);
if (!filePath) {
errors.push(`Invalid filePath: ${f.filePath}`);
continue;
}
if (typeof f.content !== "string" || !f.content.trim()) {
errors.push(`No content to write for ${filePath}`);
continue;
}
try {
await fs.writeFile(filePath, f.content, "utf-8");
console.log(chalk.green(`✅ Written: ${filePath}`));
writtenFiles.push(filePath);
}
catch (err) {
console.error(chalk.red(`❌ Failed writing ${filePath}:`), err);
errors.push(`${filePath}: ${err.message}`);
}
}
// Mark only the files we just wrote as touched
context.plan ?? (context.plan = {});
(_a = context.plan).touchedFiles ?? (_a.touchedFiles = []);
for (const file of writtenFiles) {
if (!context.plan.touchedFiles.includes(file)) {
context.plan.touchedFiles.push(file);
}
}
const output = {
query: input.query,
data: { writeMode: mode, writtenFiles, errors },
};
logInputOutput("writeFile", "output", output.data);
return output;
},
};