xibecode 0.0.4 → 0.0.7
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 +44 -31
- package/dist/commands/chat.d.ts +2 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +362 -80
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +17 -18
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -10
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +76 -102
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +10 -8
- package/dist/commands/run.js.map +1 -1
- package/dist/core/agent.d.ts +5 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +16 -4
- package/dist/core/agent.js.map +1 -1
- package/dist/core/export.d.ts +11 -0
- package/dist/core/export.d.ts.map +1 -0
- package/dist/core/export.js +54 -0
- package/dist/core/export.js.map +1 -0
- package/dist/core/mcp-client.d.ts +2 -8
- package/dist/core/mcp-client.d.ts.map +1 -1
- package/dist/core/mcp-client.js +8 -11
- package/dist/core/mcp-client.js.map +1 -1
- package/dist/core/session-manager.d.ts +78 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +161 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/tools.d.ts +1 -0
- package/dist/core/tools.d.ts.map +1 -1
- package/dist/core/tools.js +66 -1
- package/dist/core/tools.js.map +1 -1
- package/dist/index.js +6 -8
- package/dist/index.js.map +1 -1
- package/dist/ui/enhanced-tui.d.ts +22 -1
- package/dist/ui/enhanced-tui.d.ts.map +1 -1
- package/dist/ui/enhanced-tui.js +157 -128
- package/dist/ui/enhanced-tui.js.map +1 -1
- package/dist/ui/themes.d.ts +25 -0
- package/dist/ui/themes.d.ts.map +1 -0
- package/dist/ui/themes.js +176 -0
- package/dist/ui/themes.js.map +1 -0
- package/dist/utils/config.d.ts +37 -5
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +58 -15
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/mcp-servers-file.d.ts +11 -4
- package/dist/utils/mcp-servers-file.d.ts.map +1 -1
- package/dist/utils/mcp-servers-file.js +101 -49
- package/dist/utils/mcp-servers-file.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -642,7 +642,7 @@ XibeCode stores config in `~/.xibecode/`
|
|
|
642
642
|
"plugins": [], // Array of plugin paths
|
|
643
643
|
|
|
644
644
|
// Latest version
|
|
645
|
-
"mcpServers":
|
|
645
|
+
"mcpServers": {} // MCP server configurations (object-based)
|
|
646
646
|
}
|
|
647
647
|
```
|
|
648
648
|
|
|
@@ -688,26 +688,24 @@ nano ~/.xibecode/mcp-servers.json
|
|
|
688
688
|
|
|
689
689
|
```json
|
|
690
690
|
{
|
|
691
|
-
"
|
|
692
|
-
{
|
|
693
|
-
"name": "filesystem",
|
|
694
|
-
"transport": "stdio",
|
|
691
|
+
"mcpServers": {
|
|
692
|
+
"filesystem": {
|
|
695
693
|
"command": "mcp-server-filesystem",
|
|
696
694
|
"args": ["--root", "/path/to/files"]
|
|
697
695
|
},
|
|
698
|
-
{
|
|
699
|
-
"name": "github",
|
|
700
|
-
"transport": "stdio",
|
|
696
|
+
"github": {
|
|
701
697
|
"command": "mcp-server-github",
|
|
702
698
|
"args": ["--token", "YOUR_TOKEN"],
|
|
703
699
|
"env": {
|
|
704
700
|
"GITHUB_TOKEN": "your_token_here"
|
|
705
701
|
}
|
|
706
702
|
}
|
|
707
|
-
|
|
703
|
+
}
|
|
708
704
|
}
|
|
709
705
|
```
|
|
710
706
|
|
|
707
|
+
> **Note:** The configuration format has been updated to use an object-based structure. If you have an existing configuration using the legacy array format with `"servers": [...]`, it will be automatically migrated to the new format when you run any MCP command.
|
|
708
|
+
|
|
711
709
|
**File Management Commands:**
|
|
712
710
|
|
|
713
711
|
```bash
|
|
@@ -721,22 +719,25 @@ xibecode mcp reload
|
|
|
721
719
|
xibecode mcp file
|
|
722
720
|
```
|
|
723
721
|
|
|
724
|
-
####
|
|
725
|
-
|
|
726
|
-
Add servers via command line:
|
|
722
|
+
#### Commands
|
|
727
723
|
|
|
728
724
|
```bash
|
|
729
|
-
#
|
|
730
|
-
xibecode mcp add
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
xibecode mcp add postgres --command "mcp-server-postgres" --args "--host localhost" --env "PGPASSWORD=secret,PGUSER=admin"
|
|
725
|
+
# Open file to add/edit servers
|
|
726
|
+
xibecode mcp add
|
|
727
|
+
# or
|
|
728
|
+
xibecode mcp edit
|
|
734
729
|
|
|
735
730
|
# List all configured servers
|
|
736
731
|
xibecode mcp list
|
|
737
732
|
|
|
738
|
-
# Remove a server
|
|
733
|
+
# Remove a server (or edit file manually)
|
|
739
734
|
xibecode mcp remove filesystem
|
|
735
|
+
|
|
736
|
+
# Show file path
|
|
737
|
+
xibecode mcp file
|
|
738
|
+
|
|
739
|
+
# Reload after editing
|
|
740
|
+
xibecode mcp reload
|
|
740
741
|
```
|
|
741
742
|
|
|
742
743
|
#### Alternative Methods
|
|
@@ -761,16 +762,20 @@ MCP servers support two transport types:
|
|
|
761
762
|
|
|
762
763
|
For local MCP servers that run as a subprocess. Currently only stdio transport is supported:
|
|
763
764
|
|
|
765
|
+
**Note:** The MCP configuration now uses a simpler object-based format. Instead of command-line flags, edit the configuration file directly:
|
|
766
|
+
|
|
764
767
|
```bash
|
|
765
|
-
#
|
|
766
|
-
xibecode mcp
|
|
768
|
+
# Open the config file
|
|
769
|
+
xibecode mcp edit
|
|
767
770
|
|
|
768
|
-
#
|
|
771
|
+
# Add your server to the file using the new format:
|
|
769
772
|
{
|
|
770
|
-
"
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
773
|
+
"mcpServers": {
|
|
774
|
+
"filesystem": {
|
|
775
|
+
"command": "mcp-server-filesystem",
|
|
776
|
+
"args": ["--root", "/path/to/files"]
|
|
777
|
+
}
|
|
778
|
+
}
|
|
774
779
|
}
|
|
775
780
|
```
|
|
776
781
|
|
|
@@ -800,12 +805,20 @@ MCP tools are prefixed with the server name (e.g., `filesystem::read_file`, `rem
|
|
|
800
805
|
# Install the GitHub MCP server
|
|
801
806
|
npm install -g @modelcontextprotocol/server-github
|
|
802
807
|
|
|
803
|
-
#
|
|
804
|
-
xibecode mcp
|
|
805
|
-
|
|
806
|
-
#
|
|
807
|
-
|
|
808
|
-
#
|
|
808
|
+
# Open the config file to add the server
|
|
809
|
+
xibecode mcp edit
|
|
810
|
+
# Edit the file and add:
|
|
811
|
+
# {
|
|
812
|
+
# "mcpServers": {
|
|
813
|
+
# "github": {
|
|
814
|
+
# "command": "mcp-server-github",
|
|
815
|
+
# "args": ["--token", "YOUR_GITHUB_TOKEN"]
|
|
816
|
+
# }
|
|
817
|
+
# }
|
|
818
|
+
# }
|
|
819
|
+
|
|
820
|
+
# Reload servers
|
|
821
|
+
xibecode mcp reload
|
|
809
822
|
|
|
810
823
|
# Now XibeCode can use GitHub tools
|
|
811
824
|
xibecode chat
|
package/dist/commands/chat.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAcA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,iBAgoBrD"}
|
package/dist/commands/chat.js
CHANGED
|
@@ -4,14 +4,26 @@ import { CodingToolExecutor } from '../core/tools.js';
|
|
|
4
4
|
import { MCPClientManager } from '../core/mcp-client.js';
|
|
5
5
|
import { EnhancedUI } from '../ui/enhanced-tui.js';
|
|
6
6
|
import { ConfigManager } from '../utils/config.js';
|
|
7
|
+
import { SessionManager } from '../core/session-manager.js';
|
|
8
|
+
import { exportSessionToMarkdown } from '../core/export.js';
|
|
9
|
+
import { ContextManager } from '../core/context.js';
|
|
10
|
+
import { isThemeName, THEME_NAMES } from '../ui/themes.js';
|
|
7
11
|
import chalk from 'chalk';
|
|
8
12
|
import * as fs from 'fs/promises';
|
|
9
13
|
import * as path from 'path';
|
|
10
14
|
export async function chatCommand(options) {
|
|
11
|
-
const ui = new EnhancedUI(false);
|
|
12
15
|
const config = new ConfigManager();
|
|
16
|
+
const preferredTheme = (options.theme || config.getTheme());
|
|
17
|
+
const themeName = isThemeName(preferredTheme) ? preferredTheme : 'default';
|
|
18
|
+
const ui = new EnhancedUI(false, themeName);
|
|
19
|
+
ui.setShowDetails(config.getShowDetails());
|
|
20
|
+
ui.setShowThinking(config.getShowThinking());
|
|
21
|
+
const sessionManager = new SessionManager(config.getSessionDirectory());
|
|
22
|
+
const contextManager = new ContextManager(process.cwd());
|
|
13
23
|
ui.clear();
|
|
14
|
-
|
|
24
|
+
if (!config.isHeaderMinimal()) {
|
|
25
|
+
ui.header('1.0.0');
|
|
26
|
+
}
|
|
15
27
|
// Get API key
|
|
16
28
|
const apiKey = options.apiKey || config.getApiKey();
|
|
17
29
|
if (!apiKey) {
|
|
@@ -25,16 +37,18 @@ export async function chatCommand(options) {
|
|
|
25
37
|
// Load and connect to MCP servers
|
|
26
38
|
const mcpClientManager = new MCPClientManager();
|
|
27
39
|
const mcpServers = await config.getMCPServers();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
const serverNames = Object.keys(mcpServers);
|
|
41
|
+
if (serverNames.length > 0) {
|
|
42
|
+
console.log(chalk.blue(` ℹ Connecting to ${serverNames.length} MCP server(s)...\n`));
|
|
43
|
+
for (const serverName of serverNames) {
|
|
44
|
+
const serverConfig = mcpServers[serverName];
|
|
31
45
|
try {
|
|
32
|
-
await mcpClientManager.connect(serverConfig);
|
|
33
|
-
const tools = mcpClientManager.getAvailableTools().filter(t => t.serverName ===
|
|
34
|
-
console.log(chalk.green(` ✓ Connected to ${
|
|
46
|
+
await mcpClientManager.connect(serverName, serverConfig);
|
|
47
|
+
const tools = mcpClientManager.getAvailableTools().filter(t => t.serverName === serverName);
|
|
48
|
+
console.log(chalk.green(` ✓ Connected to ${serverName} (${tools.length} tool(s))`));
|
|
35
49
|
}
|
|
36
50
|
catch (error) {
|
|
37
|
-
console.log(chalk.yellow(` ✗ Failed to connect to ${
|
|
51
|
+
console.log(chalk.yellow(` ✗ Failed to connect to ${serverName}: ${error.message}`));
|
|
38
52
|
}
|
|
39
53
|
}
|
|
40
54
|
console.log('');
|
|
@@ -46,77 +60,96 @@ export async function chatCommand(options) {
|
|
|
46
60
|
// ── Create ONE agent for the entire chat session ──
|
|
47
61
|
// This keeps conversation history (messages) across all turns,
|
|
48
62
|
// so the AI remembers everything you talked about.
|
|
49
|
-
|
|
50
|
-
apiKey,
|
|
63
|
+
let agent = new EnhancedAgent({
|
|
64
|
+
apiKey: apiKey,
|
|
51
65
|
baseUrl,
|
|
52
66
|
model,
|
|
53
67
|
maxIterations: 150,
|
|
54
68
|
verbose: false,
|
|
55
69
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
hasResponse = true;
|
|
69
|
-
break;
|
|
70
|
-
case 'stream_text':
|
|
71
|
-
ui.streamText(event.data.text);
|
|
72
|
-
break;
|
|
73
|
-
case 'stream_end':
|
|
74
|
-
ui.endAssistantResponse();
|
|
75
|
-
break;
|
|
76
|
-
// ── Non-streaming fallback ──
|
|
77
|
-
case 'response':
|
|
78
|
-
if (!hasResponse) {
|
|
79
|
-
ui.response(event.data.text);
|
|
70
|
+
function setupAgentHandlers() {
|
|
71
|
+
agent.removeAllListeners('event');
|
|
72
|
+
agent.on('event', (event) => {
|
|
73
|
+
switch (event.type) {
|
|
74
|
+
case 'thinking':
|
|
75
|
+
if (!hasResponse) {
|
|
76
|
+
ui.thinking(event.data.message || 'Analyzing your request...');
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
// ── Streaming ──
|
|
80
|
+
case 'stream_start':
|
|
81
|
+
ui.startAssistantResponse();
|
|
80
82
|
hasResponse = true;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
ui.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (r?.success && event.data.name === 'write_file') {
|
|
94
|
-
ui.fileChanged('created', r.path, r.lines ? `${r.lines} lines` : undefined);
|
|
83
|
+
break;
|
|
84
|
+
case 'stream_text':
|
|
85
|
+
ui.streamText(event.data.text);
|
|
86
|
+
break;
|
|
87
|
+
case 'stream_end':
|
|
88
|
+
ui.endAssistantResponse();
|
|
89
|
+
break;
|
|
90
|
+
// ── Non-streaming fallback ──
|
|
91
|
+
case 'response':
|
|
92
|
+
if (!hasResponse) {
|
|
93
|
+
ui.response(event.data.text);
|
|
94
|
+
hasResponse = true;
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
break;
|
|
97
|
+
// ── Tools ──
|
|
98
|
+
case 'tool_call':
|
|
99
|
+
if (enableTools) {
|
|
100
|
+
ui.toolCall(event.data.name, event.data.input);
|
|
98
101
|
}
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
break;
|
|
103
|
+
case 'tool_result':
|
|
104
|
+
if (enableTools) {
|
|
105
|
+
ui.toolResult(event.data.name, event.data.result, event.data.success);
|
|
106
|
+
const r = event.data.result;
|
|
107
|
+
if (r?.success && event.data.name === 'write_file') {
|
|
108
|
+
ui.fileChanged('created', r.path, r.lines ? `${r.lines} lines` : undefined);
|
|
109
|
+
}
|
|
110
|
+
else if (r?.success && event.data.name === 'edit_file') {
|
|
111
|
+
ui.fileChanged('modified', r.path || '', r.linesChanged ? `${r.linesChanged} lines` : undefined);
|
|
112
|
+
}
|
|
113
|
+
else if (r?.success && event.data.name === 'edit_lines') {
|
|
114
|
+
ui.fileChanged('modified', r.path || '', r.linesChanged ? `${r.linesChanged} lines` : undefined);
|
|
115
|
+
}
|
|
101
116
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
break;
|
|
118
|
+
// ── Iteration ──
|
|
119
|
+
case 'iteration':
|
|
120
|
+
if (!hasResponse && event.data?.current) {
|
|
121
|
+
ui.updateThinking(`Thinking... step ${event.data.current}`);
|
|
122
|
+
}
|
|
123
|
+
hasResponse = false;
|
|
124
|
+
break;
|
|
125
|
+
// ── Errors / Warnings ──
|
|
126
|
+
case 'error':
|
|
127
|
+
ui.error(event.data.message || event.data.error);
|
|
128
|
+
break;
|
|
129
|
+
case 'warning':
|
|
130
|
+
ui.warning(event.data.message);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
let hasResponse = false;
|
|
136
|
+
setupAgentHandlers();
|
|
137
|
+
// ── Session bootstrap ──────────────────────────────────
|
|
138
|
+
let currentSession;
|
|
139
|
+
const existingId = options.session;
|
|
140
|
+
if (existingId) {
|
|
141
|
+
const loaded = await sessionManager.loadSession(existingId);
|
|
142
|
+
if (loaded) {
|
|
143
|
+
currentSession = loaded;
|
|
144
|
+
agent.setMessages(loaded.messages || []);
|
|
118
145
|
}
|
|
119
|
-
|
|
146
|
+
else {
|
|
147
|
+
currentSession = await sessionManager.createSession({ model, cwd: process.cwd() });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
currentSession = await sessionManager.createSession({ model, cwd: process.cwd() });
|
|
152
|
+
}
|
|
120
153
|
async function showPathSuggestions(raw) {
|
|
121
154
|
const input = raw.trim().slice(1).trim(); // drop leading '@'
|
|
122
155
|
const target = input ? path.resolve(process.cwd(), input) : process.cwd();
|
|
@@ -157,14 +190,198 @@ export async function chatCommand(options) {
|
|
|
157
190
|
console.log('');
|
|
158
191
|
console.log(chalk.bold(' XibeCode chat commands'));
|
|
159
192
|
console.log(' ' + chalk.hex('#6B6B7B')('────────────────────────────'));
|
|
160
|
-
console.log(' ' + chalk.hex('#00D4FF')('/help') + chalk.hex('#6B6B7B')('
|
|
161
|
-
console.log(' ' + chalk.hex('#00D4FF')('/mcp') + chalk.hex('#6B6B7B')('
|
|
162
|
-
console.log(' ' + chalk.hex('#00D4FF')('
|
|
163
|
-
console.log(' ' + chalk.hex('#00D4FF')('
|
|
164
|
-
console.log(' ' + chalk.hex('#00D4FF')('
|
|
165
|
-
console.log(' ' + chalk.hex('#00D4FF')('
|
|
166
|
-
console.log(' ' + chalk.hex('#00D4FF')('
|
|
193
|
+
console.log(' ' + chalk.hex('#00D4FF')('/help') + chalk.hex('#6B6B7B')(' show this help, not an AI reply'));
|
|
194
|
+
console.log(' ' + chalk.hex('#00D4FF')('/mcp') + chalk.hex('#6B6B7B')(' show connected MCP servers and tools'));
|
|
195
|
+
console.log(' ' + chalk.hex('#00D4FF')('/new') + chalk.hex('#6B6B7B')(' start a new chat session'));
|
|
196
|
+
console.log(' ' + chalk.hex('#00D4FF')('/sessions') + chalk.hex('#6B6B7B')(' list and switch saved sessions'));
|
|
197
|
+
console.log(' ' + chalk.hex('#00D4FF')('/models') + chalk.hex('#6B6B7B')(' show/switch models'));
|
|
198
|
+
console.log(' ' + chalk.hex('#00D4FF')('/export') + chalk.hex('#6B6B7B')(' export this session to Markdown'));
|
|
199
|
+
console.log(' ' + chalk.hex('#00D4FF')('/compact') + chalk.hex('#6B6B7B')(' compact long conversation history'));
|
|
200
|
+
console.log(' ' + chalk.hex('#00D4FF')('/details') + chalk.hex('#6B6B7B')(' toggle verbose tool details'));
|
|
201
|
+
console.log(' ' + chalk.hex('#00D4FF')('/thinking') + chalk.hex('#6B6B7B')(' toggle thinking spinner'));
|
|
202
|
+
console.log(' ' + chalk.hex('#00D4FF')('/themes') + chalk.hex('#6B6B7B')(' choose a color theme'));
|
|
203
|
+
console.log(' ' + chalk.hex('#00D4FF')('@path') + chalk.hex('#6B6B7B')(' list files/folders under path (or cwd if just "@")'));
|
|
204
|
+
console.log(' ' + chalk.hex('#00D4FF')('clear') + chalk.hex('#6B6B7B')(' clear screen and redraw header'));
|
|
205
|
+
console.log(' ' + chalk.hex('#00D4FF')('tools on/off') + chalk.hex('#6B6B7B')(' toggle tools (editor & filesystem)'));
|
|
206
|
+
console.log(' ' + chalk.hex('#00D4FF')('exit / quit') + chalk.hex('#6B6B7B')(' end the chat session'));
|
|
207
|
+
console.log(' ' + chalk.hex('#00D4FF')('!cmd') + chalk.hex('#6B6B7B')(' run a shell command and feed output to AI'));
|
|
208
|
+
console.log('');
|
|
209
|
+
}
|
|
210
|
+
async function handleShellBang(input) {
|
|
211
|
+
const cmd = input.slice(1).trim();
|
|
212
|
+
if (!cmd) {
|
|
213
|
+
ui.warning('No command provided after "!". Example: !ls -la');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
ui.info(`Running shell command: ${cmd}`);
|
|
217
|
+
const result = await toolExecutor.execute('run_command', { command: cmd, cwd: process.cwd(), timeout: 300 });
|
|
218
|
+
const stdout = result.stdout || '';
|
|
219
|
+
const stderr = result.stderr || '';
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log(chalk.bold(' Command Output'));
|
|
222
|
+
console.log(' ' + chalk.hex('#6B6B7B')('────────────────────────────'));
|
|
223
|
+
if (stdout) {
|
|
224
|
+
console.log(chalk.white(stdout));
|
|
225
|
+
}
|
|
226
|
+
if (stderr) {
|
|
227
|
+
console.log(chalk.red(stderr));
|
|
228
|
+
}
|
|
167
229
|
console.log('');
|
|
230
|
+
const summary = [
|
|
231
|
+
`Shell command: ${cmd}`,
|
|
232
|
+
'',
|
|
233
|
+
stdout ? stdout : '',
|
|
234
|
+
stderr ? `STDERR:\n${stderr}` : '',
|
|
235
|
+
].join('\n');
|
|
236
|
+
const tools = enableTools ? toolExecutor.getTools() : [];
|
|
237
|
+
await agent.run(summary, tools, toolExecutor);
|
|
238
|
+
const stats = agent.getStats();
|
|
239
|
+
if (config.isStatusBarEnabled()) {
|
|
240
|
+
ui.renderStatusBar({
|
|
241
|
+
model,
|
|
242
|
+
sessionTitle: currentSession.title,
|
|
243
|
+
cwd: process.cwd(),
|
|
244
|
+
toolsEnabled: enableTools,
|
|
245
|
+
themeName: ui.getThemeName(),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
await sessionManager.saveMessagesAndStats({
|
|
249
|
+
id: currentSession.id,
|
|
250
|
+
messages: agent.getMessages(),
|
|
251
|
+
stats,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
async function handleSessionsCommand() {
|
|
255
|
+
const sessions = await sessionManager.listSessions();
|
|
256
|
+
if (sessions.length === 0) {
|
|
257
|
+
ui.info('No saved sessions yet.');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const { picked } = await inquirer.prompt([
|
|
261
|
+
{
|
|
262
|
+
type: 'list',
|
|
263
|
+
name: 'picked',
|
|
264
|
+
message: 'Select session',
|
|
265
|
+
choices: sessions.map(s => ({
|
|
266
|
+
name: `${s.title} · ${s.model} · ${s.updated}`,
|
|
267
|
+
value: s.id,
|
|
268
|
+
})),
|
|
269
|
+
},
|
|
270
|
+
]);
|
|
271
|
+
const loaded = await sessionManager.loadSession(picked);
|
|
272
|
+
if (!loaded) {
|
|
273
|
+
ui.error('Failed to load selected session');
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
currentSession = loaded;
|
|
277
|
+
agent.setMessages(loaded.messages || []);
|
|
278
|
+
ui.success(`Switched to session ${loaded.title}`);
|
|
279
|
+
}
|
|
280
|
+
async function handleNewSession() {
|
|
281
|
+
currentSession = await sessionManager.createSession({ model, cwd: process.cwd() });
|
|
282
|
+
agent = new EnhancedAgent({
|
|
283
|
+
apiKey: apiKey,
|
|
284
|
+
baseUrl,
|
|
285
|
+
model,
|
|
286
|
+
maxIterations: 150,
|
|
287
|
+
verbose: false,
|
|
288
|
+
});
|
|
289
|
+
setupAgentHandlers();
|
|
290
|
+
ui.success('Started new session');
|
|
291
|
+
}
|
|
292
|
+
async function handleModelsCommand() {
|
|
293
|
+
const current = model;
|
|
294
|
+
const models = [
|
|
295
|
+
current,
|
|
296
|
+
'claude-sonnet-4-5-20250929',
|
|
297
|
+
'claude-opus-4-5-20251101',
|
|
298
|
+
'claude-haiku-4-5-20251015',
|
|
299
|
+
];
|
|
300
|
+
const unique = Array.from(new Set(models));
|
|
301
|
+
const { picked } = await inquirer.prompt([
|
|
302
|
+
{
|
|
303
|
+
type: 'list',
|
|
304
|
+
name: 'picked',
|
|
305
|
+
message: 'Select model',
|
|
306
|
+
choices: unique.map(m => ({
|
|
307
|
+
name: m === current ? `${m} (current)` : m,
|
|
308
|
+
value: m,
|
|
309
|
+
})),
|
|
310
|
+
},
|
|
311
|
+
]);
|
|
312
|
+
config.set('model', picked);
|
|
313
|
+
ui.success(`Model set to: ${picked}`);
|
|
314
|
+
}
|
|
315
|
+
async function handleThemesCommand() {
|
|
316
|
+
const current = ui.getThemeName();
|
|
317
|
+
const { picked } = await inquirer.prompt([
|
|
318
|
+
{
|
|
319
|
+
type: 'list',
|
|
320
|
+
name: 'picked',
|
|
321
|
+
message: 'Select theme',
|
|
322
|
+
choices: THEME_NAMES.map(name => ({
|
|
323
|
+
name: name === current ? `${name} (current)` : name,
|
|
324
|
+
value: name,
|
|
325
|
+
})),
|
|
326
|
+
},
|
|
327
|
+
]);
|
|
328
|
+
ui.setTheme(picked);
|
|
329
|
+
config.set('theme', picked);
|
|
330
|
+
ui.success(`Theme set to: ${picked}`);
|
|
331
|
+
}
|
|
332
|
+
async function handleExportCommand() {
|
|
333
|
+
const session = {
|
|
334
|
+
...currentSession,
|
|
335
|
+
messages: agent.getMessages(),
|
|
336
|
+
};
|
|
337
|
+
const markdown = exportSessionToMarkdown(session);
|
|
338
|
+
const exportsDir = path.join(config['getConfigPath'], '..', 'sessions');
|
|
339
|
+
const fileName = `${session.id}.md`;
|
|
340
|
+
const fullPath = path.join(exportsDir, fileName);
|
|
341
|
+
await fs.mkdir(exportsDir, { recursive: true });
|
|
342
|
+
await fs.writeFile(fullPath, markdown, 'utf-8');
|
|
343
|
+
ui.success(`Session exported to ${fullPath}`);
|
|
344
|
+
}
|
|
345
|
+
async function handleCompactCommand() {
|
|
346
|
+
const messages = agent.getMessages();
|
|
347
|
+
if (messages.length <= 10) {
|
|
348
|
+
ui.info('Conversation is short; no compaction needed.');
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const preserved = messages.slice(-6);
|
|
352
|
+
const summaryMessage = {
|
|
353
|
+
role: 'assistant',
|
|
354
|
+
content: 'Earlier conversation has been compacted to save context. Key details from the last messages are preserved.',
|
|
355
|
+
};
|
|
356
|
+
const compacted = [summaryMessage, ...preserved];
|
|
357
|
+
agent.setMessages(compacted);
|
|
358
|
+
await sessionManager.saveMessagesAndStats({
|
|
359
|
+
id: currentSession.id,
|
|
360
|
+
messages: compacted,
|
|
361
|
+
stats: agent.getStats(),
|
|
362
|
+
});
|
|
363
|
+
ui.success('Conversation compacted.');
|
|
364
|
+
}
|
|
365
|
+
async function handleAtPathFuzzy(raw) {
|
|
366
|
+
const input = raw.trim().slice(1).trim();
|
|
367
|
+
const pattern = input ? `**/*${input}*` : '**/*';
|
|
368
|
+
try {
|
|
369
|
+
const files = await contextManager.searchFiles(pattern, { maxResults: 100 });
|
|
370
|
+
if (!files.length) {
|
|
371
|
+
ui.info(`No matches for pattern ${pattern}`);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
console.log('');
|
|
375
|
+
console.log(' ' + chalk.bold('Files'));
|
|
376
|
+
console.log(' ' + chalk.hex('#6B6B7B')('────────────────────────────'));
|
|
377
|
+
files.forEach(f => {
|
|
378
|
+
console.log(' ' + chalk.hex('#CE93D8')('📄') + ' ' + chalk.white(f));
|
|
379
|
+
});
|
|
380
|
+
console.log('');
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
ui.error('Failed to search files for @ path', error);
|
|
384
|
+
}
|
|
168
385
|
}
|
|
169
386
|
// ── Chat loop ──
|
|
170
387
|
while (true) {
|
|
@@ -227,6 +444,44 @@ export async function chatCommand(options) {
|
|
|
227
444
|
showSlashHelp();
|
|
228
445
|
continue;
|
|
229
446
|
}
|
|
447
|
+
if (lowerMessage === '/new') {
|
|
448
|
+
await handleNewSession();
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
if (lowerMessage === '/sessions') {
|
|
452
|
+
await handleSessionsCommand();
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (lowerMessage === '/models') {
|
|
456
|
+
await handleModelsCommand();
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (lowerMessage === '/themes') {
|
|
460
|
+
await handleThemesCommand();
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (lowerMessage === '/export') {
|
|
464
|
+
await handleExportCommand();
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (lowerMessage === '/compact') {
|
|
468
|
+
await handleCompactCommand();
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (lowerMessage === '/details') {
|
|
472
|
+
const next = !ui.getShowDetails();
|
|
473
|
+
ui.setShowDetails(next);
|
|
474
|
+
config.set('showDetails', next);
|
|
475
|
+
ui.success(`Details ${next ? 'enabled' : 'disabled'}`);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (lowerMessage === '/thinking') {
|
|
479
|
+
const next = !ui.getShowThinking();
|
|
480
|
+
ui.setShowThinking(next);
|
|
481
|
+
config.set('showThinking', next);
|
|
482
|
+
ui.success(`Thinking display ${next ? 'enabled' : 'disabled'}`);
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
230
485
|
if (lowerMessage === '/mcp') {
|
|
231
486
|
console.log('');
|
|
232
487
|
console.log(chalk.bold(' MCP Servers'));
|
|
@@ -256,7 +511,12 @@ export async function chatCommand(options) {
|
|
|
256
511
|
continue;
|
|
257
512
|
}
|
|
258
513
|
if (trimmed.startsWith('@')) {
|
|
259
|
-
await
|
|
514
|
+
await handleAtPathFuzzy(trimmed);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (trimmed.startsWith('!')) {
|
|
518
|
+
await handleShellBang(trimmed);
|
|
519
|
+
console.log('');
|
|
260
520
|
continue;
|
|
261
521
|
}
|
|
262
522
|
if (lowerMessage === 'exit' || lowerMessage === 'quit') {
|
|
@@ -270,11 +530,18 @@ export async function chatCommand(options) {
|
|
|
270
530
|
console.log(chalk.hex('#3A3A4A')(' │') + chalk.hex('#00D4FF')(' 👋 See you next time! ') + chalk.hex('#3A3A4A')('│'));
|
|
271
531
|
console.log(chalk.hex('#3A3A4A')(' ╰──────────────────────────────────╯'));
|
|
272
532
|
console.log('');
|
|
533
|
+
await sessionManager.saveMessagesAndStats({
|
|
534
|
+
id: currentSession.id,
|
|
535
|
+
messages: agent.getMessages(),
|
|
536
|
+
stats,
|
|
537
|
+
});
|
|
273
538
|
break;
|
|
274
539
|
}
|
|
275
540
|
if (lowerMessage === 'clear') {
|
|
276
541
|
ui.clear();
|
|
277
|
-
|
|
542
|
+
if (!config.isHeaderMinimal()) {
|
|
543
|
+
ui.header('1.0.0');
|
|
544
|
+
}
|
|
278
545
|
ui.chatBanner(process.cwd(), model, baseUrl);
|
|
279
546
|
continue;
|
|
280
547
|
}
|
|
@@ -296,6 +563,21 @@ export async function chatCommand(options) {
|
|
|
296
563
|
// the conversation history (this.messages), so the AI has
|
|
297
564
|
// full context of everything discussed in this session.
|
|
298
565
|
await agent.run(message, tools, toolExecutor);
|
|
566
|
+
const stats = agent.getStats();
|
|
567
|
+
if (config.isStatusBarEnabled()) {
|
|
568
|
+
ui.renderStatusBar({
|
|
569
|
+
model,
|
|
570
|
+
sessionTitle: currentSession.title,
|
|
571
|
+
cwd: process.cwd(),
|
|
572
|
+
toolsEnabled: enableTools,
|
|
573
|
+
themeName: ui.getThemeName(),
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
await sessionManager.saveMessagesAndStats({
|
|
577
|
+
id: currentSession.id,
|
|
578
|
+
messages: agent.getMessages(),
|
|
579
|
+
stats,
|
|
580
|
+
});
|
|
299
581
|
}
|
|
300
582
|
catch (error) {
|
|
301
583
|
ui.error('Failed to process message', error);
|
|
@@ -303,7 +585,7 @@ export async function chatCommand(options) {
|
|
|
303
585
|
console.log('');
|
|
304
586
|
}
|
|
305
587
|
// Cleanup: disconnect from all MCP servers
|
|
306
|
-
if (
|
|
588
|
+
if (serverNames.length > 0) {
|
|
307
589
|
await mcpClientManager.disconnectAll();
|
|
308
590
|
}
|
|
309
591
|
}
|