minovative-mind-cli 2.5.2 → 2.6.1

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.
Files changed (41) hide show
  1. package/README.md +29 -26
  2. package/dist/commands/chat.js +2 -2
  3. package/dist/services/agent/inputHandler.d.ts +9 -0
  4. package/dist/services/agent/inputHandler.js +34 -0
  5. package/dist/services/agent/slashCommands.js +105 -29
  6. package/dist/services/agent/syntaxAgent.d.ts +40 -0
  7. package/dist/services/agent/syntaxAgent.js +237 -23
  8. package/dist/services/agent/toolLoop.js +10 -1
  9. package/dist/services/agent/types.d.ts +1 -0
  10. package/dist/services/agent-tools.d.ts +156 -1
  11. package/dist/services/agent-tools.js +259 -67
  12. package/dist/services/agent.d.ts +74 -0
  13. package/dist/services/agent.js +193 -31
  14. package/dist/services/ai.d.ts +5 -0
  15. package/dist/services/ai.js +80 -87
  16. package/dist/services/chatHistoryService.d.ts +11 -0
  17. package/dist/services/chatHistoryService.js +20 -1
  18. package/dist/services/contextAgent.d.ts +1 -1
  19. package/dist/services/contextAgent.js +9 -29
  20. package/dist/services/orchestration/investigationAgent.d.ts +2 -1
  21. package/dist/services/orchestration/investigationAgent.js +7 -2
  22. package/dist/services/orchestration/investigationOrchestrator.d.ts +1 -1
  23. package/dist/services/orchestration/investigationOrchestrator.js +13 -2
  24. package/dist/services/orchestration/orchestrator.js +21 -4
  25. package/dist/services/orchestration/subAgent.d.ts +2 -1
  26. package/dist/services/orchestration/subAgent.js +12 -6
  27. package/dist/utils/analysisRunner.d.ts +27 -4
  28. package/dist/utils/analysisRunner.js +100 -20
  29. package/dist/utils/config.d.ts +2 -0
  30. package/dist/utils/config.js +2 -0
  31. package/dist/utils/fuzzyMatch.d.ts +32 -0
  32. package/dist/utils/fuzzyMatch.js +215 -27
  33. package/dist/utils/localSyntaxValidator.d.ts +2 -2
  34. package/dist/utils/localSyntaxValidator.js +280 -81
  35. package/dist/utils/performanceAuditor.d.ts +2 -7
  36. package/dist/utils/performanceAuditor.js +541 -89
  37. package/dist/utils/projectStorage.js +9 -0
  38. package/dist/utils/systemPrompts.d.ts +3 -2
  39. package/dist/utils/systemPrompts.js +29 -5
  40. package/oclif.manifest.json +2 -2
  41. package/package.json +1 -1
package/README.md CHANGED
@@ -94,11 +94,13 @@ Hot-swap during a session using `/models`:
94
94
  | **Gemini 3.5 Flash Lite** | Best for speed and cost efficiency |
95
95
 
96
96
  ### BYOK (Bring Your Own Key)
97
- If you prefer to use your own Gemini API key, you can configure it via the settings.
98
- * **Configuration:** Set `useByok: true` and `geminiApiKey: "YOUR_KEY"` in your credentials storage.
99
- * **Error Handling:** If your key is invalid, expired, or you hit rate limits, the CLI will report a `BYOK AI Error`. Please check your API key status in the Google AI Studio dashboard.
100
97
 
101
- > Background tasks (routing, compression, commits) always use lightweight
98
+ If you prefer to use your own API key instead of credits, you can configure it via the `/config-key` slash command. **Only API keys from [Google AI Studio](https://aistudio.google.com) are supported** — Vertex AI, OpenAI, and Anthropic keys are not compatible.
99
+
100
+ - **Configuration:** Use `/config-key` in the chat session to set and manage your API key.
101
+ - **Error Handling:** If your key is invalid, expired, or you hit rate limits, the CLI will report a `BYOK AI Error`. Please check your API key status in the [Google AI Studio dashboard](https://aistudio.google.com).
102
+
103
+ > Background tasks (routing, history summarization, context compression, commits) always use lightweight
102
104
  > models automatically. You pay for those lightweight model
103
105
  > background ai models and for your selected model
104
106
  > during chat and code execution.
@@ -108,27 +110,27 @@ If you prefer to use your own Gemini API key, you can configure it via the setti
108
110
 
109
111
  ## Session Commands
110
112
 
111
- | Command | What it does |
112
- | --------------- | -------------------------------------------------------------------------------------------------------------------------- |
113
- | `/models` | Hot-swap the active model |
114
- | `/plan` | Toggle plan mode to review implementation strategies |
115
- | `/paste` | Multi-line input mode (cancel with Ctrl+C) |
116
- | `/clear` | Clear conversation history |
117
- | `/debug` | Expose internal agent diagnostics |
118
- | `/auto-approve` | Toggle skipping confirmation prompts for commands |
119
- | `/sub-agents` | Toggle the MMAAK Engine for parallel investigation and execution (concurrency limited to 2) |
120
- | `/stats` | View current session statistics and configuration |
121
- | `/commit` | Generate a conventional commit message from your diff |
122
- | `/revert` | Undo changes from the last turn, or toggle the revert logger |
123
- | `/chats` | View, resume, or delete previous chat sessions (with centralized token telemetry, Git branch, auto-approve status, and sub-agent state) |
124
- | `/workspaces` | Manage active workspaces and linked cross-repo aliases |
125
- | `stop` | Abort generation immediately |
113
+ | Command | What it does |
114
+ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
115
+ | `/models` | Hot-swap the active model |
116
+ | `/plan` | Toggle plan mode to review implementation strategies |
117
+ | `/paste` | Multi-line input mode (cancel with Ctrl+C) |
118
+ | `/clear` | Clear conversation history |
119
+ | `/debug` | Expose internal agent diagnostics |
120
+ | `/auto-approve` | Toggle skipping confirmation prompts for commands |
121
+ | `/sub-agents` | Toggle the MMAAK Engine for parallel investigation and execution (concurrency limited to 2) |
122
+ | `/stats` | View session and all-time statistics, plus configurations |
123
+ | `/commit` | Generate a conventional commit message from your diff |
124
+ | `/revert` | Undo changes from the last turn, or toggle the revert logger |
125
+ | `/chats` | View, resume, rename, or delete previous chat sessions (with centralized token telemetry, Git branch, auto-approve status, and sub-agent state) |
126
+ | `/workspaces` | Manage active workspaces and linked cross-repo aliases |
127
+ | `stop` | Abort generation immediately |
126
128
 
127
129
  ---
128
130
 
129
131
  ## 🌐 Multi-Workspace & Cross-Repo Support (Master & Sub-Workspaces)
130
132
 
131
- Minovative Mind CLI doesn't restrict you to a single repository. You can logically group multiple external repositories into **Master Workspaces** (Profiles) containing dedicated **Sub-Workspaces** (mapped to short aliases like `@backend` or `@frontend`).
133
+ Minovative Mind CLI doesn't restrict you to a single repository. You can logically group multiple external repositories into **Master Workspaces** (Profiles) containing dedicated **Sub-Workspaces** (mapped to short aliases like `@backend` or `@frontend`).
132
134
 
133
135
  The Context Agent and Thread Agents operate strictly within the boundaries of the active Master Workspace, ignoring other profiles to keep the context window highly targeted and memory-efficient.
134
136
 
@@ -140,7 +142,7 @@ By prefixing file paths with `@alias/` (e.g., `@backend/src/api.ts` and `@fronte
140
142
 
141
143
  Minovative Mind CLI fundamentally supports **ALL programming languages** for chat, code generation, planning, and execution, as it relies on Gemini's vast training data.
142
144
 
143
- However, the PCV engine features deep, context-aware analysis across **11 major programming language families**. Our core advanced engines—**Smart Dependency Tracing**, **Performance Auditing**, and **Ephemeral Analysis Scripts**—provide tailored support depending on the language's syntax and runtime model:
145
+ However, the PCV engine features deep, context-aware analysis across **12 major programming language families**. Our core advanced engines—**Smart Dependency Tracing**, **Performance Auditing** (across 9 language families), and **Ephemeral Analysis Scripts**—provide tailored support depending on the language's syntax and runtime model:
144
146
 
145
147
  | Language Family | Supported Extensions | Dependency Tracing | Performance Auditing | Ephemeral Analysis |
146
148
  | :--------------------------- | :------------------------------------------- | :----------------: | :------------------: | :----------------: |
@@ -148,11 +150,12 @@ However, the PCV engine features deep, context-aware analysis across **11 major
148
150
  | **Python** | `.py`, `.pyw` | ✅ | ✅ | ✅ |
149
151
  | **Rust** | `.rs` | ✅ | ✅ | ✅ |
150
152
  | **Go** | `.go` | ✅ | ✅ | ✅ |
151
- | **C / C++** | `.c`, `.h`, `.cpp`, `.hpp`, `.cc` | ✅ | ⚠️ \* | ⚠️ \* |
152
- | **Java / Kotlin** | `.java`, `.kt`, `.kts` | ✅ | ⚠️ \* | ⚠️ \* |
153
+ | **C / C++** | `.c`, `.h`, `.cpp`, `.hpp`, `.cc`, `.cxx` | ✅ || ⚠️ \* |
154
+ | **C#** | `.cs` | ✅ | ✅ | |
155
+ | **Java / Kotlin** | `.java`, `.kt`, `.kts` | ✅ | ✅ | ⚠️ \* |
153
156
  | **PHP** | `.php` | ✅ | ✅ | ❌ |
157
+ | **Ruby** | `.rb` | ✅ | ✅ | ❌ |
154
158
  | **CSS / SCSS / SASS / LESS** | `.css`, `.scss`, `.sass`, `.less` | ✅ | ❌ | ❌ |
155
- | **Ruby** | `.rb` | ✅ | ❌ | ❌ |
156
159
  | **Swift** | `.swift` | ✅ | ❌ | ❌ |
157
160
  | **Dart** | `.dart` | ✅ | ❌ | ❌ |
158
161
 
@@ -160,8 +163,8 @@ _⚠️\*Support is limited or requires custom local system tooling/environment
160
163
 
161
164
  ### Why do some features only support specific languages?
162
165
 
163
- 1. **Dependency Tracing:** Fully supported for all 11 languages. It uses lightning-fast static regex pattern matching to resolve local import structures and determine the "blast radius" of code changes without requiring local compilations.
164
- 2. **Performance Auditing:** Uses regex-based heuristic analysis to scan for issues. Extending this to complex grammars (such as Ruby or Swift) with pure regex can cause high false-positive rates; those require full AST parsing, which runs counter to the auditor's ultra-fast (<50ms) and zero-dependency goals.
166
+ 1. **Dependency Tracing:** Fully supported across language families. It uses lightning-fast static regex pattern matching to resolve local import structures and determine the "blast radius" of code changes without requiring local compilations.
167
+ 2. **Performance Auditing:** Executed natively across 9 major language families (JavaScript/TypeScript, Python, Go, Rust, PHP, C#, Java, C/C++, and Ruby). It uses zero-dependency, ultra-fast (<50ms) language-aware regex heuristics with comment/string stripping and line-preserving offset tracking to detect severe runtime anti-patterns (such as unbounded loops, synchronous I/O, chained array allocations, unnecessary allocations, and unclosed resources).
165
168
  3. **Ephemeral Analysis Scripts:** Runs temporary files in sandbox directories to parse and extract local symbols. Also used for infrastructure probing (detecting available runtimes, checking ports, identifying project type), post-change validation (importing modified modules and asserting expected behavior), and lightweight ML-powered analysis (e.g., TF-IDF file ranking, anomaly detection, Big-O estimation). Defaults to Node.js as a safe baseline, but natively leverages host-installed runtimes (Python, Rust, Go compilers) and standard libraries whenever available in the workspace.
166
169
 
167
170
  ---
@@ -30,11 +30,11 @@ Inside the chat session, you can use the following commands in the slash menu:
30
30
  /auto-approve - Toggle automatic approval of tool/command runs
31
31
  /sub-agents - Toggle the MMAAK Engine for parallel investigation and execution
32
32
  /workspaces - Manage external workspaces for cross-project development
33
- /config-key - BYOK (Bring Your Own Key) Configuration
33
+ /config-key - BYOK Use your own Google AI Studio API key
34
34
  /stats - View current session statistics and configuration
35
35
  /commit - Commit current workspace changes to Git
36
36
  /revert - Revert the last file modification made by the agent
37
- /chats - View, resume, or delete previous chat sessions (includes bulk delete)
37
+ /chats - View, resume, edit titles, or delete previous chat sessions (includes bulk delete)
38
38
 
39
39
  Chat Controls:
40
40
  - Multi-line Input: End a line with \\ to continue on the next line
@@ -73,6 +73,15 @@ export declare class AsyncInputHandler {
73
73
  * the terminal stdin stream to its original raw/cooked state.
74
74
  */
75
75
  stop(): void;
76
+ /**
77
+ * Clears the spinner reference without stopping the input handler.
78
+ */
79
+ clearSpinner(): void;
80
+ /**
81
+ * Sets a new active spinner for the input handler to manage during prompts.
82
+ * Also applies the necessary monkey-patches to track the message state.
83
+ */
84
+ setSpinner(spinner: ReturnType<typeof p.spinner>): void;
76
85
  /**
77
86
  * Retrieves and flushes all user feedback messages accumulated during execution.
78
87
  *
@@ -192,6 +192,40 @@ export class AsyncInputHandler {
192
192
  }
193
193
  this.spinner = null;
194
194
  }
195
+ /**
196
+ * Clears the spinner reference without stopping the input handler.
197
+ */
198
+ clearSpinner() {
199
+ this.spinner = null;
200
+ }
201
+ /**
202
+ * Sets a new active spinner for the input handler to manage during prompts.
203
+ * Also applies the necessary monkey-patches to track the message state.
204
+ */
205
+ setSpinner(spinner) {
206
+ this.spinner = spinner;
207
+ if (!spinner._isPatched) {
208
+ ;
209
+ spinner._isPatched = true;
210
+ spinner._lastMessage = 'Executing...';
211
+ const originalMessage = spinner.message.bind(spinner);
212
+ spinner.message = (msg) => {
213
+ if (msg) {
214
+ ;
215
+ spinner._lastMessage = msg;
216
+ }
217
+ originalMessage(msg);
218
+ };
219
+ const originalStart = spinner.start.bind(spinner);
220
+ spinner.start = (msg) => {
221
+ if (msg) {
222
+ ;
223
+ spinner._lastMessage = msg;
224
+ }
225
+ originalStart(msg);
226
+ };
227
+ }
228
+ }
195
229
  /**
196
230
  * Retrieves and flushes all user feedback messages accumulated during execution.
197
231
  *
@@ -27,7 +27,7 @@ export async function handleSlashCommand(command, context) {
27
27
  if (lowerCommand === '/config-key') {
28
28
  const creds = await loadCredentials();
29
29
  const action = await p['select']({
30
- message: 'BYOK (Bring Your Own Key) Configuration',
30
+ message: 'BYOK (Bring Your Own Key) Configuration — Google AI Studio keys only',
31
31
  options: [
32
32
  { value: 'status', label: 'Status' },
33
33
  { value: 'toggle', label: creds.useByok ? 'Disable BYOK' : 'Enable BYOK' },
@@ -47,7 +47,7 @@ export async function handleSlashCommand(command, context) {
47
47
  }
48
48
  else if (action === 'set') {
49
49
  const key = await p['text']({
50
- message: 'Enter your Gemini API Key:',
50
+ message: 'Enter your Google AI Studio API Key (aistudio.google.com):',
51
51
  validate: (value) => (value ? undefined : 'API Key is required'),
52
52
  });
53
53
  if (key && typeof key === 'string') {
@@ -64,6 +64,8 @@ export async function handleSlashCommand(command, context) {
64
64
  }
65
65
  if (lowerCommand === '/paste') {
66
66
  p.log.info(pc.cyan('Paste mode activated. Paste your text below, then press Ctrl+D on an empty line to submit. (Ctrl+C to cancel)'));
67
+ // Add space at the bottom to prevent terminal UI flickering when the cursor is at the absolute bottom
68
+ process.stdout.write('\n\n\n\x1b[3A');
67
69
  try {
68
70
  const content = await readPaste();
69
71
  if (content === null) {
@@ -105,31 +107,43 @@ export async function handleSlashCommand(command, context) {
105
107
  }
106
108
  if (lowerCommand === '/models') {
107
109
  const currentModel = getGlobalActiveModel();
110
+ const byokEnabled = await isByokEnabled();
111
+ const options = [
112
+ // {
113
+ // value: 'claude-opus-5',
114
+ // label: 'Claude 5 Opus',
115
+ // hint: 'For hardcore & complex problems',
116
+ // },
117
+ {
118
+ value: 'gemini-3.1-pro-preview',
119
+ label: 'Gemini 3.1 Pro',
120
+ hint: 'For hardcore & complex problems',
121
+ },
122
+ // {
123
+ // value: 'claude-sonnet-5',
124
+ // label: 'Claude 5 Sonnet',
125
+ // hint: 'Best for raw speed and cost efficiency',
126
+ // },
127
+ {
128
+ value: 'gemini-3.6-flash',
129
+ label: 'Gemini 3.6 Flash',
130
+ hint: 'Balanced performance & fast',
131
+ },
132
+ {
133
+ value: 'gemini-3.5-flash-lite',
134
+ label: 'Gemini 3.5 Flash-Lite',
135
+ hint: 'Best for raw speed and cost efficiency',
136
+ },
137
+ {
138
+ value: 'auto',
139
+ label: 'Auto (Flash-Lite / Flash)',
140
+ hint: 'Dynamically routes between Gemini 3.5 Flash-Lite and Gemini 3.6 based on prompt complexity',
141
+ },
142
+ ].filter(o => !(byokEnabled && o.value.includes('claude')));
108
143
  const selectedModel = await p['select']({
109
144
  message: `Select AI Model (Current: ${pc.cyan(currentModel)})`,
110
145
  initialValue: currentModel,
111
- options: [
112
- {
113
- value: 'gemini-3.1-pro-preview',
114
- label: 'Gemini 3.1 Pro',
115
- hint: 'For hardcore & complex problems',
116
- },
117
- {
118
- value: 'gemini-3.6-flash',
119
- label: 'Gemini 3.6 Flash',
120
- hint: 'Balanced performance & fast',
121
- },
122
- {
123
- value: 'gemini-3.5-flash-lite',
124
- label: 'Gemini 3.5 Flash-Lite',
125
- hint: 'Best for raw speed and cost efficiency',
126
- },
127
- {
128
- value: 'auto',
129
- label: 'Auto (Flash-Lite / Flash)',
130
- hint: 'Dynamically routes between Gemini 3.5 Flash-Lite and Gemini 3.6 based on prompt complexity',
131
- },
132
- ],
146
+ options: options,
133
147
  });
134
148
  if (!p.isCancel(selectedModel)) {
135
149
  chat.setModel(selectedModel);
@@ -196,7 +210,7 @@ export async function handleSlashCommand(command, context) {
196
210
  const autoApprove = getApprovalMode() === 'skip-all' ? 'Enabled' : 'Disabled';
197
211
  const subAgents = isSubAgentsEnabled() ? 'Enabled' : 'Disabled';
198
212
  const planMode = context.isPlanMode ? 'Enabled' : 'Disabled';
199
- const byokEnabled = await isByokEnabled() ? 'Enabled' : 'Disabled';
213
+ const byokEnabled = (await isByokEnabled()) ? 'Enabled' : 'Disabled';
200
214
  p.log.step(pc.magenta('📊 Session Statistics & Status'));
201
215
  console.log(pc.dim('----------------------------------------'));
202
216
  console.log(`${pc.bold('AI Model:')} ${pc.cyan(displayModel)}`);
@@ -218,7 +232,7 @@ export async function handleSlashCommand(command, context) {
218
232
  try {
219
233
  const fs = await import('node:fs');
220
234
  const path = await import('node:path');
221
- const stat = fs.statSync(path.join(workspaceRoot, '.minovativemind', 'context_cache.json'));
235
+ const stat = await fs.promises.stat(path.join(workspaceRoot, '.minovativemind', 'context_cache.json'));
222
236
  contextSizeKB = (stat.size / 1024).toFixed(1);
223
237
  }
224
238
  catch (e) { }
@@ -287,6 +301,26 @@ export async function handleSlashCommand(command, context) {
287
301
  if (context.chatSessionState?.totalTokens > 0) {
288
302
  console.log(`${pc.bold('Session Total:')} ${context.chatSessionState.totalTokens.toLocaleString()} tokens`);
289
303
  }
304
+ if (context.chatSessionState?.totalCreditsUsed > 0) {
305
+ console.log(`${pc.bold('Session Credits:')} ${context.chatSessionState.totalCreditsUsed.toLocaleString()} credits`);
306
+ }
307
+ try {
308
+ const { chatHistoryService } = await import('../chatHistoryService.js');
309
+ const sessions = chatHistoryService.getSessions() || [];
310
+ let allTimeTokens = 0;
311
+ let allTimeCredits = 0;
312
+ for (const s of sessions) {
313
+ allTimeTokens += s.totalTokens || 0;
314
+ allTimeCredits += s.totalCreditsUsed || 0;
315
+ }
316
+ if (allTimeTokens > 0) {
317
+ console.log(`${pc.bold('All-Time Tokens:')} ${allTimeTokens.toLocaleString()} tokens`);
318
+ }
319
+ if (allTimeCredits > 0) {
320
+ console.log(`${pc.bold('All-Time Credits:')} ${allTimeCredits.toLocaleString()} credits`);
321
+ }
322
+ }
323
+ catch (e) { }
290
324
  console.log(pc.dim('----------------------------------------\n'));
291
325
  return { shouldContinue: true };
292
326
  }
@@ -399,6 +433,7 @@ export async function handleSlashCommand(command, context) {
399
433
  const options = [{ value: 'new', label: 'New Chat' }];
400
434
  if (hasSessions) {
401
435
  options.push({ value: 'resume', label: 'View Chats' });
436
+ options.push({ value: 'rename', label: 'Rename Chat' });
402
437
  options.push({ value: 'delete', label: 'Delete Chat' });
403
438
  options.push({ value: 'bulk-delete', label: 'Bulk Delete Chats' });
404
439
  }
@@ -422,6 +457,7 @@ export async function handleSlashCommand(command, context) {
422
457
  chatSessionState.totalTokens = 0;
423
458
  chatSessionState.totalInputTokens = 0;
424
459
  chatSessionState.totalOutputTokens = 0;
460
+ chatSessionState.totalCreditsUsed = 0;
425
461
  chat.setSessionInfo(chatSessionState.id, workspaceRoot);
426
462
  }
427
463
  process.stdout.write('\x1B[2J\x1B[3J\x1B[H'); // Hard clear screen and scrollback
@@ -436,7 +472,7 @@ export async function handleSlashCommand(command, context) {
436
472
  const { workspaceRegistry } = await import('../workspaceRegistry.js');
437
473
  const sessionOptions = sessions
438
474
  .slice()
439
- .reverse()
475
+ .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
440
476
  .map((s) => {
441
477
  const externalWorkspaces = new Set();
442
478
  if (s.history) {
@@ -478,6 +514,9 @@ export async function handleSlashCommand(command, context) {
478
514
  }
479
515
  const session = sessions.find((s) => s.id === selectedSessionId);
480
516
  if (session) {
517
+ // Update timestamp so it becomes the most recently used session
518
+ session.timestamp = Date.now();
519
+ await chatHistoryService.saveSession(session);
481
520
  chat.loadRawHistory([...session.history]);
482
521
  if (chatSessionState) {
483
522
  chatSessionState.id = session.id;
@@ -485,6 +524,7 @@ export async function handleSlashCommand(command, context) {
485
524
  chatSessionState.totalTokens = session.totalTokens || 0;
486
525
  chatSessionState.totalInputTokens = session.totalInputTokens || 0;
487
526
  chatSessionState.totalOutputTokens = session.totalOutputTokens || 0;
527
+ chatSessionState.totalCreditsUsed = session.totalCreditsUsed || 0;
488
528
  chatSessionState.latestUsageMetadata = session.latestUsageMetadata;
489
529
  chatSessionState.previousUsageMetadata = session.previousUsageMetadata;
490
530
  chatSessionState.modelUsageCounts = session.modelUsageCounts || {};
@@ -583,6 +623,9 @@ export async function handleSlashCommand(command, context) {
583
623
  : '';
584
624
  p.log.info(`${pc.dim('Session Token Usage:')} ${pc.cyan(session.totalTokens.toLocaleString() + ' total tokens')}${pc.dim(inputStr)}`);
585
625
  }
626
+ if (session.totalCreditsUsed !== undefined && session.totalCreditsUsed > 0) {
627
+ p.log.info(`${pc.dim('Session Credits Used:')} ${pc.cyan(session.totalCreditsUsed.toLocaleString())}`);
628
+ }
586
629
  if (session.gitBranch) {
587
630
  p.log.info(`${pc.dim('Last Active Branch:')} ${pc.cyan(session.gitBranch)}`);
588
631
  }
@@ -594,6 +637,36 @@ export async function handleSlashCommand(command, context) {
594
637
  console.log(pc.dim('\nType your coding request below. Type "exit" or "quit" to leave.\n'));
595
638
  }
596
639
  }
640
+ else if (chatsMenu === 'rename') {
641
+ const selectedSessionId = await p['select']({
642
+ message: 'Select a session to rename:',
643
+ options: [...sessionOptions, { value: 'cancel', label: 'Cancel' }],
644
+ });
645
+ if (p.isCancel(selectedSessionId) || selectedSessionId === 'cancel') {
646
+ return { shouldContinue: true };
647
+ }
648
+ const targetSession = sessions.find((s) => s.id === selectedSessionId);
649
+ if (!targetSession) {
650
+ p.log.error('Session not found.');
651
+ return { shouldContinue: true };
652
+ }
653
+ const newTitleInput = await p['text']({
654
+ message: 'Enter new session title:',
655
+ initialValue: targetSession.title,
656
+ placeholder: 'e.g. Refactoring Auth Module',
657
+ validate: (value) => (value && value.trim().length > 0 ? undefined : 'Title cannot be empty'),
658
+ });
659
+ if (p.isCancel(newTitleInput)) {
660
+ p.log.warn('Rename canceled.');
661
+ return { shouldContinue: true };
662
+ }
663
+ const trimmedTitle = newTitleInput.trim();
664
+ await chatHistoryService.updateSessionTitle(selectedSessionId, trimmedTitle);
665
+ if (chatSessionState && chatSessionState.id === selectedSessionId) {
666
+ chatSessionState.title = trimmedTitle;
667
+ }
668
+ p.log.success(`Session renamed to: "${trimmedTitle}"`);
669
+ }
597
670
  else if (chatsMenu === 'delete') {
598
671
  const selectedSessionId = await p['select']({
599
672
  message: 'Select a session to delete:',
@@ -618,7 +691,10 @@ export async function handleSlashCommand(command, context) {
618
691
  else if (chatsMenu === 'bulk-delete') {
619
692
  const selectedIds = await p['multiselect']({
620
693
  message: 'Select chat sessions to delete:',
621
- options: sessions.map((s) => ({
694
+ options: sessions
695
+ .slice()
696
+ .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
697
+ .map((s) => ({
622
698
  value: s.id,
623
699
  label: `${truncate(s.title, 50)} (${new Date(s.timestamp).toLocaleString()})`,
624
700
  })),
@@ -927,7 +1003,7 @@ ${diffOut}
927
1003
  const creds = await loadCredentials();
928
1004
  const byokEnabled = !!(creds.useByok && creds.geminiApiKey);
929
1005
  const action = await p['select']({
930
- message: `API Key Configuration (Current Mode: ${byokEnabled ? pc.green('BYOK') : pc.yellow('Credits')})`,
1006
+ message: `API Key Configuration — Google AI Studio keys only (Current Mode: ${byokEnabled ? pc.green('BYOK') : pc.yellow('Credits')})`,
931
1007
  options: [
932
1008
  { value: 'toggle', label: byokEnabled ? 'Disable BYOK (Use Credits)' : 'Enable BYOK (Use API Key)' },
933
1009
  { value: 'set', label: 'Set/Update API Key' },
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Validates file content and attempts to fix syntax errors.
3
3
  * Uses GEMINI_MODELS.FLASH to perform language-agnostic syntax repair.
4
+ * Utilizes local validation error context and windowed snippet repair for large files.
4
5
  *
5
6
  * @param content The file content to validate.
6
7
  * @param filePath The path of the file being validated.
@@ -8,3 +9,42 @@
8
9
  * @returns The fixed content, or undefined if it was completely valid or couldn't be fixed.
9
10
  */
10
11
  export declare function validateAndFixSyntax(content: string, filePath: string, error?: string): Promise<string | undefined>;
12
+ export interface AIFuzzyMatchOptions {
13
+ /** Optional custom Gemini model override. Defaults to GEMINI_MODELS.FLASH. */
14
+ model?: string;
15
+ /** Whether to validate and fix syntax on the updated content. Defaults to true. */
16
+ validateSyntax?: boolean;
17
+ }
18
+ export interface AIFuzzyMatchResult {
19
+ /** Indicates whether the fuzzy search matching and edit application succeeded. */
20
+ success: boolean;
21
+ /** The updated file content after applying the replacement if successful. */
22
+ content?: string;
23
+ /** Error description if matching or edit application failed. */
24
+ error?: string;
25
+ }
26
+ /**
27
+ * Performs AI-assisted fuzzy search matching and edit application using Gemini Flash.
28
+ * Invoked when deterministic local fuzzy matchers fail to locate a search target block
29
+ * due to whitespace variations, formatting shifts, comment differences, or minor code drift.
30
+ *
31
+ * @param fileContent The current full file content.
32
+ * @param searchContent The search target block that local matchers failed to locate.
33
+ * @param replaceContent The replacement text block to insert in place of the target block.
34
+ * @param filePath The path of the file being edited (used for syntax and language context).
35
+ * @param options Optional configuration including custom model or syntax validation toggles.
36
+ * @returns A promise resolving to an AIFuzzyMatchResult.
37
+ */
38
+ export declare function aiFuzzyMatch(fileContent: string, searchContent: string, replaceContent: string, filePath: string, options?: AIFuzzyMatchOptions): Promise<AIFuzzyMatchResult>;
39
+ /**
40
+ * Convenience wrapper around aiFuzzyMatch that returns the updated file content string if successful,
41
+ * or undefined if fuzzy matching and replacement failed.
42
+ *
43
+ * @param fileContent The current full file content.
44
+ * @param searchContent The search target block that local matchers failed to locate.
45
+ * @param replaceContent The replacement text block to insert in place of the target block.
46
+ * @param filePath The path of the file being edited.
47
+ * @param options Optional configuration options.
48
+ * @returns The updated file content string if successful, or undefined if failed.
49
+ */
50
+ export declare function aiFuzzySearchAndReplace(fileContent: string, searchContent: string, replaceContent: string, filePath: string, options?: AIFuzzyMatchOptions): Promise<string | undefined>;