scai 0.1.138 → 0.1.139

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -230,8 +230,8 @@ export class MainAgent {
230
230
  userOutput("All input/output logs can be found at ~/.scai/input_output.log");
231
231
  logLine("RUN", "complete", stopRun());
232
232
  // ───────────── LOG FINAL CONTEXT ─────────────
233
- /* console.log("\n[DEBUG] Final MainAgent context:");
234
- console.dir(this.context, { depth: null, colors: true }); */
233
+ console.log("\n[DEBUG] Final MainAgent context:");
234
+ console.dir(this.context, { depth: null, colors: true });
235
235
  return stepIO;
236
236
  }
237
237
  /* ───────────── helpers ───────────── */
@@ -1,6 +1,7 @@
1
1
  // File: src/modules/writeFileModule.ts
2
2
  import fs from "fs/promises";
3
3
  import path from "path";
4
+ import chalk from "chalk";
4
5
  import { normalizePath } from "../utils/contentUtils.js";
5
6
  import { logInputOutput } from "../utils/promptLogHelper.js";
6
7
  export const writeFileStep = {
@@ -10,54 +11,54 @@ export const writeFileStep = {
10
11
  groups: ["finalize"],
11
12
  run: async (input) => {
12
13
  var _a;
14
+ console.log(">>> writeFileStep starting");
13
15
  const context = input.context;
14
16
  const mode = input.data?.mode ?? "overwrite";
15
17
  const writtenFiles = [];
16
18
  const errors = [];
17
- /* ───────────── guard ───────────── */
18
19
  if (!context) {
19
- return {
20
- query: input.query,
21
- data: {
22
- writeMode: mode,
23
- writtenFiles,
24
- errors: ["Missing execution context"]
25
- }
26
- };
20
+ console.error("writeFileStep: Missing execution context");
21
+ return { query: input.query, data: { writeMode: mode, writtenFiles, errors: ["Missing execution context"] } };
27
22
  }
23
+ console.log("writeFileStep: context exists");
24
+ console.log("writeFileStep: mode =", mode);
25
+ console.log("writeFileStep: existing touchedFiles =", context.plan?.touchedFiles);
28
26
  const artifacts = context.execution?.codeTransformArtifacts?.files ?? [];
29
- if (!artifacts.length) {
30
- return {
31
- query: input.query,
32
- data: {
33
- writeMode: mode,
34
- writtenFiles,
35
- errors: ["No codeTransform artifacts found to write"]
36
- }
37
- };
27
+ console.log("writeFileStep: found codeTransformArtifacts.files =", artifacts.length);
28
+ if (artifacts.length) {
29
+ console.log("writeFileStep: filePaths =", artifacts.map(f => f.filePath));
30
+ }
31
+ else {
32
+ console.warn("writeFileStep: No artifacts found to write");
38
33
  }
39
- /* ───────────── write files ───────────── */
40
34
  for (const f of artifacts) {
35
+ console.log("Processing artifact:", f.filePath);
41
36
  const filePath = normalizePath(f.filePath);
42
37
  if (!filePath) {
43
- errors.push(`Invalid filePath: ${f.filePath}`);
38
+ const msg = `Invalid filePath: ${f.filePath}`;
39
+ console.error(msg);
40
+ errors.push(msg);
44
41
  continue;
45
42
  }
46
43
  if (typeof f.content !== "string" || !f.content.trim()) {
47
- errors.push(`No content to write for ${filePath}`);
44
+ const msg = `No content to write for ${filePath}`;
45
+ console.error(msg);
46
+ errors.push(msg);
48
47
  continue;
49
48
  }
50
49
  try {
51
50
  await fs.mkdir(path.dirname(filePath), { recursive: true });
52
51
  await fs.writeFile(filePath, f.content, "utf-8");
52
+ console.log(chalk.green(`✅ Wrote file: ${filePath}`));
53
53
  writtenFiles.push(filePath);
54
54
  }
55
55
  catch (err) {
56
56
  const msg = err?.message ?? String(err);
57
+ console.error(chalk.red(`❌ Failed writing ${filePath}: ${msg}`));
57
58
  errors.push(`${filePath}: ${msg}`);
58
59
  }
59
60
  }
60
- /* ───────────── mark touched files ───────────── */
61
+ // mark files as touched if not already
61
62
  context.plan ?? (context.plan = {});
62
63
  (_a = context.plan).touchedFiles ?? (_a.touchedFiles = []);
63
64
  for (const file of writtenFiles) {
@@ -65,15 +66,13 @@ export const writeFileStep = {
65
66
  context.plan.touchedFiles.push(file);
66
67
  }
67
68
  }
69
+ console.log("writeFileStep: touchedFiles after run =", context.plan.touchedFiles);
68
70
  const output = {
69
71
  query: input.query,
70
- data: {
71
- writeMode: mode,
72
- writtenFiles,
73
- errors
74
- }
72
+ data: { writeMode: mode, writtenFiles, errors }
75
73
  };
76
74
  logInputOutput("writeFile", "output", output.data);
75
+ console.log(">>> writeFileStep complete");
77
76
  return output;
78
77
  }
79
78
  };
@@ -31,7 +31,7 @@ export const codeTransformModule = {
31
31
  description: "Transforms a single file specified in the current plan step based on user instruction.",
32
32
  groups: ["transform"],
33
33
  run: async (input) => {
34
- var _a, _b, _c, _d;
34
+ var _a, _b, _c;
35
35
  const query = typeof input.query === "string" ? input.query : String(input.query ?? "");
36
36
  const context = input.context;
37
37
  if (!context) {
@@ -48,10 +48,8 @@ export const codeTransformModule = {
48
48
  return { query, data: { files: [], errors: [`Target file not found or missing code: ${targetFile}`] } };
49
49
  }
50
50
  const normalizedQuery = context.analysis?.intent?.normalizedQuery ?? query;
51
- // 🔹 NEW: extract proposed changes for this file (if any)
52
- const fileAnalysis = context.analysis?.fileAnalysis?.[file.path]; // ✅ correct
51
+ const fileAnalysis = context.analysis?.fileAnalysis?.[file.path];
53
52
  const proposed = fileAnalysis?.proposedChanges;
54
- // 🔹 NEW: pre-render guidance block for prompts
55
53
  const proposedChangesBlock = proposed
56
54
  ? `
57
55
  Proposed changes for this file:
@@ -64,6 +62,8 @@ ${proposed.rationale ? `- Rationale: ${proposed.rationale}` : ""}
64
62
  const outputs = [];
65
63
  const perFileErrors = [];
66
64
  const tokenCount = countTokens(file.code);
65
+ context.execution || (context.execution = {});
66
+ (_a = context.execution).codeTransformArtifacts || (_a.codeTransformArtifacts = { files: [] });
67
67
  // ───────────── SMALL FILE ─────────────
68
68
  if (tokenCount <= SINGLE_SHOT_TOKEN_LIMIT) {
69
69
  logInputOutput("codeTransform", "output", {
@@ -109,29 +109,32 @@ JSON schema:
109
109
  responseSnippet: String(llmResponse.data ?? "").slice(0, 200),
110
110
  });
111
111
  const cleaned = await cleanupModule.run({ query, content: llmResponse.data });
112
- // Optional debug: show that cleanup returned raw code because it wasn't JSON
112
+ let finalContent;
113
+ let notes = undefined;
113
114
  if (typeof cleaned.data === 'string') {
114
- console.debug(chalk.yellow(` - [cleanupModule] Could not parse JSON. Likely non-JSON output (code), using cleaned content.`));
115
+ // Raw code returned
116
+ finalContent = cleaned.data;
117
+ console.debug(chalk.yellow(` - [cleanupModule] Using raw code output for ${file.path}`));
118
+ }
119
+ else if (cleaned.data &&
120
+ typeof cleaned.data === 'object' &&
121
+ Array.isArray(cleaned.data.files)) {
122
+ // Structured JSON returned
123
+ const structured = cleaned.data;
124
+ const out = structured.files.find(f => f.filePath === file.path);
125
+ finalContent = out?.content ?? file.code;
126
+ notes = out?.notes;
127
+ perFileErrors.push(...(structured.errors ?? []));
115
128
  }
116
- const structured = cleaned.data;
117
- const out = Array.isArray(structured?.files)
118
- ? structured.files.find((f) => f.filePath === file.path)
119
- : null;
120
- if (!out || typeof out.content !== "string") {
121
- return { query, data: { files: [], errors: [`Model did not return valid content for ${file.path}`] } };
129
+ else {
130
+ // Fallback
131
+ finalContent = file.code;
122
132
  }
123
- const finalContent = out.content.trim() ? out.content : file.code;
124
- outputs.push({ filePath: file.path, content: finalContent, notes: out.notes });
125
- perFileErrors.push(...(structured.errors ?? []));
126
- context.execution || (context.execution = {});
127
- (_a = context.execution).codeTransformArtifacts || (_a.codeTransformArtifacts = { files: [] });
133
+ outputs.push({ filePath: file.path, content: finalContent, notes });
134
+ // ensure artifacts are updated
128
135
  context.execution.codeTransformArtifacts.files =
129
136
  context.execution.codeTransformArtifacts.files.filter(f => f.filePath !== file.path);
130
- context.execution.codeTransformArtifacts.files.push({
131
- filePath: file.path,
132
- content: finalContent,
133
- notes: perFileErrors.length ? perFileErrors.join("; ") : undefined,
134
- });
137
+ context.execution.codeTransformArtifacts.files.push(outputs[0]);
135
138
  context.plan || (context.plan = {});
136
139
  (_b = context.plan).touchedFiles || (_b.touchedFiles = []);
137
140
  if (!context.plan.touchedFiles.includes(file.path))
@@ -141,12 +144,16 @@ JSON schema:
141
144
  return output;
142
145
  }
143
146
  catch (err) {
144
- return { query, data: { files: [], errors: [`LLM call or cleanup failed: ${err.message}`] } };
147
+ const finalContent = file.code;
148
+ outputs.push({ filePath: file.path, content: finalContent });
149
+ perFileErrors.push(`LLM call or cleanup failed: ${err.message}`);
150
+ context.execution.codeTransformArtifacts.files.push(outputs[0]);
151
+ const output = { query, data: { files: outputs, errors: perFileErrors } };
152
+ return output;
145
153
  }
146
154
  }
147
155
  // ───────────── LARGE FILE (chunked) ─────────────
148
156
  const chunks = splitCodeIntoChunks(file.code, CHUNK_TOKEN_LIMIT);
149
- // ⚠️ USER-FACING WARNING
150
157
  console.warn(chalk.yellow([
151
158
  "",
152
159
  "⚠️ Large file detected — falling back to chunked transformation",
@@ -215,13 +222,13 @@ ${chunk}
215
222
  logInputOutput("codeTransform", "output", { file: file.path, chunkIndex: i + 1, error: err.message, message: "Chunk transformation failed, original preserved" });
216
223
  }
217
224
  }
218
- logInputOutput("codeTransform", "output", { file: file.path, message: "Finished all chunks", totalChunks: chunks.length });
219
- outputs.push({ filePath: file.path, content: transformedChunks.join("\n") });
220
- context.execution || (context.execution = {});
221
- (_c = context.execution).codeTransformArtifacts || (_c.codeTransformArtifacts = { files: [] });
222
- context.execution.codeTransformArtifacts.files.push(...outputs);
225
+ const finalContent = transformedChunks.join("\n");
226
+ outputs.push({ filePath: file.path, content: finalContent });
227
+ context.execution.codeTransformArtifacts.files =
228
+ context.execution.codeTransformArtifacts.files.filter(f => f.filePath !== file.path);
229
+ context.execution.codeTransformArtifacts.files.push(outputs[0]);
223
230
  context.plan || (context.plan = {});
224
- (_d = context.plan).touchedFiles || (_d.touchedFiles = []);
231
+ (_c = context.plan).touchedFiles || (_c.touchedFiles = []);
225
232
  if (!context.plan.touchedFiles.includes(file.path))
226
233
  context.plan.touchedFiles.push(file.path);
227
234
  const output = { query, data: { files: outputs, errors: perFileErrors } };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.138",
3
+ "version": "0.1.139",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"