scai 0.1.8 → 0.1.10
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 +28 -10
- package/dist/commands/CommitSuggesterCmd.js +26 -6
- package/dist/commands/ReadmeCmd.js +5 -2
- package/dist/commands/RefactorCmd.js +3 -4
- package/dist/commands/TestGenCmd.js +14 -0
- package/dist/index.js +6 -1
- package/dist/jest.config.js +11 -0
- package/dist/jest.setup.js +7 -0
- package/dist/pipeline/modules/cleanupModule.js +37 -29
- package/dist/pipeline/modules/commentModule.js +3 -3
- package/dist/pipeline/modules/generateTestsModule.js +42 -0
- package/dist/pipeline/modules/refactorModule.js +2 -3
- package/dist/pipeline/runPipeline.js +18 -5
- package/dist/utils/runTests.js +11 -0
- package/package.json +14 -4
- package/dist/commands/CommitSuggesterCmd.refactored.js +0 -44
- package/dist/commands/RefactorCmd.refactored.js +0 -8
- package/dist/pipeline/modules/stripMarkdownModule.js +0 -15
package/README.md
CHANGED
|
@@ -11,12 +11,27 @@
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## ❤️ Philosophy: Why Local AI?
|
|
15
|
+
|
|
16
|
+
We believe your code — and your workflow — should stay **yours**. scai runs entirely on your machine using open-source models and tools.
|
|
17
|
+
|
|
18
|
+
No internet connection. No vendor lock-in. Just local, private, AI-enhanced developer experience.
|
|
19
|
+
|
|
20
|
+
✅ Works entirely offline
|
|
21
|
+
✅ No API keys, cloud accounts, or telemetry
|
|
22
|
+
✅ Backed by open-source models
|
|
23
|
+
✅ Designed for CLI-first devs and scriptable workflows
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
**scai** follows the Unix philosophy — small, composable tools with powerful output.
|
|
28
|
+
|
|
14
29
|
## 🚀 Features
|
|
15
30
|
|
|
16
31
|
- 💬 Generate commit messages from staged Git changes
|
|
17
32
|
- ✨ Refactor a single JavaScript file for improved readability
|
|
18
33
|
- 🔍 Check Git status with one command
|
|
19
|
-
- ⚡️ Powered by
|
|
34
|
+
- ⚡️ Powered by open local Ollama models like `mistral`
|
|
20
35
|
- 🛠️ CLI built with Node.js + TypeScript
|
|
21
36
|
- 🔒 No external services, full privacy by design
|
|
22
37
|
|
|
@@ -61,7 +76,7 @@ This will:
|
|
|
61
76
|
git add .
|
|
62
77
|
|
|
63
78
|
# Let scai suggest a commit message
|
|
64
|
-
scai
|
|
79
|
+
scai suggest
|
|
65
80
|
```
|
|
66
81
|
|
|
67
82
|
> Example output:
|
|
@@ -72,7 +87,7 @@ feat(api): add error handling to user service
|
|
|
72
87
|
To automatically commit with the suggested message:
|
|
73
88
|
|
|
74
89
|
```bash
|
|
75
|
-
scai
|
|
90
|
+
scai suggest --commit
|
|
76
91
|
```
|
|
77
92
|
|
|
78
93
|
---
|
|
@@ -91,6 +106,16 @@ path/to/refactored/file.refactored.js
|
|
|
91
106
|
|
|
92
107
|
---
|
|
93
108
|
|
|
109
|
+
### 🧩 Generate Tests
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
scai generate-tests <file>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Automatically generates Jest test files for specified JavaScript/TypeScript modules.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
94
119
|
### 🔍 Check Git status
|
|
95
120
|
|
|
96
121
|
```bash
|
|
@@ -118,10 +143,3 @@ However:
|
|
|
118
143
|
For full terms, see the [LICENSE](./LICENSE) file.
|
|
119
144
|
|
|
120
145
|
---
|
|
121
|
-
|
|
122
|
-
## ❤️ Why Local-First?
|
|
123
|
-
|
|
124
|
-
We believe your code — and your workflow — should stay **yours**. scai runs entirely on your machine using open-source models and tools.
|
|
125
|
-
|
|
126
|
-
No internet connection. No vendor lock-in. Just local, private, AI-enhanced developer experience.
|
|
127
|
-
|
|
@@ -7,17 +7,21 @@ function askUserToChoose(suggestions) {
|
|
|
7
7
|
console.log(`${i + 1}) ${msg}`);
|
|
8
8
|
});
|
|
9
9
|
console.log(`${suggestions.length + 1}) 🔁 Regenerate suggestions`);
|
|
10
|
+
console.log(`${suggestions.length + 2}) ✍️ Write your own commit message`);
|
|
10
11
|
const rl = readline.createInterface({
|
|
11
12
|
input: process.stdin,
|
|
12
13
|
output: process.stdout,
|
|
13
14
|
});
|
|
14
|
-
rl.question(`\n👉 Choose a commit message [1-${suggestions.length +
|
|
15
|
+
rl.question(`\n👉 Choose a commit message [1-${suggestions.length + 2}]: `, (answer) => {
|
|
15
16
|
rl.close();
|
|
16
17
|
const choice = parseInt(answer, 10);
|
|
17
|
-
if (isNaN(choice) || choice < 1 || choice > suggestions.length +
|
|
18
|
+
if (isNaN(choice) || choice < 1 || choice > suggestions.length + 2) {
|
|
18
19
|
console.log('⚠️ Invalid selection. Using the first suggestion by default.');
|
|
19
20
|
resolve(0);
|
|
20
21
|
}
|
|
22
|
+
else if (choice === suggestions.length + 2) {
|
|
23
|
+
resolve('custom');
|
|
24
|
+
}
|
|
21
25
|
else {
|
|
22
26
|
resolve(choice - 1); // Return 0-based index (0 to 3)
|
|
23
27
|
}
|
|
@@ -45,6 +49,18 @@ async function generateSuggestions(prompt) {
|
|
|
45
49
|
}
|
|
46
50
|
return messages;
|
|
47
51
|
}
|
|
52
|
+
function promptCustomMessage() {
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
const rl = readline.createInterface({
|
|
55
|
+
input: process.stdin,
|
|
56
|
+
output: process.stdout,
|
|
57
|
+
});
|
|
58
|
+
rl.question('\n📝 Enter your custom commit message:\n> ', (input) => {
|
|
59
|
+
rl.close();
|
|
60
|
+
resolve(input.trim());
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
48
64
|
export async function suggestCommitMessage(options) {
|
|
49
65
|
try {
|
|
50
66
|
let diff = execSync("git diff", { encoding: "utf-8" }).trim();
|
|
@@ -65,13 +81,17 @@ ${diff}`;
|
|
|
65
81
|
let message = null;
|
|
66
82
|
while (message === null) {
|
|
67
83
|
const suggestions = await generateSuggestions(prompt);
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
84
|
+
const choice = await askUserToChoose(suggestions);
|
|
85
|
+
if (choice === suggestions.length) {
|
|
70
86
|
// User chose "Regenerate"
|
|
71
87
|
console.log('\n🔄 Regenerating suggestions...\n');
|
|
72
88
|
continue;
|
|
73
89
|
}
|
|
74
|
-
|
|
90
|
+
if (choice === 'custom') {
|
|
91
|
+
message = await promptCustomMessage();
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
message = suggestions[choice];
|
|
75
95
|
}
|
|
76
96
|
console.log(`\n✅ Selected commit message:\n${message}\n`);
|
|
77
97
|
if (options.commit) {
|
|
@@ -80,7 +100,7 @@ ${diff}`;
|
|
|
80
100
|
execSync("git add .", { encoding: "utf-8" });
|
|
81
101
|
}
|
|
82
102
|
execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { stdio: 'inherit' });
|
|
83
|
-
console.log('✅ Committed with
|
|
103
|
+
console.log('✅ Committed with selected message.');
|
|
84
104
|
}
|
|
85
105
|
}
|
|
86
106
|
catch (err) {
|
|
@@ -3,9 +3,12 @@ import fs from 'fs/promises';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
export async function updateReadmeIfNeeded() {
|
|
5
5
|
try {
|
|
6
|
-
|
|
6
|
+
let diff = execSync("git diff", { encoding: "utf-8" }).trim();
|
|
7
7
|
if (!diff) {
|
|
8
|
-
|
|
8
|
+
diff = execSync("git diff --cached", { encoding: "utf-8" }).trim();
|
|
9
|
+
}
|
|
10
|
+
if (!diff) {
|
|
11
|
+
console.log('⚠️ No staged changes to suggest a message for.');
|
|
9
12
|
return;
|
|
10
13
|
}
|
|
11
14
|
const readmePath = path.resolve("README.md");
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { runPromptPipeline } from '../pipeline/runPipeline.js';
|
|
4
|
-
import { refactorModule } from '../pipeline/modules/refactorModule.js';
|
|
5
4
|
import { addCommentsModule } from '../pipeline/modules/commentModule.js';
|
|
6
5
|
import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
|
|
7
6
|
export async function handleRefactor(filepath, options = {}) {
|
|
@@ -28,11 +27,11 @@ export async function handleRefactor(filepath, options = {}) {
|
|
|
28
27
|
// Read source code
|
|
29
28
|
const originalCode = await fs.readFile(filepath, 'utf-8');
|
|
30
29
|
// Run through pipeline modules
|
|
31
|
-
const refactored = await runPromptPipeline([
|
|
32
|
-
if (!refactored.trim())
|
|
30
|
+
const refactored = await runPromptPipeline([addCommentsModule, cleanupModule], { code: originalCode });
|
|
31
|
+
if (!refactored.code.trim())
|
|
33
32
|
throw new Error('⚠️ Model returned empty result');
|
|
34
33
|
// Save refactored output
|
|
35
|
-
await fs.writeFile(refactoredPath, refactored, 'utf-8');
|
|
34
|
+
await fs.writeFile(refactoredPath, refactored.code, 'utf-8');
|
|
36
35
|
console.log(`✅ Refactored code saved to: ${refactoredPath}`);
|
|
37
36
|
console.log(`ℹ️ Run again with '--apply' to overwrite the original.`);
|
|
38
37
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import { generateTestsModule } from '../pipeline/modules/generateTestsModule.js';
|
|
3
|
+
import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
|
|
4
|
+
import { runPromptPipeline } from '../pipeline/runPipeline.js';
|
|
5
|
+
export async function generateTests(filepath) {
|
|
6
|
+
try {
|
|
7
|
+
const code = await fs.readFile(filepath, 'utf-8');
|
|
8
|
+
const result = await runPromptPipeline([generateTestsModule, cleanupModule], { code, filepath });
|
|
9
|
+
console.log('✅ Test generated and cleaned up.');
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
console.error('❌ Error generating tests:', err.message);
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { checkGit } from "./commands/GitCmd.js";
|
|
|
5
5
|
import { suggestCommitMessage } from "./commands/CommitSuggesterCmd.js";
|
|
6
6
|
import { handleRefactor } from "./commands/RefactorCmd.js";
|
|
7
7
|
import { updateReadmeIfNeeded } from "./commands/ReadmeCmd.js";
|
|
8
|
+
import { generateTests } from "./commands/TestGenCmd.js";
|
|
8
9
|
// Import the model check and initialization logic
|
|
9
10
|
import { bootstrap } from './modelSetup.js';
|
|
10
11
|
// Create the CLI instance
|
|
@@ -27,7 +28,7 @@ cmd
|
|
|
27
28
|
.description('Check Git status')
|
|
28
29
|
.action(checkGit);
|
|
29
30
|
cmd
|
|
30
|
-
.command('
|
|
31
|
+
.command('suggest')
|
|
31
32
|
.description('Suggest a commit message from staged changes')
|
|
32
33
|
.option('-c, --commit', 'Automatically commit with suggested message')
|
|
33
34
|
.action(suggestCommitMessage);
|
|
@@ -40,5 +41,9 @@ cmd
|
|
|
40
41
|
.command('readme')
|
|
41
42
|
.description('Update README.md if relevant changes were made')
|
|
42
43
|
.action(updateReadmeIfNeeded);
|
|
44
|
+
cmd
|
|
45
|
+
.command('generate-tests <file>')
|
|
46
|
+
.description('Generate a Jest test file for the specified JS/TS module')
|
|
47
|
+
.action((file) => generateTests(file));
|
|
43
48
|
// Parse CLI arguments
|
|
44
49
|
cmd.parse(process.argv);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const config = {
|
|
2
|
+
preset: 'ts-jest',
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
setupFilesAfterEnv: ['./jest.setup.ts'],
|
|
5
|
+
transform: {
|
|
6
|
+
'^.+\\.ts$': 'ts-jest',
|
|
7
|
+
},
|
|
8
|
+
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
|
|
9
|
+
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
|
|
10
|
+
};
|
|
11
|
+
export default config;
|
|
@@ -1,34 +1,42 @@
|
|
|
1
|
+
function isNaturalLanguageNoise(line) {
|
|
2
|
+
const trimmed = line.trim().toLowerCase();
|
|
3
|
+
return (trimmed.startsWith('i ') ||
|
|
4
|
+
trimmed.startsWith('here') ||
|
|
5
|
+
trimmed.startsWith('this') ||
|
|
6
|
+
trimmed.startsWith('the following') ||
|
|
7
|
+
trimmed.startsWith('below') ||
|
|
8
|
+
trimmed.startsWith('in this') ||
|
|
9
|
+
trimmed.startsWith('we have') ||
|
|
10
|
+
trimmed.includes('the code above') ||
|
|
11
|
+
trimmed.includes('ensures that') ||
|
|
12
|
+
trimmed.includes('it handles') ||
|
|
13
|
+
trimmed.includes('used to'));
|
|
14
|
+
}
|
|
1
15
|
export const cleanupModule = {
|
|
2
16
|
name: 'cleanup',
|
|
3
|
-
description: '
|
|
17
|
+
description: 'Remove markdown fences and natural language noise from top/bottom of code',
|
|
4
18
|
async run({ code }) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
prompt,
|
|
28
|
-
stream: false
|
|
29
|
-
}),
|
|
30
|
-
});
|
|
31
|
-
const data = await res.json();
|
|
32
|
-
return { code: data.response?.trim() ?? '' };
|
|
19
|
+
let lines = code.trim().split('\n');
|
|
20
|
+
// ───── Clean top ─────
|
|
21
|
+
while (lines.length) {
|
|
22
|
+
const line = lines[0].trim();
|
|
23
|
+
if (line === '' || line.startsWith('```') || isNaturalLanguageNoise(line)) {
|
|
24
|
+
lines.shift();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// ───── Clean bottom ─────
|
|
31
|
+
while (lines.length) {
|
|
32
|
+
const line = lines[lines.length - 1].trim();
|
|
33
|
+
if (line === '' || line.startsWith('```') || isNaturalLanguageNoise(line)) {
|
|
34
|
+
lines.pop();
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { code: lines.join('\n').trim() };
|
|
33
41
|
}
|
|
34
42
|
};
|
|
@@ -5,9 +5,9 @@ export const addCommentsModule = {
|
|
|
5
5
|
const prompt = `
|
|
6
6
|
You are a senior JavaScript engineer.
|
|
7
7
|
|
|
8
|
-
Add clear and helpful single-line // comments
|
|
9
|
-
-
|
|
10
|
-
-
|
|
8
|
+
Add clear and helpful single-line // comments to complex parts of the code.
|
|
9
|
+
- Do NOT remove or change in ANY way any parts of the code!
|
|
10
|
+
- Output ALL of the original code with your comments included
|
|
11
11
|
|
|
12
12
|
--- CODE START ---
|
|
13
13
|
${input.code}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
export const generateTestsModule = {
|
|
4
|
+
name: 'generateTests',
|
|
5
|
+
description: 'Generate a Jest test file for the class/module',
|
|
6
|
+
async run({ code, filepath }) {
|
|
7
|
+
const prompt = `
|
|
8
|
+
You're a senior TypeScript developer. Given the following class or module, generate a Jest test file.
|
|
9
|
+
|
|
10
|
+
Guidelines:
|
|
11
|
+
- Use the 'jest' test framework
|
|
12
|
+
- Cover public methods and one edge case
|
|
13
|
+
- Name the file <original>.test.ts
|
|
14
|
+
- Only return valid TypeScript code
|
|
15
|
+
|
|
16
|
+
--- CODE START ---
|
|
17
|
+
${code}
|
|
18
|
+
--- CODE END ---
|
|
19
|
+
`.trim();
|
|
20
|
+
const res = await fetch('http://localhost:11434/api/generate', {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: { 'Content-Type': 'application/json' },
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
model: 'mistral',
|
|
25
|
+
prompt,
|
|
26
|
+
stream: false
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
const data = await res.json();
|
|
30
|
+
const testCode = data.response?.trim();
|
|
31
|
+
if (!testCode)
|
|
32
|
+
throw new Error('⚠️ No test code returned from model');
|
|
33
|
+
// Determine test file path next to refactored file
|
|
34
|
+
if (!filepath)
|
|
35
|
+
throw new Error('Missing filepath in pipeline context');
|
|
36
|
+
const { dir, name } = path.parse(filepath);
|
|
37
|
+
const testPath = path.join(dir, `${name}.test.ts`);
|
|
38
|
+
await fs.writeFile(testPath, testCode, 'utf-8');
|
|
39
|
+
console.log(`✅ Test file saved to: ${testPath}`);
|
|
40
|
+
return { code, filepath }; // unchanged input
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -6,9 +6,8 @@ export const refactorModule = {
|
|
|
6
6
|
You are a senior JavaScript/TypeScript engineer.
|
|
7
7
|
|
|
8
8
|
Refactor the following code:
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Return ONLY the valid refactored code (no markdown)
|
|
9
|
+
- Refactor all functions longer than 20 lines by extracting logical parts into smaller helper functions.
|
|
10
|
+
- Each resulting function should aim for clarity and ideally be under 20 lines, unless splitting would harm readability or functionality.
|
|
12
11
|
|
|
13
12
|
--- CODE START ---
|
|
14
13
|
${code}
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
export async function runPromptPipeline(modules, input) {
|
|
2
2
|
let current = input;
|
|
3
|
-
|
|
3
|
+
// Add flag or condition for logging (optional)
|
|
4
|
+
const isDebug = true;
|
|
5
|
+
if (isDebug) {
|
|
6
|
+
console.log('Input: ', input);
|
|
7
|
+
}
|
|
4
8
|
for (const mod of modules) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
try {
|
|
10
|
+
current = await mod.run(current);
|
|
11
|
+
if (isDebug) {
|
|
12
|
+
console.log(`⚙️ Running: ${mod.name}`);
|
|
13
|
+
console.log("Current: ", current);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
console.error(`❌ Error in ${mod.name}:`, error instanceof Error ? error.message : error);
|
|
18
|
+
throw new Error(`Pipeline failed at module ${mod.name}`);
|
|
19
|
+
}
|
|
8
20
|
}
|
|
9
|
-
|
|
21
|
+
// Return the output, assuming 'code' holds the relevant transformed content
|
|
22
|
+
return { code: current.code }; // Ensure the return type matches PromptOutput
|
|
10
23
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
export function runJestTest(testFile) {
|
|
3
|
+
try {
|
|
4
|
+
execSync(`npx jest ${testFile} --silent`, { stdio: 'inherit' });
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
catch (err) {
|
|
8
|
+
console.error(`❌ Tests failed for ${testFile}`);
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"scai": "./dist/index.js"
|
|
@@ -11,7 +11,14 @@
|
|
|
11
11
|
},
|
|
12
12
|
"author": "Rasmus Uhd Norgaard",
|
|
13
13
|
"license": "SEE LICENSE IN LICENSE",
|
|
14
|
-
"keywords": [
|
|
14
|
+
"keywords": [
|
|
15
|
+
"cli",
|
|
16
|
+
"ai",
|
|
17
|
+
"refactor",
|
|
18
|
+
"devtools",
|
|
19
|
+
"local",
|
|
20
|
+
"typescript"
|
|
21
|
+
],
|
|
15
22
|
"scripts": {
|
|
16
23
|
"build": "tsc",
|
|
17
24
|
"start": "node dist/index.js"
|
|
@@ -20,11 +27,14 @@
|
|
|
20
27
|
"commander": "^11.0.0"
|
|
21
28
|
},
|
|
22
29
|
"devDependencies": {
|
|
30
|
+
"@types/jest": "^30.0.0",
|
|
23
31
|
"@types/node": "^24.0.1",
|
|
32
|
+
"jest": "^30.0.2",
|
|
33
|
+
"ts-jest": "^29.4.0",
|
|
24
34
|
"typescript": "^5.8.3"
|
|
25
35
|
},
|
|
26
36
|
"files": [
|
|
27
37
|
"dist/",
|
|
28
|
-
"README.md"
|
|
38
|
+
"README.md"
|
|
29
39
|
]
|
|
30
|
-
}
|
|
40
|
+
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Here;
|
|
3
|
-
is;
|
|
4
|
-
the;
|
|
5
|
-
refactored;
|
|
6
|
-
code;
|
|
7
|
-
with (comments)
|
|
8
|
-
added;
|
|
9
|
-
and;
|
|
10
|
-
a;
|
|
11
|
-
summary;
|
|
12
|
-
of;
|
|
13
|
-
refactor;
|
|
14
|
-
at;
|
|
15
|
-
the;
|
|
16
|
-
end: `` `typescript
|
|
17
|
-
// Import necessary modules for executing child processes, fetch requests, and readline interface
|
|
18
|
-
import { execSync } from 'child_process';
|
|
19
|
-
import readline from 'readline';
|
|
20
|
-
import { fetch } from 'node-fetch';
|
|
21
|
-
|
|
22
|
-
// Define an asynchronous function to generate messages based on the provided prompt
|
|
23
|
-
async function generateMessages(prompt: string): Promise<string[]> {
|
|
24
|
-
// ... (Existing code)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Define an asynchronous function to ask for a user choice among the given suggestions
|
|
28
|
-
async function askForChoice(options: { suggestions: string[] }): Promise<number> {
|
|
29
|
-
// ... (Existing code)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Export an asynchronous function to suggest a commit message based on the given options (with the commit flag for final committing)
|
|
33
|
-
export async function suggestCommitMessage({ commit = false }: { commit?: boolean }) {
|
|
34
|
-
try {
|
|
35
|
-
// ... (Existing code)
|
|
36
|
-
} catch (err) {
|
|
37
|
-
console.error('❌ Error in commit message suggestion:', err.message);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Summary of refactor:
|
|
42
|
-
// - Added comments to each block for better understanding.
|
|
43
|
-
// - Extracted the functions generateMessages and askForChoice into separate blocks for code organization.
|
|
44
|
-
` ``;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Summary of refactor:
|
|
3
|
-
// - Normalized file paths by adding './' if not present
|
|
4
|
-
// - Parsed normalized file paths into their directory, name, and extension
|
|
5
|
-
// - Generated a refactored file path using the original file's directory, name, and extension along with the '.refactored' suffix
|
|
6
|
-
// - Handled the refactor operation for a given file path and options (apply flag to decide whether to overwrite the original file or not)
|
|
7
|
-
// - If apply is set, attempted to read the refactored code from the refactored file path and overwrite the original file if it exists.
|
|
8
|
-
// - If not, read the original code from the file, passed it through a pipeline for refactoring, saved the refactored code to a new file path, and provided an option to run again with '--apply' to overwrite the original.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export const stripMarkdownModule = {
|
|
2
|
-
name: 'stripMarkdown',
|
|
3
|
-
description: 'Remove surrounding markdown and explanations from LLM output',
|
|
4
|
-
async run({ code }) {
|
|
5
|
-
let cleaned = code.trim();
|
|
6
|
-
// Remove common intro text like "Here is the refactored code:"
|
|
7
|
-
cleaned = cleaned.replace(/^.*?(?=\n|```|import|function|const|let)/is, '');
|
|
8
|
-
// Remove triple backtick blocks and optional language tags
|
|
9
|
-
cleaned = cleaned.replace(/^```[a-z]*\n?/i, '');
|
|
10
|
-
cleaned = cleaned.replace(/```$/, '');
|
|
11
|
-
// Remove any leftover empty lines at top and bottom
|
|
12
|
-
cleaned = cleaned.trim();
|
|
13
|
-
return { code: cleaned };
|
|
14
|
-
}
|
|
15
|
-
};
|