@abacus-ai/cli 2.0.0-canary.1 → 2.0.0-canary.11

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 (198) hide show
  1. package/README.md +25 -0
  2. package/dist/index.mjs +466 -438
  3. package/package.json +4 -1
  4. package/.oxlintrc.json +0 -8
  5. package/resources/abacus.ico +0 -0
  6. package/resources/entitlements.plist +0 -9
  7. package/src/__e2e__/README.md +0 -196
  8. package/src/__e2e__/agent-interactions.e2e.test.tsx +0 -61
  9. package/src/__e2e__/cli-commands.e2e.test.tsx +0 -77
  10. package/src/__e2e__/conversation-throttle.e2e.test.ts +0 -453
  11. package/src/__e2e__/conversation.e2e.test.tsx +0 -56
  12. package/src/__e2e__/diff-preview.e2e.test.tsx +0 -3399
  13. package/src/__e2e__/file-creation.e2e.test.tsx +0 -149
  14. package/src/__e2e__/helpers/test-helpers.ts +0 -449
  15. package/src/__e2e__/keyboard-navigation.e2e.test.tsx +0 -34
  16. package/src/__e2e__/llm-models.e2e.test.ts +0 -402
  17. package/src/__e2e__/mcp/mcp-callback-flow.e2e.test.tsx +0 -71
  18. package/src/__e2e__/mcp/mcp-full-app-ui.e2e.test.tsx +0 -167
  19. package/src/__e2e__/mcp/mcp-ui-rendering.e2e.test.tsx +0 -185
  20. package/src/__e2e__/repl.e2e.test.tsx +0 -78
  21. package/src/__e2e__/shell-compatibility.e2e.test.tsx +0 -76
  22. package/src/__e2e__/theme-mcp.e2e.test.tsx +0 -98
  23. package/src/__e2e__/tool-permissions.e2e.test.tsx +0 -66
  24. package/src/args.ts +0 -22
  25. package/src/components/__tests__/react-compiler.test.tsx +0 -78
  26. package/src/components/__tests__/status-indicator.test.tsx +0 -403
  27. package/src/components/composer/__tests__/bash-runner.test.tsx +0 -263
  28. package/src/components/composer/agent-mode-indicator.tsx +0 -63
  29. package/src/components/composer/bash-runner.tsx +0 -54
  30. package/src/components/composer/commands/default-commands.tsx +0 -615
  31. package/src/components/composer/commands/handler.tsx +0 -59
  32. package/src/components/composer/commands/picker.tsx +0 -273
  33. package/src/components/composer/commands/registry.ts +0 -233
  34. package/src/components/composer/commands/types.ts +0 -33
  35. package/src/components/composer/context.tsx +0 -88
  36. package/src/components/composer/file-mention-picker.tsx +0 -83
  37. package/src/components/composer/help.tsx +0 -44
  38. package/src/components/composer/index.tsx +0 -1007
  39. package/src/components/composer/mentions.ts +0 -57
  40. package/src/components/composer/message-queue.tsx +0 -70
  41. package/src/components/composer/mode-panel.tsx +0 -35
  42. package/src/components/composer/modes/__tests__/bash-handler.test.tsx +0 -755
  43. package/src/components/composer/modes/__tests__/bash-renderer.test.tsx +0 -1108
  44. package/src/components/composer/modes/bash-handler.tsx +0 -132
  45. package/src/components/composer/modes/bash-renderer.tsx +0 -175
  46. package/src/components/composer/modes/default-handlers.tsx +0 -33
  47. package/src/components/composer/modes/index.ts +0 -41
  48. package/src/components/composer/modes/types.ts +0 -21
  49. package/src/components/composer/persistent-shell.ts +0 -283
  50. package/src/components/composer/process.ts +0 -65
  51. package/src/components/composer/types.ts +0 -9
  52. package/src/components/composer/use-mention-search.ts +0 -68
  53. package/src/components/error-boundry.tsx +0 -60
  54. package/src/components/exit-message.tsx +0 -29
  55. package/src/components/expanded-view.tsx +0 -74
  56. package/src/components/file-completion.tsx +0 -127
  57. package/src/components/header.tsx +0 -47
  58. package/src/components/logo.tsx +0 -37
  59. package/src/components/segments.tsx +0 -356
  60. package/src/components/status-indicator.tsx +0 -306
  61. package/src/components/tool-group-summary.tsx +0 -263
  62. package/src/components/tool-permissions/ask-user-question-permission-ui.tsx +0 -319
  63. package/src/components/tool-permissions/diff-preview.tsx +0 -359
  64. package/src/components/tool-permissions/index.ts +0 -5
  65. package/src/components/tool-permissions/permission-options.tsx +0 -401
  66. package/src/components/tool-permissions/permission-preview-header.tsx +0 -57
  67. package/src/components/tool-permissions/tool-permission-ui.tsx +0 -420
  68. package/src/components/tools/agent/ask-user-question.tsx +0 -107
  69. package/src/components/tools/agent/enter-plan-mode.tsx +0 -55
  70. package/src/components/tools/agent/exit-plan-mode.tsx +0 -83
  71. package/src/components/tools/agent/handoff-to-main.tsx +0 -27
  72. package/src/components/tools/agent/subagent.tsx +0 -37
  73. package/src/components/tools/agent/todo-write.tsx +0 -104
  74. package/src/components/tools/browser/close-tab.tsx +0 -58
  75. package/src/components/tools/browser/computer.tsx +0 -70
  76. package/src/components/tools/browser/get-interactive-elements.tsx +0 -54
  77. package/src/components/tools/browser/get-tab-content.tsx +0 -51
  78. package/src/components/tools/browser/navigate-to.tsx +0 -59
  79. package/src/components/tools/browser/new-tab.tsx +0 -60
  80. package/src/components/tools/browser/perform-action.tsx +0 -63
  81. package/src/components/tools/browser/refresh-tab.tsx +0 -43
  82. package/src/components/tools/browser/switch-tab.tsx +0 -58
  83. package/src/components/tools/filesystem/delete-file.tsx +0 -104
  84. package/src/components/tools/filesystem/edit.tsx +0 -220
  85. package/src/components/tools/filesystem/list-dir.tsx +0 -78
  86. package/src/components/tools/filesystem/read-file.tsx +0 -180
  87. package/src/components/tools/filesystem/upload-image.tsx +0 -76
  88. package/src/components/tools/ide/ide-diagnostics.tsx +0 -62
  89. package/src/components/tools/index.ts +0 -91
  90. package/src/components/tools/mcp/mcp-tool.tsx +0 -158
  91. package/src/components/tools/search/fetch-url.tsx +0 -73
  92. package/src/components/tools/search/file-search.tsx +0 -78
  93. package/src/components/tools/search/grep.tsx +0 -90
  94. package/src/components/tools/search/semantic-search.tsx +0 -66
  95. package/src/components/tools/search/web-search.tsx +0 -71
  96. package/src/components/tools/shared/index.tsx +0 -48
  97. package/src/components/tools/shared/zod-coercion.ts +0 -35
  98. package/src/components/tools/terminal/bash-tool-output.tsx +0 -188
  99. package/src/components/tools/terminal/get-terminal-output.tsx +0 -91
  100. package/src/components/tools/terminal/run-in-terminal.tsx +0 -131
  101. package/src/components/tools/types.ts +0 -16
  102. package/src/components/tools.tsx +0 -68
  103. package/src/components/ui/__tests__/divider.test.tsx +0 -61
  104. package/src/components/ui/__tests__/gradient.test.tsx +0 -125
  105. package/src/components/ui/__tests__/input.test.tsx +0 -166
  106. package/src/components/ui/__tests__/select.test.tsx +0 -273
  107. package/src/components/ui/__tests__/shimmer.test.tsx +0 -99
  108. package/src/components/ui/blinking-indicator.tsx +0 -27
  109. package/src/components/ui/divider.tsx +0 -162
  110. package/src/components/ui/gradient.tsx +0 -56
  111. package/src/components/ui/input.tsx +0 -228
  112. package/src/components/ui/select.tsx +0 -151
  113. package/src/components/ui/shimmer.tsx +0 -76
  114. package/src/context/agent-mode.tsx +0 -95
  115. package/src/context/extension-file.tsx +0 -136
  116. package/src/context/network-activity.tsx +0 -45
  117. package/src/context/notification.tsx +0 -62
  118. package/src/context/shell-size.tsx +0 -49
  119. package/src/context/shell-title.tsx +0 -38
  120. package/src/entrypoints/print-mode.ts +0 -312
  121. package/src/entrypoints/repl.tsx +0 -389
  122. package/src/hooks/use-agent.ts +0 -15
  123. package/src/hooks/use-api-client.ts +0 -1
  124. package/src/hooks/use-available-height.ts +0 -8
  125. package/src/hooks/use-cleanup.ts +0 -29
  126. package/src/hooks/use-interrupt-manager.ts +0 -242
  127. package/src/hooks/use-models.ts +0 -22
  128. package/src/index.ts +0 -217
  129. package/src/lib/__tests__/ansi.test.ts +0 -255
  130. package/src/lib/__tests__/cli.test.ts +0 -122
  131. package/src/lib/__tests__/commands.test.ts +0 -325
  132. package/src/lib/__tests__/constants.test.ts +0 -15
  133. package/src/lib/__tests__/focusables.test.ts +0 -25
  134. package/src/lib/__tests__/fs.test.ts +0 -231
  135. package/src/lib/__tests__/markdown.test.tsx +0 -348
  136. package/src/lib/__tests__/mcpCommandHandler.test.ts +0 -173
  137. package/src/lib/__tests__/mcpManagement.test.ts +0 -38
  138. package/src/lib/__tests__/path-paste.test.ts +0 -144
  139. package/src/lib/__tests__/path.test.ts +0 -300
  140. package/src/lib/__tests__/queries.test.ts +0 -39
  141. package/src/lib/__tests__/standaloneMcpService.test.ts +0 -71
  142. package/src/lib/__tests__/text-buffer.test.ts +0 -328
  143. package/src/lib/__tests__/text-utils.test.ts +0 -32
  144. package/src/lib/__tests__/timing.test.ts +0 -78
  145. package/src/lib/__tests__/utils.test.ts +0 -238
  146. package/src/lib/__tests__/vim-buffer-actions.test.ts +0 -154
  147. package/src/lib/ansi.ts +0 -150
  148. package/src/lib/cli-push-server.ts +0 -112
  149. package/src/lib/cli.ts +0 -44
  150. package/src/lib/clipboard.ts +0 -226
  151. package/src/lib/command-utils.ts +0 -93
  152. package/src/lib/commands.ts +0 -270
  153. package/src/lib/constants.ts +0 -3
  154. package/src/lib/extension-connection.ts +0 -181
  155. package/src/lib/focusables.ts +0 -7
  156. package/src/lib/fs.ts +0 -533
  157. package/src/lib/markdown/code-block.tsx +0 -63
  158. package/src/lib/markdown/index.ts +0 -4
  159. package/src/lib/markdown/link.tsx +0 -19
  160. package/src/lib/markdown/markdown.tsx +0 -372
  161. package/src/lib/markdown/types.ts +0 -15
  162. package/src/lib/mcpCommandHandler.ts +0 -121
  163. package/src/lib/mcpManagement.ts +0 -44
  164. package/src/lib/path-paste.ts +0 -185
  165. package/src/lib/path.ts +0 -179
  166. package/src/lib/queries.ts +0 -15
  167. package/src/lib/standaloneMcpService.ts +0 -688
  168. package/src/lib/status-utils.ts +0 -237
  169. package/src/lib/test-utils.tsx +0 -72
  170. package/src/lib/text-buffer.ts +0 -2415
  171. package/src/lib/text-utils.ts +0 -272
  172. package/src/lib/timing.ts +0 -63
  173. package/src/lib/types.ts +0 -295
  174. package/src/lib/utils.ts +0 -182
  175. package/src/lib/vim-buffer-actions.ts +0 -732
  176. package/src/providers/agent.tsx +0 -1063
  177. package/src/providers/api-client.tsx +0 -43
  178. package/src/services/logger.ts +0 -85
  179. package/src/terminal/detection.ts +0 -187
  180. package/src/terminal/exit.ts +0 -279
  181. package/src/terminal/notification.ts +0 -83
  182. package/src/terminal/progress.ts +0 -201
  183. package/src/terminal/setup.ts +0 -797
  184. package/src/terminal/types.ts +0 -51
  185. package/src/theme/context.tsx +0 -57
  186. package/src/theme/index.ts +0 -4
  187. package/src/theme/themed.tsx +0 -35
  188. package/src/theme/themes.json +0 -546
  189. package/src/theme/types.ts +0 -110
  190. package/src/tools/types.ts +0 -59
  191. package/src/tools/utils/__tests__/zod-coercion.test.ts +0 -33
  192. package/src/tools/utils/tool-ui-components.tsx +0 -649
  193. package/src/tools/utils/zod-coercion.ts +0 -35
  194. package/tsconfig.json +0 -16
  195. package/tsconfig.node.json +0 -29
  196. package/tsconfig.test.json +0 -27
  197. package/tsdown.config.ts +0 -17
  198. package/vitest.config.ts +0 -76
@@ -1,63 +0,0 @@
1
- import { View, Text } from "@codellm/jar";
2
- import { renderMermaidAscii } from "beautiful-mermaid";
3
- import { highlight, supportsLanguage } from "cli-highlight";
4
-
5
- import type { CodeBlockProps } from "./types.js";
6
-
7
- /**
8
- * Check if the language is mermaid
9
- */
10
- function isMermaidLanguage(language: string | undefined): boolean {
11
- if (!language) return false;
12
- const normalizedLang = language.toLowerCase().trim();
13
- return normalizedLang === "mermaid" || normalizedLang === "mmd";
14
- }
15
-
16
- /**
17
- * Try to render mermaid diagram as ASCII art
18
- * Returns null if parsing fails
19
- */
20
- function tryRenderMermaid(content: string): string | null {
21
- try {
22
- return renderMermaidAscii(content);
23
- } catch {
24
- // Fallback to regular code block rendering
25
- return null;
26
- }
27
- }
28
-
29
- export function CodeBlock({ children, language }: CodeBlockProps) {
30
- // Try to render mermaid diagrams as ASCII art
31
- if (isMermaidLanguage(language)) {
32
- const mermaidOutput = tryRenderMermaid(children || "");
33
- if (mermaidOutput !== null) {
34
- return (
35
- <View paddingX={1} flexDirection="row" alignSelf="flex-start">
36
- <Text>{mermaidOutput}</Text>
37
- </View>
38
- );
39
- }
40
- // If mermaid parsing fails, fall through to regular code block rendering
41
- }
42
-
43
- let highlighted = children;
44
-
45
- if (language && supportsLanguage(language)) {
46
- try {
47
- // Use cli-highlight's default theme which works well with terminal colors
48
- highlighted = highlight(children || "", {
49
- language,
50
- });
51
- } catch {
52
- // Fallback to plain text if highlighting fails
53
- }
54
- }
55
-
56
- return (
57
- <View paddingX={1} flexDirection="row" alignSelf="flex-start">
58
- <Text>{highlighted}</Text>
59
- </View>
60
- );
61
- }
62
-
63
- CodeBlock.displayName = "CodeBlock";
@@ -1,4 +0,0 @@
1
- export { Markdown } from "./markdown.js";
2
- export { CodeBlock } from "./code-block.js";
3
- export { Link } from "./link.js";
4
- export type { MarkdownProps, CodeBlockProps, LinkProps } from "./types.js";
@@ -1,19 +0,0 @@
1
- import { Text } from "@codellm/jar";
2
-
3
- import type { LinkProps } from "./types.js";
4
-
5
- import { useTheme } from "../../theme/index.js";
6
-
7
- export function Link({ href, children }: LinkProps) {
8
- const { colors } = useTheme();
9
- // ANSI hyperlink: ESC]8;;url ST text ESC]8;; ST
10
- const open = `\x1b]8;;${href}\x07`;
11
- const close = `\x1b]8;;\x07`;
12
- return (
13
- <Text color={colors.link} underline transform={(s) => `${open}${s}${close}`}>
14
- {children}
15
- </Text>
16
- );
17
- }
18
-
19
- Link.displayName = "Link";
@@ -1,372 +0,0 @@
1
- import { View, Text } from "@codellm/jar";
2
- import { marked, type Token, type Tokens } from "marked";
3
- import { Fragment, useDeferredValue, useMemo } from "react";
4
- import remend from "remend";
5
-
6
- import type { MarkdownProps } from "./types.js";
7
-
8
- import { useTheme } from "../../theme/index.js";
9
- import { CodeBlock } from "./code-block.js";
10
- import { Link } from "./link.js";
11
-
12
- /**
13
- * Helper to extract plain text from tokens for alignment or other purposes.
14
- */
15
- function extractText(token: Token | Tokens.TableCell): string {
16
- if ("text" in token && typeof token.text === "string") {
17
- return token.text;
18
- }
19
- if ("tokens" in token && Array.isArray(token.tokens)) {
20
- return token.tokens.map((t) => extractText(t)).join("");
21
- }
22
- return "";
23
- }
24
-
25
- /**
26
- * Type guard to check if a token has a checked property (for checkbox list items)
27
- */
28
- function hasCheckedProperty(token: Token): token is Token & { checked: boolean } {
29
- return "checked" in token && typeof (token as { checked?: unknown }).checked === "boolean";
30
- }
31
-
32
- /**
33
- * Renders tokens that are guaranteed to be inline-safe (no View components).
34
- */
35
- function renderInlineTokens(
36
- tokens: Token[] | undefined,
37
- colors: ReturnType<typeof useTheme>["colors"],
38
- isDimItalic = false,
39
- ): React.ReactNode {
40
- if (!tokens) return null;
41
-
42
- return tokens.map((token, i) => (
43
- <Fragment key={i}>{renderInlineToken(token, colors, isDimItalic)}</Fragment>
44
- ));
45
- }
46
-
47
- /**
48
- * Renders a single inline-safe token.
49
- */
50
- function renderInlineToken(
51
- token: Token,
52
- colors: ReturnType<typeof useTheme>["colors"],
53
- isDimItalic = false,
54
- ): React.ReactNode {
55
- const wrapWithStyle = (node: React.ReactNode) => {
56
- if (isDimItalic) {
57
- return (
58
- <Text dimColor italic>
59
- {node}
60
- </Text>
61
- );
62
- }
63
- return node;
64
- };
65
-
66
- switch (token.type) {
67
- case "strong": {
68
- return <Text bold>{renderInlineTokens(token.tokens, colors, isDimItalic)}</Text>;
69
- }
70
-
71
- case "em": {
72
- return <Text italic>{renderInlineTokens(token.tokens, colors, isDimItalic)}</Text>;
73
- }
74
-
75
- case "del": {
76
- return <Text strikethrough>{renderInlineTokens(token.tokens, colors, isDimItalic)}</Text>;
77
- }
78
-
79
- case "codespan": {
80
- return wrapWithStyle(
81
- <Text color={colors.primary} dimColor>
82
- {token.text}
83
- </Text>,
84
- );
85
- }
86
-
87
- case "link": {
88
- return wrapWithStyle(<Link href={token.href}>{extractText(token)}</Link>);
89
- }
90
-
91
- case "image": {
92
- const imageToken = token as Tokens.Image;
93
- // Show alt text with image indicator, or URL if no alt
94
- const displayText = imageToken.text || imageToken.href;
95
- return wrapWithStyle(
96
- <Text dimColor>
97
- {displayText} {imageToken.href !== displayText && `(${imageToken.href})`}
98
- </Text>,
99
- );
100
- }
101
-
102
- case "checkbox": {
103
- const isChecked = hasCheckedProperty(token) && token.checked === true;
104
- return wrapWithStyle(<Text color={colors.secondary}>{isChecked ? "[✓]" : "[ ]"}</Text>);
105
- }
106
-
107
- case "text": {
108
- if (token.tokens) {
109
- return renderInlineTokens(token.tokens, colors, isDimItalic);
110
- }
111
- return wrapWithStyle(token.text);
112
- }
113
-
114
- case "br":
115
- return "\n";
116
-
117
- default:
118
- return wrapWithStyle(extractText(token));
119
- }
120
- }
121
-
122
- /**
123
- * Renders tokens that may contain block-level components (View).
124
- * Headings get double newline, paragraphs/code/lists get single.
125
- */
126
- function renderBlockToken(
127
- token: Token,
128
- colors: ReturnType<typeof useTheme>["colors"],
129
- listDepth = 0,
130
- isDimItalic = false,
131
- ): React.ReactNode {
132
- switch (token.type) {
133
- case "heading": {
134
- // Headings get blank line after (double newline in Claude CLI)
135
- return (
136
- <View key={`heading-${token.depth}`} flexDirection="column">
137
- <Text
138
- bold
139
- color={token.depth <= 2 ? colors.primary : colors.secondary}
140
- dimColor={token.depth > 2}
141
- >
142
- {renderInlineTokens(token.tokens, colors, isDimItalic)}
143
- </Text>
144
- </View>
145
- );
146
- }
147
-
148
- case "paragraph": {
149
- // Paragraphs get single newline after (handled by Ink's natural line flow)
150
- return (
151
- <View key="paragraph" flexDirection="column">
152
- <Text>{renderInlineTokens(token.tokens, colors, isDimItalic)}</Text>
153
- </View>
154
- );
155
- }
156
-
157
- case "code": {
158
- // Code blocks get single newline after
159
- return (
160
- <CodeBlock key="code" language={token.lang}>
161
- {token.text}
162
- </CodeBlock>
163
- );
164
- }
165
-
166
- case "blockquote": {
167
- // Blockquotes: no extra spacing, just dim/italic content (like Claude CLI)
168
- return (
169
- <View
170
- key="blockquote"
171
- borderRight={false}
172
- borderTop={false}
173
- borderBottom={false}
174
- borderStyle="double"
175
- borderColor={colors.border}
176
- paddingLeft={1}
177
- flexDirection="column"
178
- >
179
- {token.tokens?.map((t, i) => (
180
- <View key={i}>{renderBlockToken(t, colors, listDepth, true)}</View>
181
- ))}
182
- </View>
183
- );
184
- }
185
-
186
- case "list": {
187
- const listToken = token as Tokens.List;
188
- // Lists: each item gets single newline
189
- return (
190
- <View key="list" flexDirection="column">
191
- {listToken.items.map((item, index) => {
192
- const number = token.ordered ? (Number(token.start) || 1) + index : null;
193
- const isTask = item.task === true;
194
- const isChecked = item.checked === true;
195
-
196
- // Filter out checkbox tokens since we render them manually
197
- const contentTokens = item.tokens?.filter((t) => t.type !== "checkbox") || [];
198
-
199
- return (
200
- <View key={index} flexDirection="row">
201
- <Text color={colors.secondary} dimColor>
202
- {isTask ? (isChecked ? "[✓] " : "[ ] ") : number !== null ? `${number}. ` : "• "}
203
- </Text>
204
- <View flexGrow={1} flexDirection="column">
205
- {contentTokens.map((t, i) => (
206
- <View key={i}>{renderBlockToken(t, colors, listDepth + 1, isDimItalic)}</View>
207
- ))}
208
- </View>
209
- </View>
210
- );
211
- })}
212
- </View>
213
- );
214
- }
215
-
216
- case "table": {
217
- const tableToken = token as Tokens.Table;
218
-
219
- // Calculate column widths (like Claude CLI)
220
- const columnWidths = tableToken.header.map((headerCell, i) => {
221
- let maxWidth = extractText(headerCell).length;
222
- for (const row of tableToken.rows) {
223
- maxWidth = Math.max(maxWidth, extractText(row[i]).length);
224
- }
225
- return Math.max(maxWidth, 3);
226
- });
227
-
228
- // Tables get extra newline at end (like Claude CLI)
229
- return (
230
- <View key="table" flexDirection="column">
231
- <View
232
- flexDirection="row"
233
- borderTop={false}
234
- borderLeft={false}
235
- borderRight={false}
236
- borderStyle="single"
237
- borderColor={colors.border}
238
- alignSelf="flex-start"
239
- >
240
- {tableToken.header.map((cell, i) => {
241
- const align = tableToken.align?.[i] || null;
242
- return (
243
- <View
244
- key={i}
245
- width={columnWidths[i] + 2}
246
- paddingX={1}
247
- justifyContent={
248
- align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start"
249
- }
250
- >
251
- <Text bold color={colors.primary}>
252
- {renderInlineTokens(cell.tokens, colors, isDimItalic)}
253
- </Text>
254
- </View>
255
- );
256
- })}
257
- </View>
258
- {tableToken.rows.map((row, rowIndex) => (
259
- <View key={rowIndex} flexDirection="row">
260
- {row.map((cell, cellIndex) => {
261
- const align = tableToken.align?.[cellIndex] || null;
262
- return (
263
- <View
264
- key={cellIndex}
265
- width={columnWidths[cellIndex] + 2}
266
- paddingX={1}
267
- justifyContent={
268
- align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start"
269
- }
270
- >
271
- <Text>{renderInlineTokens(cell.tokens, colors, isDimItalic)}</Text>
272
- </View>
273
- );
274
- })}
275
- </View>
276
- ))}
277
- </View>
278
- );
279
- }
280
-
281
- case "hr":
282
- // HR: just the line, no extra spacing (like Claude CLI returns just "---")
283
- return (
284
- <View key="hr">
285
- <Text dimColor>{"─".repeat(50)}</Text>
286
- </View>
287
- );
288
-
289
- case "space":
290
- // Space tokens become single newline (like Claude CLI)
291
- return null;
292
-
293
- default:
294
- return (
295
- <View flexDirection="column">
296
- <Text>{renderInlineToken(token, colors, isDimItalic)}</Text>
297
- </View>
298
- );
299
- }
300
- }
301
-
302
- /**
303
- * A type-safe terminal markdown renderer using marked and Jar components.
304
- * Spacing rules:
305
- * - Headings: content + EOL + EOL (blank line after)
306
- * - Paragraphs: content + EOL (single newline)
307
- * - Code: content + EOL
308
- * - Space: EOL
309
- * - Lists: each item ends with EOL
310
- * - Tables: content + EOL
311
- */
312
- export function Markdown({ children }: MarkdownProps) {
313
- const { colors } = useTheme();
314
-
315
- // Defer markdown content to prevent blocking input during parsing
316
- const deferredChildren = useDeferredValue(children);
317
- const isRendering = children !== deferredChildren;
318
-
319
- const tokens = useMemo(() => {
320
- try {
321
- // Use remend to complete incomplete markdown during streaming
322
- const completedMarkdown = remend(deferredChildren);
323
- // Enable GFM and breaks for proper line break handling
324
- return marked.lexer(completedMarkdown, { gfm: true, breaks: true });
325
- } catch {
326
- return [];
327
- }
328
- }, [deferredChildren]);
329
-
330
- // Process tokens with Claude CLI-style spacing
331
- const renderedTokens = useMemo(() => {
332
- const result: React.ReactNode[] = [];
333
- const totalTokens = tokens.length;
334
-
335
- tokens.forEach((token, index) => {
336
- const isLast = index === totalTokens - 1;
337
-
338
- // Space tokens add a blank line (like Claude CLI returns EOL for space)
339
- if (token.type === "space") {
340
- result.push(<View key={`space-${index}`} height={1} />);
341
- return;
342
- }
343
-
344
- // Render the token
345
- result.push(
346
- <View key={index} flexDirection="column">
347
- {renderBlockToken(token, colors)}
348
- </View>,
349
- );
350
-
351
- // Add trailing blank line after headings (like Claude CLI's EOL + EOL)
352
- if (token.type === "heading" && !isLast) {
353
- result.push(<View key={`heading-space-${index}`} height={1} />);
354
- }
355
- });
356
-
357
- return result;
358
- }, [tokens, colors]);
359
-
360
- return (
361
- <View flexDirection="column">
362
- {isRendering && (
363
- <View>
364
- <Text dimColor>Rendering markdown...</Text>
365
- </View>
366
- )}
367
- {renderedTokens}
368
- </View>
369
- );
370
- }
371
-
372
- Markdown.displayName = "Markdown";
@@ -1,15 +0,0 @@
1
- import type { ReactNode } from "react";
2
-
3
- export interface MarkdownProps {
4
- children: string;
5
- }
6
-
7
- export interface CodeBlockProps {
8
- children?: string;
9
- language?: string;
10
- }
11
-
12
- export interface LinkProps {
13
- href: string;
14
- children: ReactNode;
15
- }
@@ -1,121 +0,0 @@
1
- import type { McpManager } from "@codellm/agent";
2
-
3
- import fs from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
-
7
- import { McpManagementService } from "./mcpManagement.js";
8
-
9
- function logMcpError(context: string, error: unknown): void {
10
- try {
11
- const logDir = path.join(os.tmpdir(), "abacus-tui-logs");
12
- if (!fs.existsSync(logDir)) {
13
- fs.mkdirSync(logDir, { recursive: true });
14
- }
15
- const logFile = path.join(logDir, "mcp-errors.log");
16
- const timestamp = new Date().toISOString();
17
- const errorMessage = error instanceof Error ? error.message : String(error);
18
- const errorStack = error instanceof Error ? error.stack : "";
19
- const logEntry = `[${timestamp}] ${context}: ${errorMessage}\n${errorStack ? `Stack: ${errorStack}\n` : ""}\n`;
20
- fs.appendFileSync(logFile, logEntry, "utf8");
21
- } catch {
22
- // If logging fails, just log to console as fallback
23
- console.error(`[MCP] ${context}:`, error);
24
- }
25
- }
26
-
27
- export class McpCommandHandler {
28
- private mcpService: McpManagementService;
29
- private mcp?: McpManager;
30
-
31
- constructor(mcp?: McpManager) {
32
- this.mcpService = new McpManagementService();
33
- this.mcp = mcp;
34
- }
35
-
36
- async listServers(): Promise<string> {
37
- const servers = await this.mcpService.listServers();
38
- // Update server status with real-time connection status from McpManager
39
- if (this.mcp) {
40
- const liveServers = this.mcp.listServers();
41
- for (const server of servers) {
42
- const live = liveServers.find(
43
- (s) => s.config.id === server.id || s.config.id === server.name,
44
- );
45
- server.status = live ? "running" : "stopped";
46
- }
47
- }
48
- return this.mcpService.formatServerList(servers);
49
- }
50
-
51
- async addServer(name: string, commandOrUrl: string, extraArgs: string[]): Promise<void> {
52
- await this.mcpService.addServerFromUrl(name, commandOrUrl, extraArgs);
53
- // Refresh live connections in background
54
- if (this.mcp) {
55
- this.mcp.refresh().catch((error: unknown) => {
56
- logMcpError(`Failed to refresh MCP connections after adding server '${name}'`, error);
57
- });
58
- }
59
- }
60
-
61
- async removeServer(name: string): Promise<void> {
62
- await this.mcpService.removeServer(name);
63
- // Disconnect live server in background
64
- if (this.mcp) {
65
- this.mcp.removeServer(name).catch((error: unknown) => {
66
- logMcpError(`Failed to disconnect MCP server '${name}'`, error);
67
- });
68
- }
69
- }
70
-
71
- async getServer(name: string): Promise<string | null> {
72
- const server = await this.mcpService.getServer(name);
73
- if (server) {
74
- // Update server status with real-time connection status from McpManager
75
- if (this.mcp) {
76
- const liveServers = this.mcp.listServers();
77
- const live = liveServers.find(
78
- (s) => s.config.id === server.id || s.config.id === server.name,
79
- );
80
- server.status = live ? "running" : "stopped";
81
- }
82
- return this.mcpService.formatServerInfo(server);
83
- }
84
- return null;
85
- }
86
-
87
- async addServerFromJson(name: string, jsonConfig: string): Promise<void> {
88
- await this.mcpService.addServerFromJson(name, jsonConfig);
89
- // Refresh live connections in background
90
- if (this.mcp) {
91
- this.mcp.refresh().catch((error: unknown) => {
92
- logMcpError(
93
- `Failed to refresh MCP connections after adding server '${name}' from JSON`,
94
- error,
95
- );
96
- });
97
- }
98
- }
99
-
100
- formatHelp(): string {
101
- return helpText;
102
- }
103
- }
104
-
105
- export const helpText = `
106
- MCP (Model Context Protocol) Commands:
107
-
108
- /mcp list List configured MCP servers
109
- /mcp add Add a server
110
- /mcp remove Remove an MCP server
111
- /mcp get Get details about an MCP server
112
- /mcp add-json Add an MCP server with JSON configuration
113
-
114
- Example - Add GitHub MCP Server:
115
- /mcp add-json github '{"command":"npx","args":["-y","@modelcontextprotocol/server-github"],"env":{"GITHUB_PERSONAL_ACCESS_TOKEN":"your_github_token_here"}}'
116
-
117
- TUI Features:
118
- - Full MCP server management from command line
119
- - All commands work independently in TUI
120
- - Direct file-based configuration access
121
- `;
@@ -1,44 +0,0 @@
1
- import type { IMcpServerInfo, IMcpManagementService, IMcpServerConfig } from "./types.js";
2
-
3
- import { StandaloneMcpService } from "./standaloneMcpService.js";
4
-
5
- export class McpManagementService implements IMcpManagementService {
6
- private standaloneService: StandaloneMcpService;
7
-
8
- constructor() {
9
- // Always use standalone service - no workbench dependencies
10
- this.standaloneService = new StandaloneMcpService();
11
- }
12
-
13
- async listServers(): Promise<IMcpServerInfo[]> {
14
- return this.standaloneService.listServers();
15
- }
16
-
17
- async addServer(name: string, config: IMcpServerConfig): Promise<void> {
18
- return this.standaloneService.addServer(name, config);
19
- }
20
-
21
- async removeServer(name: string): Promise<void> {
22
- return this.standaloneService.removeServer(name);
23
- }
24
-
25
- async getServer(name: string): Promise<IMcpServerInfo | undefined> {
26
- return this.standaloneService.getServer(name);
27
- }
28
-
29
- async addServerFromJson(name: string, jsonConfig: string): Promise<void> {
30
- return this.standaloneService.addServerFromJson(name, jsonConfig);
31
- }
32
-
33
- async addServerFromUrl(name: string, url: string, args?: string[]): Promise<void> {
34
- return this.standaloneService.addServerFromUrl(name, url, args);
35
- }
36
-
37
- formatServerInfo(server: IMcpServerInfo): string {
38
- return this.standaloneService.formatServerInfo(server);
39
- }
40
-
41
- formatServerList(servers: IMcpServerInfo[]): string {
42
- return this.standaloneService.formatServerList(servers);
43
- }
44
- }