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.
Files changed (288) hide show
  1. package/dist/commands/chat.d.ts +0 -1
  2. package/dist/commands/chat.d.ts.map +1 -1
  3. package/dist/commands/chat.js +10 -7
  4. package/dist/commands/chat.js.map +1 -1
  5. package/dist/commands/config.d.ts.map +1 -1
  6. package/dist/commands/config.js +5 -3
  7. package/dist/commands/config.js.map +1 -1
  8. package/dist/commands/diagnostics.js +1 -1
  9. package/dist/commands/diagnostics.js.map +1 -1
  10. package/dist/commands/mcp.js +1 -1
  11. package/dist/commands/mcp.js.map +1 -1
  12. package/dist/commands/resume.js +1 -1
  13. package/dist/commands/resume.js.map +1 -1
  14. package/dist/commands/run-pr.d.ts.map +1 -1
  15. package/dist/commands/run-pr.js +13 -10
  16. package/dist/commands/run-pr.js.map +1 -1
  17. package/dist/commands/run.d.ts.map +1 -1
  18. package/dist/commands/run.js +17 -14
  19. package/dist/commands/run.js.map +1 -1
  20. package/dist/commands/skills.d.ts.map +1 -1
  21. package/dist/commands/skills.js +3 -2
  22. package/dist/commands/skills.js.map +1 -1
  23. package/dist/components/AssistantMarkdown.js +1 -1
  24. package/dist/components/AssistantMarkdown.js.map +1 -1
  25. package/dist/index.js +2 -39
  26. package/dist/index.js.map +1 -1
  27. package/dist/ui/claude-style-chat.d.ts.map +1 -1
  28. package/dist/ui/claude-style-chat.js +15 -11
  29. package/dist/ui/claude-style-chat.js.map +1 -1
  30. package/dist/utils/built-in-skills-dir.d.ts +7 -0
  31. package/dist/utils/built-in-skills-dir.d.ts.map +1 -0
  32. package/dist/utils/built-in-skills-dir.js +11 -0
  33. package/dist/utils/built-in-skills-dir.js.map +1 -0
  34. package/dist/utils/config.d.ts +2 -119
  35. package/dist/utils/config.d.ts.map +1 -1
  36. package/dist/utils/config.js +3 -88
  37. package/dist/utils/config.js.map +1 -1
  38. package/package.json +11 -26
  39. package/dist/commands/punycode.d.ts +0 -5
  40. package/dist/commands/punycode.d.ts.map +0 -1
  41. package/dist/commands/punycode.js +0 -48
  42. package/dist/commands/punycode.js.map +0 -1
  43. package/dist/commands/tui.d.ts +0 -9
  44. package/dist/commands/tui.d.ts.map +0 -1
  45. package/dist/commands/tui.js +0 -83
  46. package/dist/commands/tui.js.map +0 -1
  47. package/dist/core/agent-tool-policies.d.ts +0 -5
  48. package/dist/core/agent-tool-policies.d.ts.map +0 -1
  49. package/dist/core/agent-tool-policies.js +0 -18
  50. package/dist/core/agent-tool-policies.js.map +0 -1
  51. package/dist/core/agent.d.ts +0 -181
  52. package/dist/core/agent.d.ts.map +0 -1
  53. package/dist/core/agent.js +0 -1777
  54. package/dist/core/agent.js.map +0 -1
  55. package/dist/core/background-agent.d.ts +0 -23
  56. package/dist/core/background-agent.d.ts.map +0 -1
  57. package/dist/core/background-agent.js +0 -175
  58. package/dist/core/background-agent.js.map +0 -1
  59. package/dist/core/code-graph.d.ts +0 -18
  60. package/dist/core/code-graph.d.ts.map +0 -1
  61. package/dist/core/code-graph.js +0 -105
  62. package/dist/core/code-graph.js.map +0 -1
  63. package/dist/core/conflict-solver.d.ts +0 -26
  64. package/dist/core/conflict-solver.d.ts.map +0 -1
  65. package/dist/core/conflict-solver.js +0 -108
  66. package/dist/core/conflict-solver.js.map +0 -1
  67. package/dist/core/context-compactor.d.ts +0 -10
  68. package/dist/core/context-compactor.d.ts.map +0 -1
  69. package/dist/core/context-compactor.js +0 -158
  70. package/dist/core/context-compactor.js.map +0 -1
  71. package/dist/core/context-pruner.d.ts +0 -19
  72. package/dist/core/context-pruner.d.ts.map +0 -1
  73. package/dist/core/context-pruner.js +0 -103
  74. package/dist/core/context-pruner.js.map +0 -1
  75. package/dist/core/context.d.ts +0 -82
  76. package/dist/core/context.d.ts.map +0 -1
  77. package/dist/core/context.js +0 -273
  78. package/dist/core/context.js.map +0 -1
  79. package/dist/core/conversation-recovery.d.ts +0 -9
  80. package/dist/core/conversation-recovery.d.ts.map +0 -1
  81. package/dist/core/conversation-recovery.js +0 -15
  82. package/dist/core/conversation-recovery.js.map +0 -1
  83. package/dist/core/debug-workflow.d.ts +0 -9
  84. package/dist/core/debug-workflow.d.ts.map +0 -1
  85. package/dist/core/debug-workflow.js +0 -19
  86. package/dist/core/debug-workflow.js.map +0 -1
  87. package/dist/core/docs-scraper.d.ts +0 -40
  88. package/dist/core/docs-scraper.d.ts.map +0 -1
  89. package/dist/core/docs-scraper.js +0 -386
  90. package/dist/core/docs-scraper.js.map +0 -1
  91. package/dist/core/editor.d.ts +0 -87
  92. package/dist/core/editor.d.ts.map +0 -1
  93. package/dist/core/editor.js +0 -377
  94. package/dist/core/editor.js.map +0 -1
  95. package/dist/core/export.d.ts +0 -11
  96. package/dist/core/export.d.ts.map +0 -1
  97. package/dist/core/export.js +0 -54
  98. package/dist/core/export.js.map +0 -1
  99. package/dist/core/history-manager.d.ts +0 -75
  100. package/dist/core/history-manager.d.ts.map +0 -1
  101. package/dist/core/history-manager.js +0 -146
  102. package/dist/core/history-manager.js.map +0 -1
  103. package/dist/core/marketplace-client.d.ts +0 -52
  104. package/dist/core/marketplace-client.d.ts.map +0 -1
  105. package/dist/core/marketplace-client.js +0 -71
  106. package/dist/core/marketplace-client.js.map +0 -1
  107. package/dist/core/mcp/mcp-config.d.ts +0 -10
  108. package/dist/core/mcp/mcp-config.d.ts.map +0 -1
  109. package/dist/core/mcp/mcp-config.js +0 -70
  110. package/dist/core/mcp/mcp-config.js.map +0 -1
  111. package/dist/core/mcp/mcp-policy.d.ts +0 -17
  112. package/dist/core/mcp/mcp-policy.d.ts.map +0 -1
  113. package/dist/core/mcp/mcp-policy.js +0 -56
  114. package/dist/core/mcp/mcp-policy.js.map +0 -1
  115. package/dist/core/mcp/oauth-flow.d.ts +0 -30
  116. package/dist/core/mcp/oauth-flow.d.ts.map +0 -1
  117. package/dist/core/mcp/oauth-flow.js +0 -230
  118. package/dist/core/mcp/oauth-flow.js.map +0 -1
  119. package/dist/core/mcp/oauth-store.d.ts +0 -13
  120. package/dist/core/mcp/oauth-store.d.ts.map +0 -1
  121. package/dist/core/mcp/oauth-store.js +0 -68
  122. package/dist/core/mcp/oauth-store.js.map +0 -1
  123. package/dist/core/mcp/resolve-mcp-servers.d.ts +0 -16
  124. package/dist/core/mcp/resolve-mcp-servers.d.ts.map +0 -1
  125. package/dist/core/mcp/resolve-mcp-servers.js +0 -83
  126. package/dist/core/mcp/resolve-mcp-servers.js.map +0 -1
  127. package/dist/core/mcp-client.d.ts +0 -99
  128. package/dist/core/mcp-client.d.ts.map +0 -1
  129. package/dist/core/mcp-client.js +0 -315
  130. package/dist/core/mcp-client.js.map +0 -1
  131. package/dist/core/memory-promotions.d.ts +0 -15
  132. package/dist/core/memory-promotions.d.ts.map +0 -1
  133. package/dist/core/memory-promotions.js +0 -38
  134. package/dist/core/memory-promotions.js.map +0 -1
  135. package/dist/core/memory.d.ts +0 -32
  136. package/dist/core/memory.d.ts.map +0 -1
  137. package/dist/core/memory.js +0 -121
  138. package/dist/core/memory.js.map +0 -1
  139. package/dist/core/modes.d.ts +0 -432
  140. package/dist/core/modes.d.ts.map +0 -1
  141. package/dist/core/modes.js +0 -1088
  142. package/dist/core/modes.js.map +0 -1
  143. package/dist/core/pattern-miner.d.ts +0 -43
  144. package/dist/core/pattern-miner.d.ts.map +0 -1
  145. package/dist/core/pattern-miner.js +0 -123
  146. package/dist/core/pattern-miner.js.map +0 -1
  147. package/dist/core/permission-store.d.ts +0 -15
  148. package/dist/core/permission-store.d.ts.map +0 -1
  149. package/dist/core/permission-store.js +0 -30
  150. package/dist/core/permission-store.js.map +0 -1
  151. package/dist/core/permissions.d.ts +0 -33
  152. package/dist/core/permissions.d.ts.map +0 -1
  153. package/dist/core/permissions.js +0 -141
  154. package/dist/core/permissions.js.map +0 -1
  155. package/dist/core/plan-artifacts.d.ts +0 -10
  156. package/dist/core/plan-artifacts.d.ts.map +0 -1
  157. package/dist/core/plan-artifacts.js +0 -60
  158. package/dist/core/plan-artifacts.js.map +0 -1
  159. package/dist/core/plan-session.d.ts +0 -25
  160. package/dist/core/plan-session.d.ts.map +0 -1
  161. package/dist/core/plan-session.js +0 -99
  162. package/dist/core/plan-session.js.map +0 -1
  163. package/dist/core/planMode.d.ts +0 -51
  164. package/dist/core/planMode.d.ts.map +0 -1
  165. package/dist/core/planMode.js +0 -245
  166. package/dist/core/planMode.js.map +0 -1
  167. package/dist/core/plugins.d.ts +0 -96
  168. package/dist/core/plugins.d.ts.map +0 -1
  169. package/dist/core/plugins.js +0 -202
  170. package/dist/core/plugins.js.map +0 -1
  171. package/dist/core/session-bridge.d.ts +0 -128
  172. package/dist/core/session-bridge.d.ts.map +0 -1
  173. package/dist/core/session-bridge.js +0 -328
  174. package/dist/core/session-bridge.js.map +0 -1
  175. package/dist/core/session-manager.d.ts +0 -80
  176. package/dist/core/session-manager.d.ts.map +0 -1
  177. package/dist/core/session-manager.js +0 -166
  178. package/dist/core/session-manager.js.map +0 -1
  179. package/dist/core/session-memory.d.ts +0 -45
  180. package/dist/core/session-memory.d.ts.map +0 -1
  181. package/dist/core/session-memory.js +0 -103
  182. package/dist/core/session-memory.js.map +0 -1
  183. package/dist/core/skill-selection.d.ts +0 -36
  184. package/dist/core/skill-selection.d.ts.map +0 -1
  185. package/dist/core/skill-selection.js +0 -172
  186. package/dist/core/skill-selection.js.map +0 -1
  187. package/dist/core/skills-sh-client.d.ts +0 -19
  188. package/dist/core/skills-sh-client.d.ts.map +0 -1
  189. package/dist/core/skills-sh-client.js +0 -75
  190. package/dist/core/skills-sh-client.js.map +0 -1
  191. package/dist/core/skills.d.ts +0 -97
  192. package/dist/core/skills.d.ts.map +0 -1
  193. package/dist/core/skills.js +0 -339
  194. package/dist/core/skills.js.map +0 -1
  195. package/dist/core/swarm.d.ts +0 -34
  196. package/dist/core/swarm.d.ts.map +0 -1
  197. package/dist/core/swarm.js +0 -111
  198. package/dist/core/swarm.js.map +0 -1
  199. package/dist/core/task-status.d.ts +0 -13
  200. package/dist/core/task-status.d.ts.map +0 -1
  201. package/dist/core/task-status.js +0 -17
  202. package/dist/core/task-status.js.map +0 -1
  203. package/dist/core/tool-orchestrator.d.ts +0 -30
  204. package/dist/core/tool-orchestrator.d.ts.map +0 -1
  205. package/dist/core/tool-orchestrator.js +0 -89
  206. package/dist/core/tool-orchestrator.js.map +0 -1
  207. package/dist/core/tools.d.ts +0 -462
  208. package/dist/core/tools.d.ts.map +0 -1
  209. package/dist/core/tools.js +0 -2916
  210. package/dist/core/tools.js.map +0 -1
  211. package/dist/core/transcript-cleanup.d.ts +0 -8
  212. package/dist/core/transcript-cleanup.d.ts.map +0 -1
  213. package/dist/core/transcript-cleanup.js +0 -52
  214. package/dist/core/transcript-cleanup.js.map +0 -1
  215. package/dist/core/visual-feedback.d.ts +0 -20
  216. package/dist/core/visual-feedback.d.ts.map +0 -1
  217. package/dist/core/visual-feedback.js +0 -117
  218. package/dist/core/visual-feedback.js.map +0 -1
  219. package/dist/tools/browser.d.ts +0 -120
  220. package/dist/tools/browser.d.ts.map +0 -1
  221. package/dist/tools/browser.js +0 -439
  222. package/dist/tools/browser.js.map +0 -1
  223. package/dist/tools/test-generator.d.ts +0 -157
  224. package/dist/tools/test-generator.d.ts.map +0 -1
  225. package/dist/tools/test-generator.js +0 -893
  226. package/dist/tools/test-generator.js.map +0 -1
  227. package/dist/tui/InkApp.d.ts +0 -21
  228. package/dist/tui/InkApp.d.ts.map +0 -1
  229. package/dist/tui/InkApp.js +0 -146
  230. package/dist/tui/InkApp.js.map +0 -1
  231. package/dist/tui/MarkdownMessage.d.ts +0 -16
  232. package/dist/tui/MarkdownMessage.d.ts.map +0 -1
  233. package/dist/tui/MarkdownMessage.js +0 -63
  234. package/dist/tui/MarkdownMessage.js.map +0 -1
  235. package/dist/tui/blessed-chat.d.ts +0 -9
  236. package/dist/tui/blessed-chat.d.ts.map +0 -1
  237. package/dist/tui/blessed-chat.js +0 -887
  238. package/dist/tui/blessed-chat.js.map +0 -1
  239. package/dist/tui/markdown-to-blessed.d.ts +0 -6
  240. package/dist/tui/markdown-to-blessed.d.ts.map +0 -1
  241. package/dist/tui/markdown-to-blessed.js +0 -26
  242. package/dist/tui/markdown-to-blessed.js.map +0 -1
  243. package/dist/ui/ink/App.d.ts +0 -25
  244. package/dist/ui/ink/App.d.ts.map +0 -1
  245. package/dist/ui/ink/App.js +0 -372
  246. package/dist/ui/ink/App.js.map +0 -1
  247. package/dist/utils/at-references.d.ts +0 -14
  248. package/dist/utils/at-references.d.ts.map +0 -1
  249. package/dist/utils/at-references.js +0 -47
  250. package/dist/utils/at-references.js.map +0 -1
  251. package/dist/utils/auto-memory.d.ts +0 -24
  252. package/dist/utils/auto-memory.d.ts.map +0 -1
  253. package/dist/utils/auto-memory.js +0 -153
  254. package/dist/utils/auto-memory.js.map +0 -1
  255. package/dist/utils/git.d.ts +0 -89
  256. package/dist/utils/git.d.ts.map +0 -1
  257. package/dist/utils/git.js +0 -444
  258. package/dist/utils/git.js.map +0 -1
  259. package/dist/utils/mcp-servers-file.d.ts +0 -46
  260. package/dist/utils/mcp-servers-file.d.ts.map +0 -1
  261. package/dist/utils/mcp-servers-file.js +0 -212
  262. package/dist/utils/mcp-servers-file.js.map +0 -1
  263. package/dist/utils/safety.d.ts +0 -60
  264. package/dist/utils/safety.d.ts.map +0 -1
  265. package/dist/utils/safety.js +0 -254
  266. package/dist/utils/safety.js.map +0 -1
  267. package/dist/utils/smithery.d.ts +0 -25
  268. package/dist/utils/smithery.d.ts.map +0 -1
  269. package/dist/utils/smithery.js +0 -50
  270. package/dist/utils/smithery.js.map +0 -1
  271. package/dist/utils/testRunner.d.ts +0 -44
  272. package/dist/utils/testRunner.d.ts.map +0 -1
  273. package/dist/utils/testRunner.js +0 -270
  274. package/dist/utils/testRunner.js.map +0 -1
  275. package/dist/webui/server.d.ts +0 -99
  276. package/dist/webui/server.d.ts.map +0 -1
  277. package/dist/webui/server.js +0 -2619
  278. package/dist/webui/server.js.map +0 -1
  279. package/webui-dist/assets/index-CSla6Lzy.css +0 -32
  280. package/webui-dist/assets/index-G_Z4gzPy.js +0 -457
  281. package/webui-dist/assets/index-G_Z4gzPy.js.map +0 -1
  282. package/webui-dist/assets/xterm-Da5jL1MD.js +0 -10
  283. package/webui-dist/assets/xterm-Da5jL1MD.js.map +0 -1
  284. package/webui-dist/assets/xterm-addon-fit-CMeqLIvm.js +0 -2
  285. package/webui-dist/assets/xterm-addon-fit-CMeqLIvm.js.map +0 -1
  286. package/webui-dist/assets/xterm-addon-web-links-D6m8jNVE.js +0 -2
  287. package/webui-dist/assets/xterm-addon-web-links-D6m8jNVE.js.map +0 -1
  288. package/webui-dist/index.html +0 -15
@@ -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