scai 0.1.117 → 0.1.118

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.
Files changed (95) hide show
  1. package/dist/agents/MainAgent.js +255 -0
  2. package/dist/agents/contextReviewStep.js +104 -0
  3. package/dist/agents/finalPlanGenStep.js +123 -0
  4. package/dist/agents/infoPlanGenStep.js +126 -0
  5. package/dist/agents/planGeneratorStep.js +118 -0
  6. package/dist/agents/planResolverStep.js +95 -0
  7. package/dist/agents/planTargetFilesStep.js +48 -0
  8. package/dist/agents/preFileSearchCheckStep.js +95 -0
  9. package/dist/agents/selectRelevantSourcesStep.js +100 -0
  10. package/dist/agents/semanticAnalysisStep.js +144 -0
  11. package/dist/agents/structuralAnalysisStep.js +46 -0
  12. package/dist/agents/transformPlanGenStep.js +107 -0
  13. package/dist/agents/understandIntentStep.js +72 -0
  14. package/dist/agents/validationAnalysisStep.js +87 -0
  15. package/dist/commands/AskCmd.js +47 -116
  16. package/dist/commands/ChangeLogUpdateCmd.js +11 -5
  17. package/dist/commands/CommitSuggesterCmd.js +50 -75
  18. package/dist/commands/DaemonCmd.js +119 -29
  19. package/dist/commands/IndexCmd.js +41 -24
  20. package/dist/commands/InspectCmd.js +0 -1
  21. package/dist/commands/ReadlineSingleton.js +18 -0
  22. package/dist/commands/ResetDbCmd.js +20 -21
  23. package/dist/commands/ReviewCmd.js +89 -54
  24. package/dist/commands/SummaryCmd.js +12 -18
  25. package/dist/commands/WorkflowCmd.js +41 -0
  26. package/dist/commands/factory.js +254 -0
  27. package/dist/config.js +67 -15
  28. package/dist/constants.js +20 -4
  29. package/dist/context.js +10 -11
  30. package/dist/daemon/daemonQueues.js +63 -0
  31. package/dist/daemon/daemonWorker.js +40 -63
  32. package/dist/daemon/generateSummaries.js +58 -0
  33. package/dist/daemon/runFolderCapsuleBatch.js +247 -0
  34. package/dist/daemon/runIndexingBatch.js +147 -0
  35. package/dist/daemon/runKgBatch.js +104 -0
  36. package/dist/db/fileIndex.js +168 -63
  37. package/dist/db/functionExtractors/extractFromJava.js +210 -6
  38. package/dist/db/functionExtractors/extractFromJs.js +173 -214
  39. package/dist/db/functionExtractors/extractFromTs.js +159 -160
  40. package/dist/db/functionExtractors/index.js +7 -5
  41. package/dist/db/schema.js +55 -20
  42. package/dist/db/sqlTemplates.js +50 -19
  43. package/dist/fileRules/builtins.js +31 -14
  44. package/dist/fileRules/codeAllowedExtensions.js +4 -0
  45. package/dist/fileRules/fileExceptions.js +0 -13
  46. package/dist/fileRules/ignoredExtensions.js +10 -0
  47. package/dist/index.js +128 -325
  48. package/dist/lib/generate.js +37 -14
  49. package/dist/lib/generateFolderCapsules.js +109 -0
  50. package/dist/lib/spinner.js +12 -5
  51. package/dist/modelSetup.js +0 -10
  52. package/dist/pipeline/modules/changeLogModule.js +16 -19
  53. package/dist/pipeline/modules/chunkManagerModule.js +24 -0
  54. package/dist/pipeline/modules/cleanupModule.js +96 -91
  55. package/dist/pipeline/modules/codeTransformModule.js +208 -0
  56. package/dist/pipeline/modules/commentModule.js +20 -11
  57. package/dist/pipeline/modules/commitSuggesterModule.js +36 -14
  58. package/dist/pipeline/modules/contextReviewModule.js +52 -0
  59. package/dist/pipeline/modules/fileReaderModule.js +72 -0
  60. package/dist/pipeline/modules/fileSearchModule.js +136 -0
  61. package/dist/pipeline/modules/finalAnswerModule.js +53 -0
  62. package/dist/pipeline/modules/gatherInfoModule.js +176 -0
  63. package/dist/pipeline/modules/generateTestsModule.js +63 -54
  64. package/dist/pipeline/modules/kgModule.js +26 -11
  65. package/dist/pipeline/modules/preserveCodeModule.js +91 -49
  66. package/dist/pipeline/modules/refactorModule.js +19 -7
  67. package/dist/pipeline/modules/repairTestsModule.js +44 -36
  68. package/dist/pipeline/modules/reviewModule.js +23 -13
  69. package/dist/pipeline/modules/summaryModule.js +27 -35
  70. package/dist/pipeline/modules/writeFileModule.js +86 -0
  71. package/dist/pipeline/registry/moduleRegistry.js +38 -93
  72. package/dist/pipeline/runModulePipeline.js +22 -19
  73. package/dist/scripts/dbcheck.js +143 -228
  74. package/dist/utils/buildContextualPrompt.js +245 -172
  75. package/dist/utils/debugContext.js +24 -0
  76. package/dist/utils/fileTree.js +16 -6
  77. package/dist/utils/loadRelevantFolderCapsules.js +64 -0
  78. package/dist/utils/log.js +2 -0
  79. package/dist/utils/normalizeData.js +23 -0
  80. package/dist/utils/planActions.js +60 -0
  81. package/dist/utils/promptBuilderHelper.js +67 -0
  82. package/dist/utils/promptLogHelper.js +52 -0
  83. package/dist/utils/sanitizeQuery.js +20 -8
  84. package/dist/utils/sleep.js +3 -0
  85. package/dist/utils/splitCodeIntoChunk.js +65 -32
  86. package/dist/utils/vscode.js +49 -0
  87. package/dist/workflow/workflowResolver.js +14 -0
  88. package/dist/workflow/workflowRunner.js +103 -0
  89. package/package.json +6 -5
  90. package/dist/agent/agentManager.js +0 -39
  91. package/dist/agent/workflowManager.js +0 -95
  92. package/dist/commands/ModulePipelineCmd.js +0 -31
  93. package/dist/daemon/daemonBatch.js +0 -186
  94. package/dist/fileRules/scoreFiles.js +0 -71
  95. package/dist/lib/generateEmbedding.js +0 -22
@@ -1,59 +1,68 @@
1
- import path from 'path';
1
+ "use strict";
2
+ /* import path from 'path';
3
+ import { PromptModule } from '../../types.js';
2
4
  import { generate } from '../../lib/generate.js';
3
5
  import { detectFileType } from '../../fileRules/detectFileType.js';
4
6
  import { Config } from '../../config.js';
5
- export const generateTestsModule = {
6
- name: 'tests',
7
- description: 'Generate a Jest test file for the class/module',
8
- async run({ content, filepath }) {
9
- if (!filepath)
10
- throw new Error('Missing filepath in pipeline context');
11
- const model = Config.getModel();
12
- const lang = detectFileType(filepath);
13
- const repoRoot = Config.getIndexDir();
14
- // Compute relative import path (repo-relative, without extension)
15
- const relativePath = path.relative(repoRoot, filepath);
16
- const { dir, name, ext } = path.parse(relativePath);
17
- const importPath = './' + path.join(dir, name).replace(/\\/g, '/');
18
- // Where the test should be written (next to source file)
19
- const absParsed = path.parse(filepath);
20
- const testPath = path.join(absParsed.dir, `${absParsed.name}.test${ext}`);
21
- const prompt = `
22
- You are a senior ${lang.toUpperCase()} engineer. Generate a Jest test file for the module below.
23
-
24
- Requirements:
25
- - Use the 'jest' test framework.
26
- - Always include imports at the top:
27
- import { describe, it, expect } from '@jest/globals';
28
- import * as moduleUnderTest from '${importPath}';
29
- - Cover only one public method: the most relevant or central function.
30
- - Include one edge case for that method.
31
- - Preserve and consider existing code comments in the module.
32
- - Only output valid ${lang} code; do not include markdown fences or explanations.
33
- - Use this scaffold at minimum:
34
-
35
- import { describe, it, expect } from '@jest/globals';
36
- import * as moduleUnderTest from '${importPath}';
37
-
38
- describe('moduleUnderTest', () => {
39
- it('should ...', () => {
40
- // test implementation
41
- });
42
- });
43
-
44
- --- MODULE CODE ---
45
- ${content}
46
- --- END MODULE CODE ---
7
+
8
+ export const generateTestsModule: PromptModule = {
9
+ name: 'tests',
10
+ description: 'Generate a Jest test file for the class/module',
11
+
12
+ async run({ content, filepath }) {
13
+ if (!filepath) throw new Error('Missing filepath in pipeline context');
14
+
15
+ const model = Config.getModel();
16
+ const lang = detectFileType(filepath);
17
+ const repoRoot = Config.getIndexDir();
18
+
19
+ // Compute relative import path (repo-relative, without extension)
20
+ const relativePath = path.relative(repoRoot, filepath);
21
+ const { dir, name, ext } = path.parse(relativePath);
22
+ const importPath = './' + path.join(dir, name).replace(/\\/g, '/');
23
+
24
+ // Where the test should be written (next to source file)
25
+ const absParsed = path.parse(filepath);
26
+ const testPath = path.join(absParsed.dir, `${absParsed.name}.test${ext}`);
27
+
28
+ const prompt = `
29
+ You are a senior ${lang.toUpperCase()} engineer. Generate a Jest test file for the module below.
30
+
31
+ Requirements:
32
+ - Use the 'jest' test framework.
33
+ - Always include imports at the top:
34
+ import { describe, it, expect } from '@jest/globals';
35
+ import * as moduleUnderTest from '${importPath}';
36
+ - Cover only one public method: the most relevant or central function.
37
+ - Include one edge case for that method.
38
+ - Preserve and consider existing code comments in the module.
39
+ - Only output valid ${lang} code; do not include markdown fences or explanations.
40
+ - Use this scaffold at minimum:
41
+
42
+ import { describe, it, expect } from '@jest/globals';
43
+ import * as moduleUnderTest from '${importPath}';
44
+
45
+ describe('moduleUnderTest', () => {
46
+ it('should ...', () => {
47
+ // test implementation
48
+ });
49
+ });
50
+
51
+ --- MODULE CODE ---
52
+ ${content}
53
+ --- END MODULE CODE ---
47
54
  `.trim();
48
- const response = await generate({ content: prompt });
49
- if (!response)
50
- throw new Error('⚠️ No test code returned from model');
51
- return {
52
- originalContent: content,
53
- content: response.content, // the test code
54
- filepath, // original file path
55
- newFilepath: testPath,
56
- mode: "newFile" // ensure it gets written as a new file
57
- };
58
- }
55
+
56
+ const response = await generate({ content: prompt });
57
+ if (!response) throw new Error('⚠️ No test code returned from model');
58
+
59
+ return {
60
+ originalContent: content,
61
+ content: response.content, // the test code
62
+ filepath, // original file path
63
+ newFilepath: testPath,
64
+ mode: "newFile" // ensure it gets written as a new file
65
+ };
66
+ }
59
67
  };
68
+ */
@@ -1,7 +1,9 @@
1
1
  import { Config } from '../../config.js';
2
2
  import { generate } from '../../lib/generate.js';
3
3
  import path from 'path';
4
+ import chalk from 'chalk';
4
5
  import { cleanupModule } from './cleanupModule.js';
6
+ import { normalizeData } from '../../utils/normalizeData.js';
5
7
  export const kgModule = {
6
8
  name: 'knowledge-graph',
7
9
  description: 'Generates a knowledge graph of entities, tags, and relationships from file content.',
@@ -9,6 +11,7 @@ export const kgModule = {
9
11
  const model = Config.getModel();
10
12
  const ext = input.filepath ? path.extname(input.filepath).toLowerCase() : '';
11
13
  const filename = input.filepath ? path.basename(input.filepath) : '';
14
+ // Build prompt for LLM
12
15
  const prompt = `
13
16
  You are an assistant specialized in building knowledge graphs from code or text.
14
17
 
@@ -21,7 +24,7 @@ Extension: ${ext}
21
24
  - Identify all entities (functions, classes, modules, or main concepts)
22
25
  - For each entity, generate tags describing its characteristics, purpose, or category
23
26
  - Identify relationships between entities (e.g., "uses", "extends", "calls")
24
- - Return output in JSON format with the following structure:
27
+ - Return output in **valid JSON** format with this structure:
25
28
 
26
29
  {
27
30
  "entities": [
@@ -32,24 +35,36 @@ Extension: ${ext}
32
35
  ]
33
36
  }
34
37
 
35
- Do NOT include raw content from the file. Only provide the structured JSON output.
38
+ ⚠️ Make sure all strings are safely JSON-encoded: escape quotes, backslashes, and newlines.
39
+
40
+ Do NOT include raw content from the file. Only provide structured JSON output.
36
41
 
37
42
  --- FILE CONTENT START ---
38
43
  ${content}
39
44
  --- FILE CONTENT END ---
40
45
  `.trim();
41
- const response = await generate({ content: prompt, filepath: input.filepath });
42
46
  try {
43
- // Clean the model output first
44
- const cleaned = await cleanupModule.run({ content: response.content });
45
- console.log("Cleaned knowledge graph data: ", cleaned);
46
- const jsonString = cleaned.content;
47
- const parsed = JSON.parse(jsonString);
47
+ // === INTERACTION #1: generate.ts (via ModuleIO) ===
48
+ const genInput = {
49
+ query: 'Extract entities and relationships for knowledge graph',
50
+ content: prompt,
51
+ };
52
+ const genOutput = await generate(genInput);
53
+ // === INTERACTION #2: cleanupModule (via ModuleIO) ===
54
+ const cleaned = await cleanupModule.run({
55
+ query: 'Clean up JSON for knowledge graph',
56
+ content: genOutput.data,
57
+ });
58
+ // === Normalize and parse JSON result ===
59
+ const parsed = normalizeData(cleaned.data);
60
+ // Ensure structure validity
61
+ parsed.entities ?? (parsed.entities = []);
62
+ parsed.edges ?? (parsed.edges = []);
48
63
  return parsed;
49
64
  }
50
65
  catch (err) {
51
- console.warn('⚠️ Failed to parse KG JSON:', err);
52
- return { entities: [], edges: [] }; // fallback
66
+ console.log(chalk.yellow(`⚠️ [KG] Garbage or invalid JSON output for ${input.filepath}`));
67
+ return { entities: [], edges: [] };
53
68
  }
54
- }
69
+ },
55
70
  };
@@ -9,63 +9,55 @@ export const preserveCodeModule = {
9
9
  if (!originalContent)
10
10
  throw new Error("Requires `originalContent`.");
11
11
  // Determine language from filepath extension
12
- console.log("Filepath: ", filepath);
13
12
  const ext = "." + (filepath?.split(".").pop() || "ts");
14
- console.log("Extension: ", ext);
15
- const language = detectFileType(filepath ?? ext); // returns "javascript", "python", etc.
13
+ const language = detectFileType(filepath ?? ext);
16
14
  const syntax = getCommentSyntax(language);
17
- console.log(`Using comment syntax for extension '${language}':`, syntax);
18
15
  // --- Normalize line endings ---
19
16
  const normalize = (txt) => txt.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
20
17
  const origLines = normalize(originalContent).split("\n");
21
18
  const newLines = normalize(content).split("\n");
22
- // --- Classify line ---
19
+ // --- Classify line for comment preservation ---
23
20
  let inBlockComment = false;
24
21
  let blockLines = [];
25
- // --- Classify line ---
26
- // returns: "code" | comment string | null
27
22
  const classifyLine = (line) => {
28
23
  const trimmed = line.trimStart();
29
- // --- Single-line comment ---
24
+ // Single-line comments
30
25
  for (const s of syntax.singleLine) {
31
26
  if (trimmed.startsWith(s))
32
27
  return line;
33
28
  }
34
- // --- Multi-line comment (optional) ---
35
29
  const multiLineComments = syntax.multiLine ?? [];
36
30
  if (!inBlockComment) {
37
- // check if line starts a multi-line comment
38
31
  for (const { start, end } of multiLineComments) {
39
- if (trimmed.startsWith(start)) {
32
+ if (trimmed.startsWith(start) || trimmed.startsWith("/**")) {
40
33
  blockLines = [line];
41
34
  if (trimmed.includes(end) && trimmed.indexOf(end) > trimmed.indexOf(start)) {
42
- // entire block on one line
43
35
  return blockLines.join("\n");
44
36
  }
45
37
  else {
46
38
  inBlockComment = true;
47
- return null; // wait for block to finish
39
+ return null;
48
40
  }
49
41
  }
50
42
  }
43
+ // ✅ New: Handle stray lines that look like part of a comment
44
+ if (trimmed.startsWith("*")) {
45
+ return line;
46
+ }
51
47
  }
52
48
  else {
53
- // currently inside a multi-line block
54
49
  blockLines.push(line);
55
- for (const { end } of multiLineComments) {
56
- if (trimmed.includes(end)) {
57
- inBlockComment = false;
58
- const fullBlock = blockLines.join("\n");
59
- blockLines = [];
60
- return fullBlock; // emit entire block
61
- }
50
+ if (trimmed.includes("*/")) {
51
+ inBlockComment = false;
52
+ const fullBlock = blockLines.join("\n");
53
+ blockLines = [];
54
+ return fullBlock;
62
55
  }
63
56
  return null; // still inside block
64
57
  }
65
- // --- default: code ---
66
58
  return "code";
67
59
  };
68
- // --- Helper: collect comment blocks into map ---
60
+ // --- Collect comments into map ---
69
61
  function collectCommentsMap(lines) {
70
62
  const map = new Map();
71
63
  let commentBuffer = [];
@@ -82,13 +74,9 @@ export const preserveCodeModule = {
82
74
  }
83
75
  continue;
84
76
  }
85
- if (typeof result === "string") {
86
- // comment line or block
77
+ if (typeof result === "string")
87
78
  commentBuffer.push(result);
88
- }
89
- // result === null => inside multi-line block, do nothing
90
79
  }
91
- // flush at EOF
92
80
  if (commentBuffer.length > 0) {
93
81
  const key = "";
94
82
  const block = commentBuffer.join("\n").trim().toLowerCase();
@@ -96,12 +84,20 @@ export const preserveCodeModule = {
96
84
  map.set(key, new Set());
97
85
  map.get(key).add(block);
98
86
  }
87
+ if (blockLines.length > 0) {
88
+ const key = "";
89
+ const block = blockLines.join("\n").trim().toLowerCase();
90
+ if (!map.has(key))
91
+ map.set(key, new Set());
92
+ map.get(key).add(block);
93
+ blockLines = [];
94
+ }
99
95
  return map;
100
96
  }
101
97
  // --- Step 1: Collect comments ---
102
- const modelComments = collectCommentsMap(newLines); // model first
103
- const origComments = collectCommentsMap(origLines); // original
104
- // --- Step 2: Remove duplicates from model (since we insert model) ---
98
+ const modelComments = collectCommentsMap(newLines);
99
+ const origComments = collectCommentsMap(origLines);
100
+ // --- Step 2: Remove duplicates ---
105
101
  for (const [key, modelSet] of modelComments.entries()) {
106
102
  const origSet = origComments.get(key);
107
103
  if (!origSet)
@@ -114,40 +110,86 @@ export const preserveCodeModule = {
114
110
  modelComments.delete(key);
115
111
  }
116
112
  // --- Step 3: Build fixed lines ---
117
- const fixedLines = [];
113
+ let fixedLines = [];
118
114
  for (const origLine of origLines) {
119
115
  const key = origLine.trim().toLowerCase();
120
116
  if (modelComments.has(key)) {
121
117
  for (const block of modelComments.get(key)) {
122
118
  for (const line of block.split("\n")) {
123
119
  const norm = line.trim().toLowerCase();
124
- const already = fixedLines.some(l => l.trim().toLowerCase() === norm);
125
- if (!already) {
120
+ if (!fixedLines.some(l => l.trim().toLowerCase() === norm)) {
126
121
  fixedLines.push(line);
127
- console.log(chalk.blue("Inserted comment:"), line.trim());
128
- }
129
- else {
130
- console.log(chalk.gray("Skipped duplicate:"), line.trim());
131
122
  }
132
123
  }
133
124
  }
134
125
  }
135
- fixedLines.push(origLine); // always keep original
126
+ fixedLines.push(origLine);
136
127
  }
137
- // --- Logging ---
138
- console.log(chalk.bold.blue("\n=== FIXED CONTENT ==="));
139
- fixedLines.forEach((line, i) => {
140
- const trimmed = line.trimStart();
141
- const isComment = syntax.singleLine.some(s => trimmed.startsWith(s)) ||
142
- (syntax.multiLine ?? []).some(({ start }) => trimmed.startsWith(start));
143
- const type = isComment ? "comment" : "code";
144
- const colored = type === "code" ? chalk.green(line) : chalk.yellow(line);
145
- console.log(`${i + 1}: ${colored} ${chalk.gray(`[${type}]`)}`);
128
+ // --- Step 4: Remove opening/closing fences at top/bottom ---
129
+ while (fixedLines.length && /^```(?:\w+)?$/.test(fixedLines[0].trim())) {
130
+ fixedLines.shift();
131
+ }
132
+ while (fixedLines.length && /^```(?:\w+)?$/.test(fixedLines[fixedLines.length - 1].trim())) {
133
+ fixedLines.pop();
134
+ }
135
+ // --- Step 5: Remove opening/closing fences at top/bottom ---
136
+ while (fixedLines.length && /^```(?:\w+)?$/.test(fixedLines[0].trim())) {
137
+ console.log(chalk.red(`[preserveCodeModule] Removing top fence: "${fixedLines[0].trim()}"`));
138
+ fixedLines.shift();
139
+ }
140
+ while (fixedLines.length && /^```(?:\w+)?$/.test(fixedLines[fixedLines.length - 1].trim())) {
141
+ console.log(chalk.red(`[preserveCodeModule] Removing bottom fence: "${fixedLines[fixedLines.length - 1].trim()}"`));
142
+ fixedLines.pop();
143
+ }
144
+ // --- Step 6: Remove any lingering triple ticks inside content ---
145
+ fixedLines = fixedLines.filter(line => {
146
+ if (/^```(?:\w+)?$/.test(line.trim())) {
147
+ console.log(chalk.red(`[preserveCodeModule] Removing lingering fence: "${line.trim()}"`));
148
+ return false;
149
+ }
150
+ return true;
146
151
  });
152
+ // --- Step 7 (final): Clean stray '*' and '*/' lines ---
153
+ let insideBlock = false;
154
+ fixedLines = fixedLines.map(line => {
155
+ const trimmed = line.trim();
156
+ // Enter block comment
157
+ if (trimmed.startsWith("/*") || trimmed.startsWith("/**")) {
158
+ insideBlock = true;
159
+ return line;
160
+ }
161
+ // Exit block comment
162
+ if (trimmed.startsWith("*/")) {
163
+ if (insideBlock) {
164
+ insideBlock = false;
165
+ return line; // keep valid closer
166
+ }
167
+ else {
168
+ return ""; // remove stray closer
169
+ }
170
+ }
171
+ // If inside a block, keep '*' lines as-is
172
+ if (insideBlock && trimmed.startsWith("*")) {
173
+ return line;
174
+ }
175
+ // If not inside a block but line starts with '*'
176
+ if (!insideBlock && trimmed.startsWith("*")) {
177
+ const afterStar = trimmed.slice(1).trim();
178
+ // ✅ If it's a JSDoc tag (starts with @), keep as-is
179
+ if (afterStar.startsWith("@")) {
180
+ return line;
181
+ }
182
+ // Otherwise, treat as stray and convert to //
183
+ const indent = line.slice(0, line.indexOf("*"));
184
+ return indent + "//" + line.slice(line.indexOf("*") + 1);
185
+ }
186
+ return line;
187
+ }).filter(line => line !== ""); // remove empty stray lines
188
+ // --- Return PromptOutput ---
147
189
  return {
148
190
  content: fixedLines.join("\n"),
149
191
  filepath,
150
- mode: "overwrite"
192
+ mode: "overwrite",
151
193
  };
152
194
  }
153
195
  };
@@ -1,11 +1,15 @@
1
+ // File: src/pipeline/modules/refactorModule.ts
1
2
  import { Config } from '../../config.js';
2
3
  import { generate } from '../../lib/generate.js';
3
4
  export const refactorModule = {
4
5
  name: 'refactor',
5
6
  description: 'Break code into small, clean functions',
6
- async run(input) {
7
- const model = Config.getModel();
7
+ run: async (input) => {
8
8
  const lang = Config.getLanguage();
9
+ const code = typeof input.content === 'string' ? input.content : '';
10
+ if (!code) {
11
+ throw new Error('⚠️ No code provided for refactoring.');
12
+ }
9
13
  const prompt = `
10
14
  You are a senior ${lang.toUpperCase()} engineer.
11
15
 
@@ -16,13 +20,21 @@ Refactor the following code:
16
20
  - Output the full, valid ${lang.toUpperCase()} code
17
21
 
18
22
  --- CODE START ---
19
- ${input.content}
23
+ ${code}
20
24
  --- CODE END ---
21
25
  `.trim();
22
- const response = await generate({ content: prompt });
23
- if (!response) {
26
+ const response = await generate({
27
+ query: input.query ?? '',
28
+ content: prompt,
29
+ });
30
+ if (!response || !response.content) {
24
31
  throw new Error('❌ Model returned empty response for refactoring.');
25
32
  }
26
- return { content: response.content };
27
- }
33
+ const output = {
34
+ query: input.query ?? '',
35
+ content: response.content,
36
+ data: { refactoredCode: response.content },
37
+ };
38
+ return output;
39
+ },
28
40
  };
@@ -1,40 +1,48 @@
1
+ "use strict";
2
+ /* import { PromptModule, PromptInput, PromptOutput } from "../../types.js";
1
3
  import { generate } from "../../lib/generate.js";
2
4
  import { Config } from "../../config.js";
3
- export const repairTestsModule = {
4
- name: "repairTestsModule",
5
- description: "Fix failing Jest tests using AI",
6
- async run({ content, filepath, summary }) {
7
- const model = Config.getModel();
8
- const prompt = `
9
- You are a senior engineer tasked with repairing Jest tests.
10
-
11
- The following test file failed:
12
-
13
- --- BEGIN TEST FILE ---
14
- ${content}
15
- --- END TEST FILE ---
16
-
17
- Failure summary:
18
- ${summary || "No summary provided."}
19
-
20
- Instructions:
21
- - Keep the overall structure, imports, and test cases.
22
- - Only fix syntax errors, invalid Jest matchers, or broken references.
23
- - Do NOT remove or replace entire test suites unless strictly necessary.
24
- - Do NOT generate trivial placeholder tests (like add(2,3) examples).
25
- - Only return valid Jest code, no explanations, no markdown fences.
26
-
27
- Output the repaired test file:
5
+
6
+ export const repairTestsModule: PromptModule = {
7
+ name: "repairTestsModule",
8
+ description: "Fix failing Jest tests using AI",
9
+
10
+ async run({ content, filepath, summary }: PromptInput): Promise<PromptOutput> {
11
+ const model = Config.getModel();
12
+
13
+ const prompt = `
14
+ You are a senior engineer tasked with repairing Jest tests.
15
+
16
+ The following test file failed:
17
+
18
+ --- BEGIN TEST FILE ---
19
+ ${content}
20
+ --- END TEST FILE ---
21
+
22
+ Failure summary:
23
+ ${summary || "No summary provided."}
24
+
25
+ Instructions:
26
+ - Keep the overall structure, imports, and test cases.
27
+ - Only fix syntax errors, invalid Jest matchers, or broken references.
28
+ - Do NOT remove or replace entire test suites unless strictly necessary.
29
+ - Do NOT generate trivial placeholder tests (like add(2,3) examples).
30
+ - Only return valid Jest code, no explanations, no markdown fences.
31
+
32
+ Output the repaired test file:
28
33
  `.trim();
29
- const response = await generate({ content: prompt });
30
- if (!response)
31
- throw new Error("⚠️ No repaired test code returned from model");
32
- return {
33
- originalContent: content,
34
- content: response.content, // repaired test code
35
- filepath, // repair in-place
36
- summary: `Repaired tests based on failure: ${summary || "unknown error"}`,
37
- mode: "overwrite" // signal to overwrite existing test file
38
- };
39
- }
34
+
35
+
36
+ const response = await generate({ content: prompt });
37
+ if (!response) throw new Error("⚠️ No repaired test code returned from model");
38
+
39
+ return {
40
+ originalContent: content,
41
+ content: response.content, // repaired test code
42
+ filepath, // repair in-place
43
+ summary: `Repaired tests based on failure: ${summary || "unknown error"}`,
44
+ mode: "overwrite" // signal to overwrite existing test file
45
+ };
46
+ }
40
47
  };
48
+ */
@@ -1,10 +1,14 @@
1
+ // File: src/pipeline/modules/reviewModule.ts
1
2
  import { generate } from '../../lib/generate.js';
2
- import { Config } from '../../config.js';
3
3
  export const reviewModule = {
4
4
  name: 'review',
5
- description: 'Reviews code diff or PR content and provides feedback',
6
- async run({ content, filepath }) {
7
- const model = Config.getModel();
5
+ description: 'Reviews code diff or PR content and provides actionable suggestions',
6
+ run: async (input) => {
7
+ const query = input.query ?? '';
8
+ const codeDiff = typeof input.content === 'string' ? input.content : '';
9
+ if (!codeDiff) {
10
+ throw new Error('⚠️ No code diff provided for review.');
11
+ }
8
12
  const prompt = `
9
13
  Suggest ALWAYS 3 concise suggestions for improvements based on the input code diff.
10
14
 
@@ -18,20 +22,26 @@ Format your response exactly as:
18
22
  3. <type>: <message>
19
23
 
20
24
  Changes:
21
- ${content}
22
- `.trim();
23
- const response = await generate({ content: prompt, filepath });
25
+ ${codeDiff}
26
+ `.trim();
27
+ const response = await generate({
28
+ query,
29
+ content: prompt,
30
+ });
31
+ // Ensure content is string
32
+ const responseText = typeof response.content === 'string' ? response.content : JSON.stringify(response.content ?? '', null, 2);
24
33
  // Parse response: only keep numbered lines
25
- const lines = response.content
34
+ const lines = responseText
26
35
  .split('\n')
27
36
  .map(line => line.trim())
28
37
  .filter(line => /^\d+\.\s+/.test(line));
29
38
  // Remove numbering and any surrounding quotes
30
39
  const suggestions = lines.map(line => line.replace(/^\d+\.\s+/, '').replace(/^"(.*)"$/, '$1').trim());
31
- return {
32
- content: response.content,
33
- filepath,
34
- suggestions
40
+ const output = {
41
+ query,
42
+ content: responseText, // raw model output
43
+ data: { suggestions }, // structured suggestions
35
44
  };
36
- }
45
+ return output;
46
+ },
37
47
  };