scai 0.1.15 → 0.1.17
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/README.md +17 -7
- package/dist/commands/ChangeLogUpdateCmd.js +2 -2
- package/dist/commands/ModulePipelineCmd.js +31 -0
- package/dist/commands/RefactorCmd.js +2 -2
- package/dist/commands/SummaryCmd.js +7 -3
- package/dist/commands/TestGenCmd.js +2 -2
- package/dist/context/generateProjectContext.js +104 -0
- package/dist/context/loadProjectContext.js +1 -0
- package/dist/index.js +20 -0
- package/dist/pipeline/modules/commentModule.js +2 -2
- package/dist/pipeline/modules/commitSuggesterModule.js +1 -1
- package/dist/pipeline/modules/summaryModule.js +31 -9
- package/dist/pipeline/{runPipeline.js → runModulePipeline.js} +1 -1
- package/dist/registry/moduleRegistry.js +21 -0
- package/dist/utils/summarizer.js +10 -0
- package/dist/utils/textWrapper.js +18 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -30,8 +30,6 @@ We believe your code — and your workflow — should stay **yours**. scai runs
|
|
|
30
30
|
✅ Backed by open-source models
|
|
31
31
|
✅ Designed for CLI-first developers
|
|
32
32
|
|
|
33
|
-
---
|
|
34
|
-
|
|
35
33
|
## 📦 Installation
|
|
36
34
|
|
|
37
35
|
1. **Install [Ollama](https://ollama.com)**
|
|
@@ -59,8 +57,6 @@ This will:
|
|
|
59
57
|
- Start the Ollama background server
|
|
60
58
|
- Download required models (`llama3`, etc.)
|
|
61
59
|
|
|
62
|
-
---
|
|
63
|
-
|
|
64
60
|
## 🧪 Usage Examples
|
|
65
61
|
|
|
66
62
|
### 💬 Suggest a commit message
|
|
@@ -108,7 +104,6 @@ To pipe input for summarization:
|
|
|
108
104
|
cat <file> | scai summ
|
|
109
105
|
```
|
|
110
106
|
|
|
111
|
-
|
|
112
107
|
---
|
|
113
108
|
|
|
114
109
|
### 📝 Update the changelog
|
|
@@ -121,6 +116,23 @@ Analyzes the current Git diff and updates (or creates) a `CHANGELOG.md` file wit
|
|
|
121
116
|
|
|
122
117
|
---
|
|
123
118
|
|
|
119
|
+
### 📦 Module Pipeline (Advanced Usage)
|
|
120
|
+
|
|
121
|
+
For more advanced functionality, scai allows you to run custom pipelines with multiple modules to process code. You can specify multiple modules to be run, such as comment, summary, cleanup, and more.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
scai <file> --modules comment,summary,cleanup
|
|
125
|
+
```
|
|
126
|
+
Like with regular Unix commands you can pipe this to a file
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
scai <file> --modules comment,summary > test.txt
|
|
130
|
+
```
|
|
131
|
+
Or to both stdout and a file with tee fx.
|
|
132
|
+
```bash
|
|
133
|
+
scai <file> --modules comment,summary | tee test.txt
|
|
134
|
+
```
|
|
135
|
+
|
|
124
136
|
## 🔐 License & Fair Use
|
|
125
137
|
|
|
126
138
|
**scai is free to use** for individuals, teams, and companies — including in commercial work.
|
|
@@ -137,5 +149,3 @@ But you may not:
|
|
|
137
149
|
- ❌ Claim ownership of the tool
|
|
138
150
|
|
|
139
151
|
See [LICENSE](./LICENSE) for full terms.
|
|
140
|
-
|
|
141
|
-
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import {
|
|
5
|
+
import { runModulePipeline } from '../pipeline/runModulePipeline.js';
|
|
6
6
|
import { changelogModule } from '../pipeline/modules/changeLogModule.js';
|
|
7
7
|
export async function handleChangelogUpdate() {
|
|
8
8
|
try {
|
|
@@ -14,7 +14,7 @@ export async function handleChangelogUpdate() {
|
|
|
14
14
|
console.log("⚠️ No staged or unstaged changes to include in changelog.");
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
|
-
const result = await
|
|
17
|
+
const result = await runModulePipeline([changelogModule], { code: diff });
|
|
18
18
|
if (!result.code.trim()) {
|
|
19
19
|
console.log("✅ No significant changes for changelog.");
|
|
20
20
|
return;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { getModuleByName } from '../registry/moduleRegistry.js';
|
|
3
|
+
import { runModulePipeline } from '../pipeline/runModulePipeline.js';
|
|
4
|
+
export async function runModulePipelineFromCLI(file, options) {
|
|
5
|
+
if (!options.modules) {
|
|
6
|
+
console.error('❌ No modules specified. Use --modules or -m.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
const moduleNames = options.modules.split(',').map((m) => m.trim());
|
|
10
|
+
// Read file content
|
|
11
|
+
let fileContent = '';
|
|
12
|
+
try {
|
|
13
|
+
fileContent = readFileSync(file, 'utf-8');
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
console.error(`❌ Could not read file: ${file}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const input = { code: fileContent, filepath: file };
|
|
20
|
+
// Retrieve modules from the registry
|
|
21
|
+
const resolvedModules = moduleNames.map((name) => {
|
|
22
|
+
const module = getModuleByName(name);
|
|
23
|
+
if (!module) {
|
|
24
|
+
console.error(`❌ Failed to load module: ${name}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
return module;
|
|
28
|
+
});
|
|
29
|
+
// Run the module pipeline
|
|
30
|
+
await runModulePipeline(resolvedModules, input);
|
|
31
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import { runModulePipeline } from '../pipeline/runModulePipeline.js';
|
|
4
4
|
import { addCommentsModule } from '../pipeline/modules/commentModule.js';
|
|
5
5
|
import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
|
|
6
6
|
export async function handleRefactor(filepath, options = {}) {
|
|
@@ -27,7 +27,7 @@ export async function handleRefactor(filepath, options = {}) {
|
|
|
27
27
|
// Read source code
|
|
28
28
|
const originalCode = await fs.readFile(filepath, 'utf-8');
|
|
29
29
|
// Run through pipeline modules
|
|
30
|
-
const refactored = await
|
|
30
|
+
const refactored = await runModulePipeline([addCommentsModule, cleanupModule], { code: originalCode });
|
|
31
31
|
if (!refactored.code.trim())
|
|
32
32
|
throw new Error('⚠️ Model returned empty result');
|
|
33
33
|
// Save refactored output
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
|
-
import { summaryModule } from '../pipeline/modules/summaryModule.js';
|
|
3
2
|
import readline from 'readline';
|
|
3
|
+
import { summaryModule } from '../pipeline/modules/summaryModule.js'; // Import summaryModule
|
|
4
|
+
import { summarizeCode } from '../utils/summarizer.js'; // Import summarizeCode
|
|
4
5
|
export async function summarizeFile(filepath) {
|
|
5
6
|
let code = '';
|
|
6
7
|
if (filepath) {
|
|
@@ -12,7 +13,6 @@ export async function summarizeFile(filepath) {
|
|
|
12
13
|
return;
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
|
-
// ⬇️ Add this check right here
|
|
16
16
|
else if (process.stdin.isTTY) {
|
|
17
17
|
console.error('❌ No file provided and no piped input.\n👉 Usage: scai summ <file> or cat file | scai summ');
|
|
18
18
|
return;
|
|
@@ -28,7 +28,11 @@ export async function summarizeFile(filepath) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
if (code.trim()) {
|
|
31
|
-
|
|
31
|
+
// Call the summary module to get the raw summary
|
|
32
|
+
const summary = await summaryModule.run({ code, filepath });
|
|
33
|
+
// Pass the summary text to the utility function for formatting
|
|
34
|
+
const formattedSummary = summarizeCode(summary.code);
|
|
35
|
+
console.log(formattedSummary);
|
|
32
36
|
}
|
|
33
37
|
else {
|
|
34
38
|
console.error('❌ No code provided to summarize.');
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import { generateTestsModule } from '../pipeline/modules/generateTestsModule.js';
|
|
3
3
|
import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
|
|
4
|
-
import {
|
|
4
|
+
import { runModulePipeline } from '../pipeline/runModulePipeline.js';
|
|
5
5
|
export async function generateTests(filepath) {
|
|
6
6
|
try {
|
|
7
7
|
const code = await fs.readFile(filepath, 'utf-8');
|
|
8
|
-
const result = await
|
|
8
|
+
const result = await runModulePipeline([generateTestsModule, cleanupModule], { code, filepath });
|
|
9
9
|
console.log('✅ Test generated and cleaned up.');
|
|
10
10
|
}
|
|
11
11
|
catch (err) {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fg from 'fast-glob';
|
|
4
|
+
import { posix as pathPosix } from 'path';
|
|
5
|
+
import { ModelConfig } from '../config/ModelConfig.js';
|
|
6
|
+
const IGNORE = ['node_modules', 'dist', 'build', 'coverage', '.git', '**/*.test.*'];
|
|
7
|
+
const EXTENSIONS_BY_LANG = {
|
|
8
|
+
ts: ['.ts', '.tsx'],
|
|
9
|
+
js: ['.js', '.jsx'],
|
|
10
|
+
java: ['.java'],
|
|
11
|
+
rust: ['.rs'],
|
|
12
|
+
python: ['.py'],
|
|
13
|
+
};
|
|
14
|
+
function getSummary(filename, lang) {
|
|
15
|
+
const base = path.basename(filename).toLowerCase();
|
|
16
|
+
const ext = path.extname(base);
|
|
17
|
+
if (base === 'package.json')
|
|
18
|
+
return 'Defines project metadata and dependencies.';
|
|
19
|
+
if (base === 'tsconfig.json')
|
|
20
|
+
return 'TypeScript compiler settings.';
|
|
21
|
+
if (base === 'pyproject.toml')
|
|
22
|
+
return 'Python build and dependency configuration.';
|
|
23
|
+
if (base === 'Cargo.toml')
|
|
24
|
+
return 'Rust project configuration.';
|
|
25
|
+
if (base === 'pom.xml')
|
|
26
|
+
return 'Maven config for a Java project.';
|
|
27
|
+
if (base === 'README.md')
|
|
28
|
+
return 'Project documentation.';
|
|
29
|
+
if (base.startsWith('index'))
|
|
30
|
+
return 'Entry point module.';
|
|
31
|
+
if (lang === 'ts' || lang === 'js') {
|
|
32
|
+
if (base.includes('service'))
|
|
33
|
+
return 'Service logic module.';
|
|
34
|
+
if (base.includes('util'))
|
|
35
|
+
return 'Utility/helper module.';
|
|
36
|
+
if (base.includes('controller'))
|
|
37
|
+
return 'Handles request/response logic.';
|
|
38
|
+
if (base.includes('router'))
|
|
39
|
+
return 'Routing definitions.';
|
|
40
|
+
}
|
|
41
|
+
if (lang === 'java') {
|
|
42
|
+
if (base.includes('controller'))
|
|
43
|
+
return 'Java controller class.';
|
|
44
|
+
if (base.includes('service'))
|
|
45
|
+
return 'Business logic in Java.';
|
|
46
|
+
}
|
|
47
|
+
if (lang === 'python') {
|
|
48
|
+
if (base.includes('main'))
|
|
49
|
+
return 'Main execution script.';
|
|
50
|
+
if (base.includes('config'))
|
|
51
|
+
return 'Configuration or settings.';
|
|
52
|
+
}
|
|
53
|
+
if (lang === 'rust') {
|
|
54
|
+
if (base === 'main.rs')
|
|
55
|
+
return 'Main Rust binary entry point.';
|
|
56
|
+
if (base === 'lib.rs')
|
|
57
|
+
return 'Rust library root module.';
|
|
58
|
+
}
|
|
59
|
+
return `Generic ${ext.replace('.', '')} file.`;
|
|
60
|
+
}
|
|
61
|
+
function insertNested(tree, parts, summary) {
|
|
62
|
+
const name = parts.pop();
|
|
63
|
+
let curr = tree;
|
|
64
|
+
for (const dir of parts) {
|
|
65
|
+
if (!curr[dir])
|
|
66
|
+
curr[dir] = {};
|
|
67
|
+
curr = curr[dir];
|
|
68
|
+
}
|
|
69
|
+
curr[name] = summary;
|
|
70
|
+
}
|
|
71
|
+
export async function generateProjectContext(root = process.cwd()) {
|
|
72
|
+
const lang = ModelConfig.getLanguage();
|
|
73
|
+
const extensions = EXTENSIONS_BY_LANG[lang];
|
|
74
|
+
const patterns = extensions.map((ext) => `**/*${ext}`);
|
|
75
|
+
// Add language-relevant config files
|
|
76
|
+
if (lang === 'ts')
|
|
77
|
+
patterns.push('tsconfig.json', 'package.json');
|
|
78
|
+
if (lang === 'js')
|
|
79
|
+
patterns.push('package.json');
|
|
80
|
+
if (lang === 'java')
|
|
81
|
+
patterns.push('pom.xml');
|
|
82
|
+
if (lang === 'python')
|
|
83
|
+
patterns.push('pyproject.toml', '*.py');
|
|
84
|
+
if (lang === 'rust')
|
|
85
|
+
patterns.push('Cargo.toml', '*.rs');
|
|
86
|
+
const files = await fg(patterns, {
|
|
87
|
+
cwd: root,
|
|
88
|
+
ignore: IGNORE,
|
|
89
|
+
});
|
|
90
|
+
const flat = {};
|
|
91
|
+
const tree = {};
|
|
92
|
+
for (const file of files) {
|
|
93
|
+
const summary = getSummary(file, lang);
|
|
94
|
+
flat[file] = summary;
|
|
95
|
+
insertNested(tree, pathPosix.normalize(file).split('/'), summary);
|
|
96
|
+
}
|
|
97
|
+
await fs.mkdir('.scai', { recursive: true });
|
|
98
|
+
await fs.writeFile('.scai/context.flat.json', JSON.stringify(flat, null, 2));
|
|
99
|
+
await fs.writeFile('.scai/context.tree.json', JSON.stringify(tree, null, 2));
|
|
100
|
+
console.log(`✅ Context generated:
|
|
101
|
+
- .scai/context.flat.json
|
|
102
|
+
- .scai/context.tree.json
|
|
103
|
+
- Language used: ${lang}`);
|
|
104
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
+
// Importing package.json for version
|
|
3
4
|
import { createRequire } from 'module';
|
|
4
5
|
const require = createRequire(import.meta.url);
|
|
5
6
|
const { version } = require('../package.json');
|
|
7
|
+
// Importing commands
|
|
6
8
|
import { checkEnv } from "./commands/EnvCmd.js";
|
|
7
9
|
import { checkGit } from "./commands/GitCmd.js";
|
|
8
10
|
import { suggestCommitMessage } from "./commands/CommitSuggesterCmd.js";
|
|
@@ -12,6 +14,8 @@ import { bootstrap } from './modelSetup.js';
|
|
|
12
14
|
import { ModelConfig } from './config/ModelConfig.js';
|
|
13
15
|
import { summarizeFile } from "./commands/SummaryCmd.js";
|
|
14
16
|
import { handleChangelogUpdate } from './commands/ChangeLogUpdateCmd.js';
|
|
17
|
+
import { runModulePipelineFromCLI } from './commands/ModulePipelineCmd.js';
|
|
18
|
+
import { generateProjectContext } from './context/generateProjectContext.js';
|
|
15
19
|
// Create the CLI instance
|
|
16
20
|
const cmd = new Command('scai')
|
|
17
21
|
.version(version)
|
|
@@ -25,6 +29,12 @@ cmd
|
|
|
25
29
|
await bootstrap();
|
|
26
30
|
console.log('✅ Model initialization completed!');
|
|
27
31
|
});
|
|
32
|
+
cmd
|
|
33
|
+
.command('context')
|
|
34
|
+
.description('Generate a summary-based context map of your project')
|
|
35
|
+
.action(async () => {
|
|
36
|
+
await generateProjectContext(); // Your new scanner module
|
|
37
|
+
});
|
|
28
38
|
cmd
|
|
29
39
|
.command('sugg')
|
|
30
40
|
.description('Suggest a commit message from staged changes')
|
|
@@ -63,6 +73,16 @@ cmd
|
|
|
63
73
|
.action(() => {
|
|
64
74
|
ModelConfig.logCurrentConfig();
|
|
65
75
|
});
|
|
76
|
+
cmd
|
|
77
|
+
.arguments('<file>')
|
|
78
|
+
.option('-m, --modules <modules>', 'Comma-separated list of modules to run (e.g., comments,cleanup,summary)')
|
|
79
|
+
.action((file, options) => {
|
|
80
|
+
if (!options.modules) {
|
|
81
|
+
console.error('❌ You must specify modules with -m or --modules');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
runModulePipelineFromCLI(file, options);
|
|
85
|
+
});
|
|
66
86
|
// ✅ Now that commands are defined, parse args
|
|
67
87
|
cmd.parse(process.argv);
|
|
68
88
|
// ✅ Apply global options after parsing
|
|
@@ -9,12 +9,12 @@ export const addCommentsModule = {
|
|
|
9
9
|
const prompt = `
|
|
10
10
|
You are a senior ${lang.toUpperCase()} engineer reviewing source code.
|
|
11
11
|
|
|
12
|
-
Your task is to add clear and
|
|
12
|
+
Your task is to add clear and insightful single-line comments to the code.
|
|
13
13
|
|
|
14
14
|
⚠️ VERY IMPORTANT RULES:
|
|
15
15
|
- You MUST return the ENTIRE original code.
|
|
16
16
|
- You MUST NOT remove, replace, reformat, or alter ANY code.
|
|
17
|
-
- Only add single-line // comments
|
|
17
|
+
- Only add single-line // comments to complex or non-obvious parts of the code.
|
|
18
18
|
- Do NOT wrap the code in markdown or code blocks.
|
|
19
19
|
- The code should be valid ${lang.toUpperCase()} after your changes.
|
|
20
20
|
|
|
@@ -6,7 +6,7 @@ export const commitSuggesterModule = {
|
|
|
6
6
|
async run({ code }) {
|
|
7
7
|
const model = ModelConfig.getModel();
|
|
8
8
|
const prompt = `
|
|
9
|
-
Suggest 3 concise, conventional Git commit messages based on
|
|
9
|
+
Suggest ALWAYS 3 concise, conventional Git commit messages based on the input code diff.
|
|
10
10
|
Use this format ONLY:
|
|
11
11
|
|
|
12
12
|
1. feat: ...
|
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
import { ModelConfig } from '../../config/ModelConfig.js';
|
|
2
2
|
import { generate } from '../../lib/generate.js';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
3
5
|
export const summaryModule = {
|
|
4
6
|
name: 'summary',
|
|
5
7
|
description: 'Prints a summary of changes to the terminal',
|
|
6
|
-
async run({ code }) {
|
|
8
|
+
async run({ code, filepath }) {
|
|
7
9
|
const model = ModelConfig.getModel();
|
|
8
10
|
const lang = ModelConfig.getLanguage();
|
|
11
|
+
let contextString = '';
|
|
12
|
+
// Try to load and filter the flat context
|
|
13
|
+
try {
|
|
14
|
+
const raw = await fs.readFile('./.scai/context.flat.json', 'utf-8');
|
|
15
|
+
const flatContext = JSON.parse(raw);
|
|
16
|
+
if (filepath) {
|
|
17
|
+
const dir = path.dirname(filepath).replace(/\\/g, '/'); // Normalize slashes
|
|
18
|
+
console.log("Dir: ", dir);
|
|
19
|
+
const contextSubset = Object.entries(flatContext)
|
|
20
|
+
.filter(([file]) => file.startsWith(dir))
|
|
21
|
+
.slice(0, 10); // limit if needed
|
|
22
|
+
if (contextSubset.length) {
|
|
23
|
+
contextString = '📁 Local Context:\n' + contextSubset
|
|
24
|
+
.map(([file, summary]) => `- ${file}: ${summary}`)
|
|
25
|
+
.join('\n');
|
|
26
|
+
console.log("Context string input to prompt: ", contextString);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.warn('⚠️ Context file not found or failed to parse.');
|
|
32
|
+
}
|
|
9
33
|
const prompt = `
|
|
10
34
|
You are a senior ${lang.toUpperCase()} engineer.
|
|
11
35
|
|
|
12
|
-
|
|
36
|
+
Project Overview:
|
|
37
|
+
${contextString ? contextString + '\n\n' : ''}
|
|
13
38
|
|
|
14
|
-
|
|
15
|
-
2. After the code, append a short summary in this format:
|
|
39
|
+
Take the following source code and do NOT modify it in any way. Your task is:
|
|
16
40
|
|
|
17
41
|
// Summary of code:
|
|
18
42
|
// - [What the code does at a high level]
|
|
@@ -20,9 +44,8 @@ Take the following source code and do NOT modify it in any way. Your task is:
|
|
|
20
44
|
// - [Any interesting logic or patterns]
|
|
21
45
|
|
|
22
46
|
⚠️ IMPORTANT:
|
|
23
|
-
- Do NOT
|
|
24
|
-
-
|
|
25
|
-
- Only append the summary AFTER the full code.
|
|
47
|
+
- Do NOT include the original code in your summary
|
|
48
|
+
- Remove the original code from you output
|
|
26
49
|
|
|
27
50
|
--- CODE START ---
|
|
28
51
|
${code}
|
|
@@ -36,7 +59,6 @@ ${code}
|
|
|
36
59
|
else {
|
|
37
60
|
console.warn('⚠️ No summary generated.');
|
|
38
61
|
}
|
|
39
|
-
|
|
40
|
-
return { code };
|
|
62
|
+
return { code }; // passthrough
|
|
41
63
|
}
|
|
42
64
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { addCommentsModule } from '../pipeline/modules/commentModule.js';
|
|
2
|
+
import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
|
|
3
|
+
import { summaryModule } from '../pipeline/modules/summaryModule.js';
|
|
4
|
+
import { generateTestsModule } from '../pipeline/modules/generateTestsModule.js';
|
|
5
|
+
import { commitSuggesterModule } from '../pipeline/modules/commitSuggesterModule.js';
|
|
6
|
+
import { changelogModule } from '../pipeline/modules/changeLogModule.js';
|
|
7
|
+
// Add more as needed...
|
|
8
|
+
const builtInModules = {
|
|
9
|
+
comments: addCommentsModule,
|
|
10
|
+
cleanup: cleanupModule,
|
|
11
|
+
summary: summaryModule,
|
|
12
|
+
tests: generateTestsModule,
|
|
13
|
+
suggest: commitSuggesterModule,
|
|
14
|
+
changelog: changelogModule,
|
|
15
|
+
};
|
|
16
|
+
export function getModuleByName(name) {
|
|
17
|
+
return builtInModules[name];
|
|
18
|
+
}
|
|
19
|
+
export function listAvailableModules() {
|
|
20
|
+
return Object.keys(builtInModules);
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { wrapText } from "./textWrapper.js";
|
|
2
|
+
// src/utils/summarizer.ts
|
|
3
|
+
export function summarizeCode(summaryText) {
|
|
4
|
+
// Get the terminal width (default to 80 if not available)
|
|
5
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
6
|
+
// Use two-thirds of the terminal width for the wrapping
|
|
7
|
+
const twoThirdsTerminalWidth = Math.floor((terminalWidth * 2) / 3);
|
|
8
|
+
// Wrap the summary output to fit within two-thirds of the terminal width
|
|
9
|
+
return wrapText(summaryText, twoThirdsTerminalWidth);
|
|
10
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function wrapText(text, maxWidth) {
|
|
2
|
+
const words = text.split(' ');
|
|
3
|
+
let wrappedText = '';
|
|
4
|
+
let currentLine = '';
|
|
5
|
+
words.forEach(word => {
|
|
6
|
+
// Check if adding the word would exceed the max width
|
|
7
|
+
if ((currentLine + word).length > maxWidth) {
|
|
8
|
+
wrappedText += currentLine + '\n'; // Add the current line and start a new one
|
|
9
|
+
currentLine = word + ' '; // Start the new line with the current word
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
currentLine += word + ' '; // Add the word to the current line
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
// Append the last line if any
|
|
16
|
+
wrappedText += currentLine;
|
|
17
|
+
return wrappedText;
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"scai": "./dist/index.js"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"commander": "^11.0.0",
|
|
28
|
+
"fast-glob": "^3.3.3",
|
|
28
29
|
"ora": "^8.2.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|