scai 0.1.95 → 0.1.97
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 +11 -1
- package/dist/commands/ResetDbCmd.js +2 -1
- package/dist/config.js +1 -1
- package/dist/context.js +65 -0
- package/dist/index.js +145 -86
- package/package.json +1 -1
package/dist/CHANGELOG.md
CHANGED
|
@@ -131,4 +131,14 @@ Type handling with the module pipeline
|
|
|
131
131
|
|
|
132
132
|
• Add DB-related commands to db subcommand (check, reset, migrate)
|
|
133
133
|
• Update table view limits for files and functions in dbcheck.ts
|
|
134
|
-
• Improved resetDatabase command with confirmation prompt before deleting database
|
|
134
|
+
• Improved resetDatabase command with confirmation prompt before deleting database
|
|
135
|
+
|
|
136
|
+
## 2025-08-23
|
|
137
|
+
|
|
138
|
+
* Improved CLI configuration settings with context-aware actions
|
|
139
|
+
* Improved logging and added active repo change detection
|
|
140
|
+
|
|
141
|
+
## 2025-08-23
|
|
142
|
+
|
|
143
|
+
* Improved CLI configuration settings with context-aware actions
|
|
144
|
+
* Added CLI configuration settings with context-aware actions and improved logging
|
|
@@ -4,6 +4,7 @@ import lockfile from 'proper-lockfile';
|
|
|
4
4
|
import readline from 'readline';
|
|
5
5
|
import { backupScaiFolder } from '../db/backup.js';
|
|
6
6
|
import { getDbPathForRepo, getDbForRepo } from '../db/client.js';
|
|
7
|
+
import chalk from 'chalk';
|
|
7
8
|
export async function resetDatabase() {
|
|
8
9
|
const dbPath = getDbPathForRepo();
|
|
9
10
|
console.log(`⚠️ You are about to delete the database at: ${dbPath}`);
|
|
@@ -74,5 +75,5 @@ export async function resetDatabase() {
|
|
|
74
75
|
console.warn('⚠️ Failed to remove lock directory:', err instanceof Error ? err.message : err);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
|
-
console.log('✅ Database has been reset. You can now re-run: scai index');
|
|
78
|
+
console.log('✅ Database has been reset.' + chalk.yellow('You can now re-run: scai index'));
|
|
78
79
|
}
|
package/dist/config.js
CHANGED
package/dist/context.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// context.ts
|
|
2
|
+
import { readConfig, writeConfig } from "./config.js";
|
|
3
|
+
import { normalizePath } from "./utils/normalizePath.js";
|
|
4
|
+
import { getHashedRepoKey } from "./utils/repoKey.js";
|
|
5
|
+
import { getDbForRepo, getDbPathForRepo } from "./db/client.js";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
export async function updateContext() {
|
|
9
|
+
const cwd = normalizePath(process.cwd());
|
|
10
|
+
const cfg = readConfig();
|
|
11
|
+
// 🔑 Find repoKey by matching indexDir to cwd
|
|
12
|
+
let repoKey = Object.keys(cfg.repos || {}).find((key) => normalizePath(cfg.repos[key]?.indexDir || "") === cwd);
|
|
13
|
+
// Initialize new repo config if not found
|
|
14
|
+
let isNewRepo = false;
|
|
15
|
+
if (!repoKey) {
|
|
16
|
+
repoKey = getHashedRepoKey(cwd);
|
|
17
|
+
if (!cfg.repos[repoKey])
|
|
18
|
+
cfg.repos[repoKey] = {};
|
|
19
|
+
cfg.repos[repoKey].indexDir = cwd;
|
|
20
|
+
isNewRepo = true;
|
|
21
|
+
}
|
|
22
|
+
// Check if active repo has changed
|
|
23
|
+
const activeRepoChanged = cfg.activeRepo !== repoKey;
|
|
24
|
+
// Always set this as active repo
|
|
25
|
+
cfg.activeRepo = repoKey;
|
|
26
|
+
writeConfig(cfg);
|
|
27
|
+
const repoCfg = cfg.repos[repoKey];
|
|
28
|
+
let ok = true;
|
|
29
|
+
// Only log detailed info if new repo or active repo changed
|
|
30
|
+
if (isNewRepo || activeRepoChanged) {
|
|
31
|
+
console.log(chalk.yellow("\n🔁 Updating context...\n"));
|
|
32
|
+
console.log(`✅ Active repo: ${chalk.green(repoKey)}`);
|
|
33
|
+
console.log(`✅ Index dir: ${chalk.cyan(repoCfg.indexDir || cwd)}`);
|
|
34
|
+
}
|
|
35
|
+
// GitHub token is optional
|
|
36
|
+
const token = repoCfg.githubToken || cfg.githubToken;
|
|
37
|
+
if (!token) {
|
|
38
|
+
console.log(`ℹ️ No GitHub token found. You can set one with the: ${chalk.bold(chalk.bgGreen("scai auth set"))} command`);
|
|
39
|
+
}
|
|
40
|
+
else if (isNewRepo || activeRepoChanged) {
|
|
41
|
+
console.log(`✅ GitHub token present`);
|
|
42
|
+
}
|
|
43
|
+
// Ensure DB exists
|
|
44
|
+
const dbPath = getDbPathForRepo();
|
|
45
|
+
if (!fs.existsSync(dbPath)) {
|
|
46
|
+
console.log(chalk.yellow(`📦 Initializing DB at ${dbPath}`));
|
|
47
|
+
try {
|
|
48
|
+
getDbForRepo();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
ok = false; // DB init failed
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (isNewRepo || activeRepoChanged) {
|
|
55
|
+
console.log(chalk.green("✅ Database present"));
|
|
56
|
+
}
|
|
57
|
+
// Final context status
|
|
58
|
+
if (ok) {
|
|
59
|
+
console.log(chalk.bold.green("\n✅ Context OK\n"));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(chalk.bold.red("\n⚠️ Context incomplete\n"));
|
|
63
|
+
}
|
|
64
|
+
return ok;
|
|
65
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import { generateTestsModule } from './pipeline/modules/generateTestsModule.js';
|
|
|
32
32
|
import { preserveCodeModule } from './pipeline/modules/preserveCodeModule.js';
|
|
33
33
|
import { runInteractiveDelete } from './commands/DeleteIndex.js';
|
|
34
34
|
import { resolveTargetsToFiles } from './utils/resolveTargetsToFiles.js';
|
|
35
|
+
import { updateContext } from './context.js';
|
|
35
36
|
// 🎛️ CLI Setup
|
|
36
37
|
const cmd = new Command('scai')
|
|
37
38
|
.version(version)
|
|
@@ -52,55 +53,70 @@ git
|
|
|
52
53
|
.description('Review an open pull request using AI')
|
|
53
54
|
.option('-a, --all', 'Show all PRs requiring a review (not just for the current user)', false)
|
|
54
55
|
.action(async (cmd) => {
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
await withContext(async () => {
|
|
57
|
+
const showAll = cmd.all;
|
|
58
|
+
await reviewPullRequestCmd('main', showAll);
|
|
59
|
+
});
|
|
57
60
|
});
|
|
58
61
|
git
|
|
59
62
|
.command('commit')
|
|
60
63
|
.description('Suggest a commit message from staged changes and optionally commit')
|
|
61
64
|
.option('-l, --changelog', 'Generate and optionally stage a changelog entry')
|
|
62
|
-
.action((options) =>
|
|
65
|
+
.action(async (options) => {
|
|
66
|
+
await withContext(async () => {
|
|
67
|
+
suggestCommitMessage(options);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
63
70
|
git
|
|
64
71
|
.command('check')
|
|
65
72
|
.description('Check Git working directory and branch status')
|
|
66
|
-
.action(() => {
|
|
67
|
-
|
|
73
|
+
.action(async () => {
|
|
74
|
+
await withContext(async () => {
|
|
75
|
+
checkGit();
|
|
76
|
+
});
|
|
68
77
|
});
|
|
69
78
|
// Add auth-related commands
|
|
70
79
|
const auth = cmd.command('auth').description('GitHub authentication commands');
|
|
80
|
+
// ⚡ Auth commands
|
|
71
81
|
auth
|
|
72
82
|
.command('check')
|
|
73
83
|
.description('Check if GitHub authentication is set up and valid')
|
|
74
84
|
.action(async () => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
await withContext(async () => {
|
|
86
|
+
try {
|
|
87
|
+
const token = Config.getGitHubToken();
|
|
88
|
+
if (!token) {
|
|
89
|
+
console.log('❌ GitHub authentication not found. Please set your token.');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const result = await validateGitHubTokenAgainstRepo();
|
|
93
|
+
console.log(result);
|
|
80
94
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
console.error(typeof err === 'string' ? err : err.message);
|
|
86
|
-
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
console.error(typeof err === 'string' ? err : err.message);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
87
99
|
});
|
|
88
100
|
auth
|
|
89
101
|
.command('reset')
|
|
90
102
|
.description('Reset GitHub authentication credentials')
|
|
91
|
-
.action(() => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
.action(async () => {
|
|
104
|
+
await withContext(async () => {
|
|
105
|
+
Config.setGitHubToken('');
|
|
106
|
+
console.log('🔄 GitHub authentication has been reset.');
|
|
107
|
+
const token = Config.getGitHubToken();
|
|
108
|
+
console.log(token ? '❌ Token still exists in the configuration.' : '✅ Token successfully removed.');
|
|
109
|
+
});
|
|
96
110
|
});
|
|
97
111
|
auth
|
|
98
112
|
.command('set')
|
|
99
113
|
.description('Set your GitHub Personal Access Token')
|
|
100
114
|
.action(async () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
await withContext(async () => {
|
|
116
|
+
const token = await promptForToken();
|
|
117
|
+
Config.setGitHubToken(token.trim());
|
|
118
|
+
console.log('🔑 GitHub token set successfully.');
|
|
119
|
+
});
|
|
104
120
|
});
|
|
105
121
|
// 🛠️ Group: `gen` commands for content generation
|
|
106
122
|
const gen = cmd.command('gen').description('Generate code-related output');
|
|
@@ -108,91 +124,109 @@ gen
|
|
|
108
124
|
.command("comm <targets...>")
|
|
109
125
|
.description("Write comments for the given file(s) or folder(s)")
|
|
110
126
|
.action(async (targets) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
await withContext(async () => {
|
|
128
|
+
const files = await resolveTargetsToFiles(targets, [".ts", ".js"]);
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
await handleAgentRun(file, [addCommentsModule, preserveCodeModule]);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
115
133
|
});
|
|
116
134
|
gen
|
|
117
135
|
.command('changelog')
|
|
118
136
|
.description('Update or create the CHANGELOG.md based on current Git diff')
|
|
119
137
|
.action(async () => {
|
|
120
|
-
await
|
|
138
|
+
await withContext(async () => {
|
|
139
|
+
await handleStandaloneChangelogUpdate();
|
|
140
|
+
});
|
|
121
141
|
});
|
|
122
142
|
gen
|
|
123
143
|
.command('summ [file]')
|
|
124
144
|
.description('Print a summary of the given file to the terminal')
|
|
125
|
-
.action((file) =>
|
|
145
|
+
.action(async (file) => {
|
|
146
|
+
await withContext(async () => {
|
|
147
|
+
summarizeFile(file);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
126
150
|
gen
|
|
127
151
|
.command('testgen <file>')
|
|
128
152
|
.description('Generate tests for the given file')
|
|
129
153
|
.option('-a, --apply', 'Apply the output to the original file')
|
|
130
|
-
.action((file) => {
|
|
131
|
-
|
|
154
|
+
.action(async (file) => {
|
|
155
|
+
await withContext(async () => {
|
|
156
|
+
handleAgentRun(file, [generateTestsModule]);
|
|
157
|
+
});
|
|
132
158
|
});
|
|
133
159
|
// ⚙️ Group: Configuration settings
|
|
134
160
|
const config = cmd.command('config').description('Manage SCAI configuration');
|
|
135
161
|
config
|
|
136
162
|
.command('set-model <model>')
|
|
137
163
|
.description('Set the model to use')
|
|
138
|
-
.action((model) => {
|
|
139
|
-
|
|
140
|
-
|
|
164
|
+
.action(async (model) => {
|
|
165
|
+
await withContext(async () => {
|
|
166
|
+
Config.setModel(model);
|
|
167
|
+
Config.show();
|
|
168
|
+
});
|
|
141
169
|
});
|
|
142
170
|
config
|
|
143
171
|
.command('set-lang <lang>')
|
|
144
172
|
.description('Set the programming language')
|
|
145
|
-
.action((lang) => {
|
|
146
|
-
|
|
147
|
-
|
|
173
|
+
.action(async (lang) => {
|
|
174
|
+
await withContext(async () => {
|
|
175
|
+
Config.setLanguage(lang);
|
|
176
|
+
Config.show();
|
|
177
|
+
});
|
|
148
178
|
});
|
|
149
179
|
config
|
|
150
|
-
.command(
|
|
151
|
-
.option(
|
|
152
|
-
.description(
|
|
153
|
-
.action((options) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
180
|
+
.command("show")
|
|
181
|
+
.option("--raw", "Show full raw config")
|
|
182
|
+
.description("Display current configuration")
|
|
183
|
+
.action(async (options) => {
|
|
184
|
+
await withContext(async () => {
|
|
185
|
+
if (options.raw) {
|
|
186
|
+
console.log(JSON.stringify(Config.getRaw(), null, 2));
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
Config.show();
|
|
190
|
+
}
|
|
191
|
+
});
|
|
160
192
|
});
|
|
161
193
|
const index = cmd.command('index').description('index operations');
|
|
162
194
|
index
|
|
163
195
|
.command('start')
|
|
164
196
|
.description('Index supported files in the configured index directory')
|
|
165
|
-
.action(
|
|
197
|
+
.action(async () => await withContext(async () => {
|
|
198
|
+
await runIndexCommand();
|
|
199
|
+
}));
|
|
166
200
|
index
|
|
167
201
|
.command('set [dir]')
|
|
168
202
|
.description('Set and activate index directory')
|
|
169
|
-
.action((dir = process.cwd()) => {
|
|
203
|
+
.action(async (dir = process.cwd()) => await withContext(async () => {
|
|
170
204
|
Config.setIndexDir(dir);
|
|
171
205
|
Config.show();
|
|
172
|
-
});
|
|
206
|
+
}));
|
|
173
207
|
index
|
|
174
208
|
.command('list')
|
|
175
209
|
.description('List all indexed repositories')
|
|
176
|
-
.action(() => {
|
|
177
|
-
Config.printAllRepos();
|
|
178
|
-
});
|
|
210
|
+
.action(async () => await withContext(async () => {
|
|
211
|
+
await Config.printAllRepos();
|
|
212
|
+
}));
|
|
179
213
|
index
|
|
180
214
|
.command('switch')
|
|
181
215
|
.description('Switch active repository (by interactive list only)')
|
|
182
|
-
.action(() => {
|
|
183
|
-
runInteractiveSwitch();
|
|
184
|
-
});
|
|
216
|
+
.action(async () => await withContext(async () => {
|
|
217
|
+
await runInteractiveSwitch();
|
|
218
|
+
}));
|
|
185
219
|
index
|
|
186
220
|
.command('delete')
|
|
187
221
|
.description('Delete a repository from the index (interactive)')
|
|
188
|
-
.action(() => {
|
|
189
|
-
runInteractiveDelete();
|
|
190
|
-
});
|
|
222
|
+
.action(async () => await withContext(async () => {
|
|
223
|
+
await runInteractiveDelete();
|
|
224
|
+
}));
|
|
191
225
|
const db = cmd.command('db').description('Database operations');
|
|
192
226
|
db
|
|
193
227
|
.command('check')
|
|
194
228
|
.description('Run the dbcheck script to check the database status')
|
|
195
|
-
.action(() => {
|
|
229
|
+
.action(async () => await withContext(async () => {
|
|
196
230
|
const __filename = fileURLToPath(import.meta.url);
|
|
197
231
|
const __dirname = dirname(__filename);
|
|
198
232
|
const scriptPath = resolve(__dirname, '..', 'dist/scripts', 'dbcheck.js');
|
|
@@ -203,47 +237,66 @@ db
|
|
|
203
237
|
catch (err) {
|
|
204
238
|
console.error('❌ Error running dbcheck script:', err instanceof Error ? err.message : err);
|
|
205
239
|
}
|
|
206
|
-
});
|
|
240
|
+
}));
|
|
207
241
|
db
|
|
208
242
|
.command('reset')
|
|
209
243
|
.description('Delete and reset the SQLite database')
|
|
210
|
-
.action(() =>
|
|
244
|
+
.action(async () => await withContext(async () => {
|
|
245
|
+
await resetDatabase();
|
|
246
|
+
}));
|
|
211
247
|
db
|
|
212
248
|
.command('migrate')
|
|
213
249
|
.description('Run DB migration scripts')
|
|
214
|
-
.action(
|
|
250
|
+
.action(async () => await withContext(async () => {
|
|
251
|
+
await runMigrateCommand();
|
|
252
|
+
}));
|
|
253
|
+
db
|
|
254
|
+
.command('inspect')
|
|
255
|
+
.argument('<filepath>', 'Path to the file to inspect')
|
|
256
|
+
.description('Inspect a specific file and print its indexed summary and functions')
|
|
257
|
+
.action(async (filepath) => await withContext(async () => {
|
|
258
|
+
await runInspectCommand(filepath);
|
|
259
|
+
}));
|
|
260
|
+
const daemon = cmd
|
|
261
|
+
.command('daemon')
|
|
262
|
+
.description('Background summarizer operations');
|
|
263
|
+
// Start the daemon
|
|
264
|
+
daemon
|
|
265
|
+
.command('start')
|
|
266
|
+
.description('Run background summarization of indexed files')
|
|
267
|
+
.action(async () => {
|
|
268
|
+
await withContext(async () => {
|
|
269
|
+
await startDaemon();
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
// Stop the daemon
|
|
273
|
+
daemon
|
|
274
|
+
.command('stop')
|
|
275
|
+
.description('Stop the background summarizer daemon')
|
|
276
|
+
.action(async () => {
|
|
277
|
+
await withContext(async () => {
|
|
278
|
+
await runStopDaemonCommand();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
215
281
|
cmd
|
|
216
282
|
.command('backup')
|
|
217
283
|
.description('Backup the current .scai folder')
|
|
218
|
-
.action(
|
|
284
|
+
.action(async () => await withContext(async () => {
|
|
285
|
+
await runBackupCommand();
|
|
286
|
+
}));
|
|
219
287
|
cmd
|
|
220
288
|
.command('find <query>')
|
|
221
289
|
.description('Search indexed files by keyword')
|
|
222
|
-
.action(
|
|
290
|
+
.action(async (query) => await withContext(async () => {
|
|
291
|
+
await runFindCommand(query);
|
|
292
|
+
}));
|
|
223
293
|
cmd
|
|
224
294
|
.command('ask [question...]')
|
|
225
295
|
.description('Ask a question based on indexed files')
|
|
226
|
-
.action((questionParts) => {
|
|
296
|
+
.action(async (questionParts) => await withContext(async () => {
|
|
227
297
|
const fullQuery = questionParts?.join(' ');
|
|
228
|
-
runAskCommand(fullQuery);
|
|
229
|
-
});
|
|
230
|
-
cmd
|
|
231
|
-
.command('daemon')
|
|
232
|
-
.description('Run background summarization of indexed files')
|
|
233
|
-
.action(async () => {
|
|
234
|
-
await startDaemon();
|
|
235
|
-
});
|
|
236
|
-
cmd
|
|
237
|
-
.command('stop-daemon')
|
|
238
|
-
.description('Stop the background summarizer daemon')
|
|
239
|
-
.action(runStopDaemonCommand);
|
|
240
|
-
cmd
|
|
241
|
-
.command('inspect')
|
|
242
|
-
.argument('<filepath>', 'Path to the file to inspect')
|
|
243
|
-
.description('Inspect a specific file and print its indexed summary and functions')
|
|
244
|
-
.action(async (filepath) => {
|
|
245
|
-
await runInspectCommand(filepath);
|
|
246
|
-
});
|
|
298
|
+
await runAskCommand(fullQuery);
|
|
299
|
+
}));
|
|
247
300
|
cmd
|
|
248
301
|
.command('pipe')
|
|
249
302
|
.description('Run a module pipeline on a given file')
|
|
@@ -269,3 +322,9 @@ if (opts.model)
|
|
|
269
322
|
Config.setModel(opts.model);
|
|
270
323
|
if (opts.lang)
|
|
271
324
|
Config.setLanguage(opts.lang);
|
|
325
|
+
async function withContext(action) {
|
|
326
|
+
const ok = await updateContext();
|
|
327
|
+
if (!ok)
|
|
328
|
+
process.exit(1);
|
|
329
|
+
await action();
|
|
330
|
+
}
|