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 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
@@ -12,7 +12,7 @@ const defaultConfig = {
12
12
  indexDir: '',
13
13
  githubToken: '',
14
14
  repos: {},
15
- activeRepo: undefined,
15
+ activeRepo: null,
16
16
  };
17
17
  function ensureConfigDir() {
18
18
  if (!fs.existsSync(SCAI_HOME)) {
@@ -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
- const showAll = cmd.all;
56
- await reviewPullRequestCmd('main', showAll);
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) => suggestCommitMessage(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
- checkGit();
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
- try {
76
- const token = Config.getGitHubToken();
77
- if (!token) {
78
- console.log('❌ GitHub authentication not found. Please set your token.');
79
- return;
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
- const result = await validateGitHubTokenAgainstRepo();
82
- console.log(result);
83
- }
84
- catch (err) {
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
- Config.setGitHubToken('');
93
- console.log('🔄 GitHub authentication has been reset.');
94
- const token = Config.getGitHubToken();
95
- console.log(token ? '❌ Token still exists in the configuration.' : '✅ Token successfully removed.');
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
- const token = await promptForToken();
102
- Config.setGitHubToken(token.trim());
103
- console.log('🔑 GitHub token set successfully.');
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
- const files = await resolveTargetsToFiles(targets, [".ts", ".js"]);
112
- for (const file of files) {
113
- await handleAgentRun(file, [addCommentsModule, preserveCodeModule]);
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 handleStandaloneChangelogUpdate();
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) => summarizeFile(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
- handleAgentRun(file, [generateTestsModule]);
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
- Config.setModel(model);
140
- Config.show();
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
- Config.setLanguage(lang);
147
- Config.show();
173
+ .action(async (lang) => {
174
+ await withContext(async () => {
175
+ Config.setLanguage(lang);
176
+ Config.show();
177
+ });
148
178
  });
149
179
  config
150
- .command('show')
151
- .option('--raw', 'Show full raw config')
152
- .description('Display current configuration')
153
- .action((options) => {
154
- if (options.raw) {
155
- console.log(JSON.stringify(Config.getRaw(), null, 2));
156
- }
157
- else {
158
- Config.show();
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(runIndexCommand);
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(); // 👈 simple and clean
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(() => resetDatabase());
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(runMigrateCommand);
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(runBackupCommand);
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(runFindCommand);
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.95",
3
+ "version": "0.1.97",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"