scai
Version:
> **AI-powered CLI for local code analysis, commit message suggestions, and natural-language queries.** > **100% local • No token cost • Private by design • GDPR-friendly** — made in Denmark/EU with ❤️.
79 lines (78 loc) • 3.42 kB
JavaScript
// File: src/modules/writeFileModule.ts
import fs from "fs/promises";
import path from "path";
import chalk from "chalk";
import { normalizePath } from "../utils/contentUtils.js";
import { logInputOutput } from "../utils/promptLogHelper.js";
export const writeFileStep = {
name: "writeFile",
description: "Writes all materialized file outputs from codeTransformModule to disk. " +
"This is a terminal side-effect step and does not depend on plan steps.",
groups: ["finalize"],
run: async (input) => {
var _a;
console.log(">>> writeFileStep starting");
const context = input.context;
const mode = input.data?.mode ?? "overwrite";
const writtenFiles = [];
const errors = [];
if (!context) {
console.error("writeFileStep: Missing execution context");
return { query: input.query, data: { writeMode: mode, writtenFiles, errors: ["Missing execution context"] } };
}
console.log("writeFileStep: context exists");
console.log("writeFileStep: mode =", mode);
console.log("writeFileStep: existing touchedFiles =", context.plan?.touchedFiles);
const artifacts = context.execution?.codeTransformArtifacts?.files ?? [];
console.log("writeFileStep: found codeTransformArtifacts.files =", artifacts.length);
if (artifacts.length) {
console.log("writeFileStep: filePaths =", artifacts.map(f => f.filePath));
}
else {
console.warn("writeFileStep: No artifacts found to write");
}
for (const f of artifacts) {
console.log("Processing artifact:", f.filePath);
const filePath = normalizePath(f.filePath);
if (!filePath) {
const msg = `Invalid filePath: ${f.filePath}`;
console.error(msg);
errors.push(msg);
continue;
}
if (typeof f.content !== "string" || !f.content.trim()) {
const msg = `No content to write for ${filePath}`;
console.error(msg);
errors.push(msg);
continue;
}
try {
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, f.content, "utf-8");
console.log(chalk.green(`✅ Wrote file: ${filePath}`));
writtenFiles.push(filePath);
}
catch (err) {
const msg = err?.message ?? String(err);
console.error(chalk.red(`❌ Failed writing ${filePath}: ${msg}`));
errors.push(`${filePath}: ${msg}`);
}
}
// mark files as touched if not already
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);
}
}
console.log("writeFileStep: touchedFiles after run =", context.plan.touchedFiles);
const output = {
query: input.query,
data: { writeMode: mode, writtenFiles, errors }
};
logInputOutput("writeFile", "output", output.data);
console.log(">>> writeFileStep complete");
return output;
}
};