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

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 (197) hide show
  1. package/dist/index.mjs +448 -422
  2. package/package.json +4 -1
  3. package/.oxlintrc.json +0 -8
  4. package/resources/abacus.ico +0 -0
  5. package/resources/entitlements.plist +0 -9
  6. package/src/__e2e__/README.md +0 -196
  7. package/src/__e2e__/agent-interactions.e2e.test.tsx +0 -61
  8. package/src/__e2e__/cli-commands.e2e.test.tsx +0 -77
  9. package/src/__e2e__/conversation-throttle.e2e.test.ts +0 -453
  10. package/src/__e2e__/conversation.e2e.test.tsx +0 -56
  11. package/src/__e2e__/diff-preview.e2e.test.tsx +0 -3399
  12. package/src/__e2e__/file-creation.e2e.test.tsx +0 -149
  13. package/src/__e2e__/helpers/test-helpers.ts +0 -449
  14. package/src/__e2e__/keyboard-navigation.e2e.test.tsx +0 -34
  15. package/src/__e2e__/llm-models.e2e.test.ts +0 -402
  16. package/src/__e2e__/mcp/mcp-callback-flow.e2e.test.tsx +0 -71
  17. package/src/__e2e__/mcp/mcp-full-app-ui.e2e.test.tsx +0 -167
  18. package/src/__e2e__/mcp/mcp-ui-rendering.e2e.test.tsx +0 -185
  19. package/src/__e2e__/repl.e2e.test.tsx +0 -78
  20. package/src/__e2e__/shell-compatibility.e2e.test.tsx +0 -76
  21. package/src/__e2e__/theme-mcp.e2e.test.tsx +0 -98
  22. package/src/__e2e__/tool-permissions.e2e.test.tsx +0 -66
  23. package/src/args.ts +0 -22
  24. package/src/components/__tests__/react-compiler.test.tsx +0 -78
  25. package/src/components/__tests__/status-indicator.test.tsx +0 -403
  26. package/src/components/composer/__tests__/bash-runner.test.tsx +0 -263
  27. package/src/components/composer/agent-mode-indicator.tsx +0 -63
  28. package/src/components/composer/bash-runner.tsx +0 -54
  29. package/src/components/composer/commands/default-commands.tsx +0 -615
  30. package/src/components/composer/commands/handler.tsx +0 -59
  31. package/src/components/composer/commands/picker.tsx +0 -273
  32. package/src/components/composer/commands/registry.ts +0 -233
  33. package/src/components/composer/commands/types.ts +0 -33
  34. package/src/components/composer/context.tsx +0 -88
  35. package/src/components/composer/file-mention-picker.tsx +0 -83
  36. package/src/components/composer/help.tsx +0 -44
  37. package/src/components/composer/index.tsx +0 -1007
  38. package/src/components/composer/mentions.ts +0 -57
  39. package/src/components/composer/message-queue.tsx +0 -70
  40. package/src/components/composer/mode-panel.tsx +0 -35
  41. package/src/components/composer/modes/__tests__/bash-handler.test.tsx +0 -755
  42. package/src/components/composer/modes/__tests__/bash-renderer.test.tsx +0 -1108
  43. package/src/components/composer/modes/bash-handler.tsx +0 -132
  44. package/src/components/composer/modes/bash-renderer.tsx +0 -175
  45. package/src/components/composer/modes/default-handlers.tsx +0 -33
  46. package/src/components/composer/modes/index.ts +0 -41
  47. package/src/components/composer/modes/types.ts +0 -21
  48. package/src/components/composer/persistent-shell.ts +0 -283
  49. package/src/components/composer/process.ts +0 -65
  50. package/src/components/composer/types.ts +0 -9
  51. package/src/components/composer/use-mention-search.ts +0 -68
  52. package/src/components/error-boundry.tsx +0 -60
  53. package/src/components/exit-message.tsx +0 -29
  54. package/src/components/expanded-view.tsx +0 -74
  55. package/src/components/file-completion.tsx +0 -127
  56. package/src/components/header.tsx +0 -47
  57. package/src/components/logo.tsx +0 -37
  58. package/src/components/segments.tsx +0 -356
  59. package/src/components/status-indicator.tsx +0 -306
  60. package/src/components/tool-group-summary.tsx +0 -263
  61. package/src/components/tool-permissions/ask-user-question-permission-ui.tsx +0 -319
  62. package/src/components/tool-permissions/diff-preview.tsx +0 -359
  63. package/src/components/tool-permissions/index.ts +0 -5
  64. package/src/components/tool-permissions/permission-options.tsx +0 -401
  65. package/src/components/tool-permissions/permission-preview-header.tsx +0 -57
  66. package/src/components/tool-permissions/tool-permission-ui.tsx +0 -420
  67. package/src/components/tools/agent/ask-user-question.tsx +0 -107
  68. package/src/components/tools/agent/enter-plan-mode.tsx +0 -55
  69. package/src/components/tools/agent/exit-plan-mode.tsx +0 -83
  70. package/src/components/tools/agent/handoff-to-main.tsx +0 -27
  71. package/src/components/tools/agent/subagent.tsx +0 -37
  72. package/src/components/tools/agent/todo-write.tsx +0 -104
  73. package/src/components/tools/browser/close-tab.tsx +0 -58
  74. package/src/components/tools/browser/computer.tsx +0 -70
  75. package/src/components/tools/browser/get-interactive-elements.tsx +0 -54
  76. package/src/components/tools/browser/get-tab-content.tsx +0 -51
  77. package/src/components/tools/browser/navigate-to.tsx +0 -59
  78. package/src/components/tools/browser/new-tab.tsx +0 -60
  79. package/src/components/tools/browser/perform-action.tsx +0 -63
  80. package/src/components/tools/browser/refresh-tab.tsx +0 -43
  81. package/src/components/tools/browser/switch-tab.tsx +0 -58
  82. package/src/components/tools/filesystem/delete-file.tsx +0 -104
  83. package/src/components/tools/filesystem/edit.tsx +0 -220
  84. package/src/components/tools/filesystem/list-dir.tsx +0 -78
  85. package/src/components/tools/filesystem/read-file.tsx +0 -180
  86. package/src/components/tools/filesystem/upload-image.tsx +0 -76
  87. package/src/components/tools/ide/ide-diagnostics.tsx +0 -62
  88. package/src/components/tools/index.ts +0 -91
  89. package/src/components/tools/mcp/mcp-tool.tsx +0 -158
  90. package/src/components/tools/search/fetch-url.tsx +0 -73
  91. package/src/components/tools/search/file-search.tsx +0 -78
  92. package/src/components/tools/search/grep.tsx +0 -90
  93. package/src/components/tools/search/semantic-search.tsx +0 -66
  94. package/src/components/tools/search/web-search.tsx +0 -71
  95. package/src/components/tools/shared/index.tsx +0 -48
  96. package/src/components/tools/shared/zod-coercion.ts +0 -35
  97. package/src/components/tools/terminal/bash-tool-output.tsx +0 -188
  98. package/src/components/tools/terminal/get-terminal-output.tsx +0 -91
  99. package/src/components/tools/terminal/run-in-terminal.tsx +0 -131
  100. package/src/components/tools/types.ts +0 -16
  101. package/src/components/tools.tsx +0 -68
  102. package/src/components/ui/__tests__/divider.test.tsx +0 -61
  103. package/src/components/ui/__tests__/gradient.test.tsx +0 -125
  104. package/src/components/ui/__tests__/input.test.tsx +0 -166
  105. package/src/components/ui/__tests__/select.test.tsx +0 -273
  106. package/src/components/ui/__tests__/shimmer.test.tsx +0 -99
  107. package/src/components/ui/blinking-indicator.tsx +0 -27
  108. package/src/components/ui/divider.tsx +0 -162
  109. package/src/components/ui/gradient.tsx +0 -56
  110. package/src/components/ui/input.tsx +0 -228
  111. package/src/components/ui/select.tsx +0 -151
  112. package/src/components/ui/shimmer.tsx +0 -76
  113. package/src/context/agent-mode.tsx +0 -95
  114. package/src/context/extension-file.tsx +0 -136
  115. package/src/context/network-activity.tsx +0 -45
  116. package/src/context/notification.tsx +0 -62
  117. package/src/context/shell-size.tsx +0 -49
  118. package/src/context/shell-title.tsx +0 -38
  119. package/src/entrypoints/print-mode.ts +0 -312
  120. package/src/entrypoints/repl.tsx +0 -389
  121. package/src/hooks/use-agent.ts +0 -15
  122. package/src/hooks/use-api-client.ts +0 -1
  123. package/src/hooks/use-available-height.ts +0 -8
  124. package/src/hooks/use-cleanup.ts +0 -29
  125. package/src/hooks/use-interrupt-manager.ts +0 -242
  126. package/src/hooks/use-models.ts +0 -22
  127. package/src/index.ts +0 -217
  128. package/src/lib/__tests__/ansi.test.ts +0 -255
  129. package/src/lib/__tests__/cli.test.ts +0 -122
  130. package/src/lib/__tests__/commands.test.ts +0 -325
  131. package/src/lib/__tests__/constants.test.ts +0 -15
  132. package/src/lib/__tests__/focusables.test.ts +0 -25
  133. package/src/lib/__tests__/fs.test.ts +0 -231
  134. package/src/lib/__tests__/markdown.test.tsx +0 -348
  135. package/src/lib/__tests__/mcpCommandHandler.test.ts +0 -173
  136. package/src/lib/__tests__/mcpManagement.test.ts +0 -38
  137. package/src/lib/__tests__/path-paste.test.ts +0 -144
  138. package/src/lib/__tests__/path.test.ts +0 -300
  139. package/src/lib/__tests__/queries.test.ts +0 -39
  140. package/src/lib/__tests__/standaloneMcpService.test.ts +0 -71
  141. package/src/lib/__tests__/text-buffer.test.ts +0 -328
  142. package/src/lib/__tests__/text-utils.test.ts +0 -32
  143. package/src/lib/__tests__/timing.test.ts +0 -78
  144. package/src/lib/__tests__/utils.test.ts +0 -238
  145. package/src/lib/__tests__/vim-buffer-actions.test.ts +0 -154
  146. package/src/lib/ansi.ts +0 -150
  147. package/src/lib/cli-push-server.ts +0 -112
  148. package/src/lib/cli.ts +0 -44
  149. package/src/lib/clipboard.ts +0 -226
  150. package/src/lib/command-utils.ts +0 -93
  151. package/src/lib/commands.ts +0 -270
  152. package/src/lib/constants.ts +0 -3
  153. package/src/lib/extension-connection.ts +0 -181
  154. package/src/lib/focusables.ts +0 -7
  155. package/src/lib/fs.ts +0 -533
  156. package/src/lib/markdown/code-block.tsx +0 -63
  157. package/src/lib/markdown/index.ts +0 -4
  158. package/src/lib/markdown/link.tsx +0 -19
  159. package/src/lib/markdown/markdown.tsx +0 -372
  160. package/src/lib/markdown/types.ts +0 -15
  161. package/src/lib/mcpCommandHandler.ts +0 -121
  162. package/src/lib/mcpManagement.ts +0 -44
  163. package/src/lib/path-paste.ts +0 -185
  164. package/src/lib/path.ts +0 -179
  165. package/src/lib/queries.ts +0 -15
  166. package/src/lib/standaloneMcpService.ts +0 -688
  167. package/src/lib/status-utils.ts +0 -237
  168. package/src/lib/test-utils.tsx +0 -72
  169. package/src/lib/text-buffer.ts +0 -2415
  170. package/src/lib/text-utils.ts +0 -272
  171. package/src/lib/timing.ts +0 -63
  172. package/src/lib/types.ts +0 -295
  173. package/src/lib/utils.ts +0 -182
  174. package/src/lib/vim-buffer-actions.ts +0 -732
  175. package/src/providers/agent.tsx +0 -1063
  176. package/src/providers/api-client.tsx +0 -43
  177. package/src/services/logger.ts +0 -85
  178. package/src/terminal/detection.ts +0 -187
  179. package/src/terminal/exit.ts +0 -279
  180. package/src/terminal/notification.ts +0 -83
  181. package/src/terminal/progress.ts +0 -201
  182. package/src/terminal/setup.ts +0 -797
  183. package/src/terminal/types.ts +0 -51
  184. package/src/theme/context.tsx +0 -57
  185. package/src/theme/index.ts +0 -4
  186. package/src/theme/themed.tsx +0 -35
  187. package/src/theme/themes.json +0 -546
  188. package/src/theme/types.ts +0 -110
  189. package/src/tools/types.ts +0 -59
  190. package/src/tools/utils/__tests__/zod-coercion.test.ts +0 -33
  191. package/src/tools/utils/tool-ui-components.tsx +0 -649
  192. package/src/tools/utils/zod-coercion.ts +0 -35
  193. package/tsconfig.json +0 -16
  194. package/tsconfig.node.json +0 -29
  195. package/tsconfig.test.json +0 -27
  196. package/tsdown.config.ts +0 -17
  197. 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
- }