scai 0.1.82 → 0.1.83
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 +102 -0
- package/dist/commands/ChangeLogUpdateCmd.js +65 -24
- package/dist/commands/CommitSuggesterCmd.js +71 -35
- package/dist/commands/RefactorCmd.js +1 -2
- package/dist/github/repo.js +1 -1
- package/dist/lib/generate.js +9 -1
- package/dist/pipeline/modules/commentModule.js +43 -12
- package/dist/utils/changeLogPrompt.js +16 -12
- package/dist/utils/editor.js +16 -0
- package/package.json +2 -2
- package/LICENSE +0 -4
- package/README.md +0 -452
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
## 2025-06-23
|
|
2
|
+
|
|
3
|
+
The new markdown bullet-point entry for CHANGELOG.md includes the following updates:
|
|
4
|
+
|
|
5
|
+
* Added support for generating changelog entries based on Git diffs using the `changelog` command.
|
|
6
|
+
* Updated the summary of the code file directly to the terminal using the `summ` command.
|
|
7
|
+
* Refactored the code to use the new `handleChangelogUpdate()` function instead of the old `updateReadmeIfNeeded()` function for generating changelogs.
|
|
8
|
+
|
|
9
|
+
## 2025-06-24
|
|
10
|
+
|
|
11
|
+
### Changelog Update (Version 0.1.16)
|
|
12
|
+
|
|
13
|
+
* Update to version 0.1.16 with changes from `package.json`.
|
|
14
|
+
* Add support for running modules using the `module` command.
|
|
15
|
+
* Add support for generating tests using the `tests` module.
|
|
16
|
+
* Add support for suggesting a commit message using the `suggest` module.
|
|
17
|
+
* Add support for updating the changelog using the `changelog` module.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## 2025-06-29
|
|
21
|
+
Type handling with the module pipeline
|
|
22
|
+
┌────────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ 1. Command Layer (CLI) │
|
|
24
|
+
│ - User interaction │
|
|
25
|
+
│ - Handles input/output │
|
|
26
|
+
│ │
|
|
27
|
+
│ 🡆 Uses: │
|
|
28
|
+
│ - Input: PromptInput │
|
|
29
|
+
│ - Output: PromptOutput (reads summary/suggestions) │
|
|
30
|
+
└────────────┬───────────────────────────────────────────────────┘
|
|
31
|
+
│
|
|
32
|
+
▼
|
|
33
|
+
┌────────────────────────────────────────────────────────────────┐
|
|
34
|
+
│ 2. PromptModule Layer │
|
|
35
|
+
│ - Domain-specific logic │
|
|
36
|
+
│ - Calls `generate()` │
|
|
37
|
+
│ - Parses model output into usable fields │
|
|
38
|
+
│ │
|
|
39
|
+
│ 🡆 Type: PromptModule │
|
|
40
|
+
│ - run(input: PromptInput) => Promise<PromptOutput> │
|
|
41
|
+
│ │
|
|
42
|
+
│ 🡆 Uses: │
|
|
43
|
+
│ - Receives: PromptInput │
|
|
44
|
+
│ - Calls: generate(PromptInput, model): Promise<PromptOutput>│
|
|
45
|
+
│ - Parses output.content → summary/suggestions fields │
|
|
46
|
+
└────────────┬───────────────────────────────────────────────────┘
|
|
47
|
+
│
|
|
48
|
+
▼
|
|
49
|
+
┌────────────────────────────────────────────────────────────────┐
|
|
50
|
+
│ 3. generate() Function │
|
|
51
|
+
│ - Sends prompt to model │
|
|
52
|
+
│ - Returns raw model text in `.content` │
|
|
53
|
+
│ │
|
|
54
|
+
│ 🡆 Signature: │
|
|
55
|
+
│ generate(input: PromptInput, model: string) │
|
|
56
|
+
│ => Promise<PromptOutput> │
|
|
57
|
+
│ │
|
|
58
|
+
│ 🡆 Output fields: │
|
|
59
|
+
│ - content: string (LLM output text) │
|
|
60
|
+
│ - filepath?: string (optional passthrough) │
|
|
61
|
+
└────────────────────────────────────────────────────────────────┘
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## 2025-06-30
|
|
66
|
+
|
|
67
|
+
* Added context generation via summaries in sqlite3 db with FTS5
|
|
68
|
+
* Improved the `dbstats.sh` script to include more accurate row counts and improved formatting for better readability. (#20)
|
|
69
|
+
* Added an `IGNORED_FOLDER_GLOBS` constant in `src/config/IgnoredPaths.ts` to allow for ignoring folders and file globs during indexing and scanning. (#17)
|
|
70
|
+
* Fixed a bug in the `searchFiles` function of `src/db/fileIndex.ts` that prevented search results from being returned correctly. (#25)
|
|
71
|
+
* Updated the `runModulePipeline` function in `src/pipeline/runModulePipeline.ts` to allow for returning more accurate output and ensure consistent return types. (#27)
|
|
72
|
+
|
|
73
|
+
## 2025-07-01
|
|
74
|
+
|
|
75
|
+
### Version 0.1.20 (2023-04-01)
|
|
76
|
+
* Added support for querying file summaries and a local model using the `scai ask` command. The command will first search for matching files and pass their summaries to the model for context-based synthesis, if any are found. If no summaries are found, it will instead query the model for context without them.
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
## 2025-07-12
|
|
80
|
+
|
|
81
|
+
* • Fixes to improve code quality and maintainability.
|
|
82
|
+
* • Enhances model initialization for better performance.
|
|
83
|
+
* • Adds support for generating changelogs based on Git diff.
|
|
84
|
+
* • Improves the handling of ignored files to reduce noise in the model.
|
|
85
|
+
* • Updates the `changelog` command to generate a meaningful summary.
|
|
86
|
+
* • Enhances the `sugg` command to provide more accurate suggestions.
|
|
87
|
+
* • Fixes issues with refactoring code to improve readability.
|
|
88
|
+
|
|
89
|
+
## 2025-07-24
|
|
90
|
+
|
|
91
|
+
* Added token and owner/repo parameters to `getPullRequestsForReview` function
|
|
92
|
+
* Updated `askUserToPickPR` function to accept a list of PR objects
|
|
93
|
+
* Removed unnecessary code from `reviewPullRequestCmd`
|
|
94
|
+
* Added `cancel` option to `askReviewApproval` function
|
|
95
|
+
|
|
96
|
+
## 2025-08-10
|
|
97
|
+
|
|
98
|
+
• Removed versioned dist folder
|
|
99
|
+
• Update .gitignore to ignore Node and build artifacts, editor/IDE files, and project-specific ignores.
|
|
100
|
+
• Added scripts for building cli, server, and dashboard
|
|
101
|
+
• Restructure the workspace into 3 modules, cli, dashboard, server
|
|
102
|
+
• Improved `askChangelogApproval` to support editing and skipping of changelog entries
|
|
@@ -5,16 +5,10 @@ import path from 'path';
|
|
|
5
5
|
import { runModulePipeline } from '../pipeline/runModulePipeline.js';
|
|
6
6
|
import { changelogModule } from '../pipeline/modules/changeLogModule.js';
|
|
7
7
|
import { askChangelogApproval } from '../utils/changeLogPrompt.js';
|
|
8
|
+
import { openTextEditor } from '../utils/editor.js';
|
|
8
9
|
export async function handleStandaloneChangelogUpdate() {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
diff = execSync("git diff --cached", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
12
|
-
}
|
|
13
|
-
if (!diff) {
|
|
14
|
-
console.log('⚠️ No changes detected for changelog.');
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
let entry = await generateChangelogEntry(diff);
|
|
10
|
+
// Don't bother with diffs at all here
|
|
11
|
+
let entry = await generateChangelogEntry();
|
|
18
12
|
if (!entry) {
|
|
19
13
|
console.log('⚠️ No significant changes found.');
|
|
20
14
|
return;
|
|
@@ -28,7 +22,7 @@ export async function handleStandaloneChangelogUpdate() {
|
|
|
28
22
|
}
|
|
29
23
|
else if (userChoice === 'redo') {
|
|
30
24
|
console.log('🔁 Regenerating changelog...');
|
|
31
|
-
entry = await generateChangelogEntry(
|
|
25
|
+
entry = await generateChangelogEntry();
|
|
32
26
|
if (!entry) {
|
|
33
27
|
console.log('⚠️ Could not regenerate entry. Exiting.');
|
|
34
28
|
break;
|
|
@@ -40,26 +34,26 @@ export async function handleStandaloneChangelogUpdate() {
|
|
|
40
34
|
}
|
|
41
35
|
}
|
|
42
36
|
}
|
|
43
|
-
export async function generateChangelogEntry(
|
|
37
|
+
export async function generateChangelogEntry(currentCommitMsg) {
|
|
44
38
|
try {
|
|
45
|
-
|
|
46
|
-
if (!
|
|
47
|
-
|
|
48
|
-
// If no unstaged changes, fall back to staged changes
|
|
49
|
-
if (!diff) {
|
|
50
|
-
diff = execSync("git diff --cached", { encoding: "utf-8", stdio: 'pipe' }).trim();
|
|
51
|
-
}
|
|
39
|
+
const lastTag = getLastGitTag();
|
|
40
|
+
if (!lastTag) {
|
|
41
|
+
console.log("⚠️ No previous git tag found. Using all commits.");
|
|
52
42
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
43
|
+
let commits = lastTag
|
|
44
|
+
? getCommitsSinceTag(lastTag)
|
|
45
|
+
: execSync('git log --pretty=format:%s', { encoding: 'utf-8' }).trim();
|
|
46
|
+
if (currentCommitMsg) {
|
|
47
|
+
commits = commits ? `${commits}\n${currentCommitMsg}` : currentCommitMsg;
|
|
48
|
+
}
|
|
49
|
+
console.log('Number of commits:', commits.split('\n').length);
|
|
50
|
+
if (!commits) {
|
|
51
|
+
console.log("⚠️ No commits found since last release.");
|
|
56
52
|
return null;
|
|
57
53
|
}
|
|
58
|
-
|
|
59
|
-
const result = await runModulePipeline([changelogModule], { content: diff });
|
|
54
|
+
const result = await runModulePipeline([changelogModule], { content: commits });
|
|
60
55
|
const output = result?.summary?.trim();
|
|
61
56
|
if (!output || output === 'NO UPDATE') {
|
|
62
|
-
// Auto-cancel if no update
|
|
63
57
|
console.log('⚠️ No significant changes detected for changelog.');
|
|
64
58
|
return null;
|
|
65
59
|
}
|
|
@@ -70,6 +64,18 @@ export async function generateChangelogEntry(diff) {
|
|
|
70
64
|
return null;
|
|
71
65
|
}
|
|
72
66
|
}
|
|
67
|
+
function getLastGitTag() {
|
|
68
|
+
try {
|
|
69
|
+
return execSync('git describe --tags --abbrev=0', { encoding: 'utf-8' }).trim();
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null; // no tags found
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function getCommitsSinceTag(tag) {
|
|
76
|
+
// Get commit messages in a simple format, e.g. only commit messages
|
|
77
|
+
return execSync(`git log ${tag}..HEAD --pretty=format:%s`, { encoding: 'utf-8' }).trim();
|
|
78
|
+
}
|
|
73
79
|
export async function updateChangelogFile(entry) {
|
|
74
80
|
const root = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
75
81
|
const changelogPath = path.join(root, "CHANGELOG.md");
|
|
@@ -85,3 +91,38 @@ export async function updateChangelogFile(entry) {
|
|
|
85
91
|
await fs.writeFile(changelogPath, existing + newEntry, 'utf-8');
|
|
86
92
|
return changelogPath;
|
|
87
93
|
}
|
|
94
|
+
export async function handleChangelogWithCommitMessage(commitMsg) {
|
|
95
|
+
let entryFinalized = false;
|
|
96
|
+
let changelogEntry = await generateChangelogEntry(commitMsg);
|
|
97
|
+
if (!changelogEntry) {
|
|
98
|
+
console.log("ℹ️ No changelog entry generated.");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
while (!entryFinalized) {
|
|
102
|
+
const userChoice = await askChangelogApproval(changelogEntry);
|
|
103
|
+
if (userChoice === 'yes') {
|
|
104
|
+
const changelogPath = await updateChangelogFile(changelogEntry);
|
|
105
|
+
execSync(`git add "${changelogPath}"`);
|
|
106
|
+
console.log("✅ CHANGELOG.md staged.");
|
|
107
|
+
entryFinalized = true;
|
|
108
|
+
}
|
|
109
|
+
else if (userChoice === 'redo') {
|
|
110
|
+
console.log("🔁 Regenerating changelog entry...");
|
|
111
|
+
changelogEntry = await generateChangelogEntry(commitMsg);
|
|
112
|
+
if (!changelogEntry) {
|
|
113
|
+
console.log('⚠️ Could not regenerate entry. Exiting.');
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (userChoice === 'edit') {
|
|
118
|
+
changelogEntry = await openTextEditor(changelogEntry, 'scai-changelog.txt');
|
|
119
|
+
if (!changelogEntry) {
|
|
120
|
+
console.log('⚠️ No changes made to changelog. Returning to prompt.');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
console.log("❌ Skipped changelog update.");
|
|
125
|
+
entryFinalized = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -2,23 +2,28 @@
|
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
3
|
import readline from 'readline';
|
|
4
4
|
import { commitSuggesterModule } from '../pipeline/modules/commitSuggesterModule.js';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { handleChangelogWithCommitMessage } from './ChangeLogUpdateCmd.js';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { spawnSync } from 'child_process';
|
|
10
|
+
import chalk from 'chalk';
|
|
7
11
|
function askUserToChoose(suggestions) {
|
|
8
12
|
return new Promise((resolve) => {
|
|
9
13
|
console.log('\n💡 AI-suggested commit messages:\n');
|
|
10
14
|
suggestions.forEach((msg, i) => {
|
|
11
|
-
console.log(`${i + 1}) ${msg}`);
|
|
15
|
+
console.log(`${i + 1}) ${chalk.hex('#FFA500')(`\`${msg}\``)}`);
|
|
12
16
|
});
|
|
13
17
|
console.log('\n---');
|
|
14
18
|
console.log(`${suggestions.length + 1}) 🔁 Regenerate suggestions`);
|
|
15
19
|
console.log(`${suggestions.length + 2}) ✍️ Write your own commit message`);
|
|
16
|
-
console.log(`${suggestions.length + 3})
|
|
20
|
+
console.log(`${suggestions.length + 3}) 🖋️ Edit a suggested commit message`);
|
|
21
|
+
console.log(`${suggestions.length + 4}) ❌ Cancel`);
|
|
17
22
|
const rl = readline.createInterface({
|
|
18
23
|
input: process.stdin,
|
|
19
24
|
output: process.stdout,
|
|
20
25
|
});
|
|
21
|
-
rl.question(`\n👉 Choose a commit message [1-${suggestions.length +
|
|
26
|
+
rl.question(`\n👉 Choose a commit message [1-${suggestions.length + 4}]: `, (answer) => {
|
|
22
27
|
rl.close();
|
|
23
28
|
const choice = parseInt(answer, 10);
|
|
24
29
|
if (choice === suggestions.length + 1) {
|
|
@@ -28,6 +33,9 @@ function askUserToChoose(suggestions) {
|
|
|
28
33
|
resolve('custom');
|
|
29
34
|
}
|
|
30
35
|
else if (choice === suggestions.length + 3) {
|
|
36
|
+
resolve('edit');
|
|
37
|
+
}
|
|
38
|
+
else if (choice === suggestions.length + 4) {
|
|
31
39
|
resolve('cancel');
|
|
32
40
|
}
|
|
33
41
|
else if (!isNaN(choice) && choice >= 1 && choice <= suggestions.length) {
|
|
@@ -40,6 +48,46 @@ function askUserToChoose(suggestions) {
|
|
|
40
48
|
});
|
|
41
49
|
});
|
|
42
50
|
}
|
|
51
|
+
async function promptEditCommitMessage(suggestedMessage) {
|
|
52
|
+
const tmpFilePath = path.join(os.tmpdir(), 'scai-commit-msg.txt');
|
|
53
|
+
fs.writeFileSync(tmpFilePath, `# Edit your commit message below.\n# Lines starting with '#' will be ignored.\n\n${suggestedMessage}`);
|
|
54
|
+
const editor = process.env.EDITOR || (process.platform === 'win32' ? 'notepad' : 'vi');
|
|
55
|
+
spawnSync(editor, [tmpFilePath], { stdio: 'inherit' });
|
|
56
|
+
const editedContent = fs.readFileSync(tmpFilePath, 'utf-8');
|
|
57
|
+
return editedContent
|
|
58
|
+
.split('\n')
|
|
59
|
+
.filter(line => !line.trim().startsWith('#'))
|
|
60
|
+
.join('\n')
|
|
61
|
+
.trim() || suggestedMessage;
|
|
62
|
+
}
|
|
63
|
+
function askWhichSuggestionToEdit(suggestions) {
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
console.log('\n🖋️ Select a commit message to edit:\n');
|
|
66
|
+
suggestions.forEach((msg, i) => {
|
|
67
|
+
console.log(`${i + 1}) ${chalk.hex('#FFA500')(`\`${msg}\``)}`);
|
|
68
|
+
});
|
|
69
|
+
console.log(`${suggestions.length + 1}) ❌ Cancel`);
|
|
70
|
+
const rl = readline.createInterface({
|
|
71
|
+
input: process.stdin,
|
|
72
|
+
output: process.stdout,
|
|
73
|
+
});
|
|
74
|
+
const editPrompt = chalk.magenta(`\n👉 Choose a commit message to edit [1-${suggestions.length + 1}]: `);
|
|
75
|
+
rl.question(editPrompt, (answer) => {
|
|
76
|
+
rl.close();
|
|
77
|
+
const choice = parseInt(answer, 10);
|
|
78
|
+
if (!isNaN(choice) && choice >= 1 && choice <= suggestions.length) {
|
|
79
|
+
resolve(choice - 1);
|
|
80
|
+
}
|
|
81
|
+
else if (choice === suggestions.length + 1) {
|
|
82
|
+
resolve('cancel');
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.log('⚠️ Invalid selection.');
|
|
86
|
+
resolve('cancel');
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
43
91
|
function promptCustomMessage() {
|
|
44
92
|
return new Promise((resolve) => {
|
|
45
93
|
const rl = readline.createInterface({
|
|
@@ -53,42 +101,15 @@ function promptCustomMessage() {
|
|
|
53
101
|
});
|
|
54
102
|
}
|
|
55
103
|
export async function suggestCommitMessage(options) {
|
|
56
|
-
// Ensure git diff does not print to console
|
|
57
104
|
try {
|
|
58
|
-
let diff = execSync("git diff", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
105
|
+
let diff = execSync("git diff --cached", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
59
106
|
if (!diff) {
|
|
60
|
-
diff = execSync("git diff
|
|
107
|
+
diff = execSync("git diff", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
61
108
|
}
|
|
62
109
|
if (!diff) {
|
|
63
|
-
console.log('⚠️
|
|
110
|
+
console.log('⚠️ No staged changes to suggest a message for.');
|
|
64
111
|
return;
|
|
65
112
|
}
|
|
66
|
-
// Handle changelog generation if the flag is provided
|
|
67
|
-
if (options.changelog) {
|
|
68
|
-
let entryFinalized = false;
|
|
69
|
-
while (!entryFinalized) {
|
|
70
|
-
const changelogEntry = await generateChangelogEntry(diff);
|
|
71
|
-
if (!changelogEntry) {
|
|
72
|
-
console.log("ℹ️ No changelog entry generated.");
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
const userChoice = await askChangelogApproval(changelogEntry);
|
|
76
|
-
if (userChoice === 'yes') {
|
|
77
|
-
const changelogPath = await updateChangelogFile(changelogEntry);
|
|
78
|
-
execSync(`git add "${changelogPath}"`);
|
|
79
|
-
console.log("✅ CHANGELOG.md staged.");
|
|
80
|
-
entryFinalized = true;
|
|
81
|
-
}
|
|
82
|
-
else if (userChoice === 'redo') {
|
|
83
|
-
console.log("🔁 Regenerating changelog entry...");
|
|
84
|
-
continue; // Loop again and regenerate
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
console.log("❌ Skipped changelog update.");
|
|
88
|
-
entryFinalized = true;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
113
|
// Continue with commit suggestions
|
|
93
114
|
const response = await commitSuggesterModule.run({ content: diff });
|
|
94
115
|
const suggestions = response.suggestions || [];
|
|
@@ -108,6 +129,17 @@ export async function suggestCommitMessage(options) {
|
|
|
108
129
|
if (choice === 'custom') {
|
|
109
130
|
message = await promptCustomMessage();
|
|
110
131
|
}
|
|
132
|
+
else if (choice === 'edit') {
|
|
133
|
+
// Ask which suggestion to edit using a dedicated prompt
|
|
134
|
+
const editChoice = await askWhichSuggestionToEdit(suggestions);
|
|
135
|
+
if (typeof editChoice === 'number') {
|
|
136
|
+
message = await promptEditCommitMessage(suggestions[editChoice]);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
console.log('⚠️ Edit cancelled, returning to main menu.');
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
111
143
|
else if (choice === 'cancel') {
|
|
112
144
|
console.log('❌ Commit cancelled.');
|
|
113
145
|
return;
|
|
@@ -117,6 +149,10 @@ export async function suggestCommitMessage(options) {
|
|
|
117
149
|
}
|
|
118
150
|
}
|
|
119
151
|
console.log(`\n✅ Selected commit message:\n${message}\n`);
|
|
152
|
+
// If changelog option is enabled, generate changelog including the selected commit message
|
|
153
|
+
if (options.changelog) {
|
|
154
|
+
await handleChangelogWithCommitMessage(message);
|
|
155
|
+
}
|
|
120
156
|
const staged = execSync("git diff --cached", { encoding: "utf-8" }).trim();
|
|
121
157
|
if (!staged) {
|
|
122
158
|
console.log("⚠️ No files are currently staged for commit.");
|
|
@@ -2,7 +2,6 @@ import fs from 'fs/promises';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { runModulePipeline } from '../pipeline/runModulePipeline.js';
|
|
4
4
|
import { addCommentsModule } from '../pipeline/modules/commentModule.js';
|
|
5
|
-
import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
|
|
6
5
|
export async function handleRefactor(filepath, options = {}) {
|
|
7
6
|
try {
|
|
8
7
|
// Normalize path: add ./ prefix if no directory specified
|
|
@@ -27,7 +26,7 @@ export async function handleRefactor(filepath, options = {}) {
|
|
|
27
26
|
// Read source code
|
|
28
27
|
const content = await fs.readFile(filepath, 'utf-8');
|
|
29
28
|
// Run through pipeline modules
|
|
30
|
-
const response = await runModulePipeline([addCommentsModule
|
|
29
|
+
const response = await runModulePipeline([addCommentsModule], { content });
|
|
31
30
|
if (!response.content.trim())
|
|
32
31
|
throw new Error('⚠️ Model returned empty result');
|
|
33
32
|
// Save refactored output
|
package/dist/github/repo.js
CHANGED
|
@@ -17,7 +17,7 @@ function getRepoOwnerAndNameFromGit(indexDir) {
|
|
|
17
17
|
try {
|
|
18
18
|
const originUrl = runGitCommand('git config --get remote.origin.url', indexDir);
|
|
19
19
|
console.log(`🔗 Git origin URL from '${indexDir}': ${originUrl}`);
|
|
20
|
-
const match = originUrl.match(/github
|
|
20
|
+
const match = originUrl.match(/github[^:/]*[:/](.+?)(?:\.git)?$/);
|
|
21
21
|
if (!match)
|
|
22
22
|
throw new Error("❌ Could not parse GitHub repo from origin URL.");
|
|
23
23
|
const [owner, repo] = match[1].split('/');
|
package/dist/lib/generate.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
// File: lib/generate.ts
|
|
2
2
|
import { Spinner } from './spinner.js';
|
|
3
|
+
import { readConfig } from '../config.js';
|
|
3
4
|
export async function generate(input, model) {
|
|
5
|
+
const contextLength = readConfig().contextLength ?? 8192;
|
|
6
|
+
let prompt = input.content;
|
|
7
|
+
if (prompt.length > contextLength) {
|
|
8
|
+
console.warn(`⚠️ Warning: Input prompt length (${prompt.length}) exceeds model context length (${contextLength}). ` +
|
|
9
|
+
`The model may truncate or not handle the entire prompt. Truncating input.`);
|
|
10
|
+
prompt = prompt.slice(0, contextLength);
|
|
11
|
+
}
|
|
4
12
|
const spinner = new Spinner(`🧠 Thinking with ${model}...`);
|
|
5
13
|
spinner.start();
|
|
6
14
|
try {
|
|
@@ -9,7 +17,7 @@ export async function generate(input, model) {
|
|
|
9
17
|
headers: { 'Content-Type': 'application/json' },
|
|
10
18
|
body: JSON.stringify({
|
|
11
19
|
model,
|
|
12
|
-
prompt
|
|
20
|
+
prompt, // use truncated prompt here
|
|
13
21
|
stream: false,
|
|
14
22
|
}),
|
|
15
23
|
});
|
|
@@ -1,26 +1,57 @@
|
|
|
1
1
|
import { Config } from '../../config.js';
|
|
2
2
|
import { generate } from '../../lib/generate.js';
|
|
3
|
+
import { detectFileType } from '../../fileRules/detectFileType.js';
|
|
3
4
|
export const addCommentsModule = {
|
|
4
5
|
name: 'addComments',
|
|
5
|
-
description: 'Adds meaningful
|
|
6
|
+
description: 'Adds meaningful comments to any file type (code, config, or data)',
|
|
6
7
|
async run(input) {
|
|
7
8
|
const model = Config.getModel();
|
|
8
|
-
const
|
|
9
|
+
const fileType = detectFileType(input.filepath || '');
|
|
10
|
+
// Map file types to valid comment syntax
|
|
11
|
+
const commentMap = {
|
|
12
|
+
javascript: '//',
|
|
13
|
+
typescript: '//',
|
|
14
|
+
java: '//',
|
|
15
|
+
rust: '//',
|
|
16
|
+
c: '//',
|
|
17
|
+
cpp: '//',
|
|
18
|
+
csharp: '//',
|
|
19
|
+
go: '//',
|
|
20
|
+
swift: '//',
|
|
21
|
+
kotlin: '//',
|
|
22
|
+
scala: '//',
|
|
23
|
+
python: '#',
|
|
24
|
+
ruby: '#',
|
|
25
|
+
php: '//',
|
|
26
|
+
shell: '#',
|
|
27
|
+
bash: '#',
|
|
28
|
+
config: '#',
|
|
29
|
+
yaml: '#',
|
|
30
|
+
markdown: '<!-- -->',
|
|
31
|
+
html: '<!-- -->',
|
|
32
|
+
xml: '<!-- -->',
|
|
33
|
+
json: '/* */',
|
|
34
|
+
sql: '--',
|
|
35
|
+
csv: '#',
|
|
36
|
+
tsv: '#',
|
|
37
|
+
text: '#',
|
|
38
|
+
};
|
|
39
|
+
const commentSyntax = commentMap[fileType] || '//';
|
|
9
40
|
const prompt = `
|
|
10
|
-
You are a senior
|
|
41
|
+
You are a senior engineer reviewing a ${fileType} file.
|
|
11
42
|
|
|
12
|
-
Your task is to add clear and
|
|
43
|
+
Your task is to add clear, helpful, and accurate comments using "${commentSyntax}" syntax.
|
|
44
|
+
This applies to *every* section where an explanation could help — even for simple code or configuration.
|
|
13
45
|
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- Do
|
|
19
|
-
- The code should be valid ${lang.toUpperCase()} after your changes.
|
|
46
|
+
Rules:
|
|
47
|
+
- Always return the full original file with comments added.
|
|
48
|
+
- Preserve all formatting, whitespace, and code exactly.
|
|
49
|
+
- Use only "${commentSyntax}" comments that are valid for ${fileType}.
|
|
50
|
+
- Do not wrap the output in markdown or code fences.
|
|
20
51
|
|
|
21
|
-
---
|
|
52
|
+
--- FILE START ---
|
|
22
53
|
${input.content}
|
|
23
|
-
---
|
|
54
|
+
--- FILE END ---
|
|
24
55
|
`.trim();
|
|
25
56
|
const response = await generate({ content: prompt }, model);
|
|
26
57
|
return { content: response.content === 'NO UPDATE' ? '' : response.content };
|
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
// src/utils/changelogPrompt.ts
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import readline from 'readline';
|
|
3
|
-
export async function askChangelogApproval(
|
|
4
|
+
export async function askChangelogApproval(changelogEntry) {
|
|
4
5
|
return new Promise((resolve) => {
|
|
5
|
-
console.log('\n📜 Proposed changelog entry:\n');
|
|
6
|
-
console.log(entry);
|
|
7
6
|
console.log('\n---');
|
|
8
|
-
console.log('
|
|
7
|
+
console.log(chalk.yellow('Suggested changelog entry:\n'));
|
|
8
|
+
console.log(changelogEntry);
|
|
9
|
+
console.log('\nOptions:');
|
|
10
|
+
console.log('1) ✅ Accept');
|
|
9
11
|
console.log('2) 🔁 Regenerate');
|
|
10
|
-
console.log('3)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
-
rl.question('\n👉 Choose an option [1-3]: ', (answer) => {
|
|
12
|
+
console.log('3) 🖋️ Edit');
|
|
13
|
+
console.log('4) ❌ Skip');
|
|
14
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
15
|
+
rl.question('\n👉 Choose an option [1-4]: ', (answer) => {
|
|
16
16
|
rl.close();
|
|
17
|
-
switch (answer
|
|
17
|
+
switch (answer) {
|
|
18
18
|
case '1':
|
|
19
19
|
resolve('yes');
|
|
20
20
|
break;
|
|
21
21
|
case '2':
|
|
22
22
|
resolve('redo');
|
|
23
23
|
break;
|
|
24
|
+
case '3':
|
|
25
|
+
resolve('edit');
|
|
26
|
+
break;
|
|
27
|
+
case '4':
|
|
24
28
|
default:
|
|
25
|
-
resolve('
|
|
29
|
+
resolve('skip');
|
|
26
30
|
break;
|
|
27
31
|
}
|
|
28
32
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { spawnSync } from 'child_process';
|
|
5
|
+
export async function openTextEditor(initialContent, filename) {
|
|
6
|
+
const tmpFilePath = path.join(os.tmpdir(), filename);
|
|
7
|
+
fs.writeFileSync(tmpFilePath, initialContent, 'utf-8');
|
|
8
|
+
const editor = process.env.EDITOR || (process.platform === 'win32' ? 'notepad' : 'vi');
|
|
9
|
+
spawnSync(editor, [tmpFilePath], { stdio: 'inherit' });
|
|
10
|
+
const editedContent = fs.readFileSync(tmpFilePath, 'utf-8');
|
|
11
|
+
return editedContent
|
|
12
|
+
.split('\n')
|
|
13
|
+
.filter(line => !line.trim().startsWith('#'))
|
|
14
|
+
.join('\n')
|
|
15
|
+
.trim();
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.83",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"scai": "./dist/index.js"
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@types/better-sqlite3": "^7.6.13",
|
|
53
53
|
"@types/columnify": "^1.5.4",
|
|
54
54
|
"@types/jest": "^30.0.0",
|
|
55
|
-
"@types/node": "^24.1
|
|
55
|
+
"@types/node": "^24.2.1",
|
|
56
56
|
"@types/proper-lockfile": "^4.1.4",
|
|
57
57
|
"jest": "^30.0.2",
|
|
58
58
|
"ts-jest": "^29.4.0",
|
package/LICENSE
DELETED
package/README.md
DELETED
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
# ⚙️ scai — Smart Commit AI ✨
|
|
2
|
-
|
|
3
|
-
> AI-powered CLI tool for commit messages **and** pull request reviews — using local models.
|
|
4
|
-
|
|
5
|
-
**scai** is your AI pair‑programmer in the terminal. Focus on coding while scai:
|
|
6
|
-
|
|
7
|
-
- 🤖 **Reviews open pull requests** and provides AI‑driven feedback (BETA)
|
|
8
|
-
- 💬 **Suggests intelligent Git commit messages** based on your staged diff
|
|
9
|
-
- 📝 Summarizes files in plain English
|
|
10
|
-
- 📜 Auto‑updates your changelog
|
|
11
|
-
- 🔍 (ALPHA) Search & ask questions across your codebase
|
|
12
|
-
- 🔐 100% local — no API keys, no cloud, no telemetry
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 🚀 Features
|
|
17
|
-
|
|
18
|
-
- ⚡ Powered by open-source models (e.g. `llama3`, `codellama`)
|
|
19
|
-
- 🔍 Full-text indexing & semantic search (ALPHA)
|
|
20
|
-
- 🛠️ Built with Node.js and TypeScript
|
|
21
|
-
- ✅ Easily configurable via CLI or global flags
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## ❤️ Why Local AI?
|
|
26
|
-
|
|
27
|
-
**Your code stays yours.**
|
|
28
|
-
scai runs entirely on your machine and doesn't require cloud APIs or API keys. That means:
|
|
29
|
-
|
|
30
|
-
- ✅ **Privacy-first**: no telemetry, no server round-trips
|
|
31
|
-
- ✅ **EU & GDPR-friendly**: designed with compliance in mind
|
|
32
|
-
- ✅ **Developer control**: full transparency and override options
|
|
33
|
-
- ✅ **Offline support**: works even without an internet connection
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## 📦 Installation
|
|
38
|
-
|
|
39
|
-
1. **Install Ollama (for local models)**
|
|
40
|
-
- macOS: `brew install ollama`
|
|
41
|
-
- Windows: [Download here](https://ollama.com/download)
|
|
42
|
-
- Start Ollama after installing.
|
|
43
|
-
|
|
44
|
-
2. **Install scai globally:**
|
|
45
|
-
```bash
|
|
46
|
-
npm install -g scai
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
3. **Initialize models:**
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
scai init
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## ✨ AI Code Review, Powered by Your Terminal
|
|
56
|
-
|
|
57
|
-
No more struggling to write pull request descriptions by hand. `scai git review` automatically generates a rich summary of your changes, complete with context, suggestions, and rationale.
|
|
58
|
-
|
|
59
|
-
> ⚠️ These features are in **beta** — feedback welcome!
|
|
60
|
-
Ping [@ticcr](https://bsky.app/profile/ticcr.xyz) on Bluesky — I'd love to hear your thoughts!
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
### 🔑 Setting Up Authentication (Required)
|
|
65
|
-
|
|
66
|
-
To interact with GitHub and create pull requests, `scai` needs a personal access token with **repo** permissions.
|
|
67
|
-
|
|
68
|
-
1. **Create your GitHub Access Token**
|
|
69
|
-
Follow this link to generate a token: [https://github.com/settings/personal-access-tokens](https://github.com/settings/personal-access-tokens)
|
|
70
|
-
|
|
71
|
-
Make sure you enable at least:
|
|
72
|
-
|
|
73
|
-
* `repo` (Full control of private repositories)
|
|
74
|
-
* `workflow` (If you want PRs to trigger CI)
|
|
75
|
-
|
|
76
|
-
2. **Set the token in scai:**
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
scai auth set
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
This stores your token locally in a secure config file. You can inspect the setup at any time:
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
scai auth check
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
3. **Set the index dir:**
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
scai index set /path/to/repo
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
This is the repo from which scai will look up pull requests that can be reviewed.
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
## ⚒️ Usage Overview
|
|
98
|
-
### 🧠 How to Use `scai git review`
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
scai git review
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
This will show you pull requests assigned to you for review:
|
|
105
|
-
|
|
106
|
-
* Understand the diffs using a local model
|
|
107
|
-
* Generate a structured pull request:
|
|
108
|
-
|
|
109
|
-
* ✅ Title
|
|
110
|
-
* ✅ Summary of changes
|
|
111
|
-
* ✅ Explanation of why the changes matter
|
|
112
|
-
* ✅ Optional changelog bullets
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
SCAI supports an integrated review flow for GitHub pull requests. To get started:
|
|
116
|
-
|
|
117
|
-
1. **Set your working index directory (once per repo):**
|
|
118
|
-
|
|
119
|
-
```sh
|
|
120
|
-
scai index set /path/to/repo
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
2. **Authenticate with GitHub:**
|
|
124
|
-
```sh
|
|
125
|
-
scai git review
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
This command will query you for the Personal Access Token and set it for you.
|
|
129
|
-
You may also do this with the auth commands below
|
|
130
|
-
|
|
131
|
-
```sh
|
|
132
|
-
scai auth set
|
|
133
|
-
scai auth check
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
3. **Fetch and review pull requests:**
|
|
137
|
-
|
|
138
|
-
```sh
|
|
139
|
-
scai git review
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Use `-a` to list all PRs that require a review:
|
|
143
|
-
|
|
144
|
-
```sh
|
|
145
|
-
scai git review -a
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
#### Example Workflow
|
|
149
|
-
|
|
150
|
-
```sh
|
|
151
|
-
$ scai git review -a
|
|
152
|
-
📦 Resolving GitHub repo info from indexDir: ./
|
|
153
|
-
🔗 Git origin URL: git@github.com:org/repo.git
|
|
154
|
-
✅ Parsed: owner='org', repo='repo'
|
|
155
|
-
👤 Authenticated user: dev-user123
|
|
156
|
-
|
|
157
|
-
🔍 Fetching pull requests and diffs...
|
|
158
|
-
✅ Fetched 5 PR(s) with diffs.
|
|
159
|
-
|
|
160
|
-
📦 Open Pull Requests with review requested:
|
|
161
|
-
| # | ID | TITLE | AUTHOR | STATUS | CREATED | REVIEWERS | REVIEWS |
|
|
162
|
-
| - | ---- | ----------------------------- | ---------- | ------ | ---------- | ----------------------------- | ------------------- |
|
|
163
|
-
| 1 | #120 | fix/session-timeout | dev-alice | Open | 2025-08-08 | code-analyzer\[bot], dev-bob | ✅ Approved |
|
|
164
|
-
| 2 | #118 | feature/1482-support-wfs2 | dev-carol | Open | 2025-08-07 | code-analyzer\[bot], dev-dave | ✅ Approved |
|
|
165
|
-
| 3 | #117 | refactor/win-server-support | dev-erin | Open | 2025-08-06 | dev-frank, dev-alice | ❌ Changes Requested |
|
|
166
|
-
| 4 | #114 | bump/vue-i18n-9.14.5 | dependabot | Open | 2025-08-04 | code-analyzer\[bot] | ✅ Approved |
|
|
167
|
-
| 5 | #113 | bugfix/null-navigator-check | dev-bob | Open | 2025-08-03 | dev-alice, dev-carol | ✅ Approved |
|
|
168
|
-
|
|
169
|
-
👉 Choose a PR to review [1-2]: 1
|
|
170
|
-
✅ Model response received.
|
|
171
|
-
|
|
172
|
-
💡 AI-suggested review:
|
|
173
|
-
|
|
174
|
-
Solid improvement — this patch improves session stability and handles async state more reliably.
|
|
175
|
-
You might consider renaming `sessionManager` to better reflect its dual role in auth and persistence.
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
1) ✅ Approve
|
|
179
|
-
2) ❌ Reject
|
|
180
|
-
3) ✍️ Edit
|
|
181
|
-
4) Write your own review
|
|
182
|
-
5) 🚪 Cancel
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
### 🔧 How to Use `scai git commit`
|
|
188
|
-
|
|
189
|
-
Use AI to suggest a meaningful commit message based on your staged code:
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
git add .
|
|
193
|
-
scai git commit
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
You can also include a changelog entry along with the commit:
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
scai git commit --changelog
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
This will:
|
|
203
|
-
1. Suggest a commit message based on your `git diff --cached`
|
|
204
|
-
2. Propose a changelog entry (if relevant)
|
|
205
|
-
3. Allow you to approve, regenerate, or skip the changelog
|
|
206
|
-
4. Automatically stage and commit the changes
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
### 📝 Generate a Standalone Changelog Entry
|
|
211
|
-
|
|
212
|
-
If you want to generate a changelog entry without committing:
|
|
213
|
-
|
|
214
|
-
```bash
|
|
215
|
-
scai gen changelog
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
This will:
|
|
219
|
-
- Analyze the current `git diff` (staged or unstaged)
|
|
220
|
-
- Propose a list of **user-facing changes** in clean markdown bullet points
|
|
221
|
-
- Let you accept, regenerate, or skip the update
|
|
222
|
-
- Append the entry to `CHANGELOG.md` and stage it if accepted
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
### 🛠️ Code Generation Commands (`gen` group)
|
|
226
|
-
|
|
227
|
-
```bash
|
|
228
|
-
scai gen summ <file>
|
|
229
|
-
scai gen comm <file>
|
|
230
|
-
scai gen changelog
|
|
231
|
-
scai gen tests <file>
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
* `summ`: Summarize a file
|
|
235
|
-
* `comm`: Add comments to a file
|
|
236
|
-
* `changelog`: Update or create `CHANGELOG.md` from Git diff
|
|
237
|
-
* `tests`: Create Jest test stubs (ALPHA)
|
|
238
|
-
|
|
239
|
-
You can also pipe file content directly:
|
|
240
|
-
|
|
241
|
-
```bash
|
|
242
|
-
cat src/utils/math.ts | scai gen summ
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## ⚙️ Configuration
|
|
248
|
-
|
|
249
|
-
scai stores settings in `~/.scai/config.json`. You can override or view them:
|
|
250
|
-
|
|
251
|
-
* **Set model:**
|
|
252
|
-
|
|
253
|
-
```bash
|
|
254
|
-
scai set model codellama:7b
|
|
255
|
-
```
|
|
256
|
-
* **Set language:**
|
|
257
|
-
|
|
258
|
-
```bash
|
|
259
|
-
scai set lang ts
|
|
260
|
-
```
|
|
261
|
-
* **Show config:**
|
|
262
|
-
|
|
263
|
-
```bash
|
|
264
|
-
scai config
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
<br>
|
|
268
|
-
|
|
269
|
-
## 🔁 Background Daemon and Indexing ⚠️ ALPHA Notice
|
|
270
|
-
|
|
271
|
-
These features are experimental and subject to change:
|
|
272
|
-
|
|
273
|
-
* `index`, `find`, `ask`
|
|
274
|
-
* `daemon`, `stop-daemon`
|
|
275
|
-
* `gen tests` (test generation)
|
|
276
|
-
|
|
277
|
-
<br>
|
|
278
|
-
|
|
279
|
-
## Commands
|
|
280
|
-
|
|
281
|
-
### `index`
|
|
282
|
-
The `index` command is used to manage and perform operations on the indexed files and repositories.
|
|
283
|
-
|
|
284
|
-
#### `scai index start`
|
|
285
|
-
|
|
286
|
-
Index supported files in the configured index directory.
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
scai index set <dir>
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
Set and activate the index directory.
|
|
293
|
-
```bash
|
|
294
|
-
scai index list
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
List all indexed repositories.
|
|
298
|
-
```bash
|
|
299
|
-
scai index switch
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
Switch active repository (by key or indexDir). Run without input for an interactive list of repositories.
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
---
|
|
306
|
-
|
|
307
|
-
> **Note:** Indexing very large repositories (millions of lines) may take **hours or days**. Please be patient, and only index huge codebases if you are ok with some extra processing taking place on your computer.
|
|
308
|
-
|
|
309
|
-
The `scai index` command **automatically** starts a background daemon that continuously:
|
|
310
|
-
|
|
311
|
-
* Scans your target directory
|
|
312
|
-
* Summarizes new or changed files
|
|
313
|
-
* Updates embeddings and the search index
|
|
314
|
-
|
|
315
|
-
You won't gain much value from the index unless you scope it to one repository.
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
### 🔍 Codebase Search & Ask (ALPHA)
|
|
320
|
-
|
|
321
|
-
> **Important:** You must `index` a **code repository** first or `find` and `ask` have no context to work with.
|
|
322
|
-
|
|
323
|
-
1. **Set index directory:**
|
|
324
|
-
|
|
325
|
-
```bash
|
|
326
|
-
scai index set /path/to/repo
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
2. **Index your repo (once):**
|
|
330
|
-
|
|
331
|
-
```bash
|
|
332
|
-
scai index start
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
3. The daemon is designed to **consume minimal resources** and run unobtrusively. You can control it with:
|
|
336
|
-
|
|
337
|
-
```bash
|
|
338
|
-
scai daemon # Start or show daemon status
|
|
339
|
-
scai stop-daemon # Stop the background indexer
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
4. **Keyword search:**
|
|
344
|
-
|
|
345
|
-
```bash
|
|
346
|
-
scai find YourClassName
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
5. **Natural-language questions:**
|
|
350
|
-
|
|
351
|
-
### 🧠 Natural-language questions
|
|
352
|
-
|
|
353
|
-
Ask questions about your codebase using `scai ask`.
|
|
354
|
-
|
|
355
|
-
You can run it in two ways:
|
|
356
|
-
|
|
357
|
-
1. **Inline question**
|
|
358
|
-
|
|
359
|
-
```bash
|
|
360
|
-
scai ask "How does the controller work?"
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
2. **Interactive prompt**
|
|
364
|
-
|
|
365
|
-
```bash
|
|
366
|
-
scai ask
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
**Press enter**
|
|
370
|
-
, then type your question when prompted:
|
|
371
|
-
|
|
372
|
-
```
|
|
373
|
-
> How does the controller work?
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
</br>
|
|
377
|
-
|
|
378
|
-
### 🚨 **OBS** 🚨
|
|
379
|
-
|
|
380
|
-
`find` and `ask` rely on that index—without it they will return no useful results.
|
|
381
|
-
|
|
382
|
-
---
|
|
383
|
-
|
|
384
|
-
Note the **Migrate** command is for **internal use only**. Do **not** run it unless explicitly instructed, as it may delete existing data or corrupt your local database.
|
|
385
|
-
|
|
386
|
-
If you're upgrading from an earlier version, please run the following commands to avoid indexing issues:
|
|
387
|
-
|
|
388
|
-
```bash
|
|
389
|
-
scai reset-db
|
|
390
|
-
scai index
|
|
391
|
-
```
|
|
392
|
-
</br>
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
## 🛍️ Maintenance & Utilities
|
|
397
|
-
|
|
398
|
-
* **Reset database (w/ backup):**
|
|
399
|
-
|
|
400
|
-
```bash
|
|
401
|
-
scai reset-db
|
|
402
|
-
```
|
|
403
|
-
* **Backup only of `~/.scai`:**
|
|
404
|
-
|
|
405
|
-
```bash
|
|
406
|
-
scai backup
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
---
|
|
410
|
-
|
|
411
|
-
## 🧺 Module Pipeline Mode
|
|
412
|
-
|
|
413
|
-
For custom pipelines on a single file:
|
|
414
|
-
|
|
415
|
-
```bash
|
|
416
|
-
scai src/file.ts -m summary,comments
|
|
417
|
-
```
|
|
418
|
-
---
|
|
419
|
-
|
|
420
|
-
## 🔐 License & Fair Use
|
|
421
|
-
|
|
422
|
-
**scai is free to use** for individuals, teams, and commercial projects.
|
|
423
|
-
|
|
424
|
-
You may:
|
|
425
|
-
|
|
426
|
-
* ✅ Use internally or commercially
|
|
427
|
-
* ✅ Fork and improve
|
|
428
|
-
* ✅ Recommend to others
|
|
429
|
-
|
|
430
|
-
You may **not**:
|
|
431
|
-
|
|
432
|
-
* ❌ Resell as a product or service
|
|
433
|
-
* ❌ Claim ownership of the tool
|
|
434
|
-
|
|
435
|
-
</br>
|
|
436
|
-
|
|
437
|
-
### 📄 License
|
|
438
|
-
|
|
439
|
-
Free for personal and internal company use only.
|
|
440
|
-
Commercial use (resale, SaaS, inclusion in paid tools) is prohibited.
|
|
441
|
-
Contact me for commercial licensing.
|
|
442
|
-
|
|
443
|
-
---
|
|
444
|
-
|
|
445
|
-
</br>
|
|
446
|
-
|
|
447
|
-
## 🙌 Feedback
|
|
448
|
-
|
|
449
|
-
Questions, ideas, or bugs?
|
|
450
|
-
Ping [@ticcr](https://bsky.app/profile/ticcr.xyz) on Bluesky — I'd love to hear your thoughts!
|
|
451
|
-
|
|
452
|
-
---
|