centaurus-cli 2.7.1 → 2.7.3
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/cli-adapter.d.ts +15 -0
- package/dist/cli-adapter.d.ts.map +1 -1
- package/dist/cli-adapter.js +380 -122
- package/dist/cli-adapter.js.map +1 -1
- package/dist/config/mcp-config-manager.d.ts +53 -0
- package/dist/config/mcp-config-manager.d.ts.map +1 -0
- package/dist/config/mcp-config-manager.js +210 -0
- package/dist/config/mcp-config-manager.js.map +1 -0
- package/dist/config/slash-commands.d.ts +14 -0
- package/dist/config/slash-commands.d.ts.map +1 -0
- package/dist/config/slash-commands.js +65 -0
- package/dist/config/slash-commands.js.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/mcp-command-handler.d.ts +16 -0
- package/dist/mcp/mcp-command-handler.d.ts.map +1 -0
- package/dist/mcp/mcp-command-handler.js +196 -0
- package/dist/mcp/mcp-command-handler.js.map +1 -0
- package/dist/mcp/mcp-server-manager.d.ts +30 -0
- package/dist/mcp/mcp-server-manager.d.ts.map +1 -0
- package/dist/mcp/mcp-server-manager.js +165 -0
- package/dist/mcp/mcp-server-manager.js.map +1 -0
- package/dist/mcp/mcp-tool-wrapper.d.ts +12 -0
- package/dist/mcp/mcp-tool-wrapper.d.ts.map +1 -0
- package/dist/mcp/mcp-tool-wrapper.js +57 -0
- package/dist/mcp/mcp-tool-wrapper.js.map +1 -0
- package/dist/services/ai-service-client.d.ts.map +1 -1
- package/dist/services/ai-service-client.js +1 -0
- package/dist/services/ai-service-client.js.map +1 -1
- package/dist/services/environment-context-injector.d.ts +2 -2
- package/dist/services/environment-context-injector.d.ts.map +1 -1
- package/dist/services/environment-context-injector.js +4 -4
- package/dist/services/environment-context-injector.js.map +1 -1
- package/dist/tools/command.d.ts +1 -2
- package/dist/tools/command.d.ts.map +1 -1
- package/dist/tools/command.js +39 -131
- package/dist/tools/command.js.map +1 -1
- package/dist/tools/file-ops.d.ts +3 -3
- package/dist/tools/file-ops.d.ts.map +1 -1
- package/dist/tools/file-ops.js +125 -99
- package/dist/tools/file-ops.js.map +1 -1
- package/dist/tools/get-diff.d.ts +5 -0
- package/dist/tools/get-diff.d.ts.map +1 -1
- package/dist/tools/get-diff.js +67 -5
- package/dist/tools/get-diff.js.map +1 -1
- package/dist/tools/grep-search.d.ts +13 -21
- package/dist/tools/grep-search.d.ts.map +1 -1
- package/dist/tools/grep-search.js +309 -280
- package/dist/tools/grep-search.js.map +1 -1
- package/dist/tools/inspect-symbol.d.ts +5 -0
- package/dist/tools/inspect-symbol.d.ts.map +1 -1
- package/dist/tools/inspect-symbol.js +102 -20
- package/dist/tools/inspect-symbol.js.map +1 -1
- package/dist/tools/types.d.ts +2 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/validation.d.ts +8 -10
- package/dist/tools/validation.d.ts.map +1 -1
- package/dist/tools/validation.js +35 -37
- package/dist/tools/validation.js.map +1 -1
- package/dist/ui/components/App.d.ts +4 -0
- package/dist/ui/components/App.d.ts.map +1 -1
- package/dist/ui/components/App.js +182 -70
- package/dist/ui/components/App.js.map +1 -1
- package/dist/ui/components/AuthWelcomeScreen.d.ts.map +1 -1
- package/dist/ui/components/AuthWelcomeScreen.js +1 -9
- package/dist/ui/components/AuthWelcomeScreen.js.map +1 -1
- package/dist/ui/components/CodeBlock.d.ts.map +1 -1
- package/dist/ui/components/CodeBlock.js +24 -13
- package/dist/ui/components/CodeBlock.js.map +1 -1
- package/dist/ui/components/DiffViewer.d.ts +1 -0
- package/dist/ui/components/DiffViewer.d.ts.map +1 -1
- package/dist/ui/components/DiffViewer.js +107 -70
- package/dist/ui/components/DiffViewer.js.map +1 -1
- package/dist/ui/components/FileCreationPreview.d.ts +8 -0
- package/dist/ui/components/FileCreationPreview.d.ts.map +1 -0
- package/dist/ui/components/FileCreationPreview.js +63 -0
- package/dist/ui/components/FileCreationPreview.js.map +1 -0
- package/dist/ui/components/InputBox.d.ts.map +1 -1
- package/dist/ui/components/InputBox.js +134 -10
- package/dist/ui/components/InputBox.js.map +1 -1
- package/dist/ui/components/InteractiveShell.d.ts +1 -0
- package/dist/ui/components/InteractiveShell.d.ts.map +1 -1
- package/dist/ui/components/InteractiveShell.js +30 -8
- package/dist/ui/components/InteractiveShell.js.map +1 -1
- package/dist/ui/components/MarkdownRenderer.d.ts.map +1 -1
- package/dist/ui/components/MarkdownRenderer.js +8 -30
- package/dist/ui/components/MarkdownRenderer.js.map +1 -1
- package/dist/ui/components/SlashCommandAutocomplete.d.ts +11 -0
- package/dist/ui/components/SlashCommandAutocomplete.d.ts.map +1 -0
- package/dist/ui/components/SlashCommandAutocomplete.js +15 -0
- package/dist/ui/components/SlashCommandAutocomplete.js.map +1 -0
- package/dist/ui/components/ToolExecutionMessage.d.ts.map +1 -1
- package/dist/ui/components/ToolExecutionMessage.js +212 -53
- package/dist/ui/components/ToolExecutionMessage.js.map +1 -1
- package/dist/ui/components/ToolExecutionStatus.d.ts.map +1 -1
- package/dist/ui/components/ToolExecutionStatus.js +28 -1
- package/dist/ui/components/ToolExecutionStatus.js.map +1 -1
- package/dist/utils/input-classifier.d.ts.map +1 -1
- package/dist/utils/input-classifier.js +3 -1
- package/dist/utils/input-classifier.js.map +1 -1
- package/dist/utils/shell.d.ts +2 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +71 -0
- package/dist/utils/shell.js.map +1 -1
- package/package.json +2 -1
- package/prompts/system-prompt-autonomous.md +22 -73
package/dist/cli-adapter.js
CHANGED
|
@@ -7,8 +7,13 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = dirname(__filename);
|
|
8
8
|
import { ConfigManager } from './config/manager.js';
|
|
9
9
|
import { ToolRegistry } from './tools/registry.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { viewFileTool, writeToFileTool, editFileTool, listDirTool } from './tools/file-ops.js';
|
|
11
|
+
import { runCommandTool } from './tools/command.js';
|
|
12
|
+
import { grepSearchTool } from './tools/grep-search.js';
|
|
13
|
+
import { findFilesTool } from './tools/find-files.js';
|
|
14
|
+
import { getDiffTool } from './tools/get-diff.js';
|
|
15
|
+
import { inspectSymbolTool } from './tools/inspect-symbol.js';
|
|
16
|
+
import { exitPlanModeTool, getPlanStatusTool, updatePlanStepTool } from './tools/plan-mode.js';
|
|
12
17
|
import { webSearchTool, fetchUrlTool } from './tools/web-search.js';
|
|
13
18
|
import { taskCompleteTool } from './tools/task-complete.js';
|
|
14
19
|
import { apiClient } from './services/api-client.js';
|
|
@@ -23,6 +28,9 @@ import { WSLHandler } from './context/handlers/wsl-handler.js';
|
|
|
23
28
|
import { DockerHandler } from './context/handlers/docker-handler.js';
|
|
24
29
|
import { AIContextInjector } from './services/ai-context-injector.js';
|
|
25
30
|
import { environmentContextInjector } from './services/environment-context-injector.js';
|
|
31
|
+
import { MCPConfigManager } from './config/mcp-config-manager.js';
|
|
32
|
+
import { MCPServerManager } from './mcp/mcp-server-manager.js';
|
|
33
|
+
import { MCPCommandHandler } from './mcp/mcp-command-handler.js';
|
|
26
34
|
export class CentaurusCLI {
|
|
27
35
|
configManager;
|
|
28
36
|
toolRegistry;
|
|
@@ -32,6 +40,7 @@ export class CentaurusCLI {
|
|
|
32
40
|
commandMode = false;
|
|
33
41
|
previousMode = 'execution';
|
|
34
42
|
onResponseCallback;
|
|
43
|
+
onDirectMessageCallback; // For slash commands - adds directly to history
|
|
35
44
|
onResponseStreamCallback;
|
|
36
45
|
onThoughtStreamCallback;
|
|
37
46
|
onThoughtCompleteCallback;
|
|
@@ -52,6 +61,7 @@ export class CentaurusCLI {
|
|
|
52
61
|
aiContextInjector;
|
|
53
62
|
onSubshellContextChange;
|
|
54
63
|
currentAbortController;
|
|
64
|
+
mcpCommandHandler;
|
|
55
65
|
constructor() {
|
|
56
66
|
this.configManager = new ConfigManager();
|
|
57
67
|
this.toolRegistry = new ToolRegistry();
|
|
@@ -70,10 +80,15 @@ export class CentaurusCLI {
|
|
|
70
80
|
this.onSubshellContextChange(context);
|
|
71
81
|
}
|
|
72
82
|
});
|
|
83
|
+
// Initialize MCP
|
|
84
|
+
this.initializeMCP();
|
|
73
85
|
}
|
|
74
86
|
setOnResponseCallback(callback) {
|
|
75
87
|
this.onResponseCallback = callback;
|
|
76
88
|
}
|
|
89
|
+
setOnDirectMessageCallback(callback) {
|
|
90
|
+
this.onDirectMessageCallback = callback;
|
|
91
|
+
}
|
|
77
92
|
setOnResponseStreamCallback(callback) {
|
|
78
93
|
this.onResponseStreamCallback = callback;
|
|
79
94
|
}
|
|
@@ -120,6 +135,18 @@ export class CentaurusCLI {
|
|
|
120
135
|
this.sshHandler.setPasswordRequestCallback(callback);
|
|
121
136
|
}
|
|
122
137
|
}
|
|
138
|
+
async initializeMCP() {
|
|
139
|
+
try {
|
|
140
|
+
const mcpConfigManager = new MCPConfigManager();
|
|
141
|
+
const mcpServerManager = new MCPServerManager();
|
|
142
|
+
this.mcpCommandHandler = new MCPCommandHandler(mcpConfigManager, mcpServerManager, this.toolRegistry);
|
|
143
|
+
// Initialize MCP servers and tools
|
|
144
|
+
await this.mcpCommandHandler.initializeMCP();
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error('Failed to initialize MCP:', error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
123
150
|
writeToShellStdin(input) {
|
|
124
151
|
if (this.currentInteractiveProcess) {
|
|
125
152
|
try {
|
|
@@ -130,6 +157,27 @@ export class CentaurusCLI {
|
|
|
130
157
|
}
|
|
131
158
|
}
|
|
132
159
|
}
|
|
160
|
+
sendSignalToShell(signal) {
|
|
161
|
+
if (this.currentInteractiveProcess) {
|
|
162
|
+
try {
|
|
163
|
+
this.currentInteractiveProcess.signal(signal);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
// Silently ignore - process might have already exited
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
killCurrentProcess() {
|
|
171
|
+
if (this.currentInteractiveProcess && this.currentInteractiveProcess.process.pid) {
|
|
172
|
+
try {
|
|
173
|
+
const { killProcessTree } = require('./utils/shell.js');
|
|
174
|
+
killProcessTree(this.currentInteractiveProcess.process.pid, 'SIGKILL');
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
// Ignore
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
133
181
|
getPlanMode() {
|
|
134
182
|
return this.planMode;
|
|
135
183
|
}
|
|
@@ -173,14 +221,50 @@ export class CentaurusCLI {
|
|
|
173
221
|
}
|
|
174
222
|
}
|
|
175
223
|
sshHandler;
|
|
224
|
+
/**
|
|
225
|
+
* Notify UI about tool execution status
|
|
226
|
+
*/
|
|
227
|
+
notifyToolStatus(toolName, status, args, result, error) {
|
|
228
|
+
if (this.onToolExecutionUpdate) {
|
|
229
|
+
// Add cwd to arguments for execute_command tool
|
|
230
|
+
const toolArgs = toolName === 'execute_command' && args
|
|
231
|
+
? { ...args, cwd: this.cwd }
|
|
232
|
+
: args;
|
|
233
|
+
this.onToolExecutionUpdate({
|
|
234
|
+
toolName,
|
|
235
|
+
status,
|
|
236
|
+
result,
|
|
237
|
+
error,
|
|
238
|
+
arguments: toolArgs
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Truncate large results for AI consumption
|
|
244
|
+
*/
|
|
245
|
+
truncateResult(result, maxLength = 500000) {
|
|
246
|
+
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
|
247
|
+
if (resultStr.length > maxLength) {
|
|
248
|
+
const truncated = resultStr.substring(0, maxLength);
|
|
249
|
+
const linesRemoved = (resultStr.match(/\n/g) || []).length - (truncated.match(/\n/g) || []).length;
|
|
250
|
+
return truncated + `\n\n[... truncated ${resultStr.length - maxLength} characters, ~${linesRemoved} lines removed for brevity ...]`;
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
176
254
|
async initialize() {
|
|
177
255
|
// Register tools
|
|
178
|
-
this.toolRegistry.register(
|
|
179
|
-
this.toolRegistry.register(
|
|
256
|
+
this.toolRegistry.register(viewFileTool);
|
|
257
|
+
this.toolRegistry.register(writeToFileTool);
|
|
180
258
|
this.toolRegistry.register(editFileTool);
|
|
181
|
-
this.toolRegistry.register(
|
|
182
|
-
this.toolRegistry.register(
|
|
259
|
+
this.toolRegistry.register(listDirTool);
|
|
260
|
+
this.toolRegistry.register(runCommandTool);
|
|
183
261
|
this.toolRegistry.register(grepSearchTool);
|
|
262
|
+
this.toolRegistry.register(findFilesTool);
|
|
263
|
+
this.toolRegistry.register(getDiffTool);
|
|
264
|
+
this.toolRegistry.register(inspectSymbolTool);
|
|
265
|
+
this.toolRegistry.register(exitPlanModeTool);
|
|
266
|
+
this.toolRegistry.register(getPlanStatusTool);
|
|
267
|
+
this.toolRegistry.register(updatePlanStepTool);
|
|
184
268
|
this.toolRegistry.register(webSearchTool);
|
|
185
269
|
this.toolRegistry.register(fetchUrlTool);
|
|
186
270
|
this.toolRegistry.register(taskCompleteTool);
|
|
@@ -370,7 +454,8 @@ Press Enter to continue...
|
|
|
370
454
|
const currentContext = this.contextManager.getCurrentContext();
|
|
371
455
|
messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);
|
|
372
456
|
// Build environment context to send to backend
|
|
373
|
-
|
|
457
|
+
// Initial context - will be updated in loop
|
|
458
|
+
let environmentContext = this.getEnvironmentContext();
|
|
374
459
|
const mode = this.getMode();
|
|
375
460
|
let finalAssistantMessage = '';
|
|
376
461
|
const MAX_TURNS = 500; // Allow up to 500 turns for complex tasks
|
|
@@ -378,6 +463,7 @@ Press Enter to continue...
|
|
|
378
463
|
const MAX_NARRATION_ATTEMPTS = 3; // Maximum times we'll prompt AI to stop narrating
|
|
379
464
|
let turnCount = 0;
|
|
380
465
|
let narrationAttempts = 0; // Track how many times AI narrated without executing
|
|
466
|
+
let completionAttempts = 0; // Track how many times AI provided text summary without task_complete
|
|
381
467
|
let thoughtStartTime = null; // Track when thinking started
|
|
382
468
|
let thoughtContent = ''; // Accumulate thought content
|
|
383
469
|
// Create AbortController for this request
|
|
@@ -385,6 +471,14 @@ Press Enter to continue...
|
|
|
385
471
|
// Multi-turn tool execution loop
|
|
386
472
|
while (turnCount < MAX_TURNS) {
|
|
387
473
|
turnCount++;
|
|
474
|
+
// Refresh environment context to capture any CWD changes from previous turns
|
|
475
|
+
environmentContext = this.getEnvironmentContext();
|
|
476
|
+
// Refresh system prompt with new CWD
|
|
477
|
+
const refreshedSystemPrompt = environmentContextInjector.getEnhancedSystemPrompt(systemPrompt, this.cwd);
|
|
478
|
+
// Update the system message in the messages array
|
|
479
|
+
if (messages.length > 0 && messages[0].role === 'system') {
|
|
480
|
+
messages[0].content = refreshedSystemPrompt;
|
|
481
|
+
}
|
|
388
482
|
let assistantMessage = '';
|
|
389
483
|
let toolCalls = [];
|
|
390
484
|
// Stream AI response from backend
|
|
@@ -469,8 +563,8 @@ Press Enter to continue...
|
|
|
469
563
|
}
|
|
470
564
|
break;
|
|
471
565
|
}
|
|
472
|
-
}
|
|
473
|
-
// If there are tool calls, execute them
|
|
566
|
+
} // End of stream loop
|
|
567
|
+
// If there are tool calls, execute them
|
|
474
568
|
if (toolCalls.length > 0) {
|
|
475
569
|
// CRITICAL: AI should ONLY communicate via reason_text and task_complete summary
|
|
476
570
|
// Any text output alongside tool calls should be suppressed
|
|
@@ -483,16 +577,6 @@ Press Enter to continue...
|
|
|
483
577
|
// Silently limit tool calls
|
|
484
578
|
toolCalls = toolCalls.slice(0, MAX_TOOL_CALLS_PER_TURN);
|
|
485
579
|
}
|
|
486
|
-
// Helper function to truncate large results
|
|
487
|
-
const truncateResult = (result, maxLength = 500000) => {
|
|
488
|
-
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
|
489
|
-
if (resultStr.length > maxLength) {
|
|
490
|
-
const truncated = resultStr.substring(0, maxLength);
|
|
491
|
-
const linesRemoved = (resultStr.match(/\n/g) || []).length - (truncated.match(/\n/g) || []).length;
|
|
492
|
-
return truncated + `\n\n[... truncated ${resultStr.length - maxLength} characters, ~${linesRemoved} lines removed for brevity ...]`;
|
|
493
|
-
}
|
|
494
|
-
return result;
|
|
495
|
-
};
|
|
496
580
|
const toolResults = [];
|
|
497
581
|
let userCancelledOperation = false;
|
|
498
582
|
let taskCompleted = false;
|
|
@@ -561,7 +645,7 @@ Press Enter to continue...
|
|
|
561
645
|
}
|
|
562
646
|
}
|
|
563
647
|
// Truncate result before sending to AI (to avoid exceeding message size limits)
|
|
564
|
-
const truncatedResult = truncateResult(parsedResult);
|
|
648
|
+
const truncatedResult = this.truncateResult(parsedResult);
|
|
565
649
|
toolResults.push({
|
|
566
650
|
tool_call_id: toolCall.id,
|
|
567
651
|
name: toolCall.name,
|
|
@@ -691,107 +775,115 @@ Press Enter to continue...
|
|
|
691
775
|
},
|
|
692
776
|
...this.conversationHistory
|
|
693
777
|
];
|
|
694
|
-
// Re-inject subshell context
|
|
778
|
+
// Re-inject subshell context
|
|
695
779
|
messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);
|
|
696
|
-
//
|
|
697
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
698
|
-
// Continue to next turn to let AI process results
|
|
699
|
-
continue;
|
|
780
|
+
continue; // Loop back to AI service
|
|
700
781
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
//
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
// If AI keeps narrating without executing, force completion immediately
|
|
719
|
-
if (narrationAttempts >= MAX_NARRATION_ATTEMPTS) {
|
|
720
|
-
// Force task completion with error message
|
|
721
|
-
finalAssistantMessage = '⚠️ **Task Incomplete**: The AI repeatedly described actions without executing them.\n\n' +
|
|
722
|
-
'**What happened**: The AI entered a narration loop, describing what it wanted to do instead of using tool calls.\n\n' +
|
|
723
|
-
'**Suggestions**:\n' +
|
|
724
|
-
'1. Try rephrasing your request more specifically\n' +
|
|
725
|
-
'2. Break the task into smaller, concrete steps\n' +
|
|
726
|
-
'3. Provide explicit file paths if known\n' +
|
|
727
|
-
'4. Check if the model supports tool calling properly\n\n' +
|
|
728
|
-
'**Last message**: ' + assistantMessage;
|
|
729
|
-
break;
|
|
730
|
-
}
|
|
731
|
-
// First narration attempt - give a strong warning with specific guidance
|
|
732
|
-
if (narrationAttempts === 1) {
|
|
733
|
-
const completionPrompt = '🛑 **CRITICAL ERROR**: You output text without using tools.\n\n' +
|
|
734
|
-
'**COMMUNICATION RULE VIOLATION**: You can ONLY communicate through:\n' +
|
|
735
|
-
'1. `reason_text` parameter in tool calls\n' +
|
|
736
|
-
'2. `summary` parameter in task_complete tool\n\n' +
|
|
737
|
-
'**Your text output was HIDDEN from the user.**\n\n' +
|
|
738
|
-
'**MANDATORY CORRECTION**:\n' +
|
|
739
|
-
'- If you need to DO something: Call the tool with `reason_text`\n' +
|
|
740
|
-
'- If you are DONE: Call `task_complete(summary="your message")`\n' +
|
|
741
|
-
'- NEVER output plain text - it will be hidden\n\n' +
|
|
742
|
-
'**Example for greeting**:\n' +
|
|
743
|
-
'```\n' +
|
|
744
|
-
'<thought>User said hello, I should greet back</thought>\n' +
|
|
745
|
-
'(Call task_complete with summary="Hello! How can I help you today?")\n' +
|
|
746
|
-
'```\n\n' +
|
|
747
|
-
'**Your NEXT response MUST use tools.**';
|
|
748
|
-
this.conversationHistory.push({
|
|
749
|
-
role: 'user',
|
|
750
|
-
content: completionPrompt,
|
|
751
|
-
});
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
// Second narration attempt - final warning before forced completion
|
|
755
|
-
const completionPrompt = '🚨 **FINAL WARNING** (Attempt ' + narrationAttempts + '/' + MAX_NARRATION_ATTEMPTS + '): You are STILL narrating instead of executing.\n\n' +
|
|
756
|
-
'**This is your LAST chance**:\n' +
|
|
757
|
-
'1. Execute a tool call NOW, or\n' +
|
|
758
|
-
'2. Call task_complete() to end\n\n' +
|
|
759
|
-
'If you output narration text again, the task will be forcibly terminated.';
|
|
760
|
-
this.conversationHistory.push({
|
|
761
|
-
role: 'user',
|
|
762
|
-
content: completionPrompt,
|
|
763
|
-
});
|
|
764
|
-
}
|
|
782
|
+
else {
|
|
783
|
+
// No tool calls - check for silent stop or text-only response
|
|
784
|
+
// Case 1: Silent Stop (No tool calls AND no text)
|
|
785
|
+
if (!assistantMessage || !assistantMessage.trim()) {
|
|
786
|
+
// No tool calls and no message - AI stopped silently
|
|
787
|
+
// This usually means the AI thinks it's done but didn't call task_complete
|
|
788
|
+
// Prompt it to either continue or complete
|
|
789
|
+
const silentStopPrompt = '⚠️ **SILENT STOP DETECTED**: You ended your turn without any output or tool calls.\n\n' +
|
|
790
|
+
'**This is not allowed.** You must either:\n' +
|
|
791
|
+
'1. Execute a tool call if more work is needed, OR\n' +
|
|
792
|
+
'2. Call task_complete() with a summary of what you accomplished\n\n' +
|
|
793
|
+
'**If you have completed the task**, call task_complete() NOW with a comprehensive summary.\n' +
|
|
794
|
+
'**If more work is needed**, execute the next tool call immediately.';
|
|
795
|
+
this.conversationHistory.push({
|
|
796
|
+
role: 'user',
|
|
797
|
+
content: silentStopPrompt,
|
|
798
|
+
});
|
|
765
799
|
}
|
|
800
|
+
// Case 2: Text-only response (Narration or Summary)
|
|
766
801
|
else {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
802
|
+
const isNarration = /\b(I will|I'll|Let me|Let's|I need to|I'm going to|I should|I can)\b/i.test(assistantMessage);
|
|
803
|
+
if (isNarration) {
|
|
804
|
+
narrationAttempts++;
|
|
805
|
+
// If AI keeps narrating without executing, force completion immediately
|
|
806
|
+
if (narrationAttempts >= MAX_NARRATION_ATTEMPTS) {
|
|
807
|
+
// Force task completion with error message
|
|
808
|
+
finalAssistantMessage = '⚠️ **Task Incomplete**: The AI repeatedly described actions without executing them.\n\n' +
|
|
809
|
+
'**What happened**: The AI entered a narration loop, describing what it wanted to do instead of using tool calls.\n\n' +
|
|
810
|
+
'**Suggestions**:\n' +
|
|
811
|
+
'1. Try rephrasing your request more specifically\n' +
|
|
812
|
+
'2. Break the task into smaller, concrete steps\n' +
|
|
813
|
+
'3. Provide explicit file paths if known\n' +
|
|
814
|
+
'4. Check if the model supports tool calling properly\n\n' +
|
|
815
|
+
'**Last message**: ' + assistantMessage;
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
// First narration attempt - give a strong warning with specific guidance
|
|
819
|
+
if (narrationAttempts === 1) {
|
|
820
|
+
const completionPrompt = '🛑 **CRITICAL ERROR**: You output text without using tools.\n\n' +
|
|
821
|
+
'**COMMUNICATION RULE VIOLATION**: You can ONLY communicate through:\n' +
|
|
822
|
+
'1. `reason_text` parameter in tool calls\n' +
|
|
823
|
+
'2. `summary` parameter in task_complete tool\n\n' +
|
|
824
|
+
'**Your text output was HIDDEN from the user.**\n\n' +
|
|
825
|
+
'**MANDATORY CORRECTION**:\n' +
|
|
826
|
+
'- If you need to DO something: Call the tool with `reason_text`\n' +
|
|
827
|
+
'- If you are DONE: Call `task_complete(summary="your message")`\n' +
|
|
828
|
+
'- NEVER output plain text - it will be hidden\n\n' +
|
|
829
|
+
'**Example for greeting**:\n' +
|
|
830
|
+
'```\n' +
|
|
831
|
+
'<thought>User said hello, I should greet back</thought>\n' +
|
|
832
|
+
'(Call task_complete with summary="Hello! How can I help you today?")\n' +
|
|
833
|
+
'```\n\n' +
|
|
834
|
+
'**Your NEXT response MUST use tools.**';
|
|
835
|
+
this.conversationHistory.push({
|
|
836
|
+
role: 'user',
|
|
837
|
+
content: completionPrompt,
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
// Second narration attempt - final warning before forced completion
|
|
842
|
+
const completionPrompt = '🚨 **FINAL WARNING** (Attempt ' + narrationAttempts + '/' + MAX_NARRATION_ATTEMPTS + '): You are STILL narrating instead of executing.\n\n' +
|
|
843
|
+
'**This is your LAST chance**:\n' +
|
|
844
|
+
'1. Execute a tool call NOW, or\n' +
|
|
845
|
+
'2. Call task_complete() to end\n\n' +
|
|
846
|
+
'If you output narration text again, the task will be forcibly terminated.';
|
|
847
|
+
this.conversationHistory.push({
|
|
848
|
+
role: 'user',
|
|
849
|
+
content: completionPrompt,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
785
852
|
}
|
|
786
853
|
else {
|
|
787
|
-
//
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
854
|
+
// AI output a response without narration - it should finish
|
|
855
|
+
// Reset narration counter since this is a valid response
|
|
856
|
+
narrationAttempts = 0;
|
|
857
|
+
// Check if the message looks like a final answer/summary
|
|
858
|
+
// If it has substantial length, assume it's a summary attempt
|
|
859
|
+
const isFinalAnswer = assistantMessage.length > 20;
|
|
860
|
+
if (isFinalAnswer) {
|
|
861
|
+
completionAttempts++;
|
|
862
|
+
// If AI keeps providing text summaries without calling task_complete, accept the text and finish
|
|
863
|
+
// This prevents the infinite loop where the AI keeps summarizing in response to our prompt
|
|
864
|
+
if (completionAttempts > 1) {
|
|
865
|
+
finalAssistantMessage = assistantMessage;
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
// This looks like a final answer - prompt to call task_complete
|
|
869
|
+
const completionPrompt = '✅ **Possible Completion Detected**: You provided a text response but did not call `task_complete`.\n\n' +
|
|
870
|
+
'**To finish the conversation, you MUST call the `task_complete` tool.**\n\n' +
|
|
871
|
+
'Please call `task_complete` now with your summary as the argument.';
|
|
872
|
+
this.conversationHistory.push({
|
|
873
|
+
role: 'user',
|
|
874
|
+
content: completionPrompt,
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
// Short message without clear intent - ask for clarification or completion
|
|
879
|
+
const completionPrompt = 'Your response is unclear. Either:\n' +
|
|
880
|
+
'1. Execute the next tool call if more work is needed, or\n' +
|
|
881
|
+
'2. Call task_complete() if the task is done';
|
|
882
|
+
this.conversationHistory.push({
|
|
883
|
+
role: 'user',
|
|
884
|
+
content: completionPrompt,
|
|
885
|
+
});
|
|
886
|
+
}
|
|
795
887
|
}
|
|
796
888
|
}
|
|
797
889
|
// Rebuild messages array
|
|
@@ -850,10 +942,10 @@ Press Enter to continue...
|
|
|
850
942
|
});
|
|
851
943
|
// Save assistant message to backend
|
|
852
944
|
await this.saveMessageToBackend('assistant', finalMessage);
|
|
853
|
-
}
|
|
945
|
+
} // End of while loop
|
|
854
946
|
// Parse response for plan mode
|
|
855
947
|
if (this.planMode) {
|
|
856
|
-
const planData = this.parsePlanResponse(
|
|
948
|
+
const planData = this.parsePlanResponse(finalAssistantMessage);
|
|
857
949
|
if (planData && this.onPlanApprovalRequest) {
|
|
858
950
|
// Ask user for approval
|
|
859
951
|
const approved = await this.onPlanApprovalRequest(planData);
|
|
@@ -870,8 +962,8 @@ Press Enter to continue...
|
|
|
870
962
|
}
|
|
871
963
|
}
|
|
872
964
|
// Send response back to UI (only if there's a message)
|
|
873
|
-
if (this.onResponseCallback &&
|
|
874
|
-
this.onResponseCallback(
|
|
965
|
+
if (this.onResponseCallback && finalAssistantMessage) {
|
|
966
|
+
this.onResponseCallback(finalAssistantMessage);
|
|
875
967
|
}
|
|
876
968
|
}
|
|
877
969
|
catch (error) {
|
|
@@ -898,6 +990,7 @@ Press Enter to continue...
|
|
|
898
990
|
case 'help':
|
|
899
991
|
responseMessage = `Available Commands:\n\n` +
|
|
900
992
|
`/help - Show this help message\n` +
|
|
993
|
+
`/init - Analyze project and create/load centaurus.md context file\n` +
|
|
901
994
|
`/clear - Clear conversation history\n` +
|
|
902
995
|
`/config - View current configuration\n` +
|
|
903
996
|
`/model - Select from available Google models\n` +
|
|
@@ -913,6 +1006,163 @@ Press Enter to continue...
|
|
|
913
1006
|
`Ctrl+T - Toggle auto-accept mode\n` +
|
|
914
1007
|
`? - Show keyboard shortcuts help`;
|
|
915
1008
|
break;
|
|
1009
|
+
case 'init':
|
|
1010
|
+
try {
|
|
1011
|
+
// Define the context file names in priority order
|
|
1012
|
+
const contextFiles = ['centaurus.md', 'gemini.md', 'claude.md'];
|
|
1013
|
+
let foundFile = null;
|
|
1014
|
+
let foundFilePath = null;
|
|
1015
|
+
// Check for existing context files in priority order
|
|
1016
|
+
for (const fileName of contextFiles) {
|
|
1017
|
+
const filePath = path.join(this.cwd, fileName);
|
|
1018
|
+
if (fs.existsSync(filePath)) {
|
|
1019
|
+
foundFile = fileName;
|
|
1020
|
+
foundFilePath = filePath;
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
if (foundFile) {
|
|
1025
|
+
// Context file exists - read and load it
|
|
1026
|
+
try {
|
|
1027
|
+
const content = fs.readFileSync(foundFilePath, 'utf-8');
|
|
1028
|
+
// Check if content is empty
|
|
1029
|
+
if (!content.trim()) {
|
|
1030
|
+
responseMessage = `⚠️ Found ${foundFile} but it's empty.\n\n` +
|
|
1031
|
+
`Please either:\n` +
|
|
1032
|
+
`1. Delete the file and run /init again to generate new documentation\n` +
|
|
1033
|
+
`2. Add content to the file manually`;
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
1036
|
+
// If the found file is not centaurus.md, create a copy
|
|
1037
|
+
if (foundFile !== 'centaurus.md') {
|
|
1038
|
+
const centaurusPath = path.join(this.cwd, 'centaurus.md');
|
|
1039
|
+
// Check if centaurus.md already exists
|
|
1040
|
+
if (fs.existsSync(centaurusPath)) {
|
|
1041
|
+
responseMessage = `✅ Found both ${foundFile} and centaurus.md. Loaded project context from centaurus.md`;
|
|
1042
|
+
}
|
|
1043
|
+
else {
|
|
1044
|
+
// Create a copy as centaurus.md
|
|
1045
|
+
fs.copyFileSync(foundFilePath, centaurusPath);
|
|
1046
|
+
responseMessage = `✅ Found ${foundFile} and created a copy as centaurus.md. Loaded project context.`;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
// Already centaurus.md
|
|
1051
|
+
responseMessage = `✅ Loaded project context from centaurus.md`;
|
|
1052
|
+
}
|
|
1053
|
+
// Add the context to conversation history so the AI actually has it
|
|
1054
|
+
this.conversationHistory.push({
|
|
1055
|
+
role: 'user',
|
|
1056
|
+
content: `Here is the project context from ${foundFile}:\n\n${content}`
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
catch (readError) {
|
|
1060
|
+
responseMessage = `❌ Error reading ${foundFile}: ${readError.message}\n\n` +
|
|
1061
|
+
`Please check file permissions and try again.`;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
// No context file exists - trigger AI analysis
|
|
1066
|
+
responseMessage = `🔍 I will now analyze the contents of this directory and then create a centaurus.md`;
|
|
1067
|
+
// Send initial response
|
|
1068
|
+
if (this.onResponseCallback) {
|
|
1069
|
+
this.onResponseCallback(responseMessage);
|
|
1070
|
+
}
|
|
1071
|
+
// Create a specialized prompt for project analysis
|
|
1072
|
+
const initPrompt = `You are tasked with analyzing the current project directory and creating a comprehensive documentation file called "centaurus.md".
|
|
1073
|
+
|
|
1074
|
+
**Your Task:**
|
|
1075
|
+
1. Use the list_directory tool to explore the project structure starting from the current directory
|
|
1076
|
+
2. Use read_file to examine key files such as:
|
|
1077
|
+
- package.json, requirements.txt, or similar dependency files
|
|
1078
|
+
- README files
|
|
1079
|
+
- Configuration files (tsconfig.json, .eslintrc, etc.)
|
|
1080
|
+
- Main entry points (index.js, main.py, App.tsx, etc.)
|
|
1081
|
+
3. Use grep_search to find important patterns and understand the codebase
|
|
1082
|
+
4. Create a comprehensive centaurus.md file using the write_file tool
|
|
1083
|
+
|
|
1084
|
+
**The centaurus.md file should include:**
|
|
1085
|
+
|
|
1086
|
+
# [Project Name]
|
|
1087
|
+
|
|
1088
|
+
## Overview
|
|
1089
|
+
Brief description of what this project does and its purpose
|
|
1090
|
+
|
|
1091
|
+
## Technology Stack
|
|
1092
|
+
- **Languages**: List programming languages used
|
|
1093
|
+
- **Frameworks**: Main frameworks (React, Express, Django, etc.)
|
|
1094
|
+
- **Key Dependencies**: Important libraries and tools
|
|
1095
|
+
- **Build Tools**: Webpack, Vite, Maven, etc.
|
|
1096
|
+
|
|
1097
|
+
## Directory Structure
|
|
1098
|
+
\`\`\`
|
|
1099
|
+
/
|
|
1100
|
+
/src - Main source code
|
|
1101
|
+
/tests - Test files
|
|
1102
|
+
/config - Configuration files
|
|
1103
|
+
...
|
|
1104
|
+
\`\`\`
|
|
1105
|
+
|
|
1106
|
+
Brief explanation of each major directory's purpose
|
|
1107
|
+
|
|
1108
|
+
## Key Components
|
|
1109
|
+
Describe the main modules, classes, or components:
|
|
1110
|
+
- **Component Name**: What it does, where it's located
|
|
1111
|
+
- **Component Name**: What it does, where it's located
|
|
1112
|
+
|
|
1113
|
+
## Architecture
|
|
1114
|
+
Explain how the components interact:
|
|
1115
|
+
- Data flow
|
|
1116
|
+
- Communication patterns (REST APIs, GraphQL, event-driven, etc.)
|
|
1117
|
+
- State management
|
|
1118
|
+
- Database architecture (if applicable)
|
|
1119
|
+
|
|
1120
|
+
## Development Workflow
|
|
1121
|
+
- How to install dependencies
|
|
1122
|
+
- How to run the development server
|
|
1123
|
+
- How to run tests
|
|
1124
|
+
- How to build for production
|
|
1125
|
+
|
|
1126
|
+
## Important Conventions
|
|
1127
|
+
- Code organization patterns
|
|
1128
|
+
- Naming conventions
|
|
1129
|
+
- File structure patterns
|
|
1130
|
+
- Any special patterns or best practices used
|
|
1131
|
+
|
|
1132
|
+
## Entry Points
|
|
1133
|
+
- Main files where execution starts
|
|
1134
|
+
- API endpoints (if applicable)
|
|
1135
|
+
- CLI commands (if applicable)
|
|
1136
|
+
|
|
1137
|
+
## Quick Reference
|
|
1138
|
+
Useful commands, key files to know about, common tasks
|
|
1139
|
+
|
|
1140
|
+
**IMPORTANT INSTRUCTIONS:**
|
|
1141
|
+
- Be thorough but concise
|
|
1142
|
+
- Use clear, professional language
|
|
1143
|
+
- Include code examples where helpful
|
|
1144
|
+
- Create the file in the current working directory: ${this.cwd}
|
|
1145
|
+
- After creating the file, call task_complete with a summary
|
|
1146
|
+
|
|
1147
|
+
**Current working directory**: ${this.cwd}
|
|
1148
|
+
|
|
1149
|
+
Start by listing the directory structure to understand what you're working with.`;
|
|
1150
|
+
// Add to conversation history
|
|
1151
|
+
this.conversationHistory.push({
|
|
1152
|
+
role: 'user',
|
|
1153
|
+
content: initPrompt
|
|
1154
|
+
});
|
|
1155
|
+
// Trigger the AI to process this
|
|
1156
|
+
await this.handleMessage('');
|
|
1157
|
+
// Don't send another response - the AI will respond
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
catch (error) {
|
|
1162
|
+
responseMessage = `❌ Error executing /init command: ${error.message}\n\n` +
|
|
1163
|
+
`Please try again or check file permissions.`;
|
|
1164
|
+
}
|
|
1165
|
+
break;
|
|
916
1166
|
case 'sign-in':
|
|
917
1167
|
// Check if already authenticated
|
|
918
1168
|
if (apiClient.isAuthenticated()) {
|
|
@@ -1134,15 +1384,23 @@ Press Enter to continue...
|
|
|
1134
1384
|
}
|
|
1135
1385
|
}
|
|
1136
1386
|
break;
|
|
1387
|
+
case 'mcp':
|
|
1388
|
+
if (this.mcpCommandHandler) {
|
|
1389
|
+
responseMessage = await this.mcpCommandHandler.handleCommand(args);
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
responseMessage = '❌ MCP is not initialized. Please restart the CLI.';
|
|
1393
|
+
}
|
|
1394
|
+
break;
|
|
1137
1395
|
case 'exit':
|
|
1138
1396
|
process.exit(0);
|
|
1139
1397
|
break;
|
|
1140
1398
|
default:
|
|
1141
1399
|
responseMessage = `Unknown command: /${cmd}\nType /help for available commands.`;
|
|
1142
1400
|
}
|
|
1143
|
-
// Send command response
|
|
1144
|
-
if (responseMessage && this.
|
|
1145
|
-
this.
|
|
1401
|
+
// Send command response directly to history (not streaming)
|
|
1402
|
+
if (responseMessage && this.onDirectMessageCallback) {
|
|
1403
|
+
this.onDirectMessageCallback(responseMessage);
|
|
1146
1404
|
}
|
|
1147
1405
|
}
|
|
1148
1406
|
/**
|