scai 0.1.86 → 0.1.88
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.
- package/dist/agentManager.js +2 -1
- package/dist/commands/ReviewCmd.js +4 -4
- package/dist/index.js +2 -2
- package/dist/pipeline/modules/commentModule.js +6 -5
- package/dist/pipeline/modules/preserveCodeModule.js +93 -0
- package/dist/pipeline/runModulePipeline.js +7 -7
- package/dist/utils/commentMap.js +79 -0
- package/package.json +4 -2
package/dist/agentManager.js
CHANGED
|
@@ -22,6 +22,7 @@ export async function handleAgentRun(filepath, modules) {
|
|
|
22
22
|
}
|
|
23
23
|
console.log(chalk.gray(` - Chunk ${i + 1} tokens:`), chalk.yellow(chunkTokens.toString()));
|
|
24
24
|
const chunkInput = {
|
|
25
|
+
originalContent: chunk,
|
|
25
26
|
content: chunk,
|
|
26
27
|
filepath,
|
|
27
28
|
chunkIndex: i,
|
|
@@ -32,7 +33,7 @@ export async function handleAgentRun(filepath, modules) {
|
|
|
32
33
|
throw new Error(`⚠️ Model returned empty result on chunk ${i + 1}`);
|
|
33
34
|
}
|
|
34
35
|
processedChunks.push(response.content);
|
|
35
|
-
console.log(chalk.green(`✅ Finished chunk ${i + 1}/${chunks.length}`));
|
|
36
|
+
//console.log(chalk.green(`✅ Finished chunk ${i + 1}/${chunks.length}`));
|
|
36
37
|
}
|
|
37
38
|
// Join all chunk outputs into one string
|
|
38
39
|
const finalOutput = processedChunks.join('\n\n');
|
|
@@ -308,8 +308,8 @@ export async function promptChunkReviewMenu() {
|
|
|
308
308
|
console.log(' 2) ✍️ Edit the review before posting');
|
|
309
309
|
console.log(' 3) ⌨️ Write a custom comment');
|
|
310
310
|
console.log(' 4) ❌ Mark this chunk as needing changes');
|
|
311
|
-
console.log(' 5) ⏭️
|
|
312
|
-
console.log(chalk.gray(' (Press [space] to skip chunk, [q] to quit review, or any other key to show menu)\n'));
|
|
311
|
+
console.log(' 5) ⏭️ Approve this chunk without commenting');
|
|
312
|
+
console.log(chalk.gray(' (Press [space] to skip and approve chunk, [q] to quit review, or any other key to show menu)\n'));
|
|
313
313
|
// Fallback to menu input if key was not space/q
|
|
314
314
|
function askWithReadline() {
|
|
315
315
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -434,8 +434,8 @@ export async function reviewPullRequestCmd(branch = 'main', showAll = false) {
|
|
|
434
434
|
// We always submit comments first if any
|
|
435
435
|
const initialReviewState = shouldApprove ? 'APPROVE' : 'REQUEST_CHANGES';
|
|
436
436
|
const initialReviewBody = shouldApprove
|
|
437
|
-
? 'Reviewed
|
|
438
|
-
: 'Requested changes based on
|
|
437
|
+
? '✅ Reviewed.'
|
|
438
|
+
: '⛔ Requested changes based on review.';
|
|
439
439
|
console.log(shouldApprove && !hasInlineComments
|
|
440
440
|
? chalk.green('✔️ All chunks approved. Submitting final PR approval.')
|
|
441
441
|
: !shouldApprove
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import { dirname, resolve } from "path";
|
|
|
29
29
|
import { handleAgentRun } from './agentManager.js';
|
|
30
30
|
import { addCommentsModule } from './pipeline/modules/commentModule.js';
|
|
31
31
|
import { generateTestsModule } from './pipeline/modules/generateTestsModule.js';
|
|
32
|
-
import {
|
|
32
|
+
import { preserveCodeModule } from './pipeline/modules/preserveCodeModule.js';
|
|
33
33
|
// 🎛️ CLI Setup
|
|
34
34
|
const cmd = new Command('scai')
|
|
35
35
|
.version(version)
|
|
@@ -106,7 +106,7 @@ gen
|
|
|
106
106
|
.command('comm <file>')
|
|
107
107
|
.description('Write comments for the given file')
|
|
108
108
|
.action((file) => {
|
|
109
|
-
handleAgentRun(file, [addCommentsModule,
|
|
109
|
+
handleAgentRun(file, [addCommentsModule, preserveCodeModule]);
|
|
110
110
|
});
|
|
111
111
|
gen
|
|
112
112
|
.command('changelog')
|
|
@@ -37,9 +37,6 @@ export const addCommentsModule = {
|
|
|
37
37
|
text: '#',
|
|
38
38
|
};
|
|
39
39
|
const commentSyntax = commentMap[fileType] || '//';
|
|
40
|
-
const chunkLabel = (input.chunkIndex && input.chunkCount && input.chunkCount > 1)
|
|
41
|
-
? `CHUNK ${input.chunkIndex + 1}/${input.chunkCount} of ${input.filepath}`
|
|
42
|
-
: `FULL FILE: ${input.filepath}`;
|
|
43
40
|
const prompt = `
|
|
44
41
|
You are a senior engineer reviewing a ${fileType} file.
|
|
45
42
|
|
|
@@ -56,15 +53,19 @@ Please:
|
|
|
56
53
|
|
|
57
54
|
4. Preserve all original formatting, whitespace, and code exactly.
|
|
58
55
|
|
|
56
|
+
5. You may only be shown parts of a file. Add comments as instructed.
|
|
57
|
+
|
|
59
58
|
Rules:
|
|
60
59
|
- Return the full original chunk of code with added comments only.
|
|
61
60
|
- Do NOT remove, change, or overwrite any existing code or comments.
|
|
62
61
|
- Summaries should be brief but meaningful.
|
|
63
62
|
- Inline comments should clarify complex or tricky parts only.
|
|
63
|
+
- Do NOT add any “chunk” start/end markers, numbering, or metadata to the output.
|
|
64
|
+
|
|
65
|
+
Code to comments is below this line:
|
|
64
66
|
|
|
65
|
-
--- ${chunkLabel} START ---
|
|
66
67
|
${input.content}
|
|
67
|
-
|
|
68
|
+
|
|
68
69
|
`.trim();
|
|
69
70
|
const response = await generate({ content: prompt }, model);
|
|
70
71
|
const contentToReturn = (response.content && response.content !== 'NO UPDATE') ? response.content : input.content;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export const preserveCodeModule = {
|
|
3
|
+
name: "preserveCodeModule",
|
|
4
|
+
description: "Ensure code matches original exactly, preserving comments with clear before/after output",
|
|
5
|
+
async run(input) {
|
|
6
|
+
const { originalContent, content, filepath, language: forcedLang } = input;
|
|
7
|
+
if (!originalContent)
|
|
8
|
+
throw new Error("Requires `originalContent`.");
|
|
9
|
+
const syntax = {
|
|
10
|
+
singleLine: ["//"],
|
|
11
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
12
|
+
};
|
|
13
|
+
// Detect comments
|
|
14
|
+
const isComment = (line) => {
|
|
15
|
+
const ltrim = line.trimStart();
|
|
16
|
+
for (const s of syntax.singleLine) {
|
|
17
|
+
if (ltrim.startsWith(s))
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (syntax.multiLine) {
|
|
21
|
+
for (const { start } of syntax.multiLine) {
|
|
22
|
+
if (ltrim.startsWith(start))
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
};
|
|
28
|
+
const origLines = originalContent.split("\n");
|
|
29
|
+
const newLines = content.split("\n");
|
|
30
|
+
const fixedLines = [];
|
|
31
|
+
let origIndex = 0;
|
|
32
|
+
let newIndex = 0;
|
|
33
|
+
while (origIndex < origLines.length) {
|
|
34
|
+
const origLine = origLines[origIndex];
|
|
35
|
+
let newLine = newLines[newIndex] ?? "";
|
|
36
|
+
// Preserve comments from model output if not already in original
|
|
37
|
+
while (newIndex < newLines.length && isComment(newLine)) {
|
|
38
|
+
if (origLine && newLine.trim() === origLine.trim()) {
|
|
39
|
+
// Comment already exists in original, advance both
|
|
40
|
+
fixedLines.push(origLine);
|
|
41
|
+
origIndex++;
|
|
42
|
+
newIndex++;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// New comment from model
|
|
46
|
+
fixedLines.push(newLine);
|
|
47
|
+
newIndex++;
|
|
48
|
+
}
|
|
49
|
+
newLine = newLines[newIndex] ?? "";
|
|
50
|
+
}
|
|
51
|
+
if (newLine.trim() === origLine.trim()) {
|
|
52
|
+
fixedLines.push(newLine);
|
|
53
|
+
origIndex++;
|
|
54
|
+
newIndex++;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
fixedLines.push(origLine);
|
|
58
|
+
origIndex++;
|
|
59
|
+
if (!isComment(newLine)) {
|
|
60
|
+
newIndex++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Add trailing comments from model
|
|
65
|
+
while (newIndex < newLines.length) {
|
|
66
|
+
if (isComment(newLines[newIndex])) {
|
|
67
|
+
fixedLines.push(newLines[newIndex]);
|
|
68
|
+
}
|
|
69
|
+
newIndex++;
|
|
70
|
+
}
|
|
71
|
+
// Diff display
|
|
72
|
+
console.log(chalk.bold.blue("\n=== ORIGINAL CONTENT ==="));
|
|
73
|
+
console.log(chalk.green(originalContent));
|
|
74
|
+
console.log(chalk.bold.blue("\n=== MODEL CONTENT ==="));
|
|
75
|
+
console.log(chalk.gray(content));
|
|
76
|
+
console.log(chalk.bold.blue("\n=== FIXED CONTENT ==="));
|
|
77
|
+
console.log(fixedLines
|
|
78
|
+
.map(line => {
|
|
79
|
+
const comment = isComment(line);
|
|
80
|
+
if (origLines.includes(line.trim()) && !comment) {
|
|
81
|
+
return chalk.green(line);
|
|
82
|
+
}
|
|
83
|
+
else if (comment) {
|
|
84
|
+
return chalk.yellow(line);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return chalk.red(line);
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
.join("\n"));
|
|
91
|
+
return { content: fixedLines.join("\n"), filepath };
|
|
92
|
+
}
|
|
93
|
+
};
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
export async function runModulePipeline(modules, input) {
|
|
3
3
|
let current = input;
|
|
4
|
-
const isDebug =
|
|
4
|
+
const isDebug = false;
|
|
5
5
|
for (const mod of modules) {
|
|
6
6
|
try {
|
|
7
|
-
if (isDebug) {
|
|
8
|
-
console.log(chalk.green('➡️ Input:', current.content));
|
|
9
|
-
}
|
|
10
7
|
const response = await mod.run(current);
|
|
11
8
|
console.log(`⚙️ Running: ${mod.name}`);
|
|
12
9
|
if (isDebug) {
|
|
13
|
-
console.log(chalk.
|
|
10
|
+
console.log(chalk.yellow('➡️ Output:', response.content));
|
|
14
11
|
}
|
|
15
|
-
//
|
|
16
|
-
current =
|
|
12
|
+
// Safeguard if originalContent is overwritten by module
|
|
13
|
+
current = {
|
|
14
|
+
...response,
|
|
15
|
+
originalContent: current.originalContent
|
|
16
|
+
};
|
|
17
17
|
}
|
|
18
18
|
catch (error) {
|
|
19
19
|
console.error(`❌ Error in ${mod.name}:`, error instanceof Error ? error.message : error);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// utils/commentMap.ts
|
|
2
|
+
export const commentMap = {
|
|
3
|
+
javascript: {
|
|
4
|
+
singleLine: ["//"],
|
|
5
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
6
|
+
},
|
|
7
|
+
typescript: {
|
|
8
|
+
singleLine: ["//"],
|
|
9
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
10
|
+
},
|
|
11
|
+
java: {
|
|
12
|
+
singleLine: ["//"],
|
|
13
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
14
|
+
},
|
|
15
|
+
c: {
|
|
16
|
+
singleLine: ["//"],
|
|
17
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
18
|
+
},
|
|
19
|
+
cpp: {
|
|
20
|
+
singleLine: ["//"],
|
|
21
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
22
|
+
},
|
|
23
|
+
csharp: {
|
|
24
|
+
singleLine: ["//"],
|
|
25
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
26
|
+
},
|
|
27
|
+
python: {
|
|
28
|
+
singleLine: ["#"],
|
|
29
|
+
multiLine: [
|
|
30
|
+
{ start: `"""`, end: `"""` },
|
|
31
|
+
{ start: `'''`, end: `'''` }
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
ruby: {
|
|
35
|
+
singleLine: ["#"],
|
|
36
|
+
multiLine: [{ start: `=begin`, end: `=end` }]
|
|
37
|
+
},
|
|
38
|
+
shell: {
|
|
39
|
+
singleLine: ["#"]
|
|
40
|
+
},
|
|
41
|
+
bash: {
|
|
42
|
+
singleLine: ["#"]
|
|
43
|
+
},
|
|
44
|
+
html: {
|
|
45
|
+
singleLine: [],
|
|
46
|
+
multiLine: [{ start: "<!--", end: "-->" }]
|
|
47
|
+
},
|
|
48
|
+
css: {
|
|
49
|
+
singleLine: [],
|
|
50
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
51
|
+
},
|
|
52
|
+
sql: {
|
|
53
|
+
singleLine: ["--"],
|
|
54
|
+
multiLine: []
|
|
55
|
+
},
|
|
56
|
+
xml: {
|
|
57
|
+
singleLine: [],
|
|
58
|
+
multiLine: [{ start: "<!--", end: "-->" }]
|
|
59
|
+
},
|
|
60
|
+
json: {
|
|
61
|
+
singleLine: [],
|
|
62
|
+
multiLine: [{ start: "/*", end: "*/" }]
|
|
63
|
+
},
|
|
64
|
+
markdown: {
|
|
65
|
+
singleLine: [],
|
|
66
|
+
multiLine: [{ start: "<!--", end: "-->" }]
|
|
67
|
+
},
|
|
68
|
+
text: {
|
|
69
|
+
singleLine: ["#"]
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Returns the CommentSyntax for a given language.
|
|
74
|
+
* Falls back to single-line `#` if language is unknown.
|
|
75
|
+
*/
|
|
76
|
+
export function getCommentSyntax(language) {
|
|
77
|
+
const normalized = language.toLowerCase();
|
|
78
|
+
return commentMap[normalized] || { singleLine: ["#"], multiLine: [] };
|
|
79
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.88",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"scai": "./dist/index.js"
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
"productivity",
|
|
30
30
|
"scai",
|
|
31
31
|
"review",
|
|
32
|
-
"commit"
|
|
32
|
+
"commit",
|
|
33
|
+
"agent",
|
|
34
|
+
"workflow"
|
|
33
35
|
],
|
|
34
36
|
"scripts": {
|
|
35
37
|
"build": "rm -rfd dist && tsc && git add .",
|