scai 0.1.104 → 0.1.105
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/CHANGELOG.md +8 -1
- package/dist/agent/agentManager.js +28 -0
- package/dist/{workflowManager.js → agent/workflowManager.js} +3 -3
- package/dist/index.js +31 -14
- package/dist/pipeline/modules/cleanGeneratedTestsModule.js +2 -1
- package/dist/pipeline/modules/generateTestsModule.js +33 -15
- package/dist/pipeline/registry/moduleRegistry.js +2 -2
- package/package.json +1 -1
package/dist/CHANGELOG.md
CHANGED
|
@@ -150,4 +150,11 @@ Type handling with the module pipeline
|
|
|
150
150
|
|
|
151
151
|
## 2025-08-30
|
|
152
152
|
|
|
153
|
-
* Add new workflow management functionality to handle file writes.
|
|
153
|
+
* Add new workflow management functionality to handle file writes.
|
|
154
|
+
|
|
155
|
+
## 2025-08-31
|
|
156
|
+
|
|
157
|
+
• Introduce Agent class with minimal implementation
|
|
158
|
+
• Improve test-module's filepath handling and variable naming
|
|
159
|
+
• Rename workflowManager.ts to agent/workflowManager.ts
|
|
160
|
+
• Improved formatting of agent run summary output
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/agent/agentManager.ts
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import fs from "fs/promises";
|
|
4
|
+
import { getModuleByName } from "../pipeline/registry/moduleRegistry.js";
|
|
5
|
+
import { handleAgentRun } from "./workflowManager.js";
|
|
6
|
+
// Minimal agent: just collects modules and delegates to handleAgentRun
|
|
7
|
+
export class Agent {
|
|
8
|
+
constructor(goals) {
|
|
9
|
+
// Trim goal names to avoid whitespace issues
|
|
10
|
+
this.goals = goals.map(g => g.trim());
|
|
11
|
+
}
|
|
12
|
+
async execute(filepath) {
|
|
13
|
+
console.log(chalk.cyan(`🤖 Agent starting on: ${filepath}`));
|
|
14
|
+
// Map goal names → module objects
|
|
15
|
+
const modules = this.goals.map(goal => {
|
|
16
|
+
const mod = getModuleByName(goal);
|
|
17
|
+
if (!mod)
|
|
18
|
+
throw new Error(`❌ Unknown module: ${goal}`);
|
|
19
|
+
return mod;
|
|
20
|
+
});
|
|
21
|
+
console.log(chalk.green("📋 Modules to run:"), modules.map(m => m.name).join(" → "));
|
|
22
|
+
// Read file content
|
|
23
|
+
const content = await fs.readFile(filepath, "utf-8");
|
|
24
|
+
// Delegate everything to handleAgentRun (like CLI commands do)
|
|
25
|
+
await handleAgentRun(filepath, modules);
|
|
26
|
+
console.log(chalk.green("✅ Agent finished!"));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// agentManager.ts
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { runModulePipeline } from '
|
|
5
|
-
import { countTokens, splitCodeIntoChunks } from '
|
|
6
|
-
import { normalizePath } from '
|
|
4
|
+
import { runModulePipeline } from '../pipeline/runModulePipeline.js';
|
|
5
|
+
import { countTokens, splitCodeIntoChunks } from '../utils/splitCodeIntoChunk.js';
|
|
6
|
+
import { normalizePath } from '../utils/contentUtils.js';
|
|
7
7
|
export async function handleAgentRun(filepath, modules) {
|
|
8
8
|
try {
|
|
9
9
|
filepath = normalizePath(filepath);
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,6 @@ import { suggestCommitMessage } from "./commands/CommitSuggesterCmd.js";
|
|
|
8
8
|
import { bootstrap } from './modelSetup.js';
|
|
9
9
|
import { summarizeFile } from "./commands/SummaryCmd.js";
|
|
10
10
|
import { handleStandaloneChangelogUpdate } from './commands/ChangeLogUpdateCmd.js';
|
|
11
|
-
import { runModulePipelineFromCLI } from './commands/ModulePipelineCmd.js';
|
|
12
11
|
import { runIndexCommand } from './commands/IndexCmd.js';
|
|
13
12
|
import { resetDatabase } from './commands/ResetDbCmd.js';
|
|
14
13
|
import { runFindCommand } from './commands/FindCmd.js';
|
|
@@ -26,7 +25,7 @@ import { runInteractiveSwitch } from "./commands/SwitchCmd.js";
|
|
|
26
25
|
import { execSync } from "child_process";
|
|
27
26
|
import { fileURLToPath } from "url";
|
|
28
27
|
import { dirname, resolve } from "path";
|
|
29
|
-
import { handleAgentRun } from './workflowManager.js';
|
|
28
|
+
import { handleAgentRun } from './agent/workflowManager.js';
|
|
30
29
|
import { addCommentsModule } from './pipeline/modules/commentModule.js';
|
|
31
30
|
import { generateTestsModule } from './pipeline/modules/generateTestsModule.js';
|
|
32
31
|
import { preserveCodeModule } from './pipeline/modules/preserveCodeModule.js';
|
|
@@ -34,6 +33,8 @@ import { runInteractiveDelete } from './commands/DeleteIndex.js';
|
|
|
34
33
|
import { resolveTargetsToFiles } from './utils/resolveTargetsToFiles.js';
|
|
35
34
|
import { updateContext } from './context.js';
|
|
36
35
|
import { cleanGeneratedTestsModule } from './pipeline/modules/cleanGeneratedTestsModule.js';
|
|
36
|
+
import { Agent } from './agent/agentManager.js';
|
|
37
|
+
import { builtInModules } from './pipeline/registry/moduleRegistry.js';
|
|
37
38
|
// 🎛️ CLI Setup
|
|
38
39
|
const cmd = new Command('scai')
|
|
39
40
|
.version(version)
|
|
@@ -47,6 +48,34 @@ cmd
|
|
|
47
48
|
await bootstrap();
|
|
48
49
|
console.log('✅ Model initialization completed!');
|
|
49
50
|
});
|
|
51
|
+
// 🔧 Group: Agent-related commands
|
|
52
|
+
const agent = cmd
|
|
53
|
+
.command('agent')
|
|
54
|
+
.description(`Run an agent workflow. Available tools:\n` +
|
|
55
|
+
Object.keys(builtInModules).map(m => ` - ${m}`).join('\n') +
|
|
56
|
+
`\n\nExample usage:\n` +
|
|
57
|
+
` $ scai agent run summary cleanup tests\n` +
|
|
58
|
+
` This will run the agent with the goals: summary → cleanup → tests\n`);
|
|
59
|
+
// Run workflow subcommand
|
|
60
|
+
const runCmd = agent
|
|
61
|
+
.command('run <goals...>')
|
|
62
|
+
.description('Run an agent workflow with a list of goals')
|
|
63
|
+
.option('-f, --file <filepath>', 'File to process', 'example.txt')
|
|
64
|
+
.action(async (cmdGoals, cmd) => {
|
|
65
|
+
await withContext(async () => {
|
|
66
|
+
const goals = cmdGoals;
|
|
67
|
+
const file = cmd.file;
|
|
68
|
+
console.log('Agent will execute:', goals.join(' → '));
|
|
69
|
+
console.log('On file:', file);
|
|
70
|
+
const agentInstance = new Agent(goals);
|
|
71
|
+
await agentInstance.execute(file);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
// Inject modules list into the --help output
|
|
75
|
+
runCmd.on('--help', () => {
|
|
76
|
+
console.log('\nAvailable tools:');
|
|
77
|
+
Object.keys(builtInModules).forEach(m => console.log(` - ${m}`));
|
|
78
|
+
});
|
|
50
79
|
// 🔧 Group: Git-related commands
|
|
51
80
|
const git = cmd.command('git').description('Git utilities');
|
|
52
81
|
git
|
|
@@ -301,18 +330,6 @@ cmd
|
|
|
301
330
|
const fullQuery = questionParts?.join(' ');
|
|
302
331
|
await runAskCommand(fullQuery);
|
|
303
332
|
}));
|
|
304
|
-
cmd
|
|
305
|
-
.command('pipe')
|
|
306
|
-
.description('Run a module pipeline on a given file')
|
|
307
|
-
.argument('<file>', 'Target file')
|
|
308
|
-
.option('-m, --modules <modules>', 'Comma-separated list of modules to run (e.g., comments,cleanup,summary)')
|
|
309
|
-
.action((file, options) => {
|
|
310
|
-
if (!options.modules) {
|
|
311
|
-
console.error('❌ You must specify modules with -m or --modules');
|
|
312
|
-
process.exit(1);
|
|
313
|
-
}
|
|
314
|
-
runModulePipelineFromCLI(file, options);
|
|
315
|
-
});
|
|
316
333
|
cmd.addHelpText('after', `
|
|
317
334
|
🚨 Alpha Features:
|
|
318
335
|
- The "index", "daemon", "stop-daemon", "reset-db" commands are considered alpha features.
|
|
@@ -9,7 +9,8 @@ export const cleanGeneratedTestsModule = {
|
|
|
9
9
|
const stripped = stripMarkdownFences(normalized);
|
|
10
10
|
// filter non-code lines
|
|
11
11
|
const lines = stripped.split("\n");
|
|
12
|
-
|
|
12
|
+
// filter non-code lines, but keep blank ones
|
|
13
|
+
const codeLines = lines.filter(line => line.trim() === "" || isCodeLike(line));
|
|
13
14
|
const cleanedCode = codeLines.join("\n");
|
|
14
15
|
return {
|
|
15
16
|
originalContent: content,
|
|
@@ -10,32 +10,50 @@ export const generateTestsModule = {
|
|
|
10
10
|
throw new Error('Missing filepath in pipeline context');
|
|
11
11
|
const model = Config.getModel();
|
|
12
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}`);
|
|
13
21
|
const prompt = `
|
|
14
|
-
|
|
22
|
+
You are a senior ${lang.toUpperCase()} engineer. Generate a Jest test file for the module below.
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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:
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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 ---
|
|
47
|
+
`.trim();
|
|
28
48
|
const response = await generate({ content: prompt }, model);
|
|
29
49
|
if (!response)
|
|
30
50
|
throw new Error('⚠️ No test code returned from model');
|
|
31
|
-
const { dir, name } = path.parse(filepath);
|
|
32
|
-
const testPath = path.join(dir, `${name}.test.ts`);
|
|
33
51
|
return {
|
|
34
52
|
originalContent: content,
|
|
35
53
|
content: response.content, // the test code
|
|
36
54
|
filepath, // original file path
|
|
37
55
|
newFilepath: testPath,
|
|
38
|
-
mode: "newFile" //
|
|
56
|
+
mode: "newFile" // ensure it gets written as a new file
|
|
39
57
|
};
|
|
40
58
|
}
|
|
41
59
|
};
|
|
@@ -7,14 +7,14 @@ import { changelogModule } from '../modules/changeLogModule.js';
|
|
|
7
7
|
import { cleanGeneratedTestsModule } from '../modules/cleanGeneratedTestsModule.js';
|
|
8
8
|
import { preserveCodeModule } from '../modules/preserveCodeModule.js';
|
|
9
9
|
// Add more as needed...
|
|
10
|
-
const builtInModules = {
|
|
10
|
+
export const builtInModules = {
|
|
11
11
|
comments: addCommentsModule,
|
|
12
12
|
cleanup: cleanupModule,
|
|
13
13
|
summary: summaryModule,
|
|
14
14
|
tests: generateTestsModule,
|
|
15
15
|
suggest: commitSuggesterModule,
|
|
16
16
|
changelog: changelogModule,
|
|
17
|
-
|
|
17
|
+
cleanTests: cleanGeneratedTestsModule,
|
|
18
18
|
cleanComments: preserveCodeModule
|
|
19
19
|
};
|
|
20
20
|
export function getModuleByName(name) {
|