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.
- package/README.md +29 -26
- package/dist/commands/chat.js +2 -2
- package/dist/services/agent/inputHandler.d.ts +9 -0
- package/dist/services/agent/inputHandler.js +34 -0
- package/dist/services/agent/slashCommands.js +105 -29
- package/dist/services/agent/syntaxAgent.d.ts +40 -0
- package/dist/services/agent/syntaxAgent.js +237 -23
- package/dist/services/agent/toolLoop.js +10 -1
- package/dist/services/agent/types.d.ts +1 -0
- package/dist/services/agent-tools.d.ts +156 -1
- package/dist/services/agent-tools.js +259 -67
- package/dist/services/agent.d.ts +74 -0
- package/dist/services/agent.js +193 -31
- package/dist/services/ai.d.ts +5 -0
- package/dist/services/ai.js +80 -87
- package/dist/services/chatHistoryService.d.ts +11 -0
- package/dist/services/chatHistoryService.js +20 -1
- package/dist/services/contextAgent.d.ts +1 -1
- package/dist/services/contextAgent.js +9 -29
- package/dist/services/orchestration/investigationAgent.d.ts +2 -1
- package/dist/services/orchestration/investigationAgent.js +7 -2
- package/dist/services/orchestration/investigationOrchestrator.d.ts +1 -1
- package/dist/services/orchestration/investigationOrchestrator.js +13 -2
- package/dist/services/orchestration/orchestrator.js +21 -4
- package/dist/services/orchestration/subAgent.d.ts +2 -1
- package/dist/services/orchestration/subAgent.js +12 -6
- package/dist/utils/analysisRunner.d.ts +27 -4
- package/dist/utils/analysisRunner.js +100 -20
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.js +2 -0
- package/dist/utils/fuzzyMatch.d.ts +32 -0
- package/dist/utils/fuzzyMatch.js +215 -27
- package/dist/utils/localSyntaxValidator.d.ts +2 -2
- package/dist/utils/localSyntaxValidator.js +280 -81
- package/dist/utils/performanceAuditor.d.ts +2 -7
- package/dist/utils/performanceAuditor.js +541 -89
- package/dist/utils/projectStorage.js +9 -0
- package/dist/utils/systemPrompts.d.ts +3 -2
- package/dist/utils/systemPrompts.js +29 -5
- package/oclif.manifest.json +2 -2
- 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
|
-
|
|
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
|
|
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 **
|
|
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
|
-
| **
|
|
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
|
|
164
|
-
2. **Performance Auditing:**
|
|
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
|
---
|
package/dist/commands/chat.js
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
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
|
-
.
|
|
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
|
|
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>;
|