xibecode 1.0.2 → 1.0.6
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/commands/chat.d.ts +0 -1
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +10 -7
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +5 -3
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/diagnostics.js +1 -1
- package/dist/commands/diagnostics.js.map +1 -1
- package/dist/commands/mcp.js +1 -1
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/resume.js +1 -1
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/run-pr.d.ts.map +1 -1
- package/dist/commands/run-pr.js +13 -10
- package/dist/commands/run-pr.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +17 -14
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +3 -2
- package/dist/commands/skills.js.map +1 -1
- package/dist/components/AssistantMarkdown.js +1 -1
- package/dist/components/AssistantMarkdown.js.map +1 -1
- package/dist/index.js +2 -39
- package/dist/index.js.map +1 -1
- package/dist/ui/claude-style-chat.d.ts.map +1 -1
- package/dist/ui/claude-style-chat.js +15 -11
- package/dist/ui/claude-style-chat.js.map +1 -1
- package/dist/utils/built-in-skills-dir.d.ts +7 -0
- package/dist/utils/built-in-skills-dir.d.ts.map +1 -0
- package/dist/utils/built-in-skills-dir.js +11 -0
- package/dist/utils/built-in-skills-dir.js.map +1 -0
- package/dist/utils/config.d.ts +2 -119
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +3 -88
- package/dist/utils/config.js.map +1 -1
- package/package.json +11 -26
- package/dist/commands/punycode.d.ts +0 -5
- package/dist/commands/punycode.d.ts.map +0 -1
- package/dist/commands/punycode.js +0 -48
- package/dist/commands/punycode.js.map +0 -1
- package/dist/commands/tui.d.ts +0 -9
- package/dist/commands/tui.d.ts.map +0 -1
- package/dist/commands/tui.js +0 -83
- package/dist/commands/tui.js.map +0 -1
- package/dist/core/agent-tool-policies.d.ts +0 -5
- package/dist/core/agent-tool-policies.d.ts.map +0 -1
- package/dist/core/agent-tool-policies.js +0 -18
- package/dist/core/agent-tool-policies.js.map +0 -1
- package/dist/core/agent.d.ts +0 -181
- package/dist/core/agent.d.ts.map +0 -1
- package/dist/core/agent.js +0 -1777
- package/dist/core/agent.js.map +0 -1
- package/dist/core/background-agent.d.ts +0 -23
- package/dist/core/background-agent.d.ts.map +0 -1
- package/dist/core/background-agent.js +0 -175
- package/dist/core/background-agent.js.map +0 -1
- package/dist/core/code-graph.d.ts +0 -18
- package/dist/core/code-graph.d.ts.map +0 -1
- package/dist/core/code-graph.js +0 -105
- package/dist/core/code-graph.js.map +0 -1
- package/dist/core/conflict-solver.d.ts +0 -26
- package/dist/core/conflict-solver.d.ts.map +0 -1
- package/dist/core/conflict-solver.js +0 -108
- package/dist/core/conflict-solver.js.map +0 -1
- package/dist/core/context-compactor.d.ts +0 -10
- package/dist/core/context-compactor.d.ts.map +0 -1
- package/dist/core/context-compactor.js +0 -158
- package/dist/core/context-compactor.js.map +0 -1
- package/dist/core/context-pruner.d.ts +0 -19
- package/dist/core/context-pruner.d.ts.map +0 -1
- package/dist/core/context-pruner.js +0 -103
- package/dist/core/context-pruner.js.map +0 -1
- package/dist/core/context.d.ts +0 -82
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/context.js +0 -273
- package/dist/core/context.js.map +0 -1
- package/dist/core/conversation-recovery.d.ts +0 -9
- package/dist/core/conversation-recovery.d.ts.map +0 -1
- package/dist/core/conversation-recovery.js +0 -15
- package/dist/core/conversation-recovery.js.map +0 -1
- package/dist/core/debug-workflow.d.ts +0 -9
- package/dist/core/debug-workflow.d.ts.map +0 -1
- package/dist/core/debug-workflow.js +0 -19
- package/dist/core/debug-workflow.js.map +0 -1
- package/dist/core/docs-scraper.d.ts +0 -40
- package/dist/core/docs-scraper.d.ts.map +0 -1
- package/dist/core/docs-scraper.js +0 -386
- package/dist/core/docs-scraper.js.map +0 -1
- package/dist/core/editor.d.ts +0 -87
- package/dist/core/editor.d.ts.map +0 -1
- package/dist/core/editor.js +0 -377
- package/dist/core/editor.js.map +0 -1
- package/dist/core/export.d.ts +0 -11
- package/dist/core/export.d.ts.map +0 -1
- package/dist/core/export.js +0 -54
- package/dist/core/export.js.map +0 -1
- package/dist/core/history-manager.d.ts +0 -75
- package/dist/core/history-manager.d.ts.map +0 -1
- package/dist/core/history-manager.js +0 -146
- package/dist/core/history-manager.js.map +0 -1
- package/dist/core/marketplace-client.d.ts +0 -52
- package/dist/core/marketplace-client.d.ts.map +0 -1
- package/dist/core/marketplace-client.js +0 -71
- package/dist/core/marketplace-client.js.map +0 -1
- package/dist/core/mcp/mcp-config.d.ts +0 -10
- package/dist/core/mcp/mcp-config.d.ts.map +0 -1
- package/dist/core/mcp/mcp-config.js +0 -70
- package/dist/core/mcp/mcp-config.js.map +0 -1
- package/dist/core/mcp/mcp-policy.d.ts +0 -17
- package/dist/core/mcp/mcp-policy.d.ts.map +0 -1
- package/dist/core/mcp/mcp-policy.js +0 -56
- package/dist/core/mcp/mcp-policy.js.map +0 -1
- package/dist/core/mcp/oauth-flow.d.ts +0 -30
- package/dist/core/mcp/oauth-flow.d.ts.map +0 -1
- package/dist/core/mcp/oauth-flow.js +0 -230
- package/dist/core/mcp/oauth-flow.js.map +0 -1
- package/dist/core/mcp/oauth-store.d.ts +0 -13
- package/dist/core/mcp/oauth-store.d.ts.map +0 -1
- package/dist/core/mcp/oauth-store.js +0 -68
- package/dist/core/mcp/oauth-store.js.map +0 -1
- package/dist/core/mcp/resolve-mcp-servers.d.ts +0 -16
- package/dist/core/mcp/resolve-mcp-servers.d.ts.map +0 -1
- package/dist/core/mcp/resolve-mcp-servers.js +0 -83
- package/dist/core/mcp/resolve-mcp-servers.js.map +0 -1
- package/dist/core/mcp-client.d.ts +0 -99
- package/dist/core/mcp-client.d.ts.map +0 -1
- package/dist/core/mcp-client.js +0 -315
- package/dist/core/mcp-client.js.map +0 -1
- package/dist/core/memory-promotions.d.ts +0 -15
- package/dist/core/memory-promotions.d.ts.map +0 -1
- package/dist/core/memory-promotions.js +0 -38
- package/dist/core/memory-promotions.js.map +0 -1
- package/dist/core/memory.d.ts +0 -32
- package/dist/core/memory.d.ts.map +0 -1
- package/dist/core/memory.js +0 -121
- package/dist/core/memory.js.map +0 -1
- package/dist/core/modes.d.ts +0 -432
- package/dist/core/modes.d.ts.map +0 -1
- package/dist/core/modes.js +0 -1088
- package/dist/core/modes.js.map +0 -1
- package/dist/core/pattern-miner.d.ts +0 -43
- package/dist/core/pattern-miner.d.ts.map +0 -1
- package/dist/core/pattern-miner.js +0 -123
- package/dist/core/pattern-miner.js.map +0 -1
- package/dist/core/permission-store.d.ts +0 -15
- package/dist/core/permission-store.d.ts.map +0 -1
- package/dist/core/permission-store.js +0 -30
- package/dist/core/permission-store.js.map +0 -1
- package/dist/core/permissions.d.ts +0 -33
- package/dist/core/permissions.d.ts.map +0 -1
- package/dist/core/permissions.js +0 -141
- package/dist/core/permissions.js.map +0 -1
- package/dist/core/plan-artifacts.d.ts +0 -10
- package/dist/core/plan-artifacts.d.ts.map +0 -1
- package/dist/core/plan-artifacts.js +0 -60
- package/dist/core/plan-artifacts.js.map +0 -1
- package/dist/core/plan-session.d.ts +0 -25
- package/dist/core/plan-session.d.ts.map +0 -1
- package/dist/core/plan-session.js +0 -99
- package/dist/core/plan-session.js.map +0 -1
- package/dist/core/planMode.d.ts +0 -51
- package/dist/core/planMode.d.ts.map +0 -1
- package/dist/core/planMode.js +0 -245
- package/dist/core/planMode.js.map +0 -1
- package/dist/core/plugins.d.ts +0 -96
- package/dist/core/plugins.d.ts.map +0 -1
- package/dist/core/plugins.js +0 -202
- package/dist/core/plugins.js.map +0 -1
- package/dist/core/session-bridge.d.ts +0 -128
- package/dist/core/session-bridge.d.ts.map +0 -1
- package/dist/core/session-bridge.js +0 -328
- package/dist/core/session-bridge.js.map +0 -1
- package/dist/core/session-manager.d.ts +0 -80
- package/dist/core/session-manager.d.ts.map +0 -1
- package/dist/core/session-manager.js +0 -166
- package/dist/core/session-manager.js.map +0 -1
- package/dist/core/session-memory.d.ts +0 -45
- package/dist/core/session-memory.d.ts.map +0 -1
- package/dist/core/session-memory.js +0 -103
- package/dist/core/session-memory.js.map +0 -1
- package/dist/core/skill-selection.d.ts +0 -36
- package/dist/core/skill-selection.d.ts.map +0 -1
- package/dist/core/skill-selection.js +0 -172
- package/dist/core/skill-selection.js.map +0 -1
- package/dist/core/skills-sh-client.d.ts +0 -19
- package/dist/core/skills-sh-client.d.ts.map +0 -1
- package/dist/core/skills-sh-client.js +0 -75
- package/dist/core/skills-sh-client.js.map +0 -1
- package/dist/core/skills.d.ts +0 -97
- package/dist/core/skills.d.ts.map +0 -1
- package/dist/core/skills.js +0 -339
- package/dist/core/skills.js.map +0 -1
- package/dist/core/swarm.d.ts +0 -34
- package/dist/core/swarm.d.ts.map +0 -1
- package/dist/core/swarm.js +0 -111
- package/dist/core/swarm.js.map +0 -1
- package/dist/core/task-status.d.ts +0 -13
- package/dist/core/task-status.d.ts.map +0 -1
- package/dist/core/task-status.js +0 -17
- package/dist/core/task-status.js.map +0 -1
- package/dist/core/tool-orchestrator.d.ts +0 -30
- package/dist/core/tool-orchestrator.d.ts.map +0 -1
- package/dist/core/tool-orchestrator.js +0 -89
- package/dist/core/tool-orchestrator.js.map +0 -1
- package/dist/core/tools.d.ts +0 -462
- package/dist/core/tools.d.ts.map +0 -1
- package/dist/core/tools.js +0 -2916
- package/dist/core/tools.js.map +0 -1
- package/dist/core/transcript-cleanup.d.ts +0 -8
- package/dist/core/transcript-cleanup.d.ts.map +0 -1
- package/dist/core/transcript-cleanup.js +0 -52
- package/dist/core/transcript-cleanup.js.map +0 -1
- package/dist/core/visual-feedback.d.ts +0 -20
- package/dist/core/visual-feedback.d.ts.map +0 -1
- package/dist/core/visual-feedback.js +0 -117
- package/dist/core/visual-feedback.js.map +0 -1
- package/dist/tools/browser.d.ts +0 -120
- package/dist/tools/browser.d.ts.map +0 -1
- package/dist/tools/browser.js +0 -439
- package/dist/tools/browser.js.map +0 -1
- package/dist/tools/test-generator.d.ts +0 -157
- package/dist/tools/test-generator.d.ts.map +0 -1
- package/dist/tools/test-generator.js +0 -893
- package/dist/tools/test-generator.js.map +0 -1
- package/dist/tui/InkApp.d.ts +0 -21
- package/dist/tui/InkApp.d.ts.map +0 -1
- package/dist/tui/InkApp.js +0 -146
- package/dist/tui/InkApp.js.map +0 -1
- package/dist/tui/MarkdownMessage.d.ts +0 -16
- package/dist/tui/MarkdownMessage.d.ts.map +0 -1
- package/dist/tui/MarkdownMessage.js +0 -63
- package/dist/tui/MarkdownMessage.js.map +0 -1
- package/dist/tui/blessed-chat.d.ts +0 -9
- package/dist/tui/blessed-chat.d.ts.map +0 -1
- package/dist/tui/blessed-chat.js +0 -887
- package/dist/tui/blessed-chat.js.map +0 -1
- package/dist/tui/markdown-to-blessed.d.ts +0 -6
- package/dist/tui/markdown-to-blessed.d.ts.map +0 -1
- package/dist/tui/markdown-to-blessed.js +0 -26
- package/dist/tui/markdown-to-blessed.js.map +0 -1
- package/dist/ui/ink/App.d.ts +0 -25
- package/dist/ui/ink/App.d.ts.map +0 -1
- package/dist/ui/ink/App.js +0 -372
- package/dist/ui/ink/App.js.map +0 -1
- package/dist/utils/at-references.d.ts +0 -14
- package/dist/utils/at-references.d.ts.map +0 -1
- package/dist/utils/at-references.js +0 -47
- package/dist/utils/at-references.js.map +0 -1
- package/dist/utils/auto-memory.d.ts +0 -24
- package/dist/utils/auto-memory.d.ts.map +0 -1
- package/dist/utils/auto-memory.js +0 -153
- package/dist/utils/auto-memory.js.map +0 -1
- package/dist/utils/git.d.ts +0 -89
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js +0 -444
- package/dist/utils/git.js.map +0 -1
- package/dist/utils/mcp-servers-file.d.ts +0 -46
- package/dist/utils/mcp-servers-file.d.ts.map +0 -1
- package/dist/utils/mcp-servers-file.js +0 -212
- package/dist/utils/mcp-servers-file.js.map +0 -1
- package/dist/utils/safety.d.ts +0 -60
- package/dist/utils/safety.d.ts.map +0 -1
- package/dist/utils/safety.js +0 -254
- package/dist/utils/safety.js.map +0 -1
- package/dist/utils/smithery.d.ts +0 -25
- package/dist/utils/smithery.d.ts.map +0 -1
- package/dist/utils/smithery.js +0 -50
- package/dist/utils/smithery.js.map +0 -1
- package/dist/utils/testRunner.d.ts +0 -44
- package/dist/utils/testRunner.d.ts.map +0 -1
- package/dist/utils/testRunner.js +0 -270
- package/dist/utils/testRunner.js.map +0 -1
- package/dist/webui/server.d.ts +0 -99
- package/dist/webui/server.d.ts.map +0 -1
- package/dist/webui/server.js +0 -2619
- package/dist/webui/server.js.map +0 -1
- package/webui-dist/assets/index-CSla6Lzy.css +0 -32
- package/webui-dist/assets/index-G_Z4gzPy.js +0 -457
- package/webui-dist/assets/index-G_Z4gzPy.js.map +0 -1
- package/webui-dist/assets/xterm-Da5jL1MD.js +0 -10
- package/webui-dist/assets/xterm-Da5jL1MD.js.map +0 -1
- package/webui-dist/assets/xterm-addon-fit-CMeqLIvm.js +0 -2
- package/webui-dist/assets/xterm-addon-fit-CMeqLIvm.js.map +0 -1
- package/webui-dist/assets/xterm-addon-web-links-D6m8jNVE.js +0 -2
- package/webui-dist/assets/xterm-addon-web-links-D6m8jNVE.js.map +0 -1
- package/webui-dist/index.html +0 -15
package/dist/tui/blessed-chat.js
DELETED
|
@@ -1,887 +0,0 @@
|
|
|
1
|
-
import blessed from 'blessed';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import * as fs from 'fs/promises';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import { createRequire } from 'module';
|
|
6
|
-
import { EnhancedAgent } from '../core/agent.js';
|
|
7
|
-
import { CodingToolExecutor } from '../core/tools.js';
|
|
8
|
-
import { MCPClientManager } from '../core/mcp-client.js';
|
|
9
|
-
import { ConfigManager } from '../utils/config.js';
|
|
10
|
-
import { SessionManager } from '../core/session-manager.js';
|
|
11
|
-
import { ContextManager } from '../core/context.js';
|
|
12
|
-
import { isThemeName, THEME_NAMES } from '../ui/themes.js';
|
|
13
|
-
import { renderMarkdownToAnsi } from './markdown-to-blessed.js';
|
|
14
|
-
import { getAllModes } from '../core/modes.js';
|
|
15
|
-
import { exportSessionToMarkdown } from '../core/export.js';
|
|
16
|
-
export async function runBlessedChat(options) {
|
|
17
|
-
const config = new ConfigManager();
|
|
18
|
-
const preferredTheme = (options.theme || config.getTheme());
|
|
19
|
-
const themeName = isThemeName(preferredTheme) ? preferredTheme : 'default';
|
|
20
|
-
const apiKey = options.apiKey || config.getApiKey();
|
|
21
|
-
if (!apiKey) {
|
|
22
|
-
// Use plain console output before blessed is initialised.
|
|
23
|
-
// eslint-disable-next-line no-console
|
|
24
|
-
console.error('No API key found!');
|
|
25
|
-
// eslint-disable-next-line no-console
|
|
26
|
-
console.log(' Set your API key:\n');
|
|
27
|
-
// eslint-disable-next-line no-console
|
|
28
|
-
console.log(' xibecode config --set-key YOUR_KEY\n');
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
const model = options.model || config.getModel();
|
|
32
|
-
const baseUrl = options.baseUrl || config.getBaseUrl();
|
|
33
|
-
const sessionManager = new SessionManager(config.getSessionDirectory());
|
|
34
|
-
const contextManager = new ContextManager(process.cwd());
|
|
35
|
-
const mcpClientManager = new MCPClientManager();
|
|
36
|
-
const mcpServers = await config.getMCPServers();
|
|
37
|
-
const serverNames = Object.keys(mcpServers);
|
|
38
|
-
// Connect MCP servers before launching TUI so we can show status.
|
|
39
|
-
for (const serverName of serverNames) {
|
|
40
|
-
const serverConfig = mcpServers[serverName];
|
|
41
|
-
try {
|
|
42
|
-
await mcpClientManager.connect(serverName, serverConfig);
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// Ignore connection failures here; they will be visible via /mcp later.
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const toolExecutor = new CodingToolExecutor(process.cwd(), { mcpClientManager });
|
|
49
|
-
let agent = new EnhancedAgent({
|
|
50
|
-
apiKey: apiKey,
|
|
51
|
-
baseUrl,
|
|
52
|
-
model,
|
|
53
|
-
maxIterations: 150,
|
|
54
|
-
verbose: false,
|
|
55
|
-
});
|
|
56
|
-
let currentMode = agent.getMode();
|
|
57
|
-
const allModes = getAllModes();
|
|
58
|
-
let enableTools = true;
|
|
59
|
-
let currentSession;
|
|
60
|
-
if (options.session) {
|
|
61
|
-
const loaded = await sessionManager.loadSession(options.session);
|
|
62
|
-
if (loaded) {
|
|
63
|
-
currentSession = loaded;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
currentSession = await sessionManager.createSession({ model, cwd: process.cwd() });
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
currentSession = await sessionManager.createSession({ model, cwd: process.cwd() });
|
|
71
|
-
}
|
|
72
|
-
agent.setMessages(currentSession.messages || []);
|
|
73
|
-
// ─── Blessed screen & layout ─────────────────────────────
|
|
74
|
-
const screen = blessed.screen({
|
|
75
|
-
smartCSR: true,
|
|
76
|
-
title: 'XibeCode',
|
|
77
|
-
});
|
|
78
|
-
// Node ESM-safe require for reading package.json at runtime
|
|
79
|
-
const require = createRequire(import.meta.url);
|
|
80
|
-
const pkg = require('../../package.json');
|
|
81
|
-
// ─── Big XibeCode banner (gradient ASCII, similar to classic UI) ───
|
|
82
|
-
const logoLines = [
|
|
83
|
-
'██╗ ██╗██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗',
|
|
84
|
-
'╚██╗██╔╝██║██╔══██╗██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝',
|
|
85
|
-
' ╚███╔╝ ██║██████╔╝█████╗ ██║ ██║ ██║██║ ██║█████╗ ',
|
|
86
|
-
' ██╔██╗ ██║██╔══██╗██╔══╝ ██║ ██║ ██║██╔══██╗██╔══╝ ',
|
|
87
|
-
'██╔╝ ██╗██║██████╔╝███████╗╚██████╗╚██████╔╝██████╔╝███████╗',
|
|
88
|
-
'╚═╝ ╚═╝╚═╝╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝',
|
|
89
|
-
];
|
|
90
|
-
function buildGradientBanner(lines) {
|
|
91
|
-
const start = { r: 89, g: 149, b: 235 };
|
|
92
|
-
const end = { r: 224, g: 108, b: 117 };
|
|
93
|
-
const out = [];
|
|
94
|
-
for (const line of lines) {
|
|
95
|
-
let coloredLine = '';
|
|
96
|
-
const len = line.length;
|
|
97
|
-
for (let i = 0; i < len; i++) {
|
|
98
|
-
const ratio = i / len;
|
|
99
|
-
const r = Math.floor(start.r + (end.r - start.r) * ratio);
|
|
100
|
-
const g = Math.floor(start.g + (end.g - start.g) * ratio);
|
|
101
|
-
const b = Math.floor(start.b + (end.b - start.b) * ratio);
|
|
102
|
-
coloredLine += `\x1b[38;2;${r};${g};${b}m${line[i]}`;
|
|
103
|
-
}
|
|
104
|
-
out.push(coloredLine + '\x1b[0m');
|
|
105
|
-
}
|
|
106
|
-
out.push('');
|
|
107
|
-
out.push(chalk.hex('#00D4FF').bold('XibeCode'));
|
|
108
|
-
out.push(chalk.gray('AI-powered autonomous coding assistant') + chalk.gray(` · v${pkg.version ?? 'dev'}`));
|
|
109
|
-
return out.join('\n');
|
|
110
|
-
}
|
|
111
|
-
const bannerHeight = logoLines.length + 4;
|
|
112
|
-
const banner = blessed.box({
|
|
113
|
-
top: 0,
|
|
114
|
-
left: 0,
|
|
115
|
-
right: 0,
|
|
116
|
-
height: bannerHeight,
|
|
117
|
-
tags: false, // use raw ANSI colors
|
|
118
|
-
content: buildGradientBanner(logoLines),
|
|
119
|
-
});
|
|
120
|
-
const header = blessed.box({
|
|
121
|
-
top: bannerHeight,
|
|
122
|
-
left: 0,
|
|
123
|
-
right: 0,
|
|
124
|
-
height: 1,
|
|
125
|
-
tags: true,
|
|
126
|
-
style: {
|
|
127
|
-
fg: 'white',
|
|
128
|
-
bg: 'black',
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
const messagesBox = blessed.box({
|
|
132
|
-
top: bannerHeight + 1,
|
|
133
|
-
left: 0,
|
|
134
|
-
right: 0,
|
|
135
|
-
bottom: 3,
|
|
136
|
-
scrollable: true,
|
|
137
|
-
alwaysScroll: true,
|
|
138
|
-
scrollbar: { ch: ' ' },
|
|
139
|
-
keys: true,
|
|
140
|
-
mouse: true,
|
|
141
|
-
tags: false,
|
|
142
|
-
border: { type: 'line' },
|
|
143
|
-
label: ' Conversation ',
|
|
144
|
-
style: {
|
|
145
|
-
border: { fg: 'grey' },
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
const statusBar = blessed.box({
|
|
149
|
-
bottom: 3,
|
|
150
|
-
left: 0,
|
|
151
|
-
right: 0,
|
|
152
|
-
height: 2,
|
|
153
|
-
tags: true,
|
|
154
|
-
style: {
|
|
155
|
-
fg: 'grey',
|
|
156
|
-
bg: 'black',
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
const thinkingBox = blessed.box({
|
|
160
|
-
bottom: 3,
|
|
161
|
-
right: 2,
|
|
162
|
-
width: 20,
|
|
163
|
-
height: 1,
|
|
164
|
-
tags: true,
|
|
165
|
-
style: {
|
|
166
|
-
fg: 'cyan',
|
|
167
|
-
bg: 'black',
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
const input = blessed.textbox({
|
|
171
|
-
bottom: 0,
|
|
172
|
-
left: 0,
|
|
173
|
-
right: 0,
|
|
174
|
-
height: 3,
|
|
175
|
-
inputOnFocus: true,
|
|
176
|
-
keys: true,
|
|
177
|
-
mouse: true,
|
|
178
|
-
border: { type: 'line' },
|
|
179
|
-
label: ' Type your message or @path/to/file ',
|
|
180
|
-
style: {
|
|
181
|
-
fg: 'white',
|
|
182
|
-
bg: 'black',
|
|
183
|
-
border: { fg: 'gray' },
|
|
184
|
-
focus: {
|
|
185
|
-
border: { fg: 'cyan' },
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
});
|
|
189
|
-
screen.append(banner);
|
|
190
|
-
screen.append(header);
|
|
191
|
-
screen.append(messagesBox);
|
|
192
|
-
screen.append(statusBar);
|
|
193
|
-
screen.append(thinkingBox);
|
|
194
|
-
screen.append(input);
|
|
195
|
-
const lines = [];
|
|
196
|
-
let showDetails = config.getShowDetails();
|
|
197
|
-
let showThinking = config.getShowThinking();
|
|
198
|
-
let thinkingInterval = null;
|
|
199
|
-
let thinkingFrame = 0;
|
|
200
|
-
const thinkingFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
201
|
-
function getToolIcon(name) {
|
|
202
|
-
const icons = {
|
|
203
|
-
read_file: '📄',
|
|
204
|
-
read_multiple_files: '📚',
|
|
205
|
-
write_file: '✍️',
|
|
206
|
-
edit_file: '✏️',
|
|
207
|
-
edit_lines: '🔧',
|
|
208
|
-
delete_file: '🗑️',
|
|
209
|
-
run_command: '⚡',
|
|
210
|
-
search_files: '🔎',
|
|
211
|
-
list_directory: '📂',
|
|
212
|
-
create_directory: '📁',
|
|
213
|
-
move_file: '📦',
|
|
214
|
-
get_context: '🧠',
|
|
215
|
-
revert_file: '↩️',
|
|
216
|
-
insert_at_line: '➕',
|
|
217
|
-
};
|
|
218
|
-
return icons[name] || '🔧';
|
|
219
|
-
}
|
|
220
|
-
function summarizeToolInput(name, input) {
|
|
221
|
-
if (!input)
|
|
222
|
-
return null;
|
|
223
|
-
switch (name) {
|
|
224
|
-
case 'read_file':
|
|
225
|
-
return input.start_line
|
|
226
|
-
? `${input.path} (${input.start_line}-${input.end_line})`
|
|
227
|
-
: input.path || null;
|
|
228
|
-
case 'read_multiple_files':
|
|
229
|
-
return Array.isArray(input.paths) ? `${input.paths.length} files` : null;
|
|
230
|
-
case 'write_file':
|
|
231
|
-
case 'edit_file':
|
|
232
|
-
case 'edit_lines':
|
|
233
|
-
return input.path || null;
|
|
234
|
-
case 'run_command':
|
|
235
|
-
if (!input.command)
|
|
236
|
-
return null;
|
|
237
|
-
return input.command.length > 60 ? `${input.command.slice(0, 57)}...` : input.command;
|
|
238
|
-
case 'search_files':
|
|
239
|
-
return input.pattern || null;
|
|
240
|
-
case 'list_directory':
|
|
241
|
-
return input.path || '.';
|
|
242
|
-
default:
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
function summarizeToolResult(name, result) {
|
|
247
|
-
if (!result)
|
|
248
|
-
return null;
|
|
249
|
-
if (result.error || result.success === false) {
|
|
250
|
-
return result.message || 'failed';
|
|
251
|
-
}
|
|
252
|
-
switch (name) {
|
|
253
|
-
case 'read_file':
|
|
254
|
-
return result.lines !== undefined ? `${result.lines} lines` : null;
|
|
255
|
-
case 'read_multiple_files':
|
|
256
|
-
return result.files ? `${result.files.length} files read` : null;
|
|
257
|
-
case 'write_file':
|
|
258
|
-
return result.lines ? `${result.lines} lines written` : 'written';
|
|
259
|
-
case 'edit_file':
|
|
260
|
-
return result.linesChanged ? `${result.linesChanged} lines changed` : 'edited';
|
|
261
|
-
case 'run_command':
|
|
262
|
-
return result.success ? 'ok' : 'failed';
|
|
263
|
-
case 'search_files':
|
|
264
|
-
return `${result.count ?? 0} matches`;
|
|
265
|
-
case 'list_directory':
|
|
266
|
-
return `${result.count ?? 0} items`;
|
|
267
|
-
default:
|
|
268
|
-
return 'ok';
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
function formatToolCall(name, input) {
|
|
272
|
-
const icon = getToolIcon(name);
|
|
273
|
-
const summary = summarizeToolInput(name, input);
|
|
274
|
-
let header = `${chalk.gray('╭─')} ${icon} ${chalk.cyan(name)}`;
|
|
275
|
-
if (summary) {
|
|
276
|
-
header += ' ' + chalk.dim(summary);
|
|
277
|
-
}
|
|
278
|
-
if (!showDetails || !input)
|
|
279
|
-
return header;
|
|
280
|
-
const json = JSON.stringify(input, null, 2)
|
|
281
|
-
.split('\n')
|
|
282
|
-
.slice(0, 20)
|
|
283
|
-
.map(line => `${chalk.gray('│')} ${chalk.dim(line)}`)
|
|
284
|
-
.join('\n');
|
|
285
|
-
return `${header}\n${json}`;
|
|
286
|
-
}
|
|
287
|
-
function formatToolResult(name, result, success) {
|
|
288
|
-
const icon = success ? chalk.green('✔') : chalk.red('✘');
|
|
289
|
-
const summary = summarizeToolResult(name, result);
|
|
290
|
-
let header = `${chalk.gray('╰─')} ${icon}`;
|
|
291
|
-
if (summary) {
|
|
292
|
-
header += ' ' + chalk.dim(summary);
|
|
293
|
-
}
|
|
294
|
-
if (!showDetails || !result || !success)
|
|
295
|
-
return header;
|
|
296
|
-
const bodyStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
297
|
-
const json = bodyStr
|
|
298
|
-
.split('\n')
|
|
299
|
-
.slice(0, 30)
|
|
300
|
-
.map(line => ` ${chalk.dim(line)}`)
|
|
301
|
-
.join('\n');
|
|
302
|
-
return `${header}\n${json}`;
|
|
303
|
-
}
|
|
304
|
-
function pushLine(line) {
|
|
305
|
-
lines.push(line);
|
|
306
|
-
renderMessages();
|
|
307
|
-
}
|
|
308
|
-
function renderMessages() {
|
|
309
|
-
const rendered = [];
|
|
310
|
-
for (const line of lines) {
|
|
311
|
-
if (line.role === 'user') {
|
|
312
|
-
rendered.push(chalk.green('You: ') + line.text);
|
|
313
|
-
}
|
|
314
|
-
else if (line.role === 'assistant') {
|
|
315
|
-
const body = renderMarkdownToAnsi(line.text);
|
|
316
|
-
rendered.push(chalk.cyan('Assistant:') + '\n' + body);
|
|
317
|
-
}
|
|
318
|
-
else if (line.role === 'tool') {
|
|
319
|
-
rendered.push(line.text);
|
|
320
|
-
}
|
|
321
|
-
else {
|
|
322
|
-
rendered.push(chalk.yellow(line.text));
|
|
323
|
-
}
|
|
324
|
-
rendered.push('');
|
|
325
|
-
}
|
|
326
|
-
messagesBox.setContent(rendered.join('\n'));
|
|
327
|
-
messagesBox.setScrollPerc(100);
|
|
328
|
-
updateStatus();
|
|
329
|
-
screen.render();
|
|
330
|
-
}
|
|
331
|
-
function updateStatus() {
|
|
332
|
-
const parts = [];
|
|
333
|
-
parts.push(`model: ${model}`);
|
|
334
|
-
parts.push(`mode: ${currentMode}`);
|
|
335
|
-
if (currentSession?.title)
|
|
336
|
-
parts.push(`session: ${currentSession.title}`);
|
|
337
|
-
parts.push(`tools: ${enableTools ? 'on' : 'off'}`);
|
|
338
|
-
parts.push(`theme: ${themeName}`);
|
|
339
|
-
const cwd = process.cwd();
|
|
340
|
-
statusBar.setContent(`${chalk.gray(cwd)}\n${chalk.gray(parts.join(' | '))}`);
|
|
341
|
-
}
|
|
342
|
-
function updateHeader() {
|
|
343
|
-
header.setContent(` Tab: {green-fg}mode{/green-fg} (${currentMode}) | /help: {green-fg}commands{/green-fg} | {red-fg}q{/red-fg}: quit`);
|
|
344
|
-
}
|
|
345
|
-
function startThinking(label = 'Thinking') {
|
|
346
|
-
if (!showThinking)
|
|
347
|
-
return;
|
|
348
|
-
if (thinkingInterval)
|
|
349
|
-
clearInterval(thinkingInterval);
|
|
350
|
-
thinkingInterval = setInterval(() => {
|
|
351
|
-
const frame = thinkingFrames[thinkingFrame % thinkingFrames.length];
|
|
352
|
-
thinkingFrame += 1;
|
|
353
|
-
thinkingBox.setContent(` {cyan-fg}${label} ${frame}{/cyan-fg}`);
|
|
354
|
-
screen.render();
|
|
355
|
-
}, 120);
|
|
356
|
-
}
|
|
357
|
-
function stopThinking() {
|
|
358
|
-
if (thinkingInterval) {
|
|
359
|
-
clearInterval(thinkingInterval);
|
|
360
|
-
thinkingInterval = null;
|
|
361
|
-
}
|
|
362
|
-
thinkingBox.setContent('');
|
|
363
|
-
}
|
|
364
|
-
function cycleMode() {
|
|
365
|
-
const idx = allModes.indexOf(currentMode);
|
|
366
|
-
const next = allModes[(idx + 1) % allModes.length];
|
|
367
|
-
agent.setModeFromUser(next, 'User pressed Tab to cycle mode');
|
|
368
|
-
currentMode = next;
|
|
369
|
-
updateHeader();
|
|
370
|
-
pushLine({
|
|
371
|
-
role: 'system',
|
|
372
|
-
text: `Mode changed to ${next}`,
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
async function runUserMessage(raw) {
|
|
376
|
-
const trimmed = raw.trim();
|
|
377
|
-
if (!trimmed)
|
|
378
|
-
return;
|
|
379
|
-
// Slash commands
|
|
380
|
-
if (trimmed === '/help') {
|
|
381
|
-
pushLine({
|
|
382
|
-
role: 'system',
|
|
383
|
-
text: 'Commands: /help, /new, /sessions, /models, /themes, /export, /compact, /details, /thinking, /mcp\n' +
|
|
384
|
-
'Use !<cmd> to run shell commands. Tab to cycle modes.',
|
|
385
|
-
});
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
if (trimmed === '/new') {
|
|
389
|
-
currentSession = await sessionManager.createSession({ model, cwd: process.cwd() });
|
|
390
|
-
agent = new EnhancedAgent({
|
|
391
|
-
apiKey: apiKey,
|
|
392
|
-
baseUrl,
|
|
393
|
-
model,
|
|
394
|
-
maxIterations: 150,
|
|
395
|
-
verbose: false,
|
|
396
|
-
});
|
|
397
|
-
lines.length = 0;
|
|
398
|
-
setupAgentHandlers();
|
|
399
|
-
currentMode = agent.getMode();
|
|
400
|
-
pushLine({ role: 'system', text: 'Started new session.' });
|
|
401
|
-
renderMessages();
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
if (trimmed === '/sessions') {
|
|
405
|
-
const sessions = await sessionManager.listSessions();
|
|
406
|
-
if (!sessions.length) {
|
|
407
|
-
pushLine({ role: 'system', text: 'No saved sessions.' });
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
const items = sessions.map((s, i) => `${i + 1}. ${s.title} · ${s.model} · ${s.updated} [${s.id}]`);
|
|
411
|
-
const list = blessed.list({
|
|
412
|
-
parent: screen,
|
|
413
|
-
top: 'center',
|
|
414
|
-
left: 'center',
|
|
415
|
-
width: '70%',
|
|
416
|
-
height: '60%',
|
|
417
|
-
border: 'line',
|
|
418
|
-
label: ' Sessions ',
|
|
419
|
-
keys: true,
|
|
420
|
-
mouse: true,
|
|
421
|
-
items,
|
|
422
|
-
style: {
|
|
423
|
-
selected: { bg: 'blue', fg: 'white' },
|
|
424
|
-
},
|
|
425
|
-
});
|
|
426
|
-
list.focus();
|
|
427
|
-
list.key(['escape', 'q'], () => {
|
|
428
|
-
list.destroy();
|
|
429
|
-
input.focus();
|
|
430
|
-
screen.render();
|
|
431
|
-
});
|
|
432
|
-
list.on('select', async (item, index) => {
|
|
433
|
-
const session = sessions[index];
|
|
434
|
-
const loaded = await sessionManager.loadSession(session.id);
|
|
435
|
-
if (loaded) {
|
|
436
|
-
currentSession = loaded;
|
|
437
|
-
agent.setMessages(loaded.messages || []);
|
|
438
|
-
lines.length = 0;
|
|
439
|
-
pushLine({ role: 'system', text: `Switched to session: ${loaded.title}` });
|
|
440
|
-
renderMessages();
|
|
441
|
-
}
|
|
442
|
-
else {
|
|
443
|
-
pushLine({ role: 'system', text: `Failed to load session ${session.id}` });
|
|
444
|
-
}
|
|
445
|
-
list.destroy();
|
|
446
|
-
input.focus();
|
|
447
|
-
screen.render();
|
|
448
|
-
});
|
|
449
|
-
screen.render();
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
if (trimmed === '/models') {
|
|
453
|
-
const current = model;
|
|
454
|
-
const candidates = [
|
|
455
|
-
current,
|
|
456
|
-
'claude-sonnet-4-5-20250929',
|
|
457
|
-
'claude-opus-4-5-20251101',
|
|
458
|
-
'claude-haiku-4-5-20251015',
|
|
459
|
-
];
|
|
460
|
-
const unique = Array.from(new Set(candidates));
|
|
461
|
-
const list = blessed.list({
|
|
462
|
-
parent: screen,
|
|
463
|
-
top: 'center',
|
|
464
|
-
left: 'center',
|
|
465
|
-
width: '60%',
|
|
466
|
-
height: '50%',
|
|
467
|
-
border: 'line',
|
|
468
|
-
label: ' Models ',
|
|
469
|
-
keys: true,
|
|
470
|
-
mouse: true,
|
|
471
|
-
items: unique.map(m => (m === current ? `${m} (current)` : m)),
|
|
472
|
-
style: {
|
|
473
|
-
selected: { bg: 'blue', fg: 'white' },
|
|
474
|
-
},
|
|
475
|
-
});
|
|
476
|
-
list.focus();
|
|
477
|
-
list.key(['escape', 'q'], () => {
|
|
478
|
-
list.destroy();
|
|
479
|
-
input.focus();
|
|
480
|
-
screen.render();
|
|
481
|
-
});
|
|
482
|
-
list.on('select', async (item) => {
|
|
483
|
-
const text = item.getText().replace(' (current)', '');
|
|
484
|
-
config.set('model', text);
|
|
485
|
-
pushLine({ role: 'system', text: `Model set to ${text}. Restart chat to apply.` });
|
|
486
|
-
list.destroy();
|
|
487
|
-
input.focus();
|
|
488
|
-
screen.render();
|
|
489
|
-
});
|
|
490
|
-
screen.render();
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
if (trimmed === '/themes') {
|
|
494
|
-
const current = themeName;
|
|
495
|
-
const list = blessed.list({
|
|
496
|
-
parent: screen,
|
|
497
|
-
top: 'center',
|
|
498
|
-
left: 'center',
|
|
499
|
-
width: '60%',
|
|
500
|
-
height: '50%',
|
|
501
|
-
border: 'line',
|
|
502
|
-
label: ' Themes ',
|
|
503
|
-
keys: true,
|
|
504
|
-
mouse: true,
|
|
505
|
-
items: THEME_NAMES.map((name) => (name === current ? `${name} (current)` : name)),
|
|
506
|
-
style: {
|
|
507
|
-
selected: { bg: 'blue', fg: 'white' },
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
list.focus();
|
|
511
|
-
list.key(['escape', 'q'], () => {
|
|
512
|
-
list.destroy();
|
|
513
|
-
input.focus();
|
|
514
|
-
screen.render();
|
|
515
|
-
});
|
|
516
|
-
list.on('select', item => {
|
|
517
|
-
const text = item.getText().replace(' (current)', '');
|
|
518
|
-
config.set('theme', text);
|
|
519
|
-
pushLine({ role: 'system', text: `Theme set to ${text}. Restart chat to apply.` });
|
|
520
|
-
list.destroy();
|
|
521
|
-
input.focus();
|
|
522
|
-
screen.render();
|
|
523
|
-
});
|
|
524
|
-
screen.render();
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
if (trimmed === '/export') {
|
|
528
|
-
const session = {
|
|
529
|
-
...currentSession,
|
|
530
|
-
messages: agent.getMessages(),
|
|
531
|
-
};
|
|
532
|
-
const markdown = exportSessionToMarkdown(session);
|
|
533
|
-
const exportsDir = path.join(config['getConfigPath'], '..', 'sessions');
|
|
534
|
-
const fileName = `${session.id}.md`;
|
|
535
|
-
const fullPath = path.join(exportsDir, fileName);
|
|
536
|
-
await fs.mkdir(exportsDir, { recursive: true });
|
|
537
|
-
await fs.writeFile(fullPath, markdown, 'utf-8');
|
|
538
|
-
pushLine({ role: 'system', text: `Session exported to ${fullPath}` });
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
if (trimmed === '/compact') {
|
|
542
|
-
const messages = agent.getMessages();
|
|
543
|
-
if (messages.length <= 10) {
|
|
544
|
-
pushLine({ role: 'system', text: 'Conversation is short; no compaction needed.' });
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
const preserved = messages.slice(-6);
|
|
548
|
-
const summaryMessage = {
|
|
549
|
-
role: 'assistant',
|
|
550
|
-
content: 'Earlier conversation has been compacted to save context. Key details from the last messages are preserved.',
|
|
551
|
-
};
|
|
552
|
-
const compacted = [summaryMessage, ...preserved];
|
|
553
|
-
agent.setMessages(compacted);
|
|
554
|
-
await sessionManager.saveMessagesAndStats({
|
|
555
|
-
id: currentSession.id,
|
|
556
|
-
messages: compacted,
|
|
557
|
-
stats: agent.getStats(),
|
|
558
|
-
});
|
|
559
|
-
lines.length = 0;
|
|
560
|
-
pushLine({ role: 'system', text: 'Conversation compacted.' });
|
|
561
|
-
renderMessages();
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (trimmed === '/details') {
|
|
565
|
-
showDetails = !showDetails;
|
|
566
|
-
config.set('showDetails', showDetails);
|
|
567
|
-
pushLine({ role: 'system', text: `Details ${showDetails ? 'enabled' : 'disabled'}.` });
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
if (trimmed === '/thinking') {
|
|
571
|
-
showThinking = !showThinking;
|
|
572
|
-
config.set('showThinking', showThinking);
|
|
573
|
-
if (!showThinking)
|
|
574
|
-
stopThinking();
|
|
575
|
-
pushLine({ role: 'system', text: `Thinking display ${showThinking ? 'enabled' : 'disabled'}.` });
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (trimmed === '/mcp') {
|
|
579
|
-
const connectedServers = mcpClientManager.getConnectedServers();
|
|
580
|
-
if (!connectedServers.length) {
|
|
581
|
-
pushLine({
|
|
582
|
-
role: 'system',
|
|
583
|
-
text: 'No MCP servers connected. Configure with: xibecode config --add-mcp-server or xibecode mcp add',
|
|
584
|
-
});
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
587
|
-
const serverLines = [];
|
|
588
|
-
for (const serverName of connectedServers) {
|
|
589
|
-
const serverTools = mcpClientManager.getAvailableTools().filter(t => t.serverName === serverName);
|
|
590
|
-
const serverResources = mcpClientManager.getAvailableResources().filter(r => r.serverName === serverName);
|
|
591
|
-
const serverPrompts = mcpClientManager.getAvailablePrompts().filter(p => p.serverName === serverName);
|
|
592
|
-
serverLines.push(`${serverName} — tools: ${serverTools.length}, resources: ${serverResources.length}, prompts: ${serverPrompts.length}`);
|
|
593
|
-
}
|
|
594
|
-
pushLine({ role: 'system', text: serverLines.join('\n') });
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
if (trimmed.startsWith('@')) {
|
|
598
|
-
await handleAtPathFuzzy(trimmed);
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
if (trimmed.startsWith('!')) {
|
|
602
|
-
await handleShellBang(trimmed);
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
// Regular user message
|
|
606
|
-
lines.push({ role: 'user', text: trimmed });
|
|
607
|
-
renderMessages();
|
|
608
|
-
try {
|
|
609
|
-
const tools = enableTools ? toolExecutor.getTools() : [];
|
|
610
|
-
await agent.run(trimmed, tools, toolExecutor);
|
|
611
|
-
const stats = agent.getStats();
|
|
612
|
-
await sessionManager.saveMessagesAndStats({
|
|
613
|
-
id: currentSession.id,
|
|
614
|
-
messages: agent.getMessages(),
|
|
615
|
-
stats,
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
catch (error) {
|
|
619
|
-
pushLine({ role: 'system', text: `Error: ${error.message || String(error)}` });
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
async function handleShellBang(inputText) {
|
|
623
|
-
const cmd = inputText.slice(1).trim();
|
|
624
|
-
if (!cmd) {
|
|
625
|
-
pushLine({ role: 'system', text: 'No command provided after \"!\". Example: !ls -la' });
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
|
-
pushLine({ role: 'system', text: `Running shell command: ${cmd}` });
|
|
629
|
-
const result = await toolExecutor.execute('run_command', { command: cmd, cwd: process.cwd(), timeout: 300 });
|
|
630
|
-
const stdout = result.stdout || '';
|
|
631
|
-
const stderr = result.stderr || '';
|
|
632
|
-
if (stdout) {
|
|
633
|
-
pushLine({ role: 'system', text: stdout });
|
|
634
|
-
}
|
|
635
|
-
if (stderr) {
|
|
636
|
-
pushLine({ role: 'system', text: `STDERR:\n${stderr}` });
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
async function handleAtPathFuzzy(raw) {
|
|
640
|
-
const inputText = raw.trim().slice(1).trim();
|
|
641
|
-
const pattern = inputText ? `**/*${inputText}*` : '**/*';
|
|
642
|
-
try {
|
|
643
|
-
const files = await contextManager.searchFiles(pattern, { maxResults: 100 });
|
|
644
|
-
if (!files.length) {
|
|
645
|
-
pushLine({ role: 'system', text: `No matches for pattern ${pattern}` });
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
const text = files.map(f => `- ${f}`).join('\n');
|
|
649
|
-
pushLine({ role: 'system', text: `Matches for ${pattern}:\n${text}` });
|
|
650
|
-
}
|
|
651
|
-
catch (error) {
|
|
652
|
-
pushLine({ role: 'system', text: `Failed to search files: ${error.message || String(error)}` });
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
function setupInput() {
|
|
656
|
-
// Slash and @ suggestion popups
|
|
657
|
-
let slashList = null;
|
|
658
|
-
let atList = null;
|
|
659
|
-
function closeSlashList() {
|
|
660
|
-
if (slashList) {
|
|
661
|
-
slashList.destroy();
|
|
662
|
-
slashList = null;
|
|
663
|
-
screen.render();
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
function closeAtList() {
|
|
667
|
-
if (atList) {
|
|
668
|
-
atList.destroy();
|
|
669
|
-
atList = null;
|
|
670
|
-
screen.render();
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
const slashCommands = [
|
|
674
|
-
'/help',
|
|
675
|
-
'/new',
|
|
676
|
-
'/sessions',
|
|
677
|
-
'/models',
|
|
678
|
-
'/themes',
|
|
679
|
-
'/export',
|
|
680
|
-
'/compact',
|
|
681
|
-
'/details',
|
|
682
|
-
'/thinking',
|
|
683
|
-
'/mcp',
|
|
684
|
-
];
|
|
685
|
-
// Use change event instead of keypress to avoid interfering with typing
|
|
686
|
-
input.on('change', async () => {
|
|
687
|
-
const current = (input.getValue() || '');
|
|
688
|
-
// Slash suggestions only when input is exactly "/"
|
|
689
|
-
if (current === '/') {
|
|
690
|
-
if (!slashList) {
|
|
691
|
-
slashList = blessed.list({
|
|
692
|
-
parent: screen,
|
|
693
|
-
bottom: 6,
|
|
694
|
-
left: 2,
|
|
695
|
-
width: 24,
|
|
696
|
-
height: 10,
|
|
697
|
-
border: 'line',
|
|
698
|
-
label: ' Commands ',
|
|
699
|
-
keys: true,
|
|
700
|
-
mouse: true,
|
|
701
|
-
items: slashCommands,
|
|
702
|
-
style: {
|
|
703
|
-
selected: { bg: 'blue', fg: 'white' },
|
|
704
|
-
},
|
|
705
|
-
});
|
|
706
|
-
slashList.focus();
|
|
707
|
-
slashList.key(['up', 'k'], () => {
|
|
708
|
-
slashList?.up(1);
|
|
709
|
-
screen.render();
|
|
710
|
-
});
|
|
711
|
-
slashList.key(['down', 'j'], () => {
|
|
712
|
-
slashList?.down(1);
|
|
713
|
-
screen.render();
|
|
714
|
-
});
|
|
715
|
-
slashList.on('select', item => {
|
|
716
|
-
const text = item.getText();
|
|
717
|
-
input.setValue(text + ' ');
|
|
718
|
-
closeSlashList();
|
|
719
|
-
input.focus();
|
|
720
|
-
screen.render();
|
|
721
|
-
});
|
|
722
|
-
slashList.key(['escape', 'q'], () => {
|
|
723
|
-
closeSlashList();
|
|
724
|
-
input.focus();
|
|
725
|
-
});
|
|
726
|
-
screen.render();
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
else {
|
|
730
|
-
closeSlashList();
|
|
731
|
-
}
|
|
732
|
-
// File suggestions when starting with '@'
|
|
733
|
-
if (current.startsWith('@')) {
|
|
734
|
-
const patternText = current.slice(1).trim();
|
|
735
|
-
const pattern = patternText ? `**/*${patternText}*` : '**/*';
|
|
736
|
-
try {
|
|
737
|
-
const files = await contextManager.searchFiles(pattern, { maxResults: 30 });
|
|
738
|
-
if (!files.length) {
|
|
739
|
-
closeAtList();
|
|
740
|
-
}
|
|
741
|
-
else {
|
|
742
|
-
if (!atList) {
|
|
743
|
-
atList = blessed.list({
|
|
744
|
-
parent: screen,
|
|
745
|
-
bottom: 6,
|
|
746
|
-
left: 28,
|
|
747
|
-
width: 40,
|
|
748
|
-
height: 12,
|
|
749
|
-
border: 'line',
|
|
750
|
-
label: ' Files ',
|
|
751
|
-
keys: true,
|
|
752
|
-
mouse: true,
|
|
753
|
-
style: {
|
|
754
|
-
selected: { bg: 'blue', fg: 'white' },
|
|
755
|
-
},
|
|
756
|
-
});
|
|
757
|
-
atList.key(['up', 'k'], () => {
|
|
758
|
-
atList?.up(1);
|
|
759
|
-
screen.render();
|
|
760
|
-
});
|
|
761
|
-
atList.key(['down', 'j'], () => {
|
|
762
|
-
atList?.down(1);
|
|
763
|
-
screen.render();
|
|
764
|
-
});
|
|
765
|
-
atList.on('select', item => {
|
|
766
|
-
const text = item.getText();
|
|
767
|
-
input.setValue('@' + text);
|
|
768
|
-
closeAtList();
|
|
769
|
-
input.focus();
|
|
770
|
-
screen.render();
|
|
771
|
-
});
|
|
772
|
-
atList.key(['escape', 'q'], () => {
|
|
773
|
-
closeAtList();
|
|
774
|
-
input.focus();
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
atList.setItems(files);
|
|
778
|
-
screen.render();
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
catch {
|
|
782
|
-
// ignore search errors in suggestions
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
closeAtList();
|
|
787
|
-
}
|
|
788
|
-
});
|
|
789
|
-
input.on('submit', async (value) => {
|
|
790
|
-
input.clearValue();
|
|
791
|
-
screen.render();
|
|
792
|
-
await runUserMessage(value);
|
|
793
|
-
input.focus();
|
|
794
|
-
});
|
|
795
|
-
input.key(['C-c', 'escape'], () => {
|
|
796
|
-
shutdown();
|
|
797
|
-
});
|
|
798
|
-
// Tab should work even when textbox is focused
|
|
799
|
-
input.key(['tab'], () => {
|
|
800
|
-
cycleMode();
|
|
801
|
-
});
|
|
802
|
-
screen.key(['tab'], () => {
|
|
803
|
-
cycleMode();
|
|
804
|
-
});
|
|
805
|
-
screen.key(['C-n'], async () => {
|
|
806
|
-
await runUserMessage('/new');
|
|
807
|
-
});
|
|
808
|
-
screen.key(['q', 'C-c'], () => {
|
|
809
|
-
shutdown();
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
async function shutdown() {
|
|
813
|
-
try {
|
|
814
|
-
if (serverNames.length > 0) {
|
|
815
|
-
await mcpClientManager.disconnectAll();
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
finally {
|
|
819
|
-
screen.destroy();
|
|
820
|
-
process.exit(0);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
function setupAgentHandlers() {
|
|
824
|
-
agent.removeAllListeners('event');
|
|
825
|
-
agent.on('event', (event) => {
|
|
826
|
-
switch (event.type) {
|
|
827
|
-
case 'thinking':
|
|
828
|
-
startThinking('Thinking');
|
|
829
|
-
break;
|
|
830
|
-
case 'stream_start':
|
|
831
|
-
startThinking('Thinking');
|
|
832
|
-
lines.push({ role: 'assistant', text: '' });
|
|
833
|
-
break;
|
|
834
|
-
case 'stream_text': {
|
|
835
|
-
const last = lines[lines.length - 1];
|
|
836
|
-
if (last && last.role === 'assistant') {
|
|
837
|
-
last.text += event.data.text;
|
|
838
|
-
renderMessages();
|
|
839
|
-
}
|
|
840
|
-
break;
|
|
841
|
-
}
|
|
842
|
-
case 'stream_end':
|
|
843
|
-
stopThinking();
|
|
844
|
-
renderMessages();
|
|
845
|
-
break;
|
|
846
|
-
case 'response':
|
|
847
|
-
stopThinking();
|
|
848
|
-
lines.push({ role: 'assistant', text: event.data.text });
|
|
849
|
-
renderMessages();
|
|
850
|
-
break;
|
|
851
|
-
case 'tool_call':
|
|
852
|
-
if (enableTools) {
|
|
853
|
-
const text = formatToolCall(event.data.name, event.data.input);
|
|
854
|
-
lines.push({ role: 'tool', text });
|
|
855
|
-
renderMessages();
|
|
856
|
-
}
|
|
857
|
-
break;
|
|
858
|
-
case 'tool_result':
|
|
859
|
-
if (enableTools) {
|
|
860
|
-
const text = formatToolResult(event.data.name, event.data.result, event.data.success !== false);
|
|
861
|
-
lines.push({ role: 'tool', text });
|
|
862
|
-
renderMessages();
|
|
863
|
-
}
|
|
864
|
-
break;
|
|
865
|
-
case 'mode_changed':
|
|
866
|
-
currentMode = event.data.to;
|
|
867
|
-
updateHeader();
|
|
868
|
-
pushLine({ role: 'system', text: `Mode changed to ${currentMode}` });
|
|
869
|
-
break;
|
|
870
|
-
case 'error':
|
|
871
|
-
stopThinking();
|
|
872
|
-
pushLine({ role: 'system', text: `Error: ${event.data.message || event.data.error}` });
|
|
873
|
-
break;
|
|
874
|
-
case 'warning':
|
|
875
|
-
pushLine({ role: 'system', text: `Warning: ${event.data.message}` });
|
|
876
|
-
break;
|
|
877
|
-
}
|
|
878
|
-
});
|
|
879
|
-
}
|
|
880
|
-
setupAgentHandlers();
|
|
881
|
-
setupInput();
|
|
882
|
-
input.focus();
|
|
883
|
-
updateHeader();
|
|
884
|
-
updateStatus();
|
|
885
|
-
screen.render();
|
|
886
|
-
}
|
|
887
|
-
//# sourceMappingURL=blessed-chat.js.map
|