@shareai-lab/kode 1.0.70 → 1.0.73

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 (278) hide show
  1. package/README.md +342 -75
  2. package/README.zh-CN.md +292 -0
  3. package/cli.js +62 -0
  4. package/package.json +49 -25
  5. package/scripts/postinstall.js +56 -0
  6. package/src/ProjectOnboarding.tsx +198 -0
  7. package/src/Tool.ts +82 -0
  8. package/src/commands/agents.tsx +3401 -0
  9. package/src/commands/approvedTools.ts +53 -0
  10. package/src/commands/bug.tsx +20 -0
  11. package/src/commands/clear.ts +43 -0
  12. package/src/commands/compact.ts +120 -0
  13. package/src/commands/config.tsx +19 -0
  14. package/src/commands/cost.ts +18 -0
  15. package/src/commands/ctx_viz.ts +209 -0
  16. package/src/commands/doctor.ts +24 -0
  17. package/src/commands/help.tsx +19 -0
  18. package/src/commands/init.ts +37 -0
  19. package/src/commands/listen.ts +42 -0
  20. package/src/commands/login.tsx +51 -0
  21. package/src/commands/logout.tsx +40 -0
  22. package/src/commands/mcp.ts +41 -0
  23. package/src/commands/model.tsx +40 -0
  24. package/src/commands/modelstatus.tsx +20 -0
  25. package/src/commands/onboarding.tsx +34 -0
  26. package/src/commands/pr_comments.ts +59 -0
  27. package/src/commands/refreshCommands.ts +54 -0
  28. package/src/commands/release-notes.ts +34 -0
  29. package/src/commands/resume.tsx +31 -0
  30. package/src/commands/review.ts +49 -0
  31. package/src/commands/terminalSetup.ts +221 -0
  32. package/src/commands.ts +139 -0
  33. package/src/components/ApproveApiKey.tsx +93 -0
  34. package/src/components/AsciiLogo.tsx +13 -0
  35. package/src/components/AutoUpdater.tsx +148 -0
  36. package/src/components/Bug.tsx +367 -0
  37. package/src/components/Config.tsx +293 -0
  38. package/src/components/ConsoleOAuthFlow.tsx +327 -0
  39. package/src/components/Cost.tsx +23 -0
  40. package/src/components/CostThresholdDialog.tsx +46 -0
  41. package/src/components/CustomSelect/option-map.ts +42 -0
  42. package/src/components/CustomSelect/select-option.tsx +78 -0
  43. package/src/components/CustomSelect/select.tsx +152 -0
  44. package/src/components/CustomSelect/theme.ts +45 -0
  45. package/src/components/CustomSelect/use-select-state.ts +414 -0
  46. package/src/components/CustomSelect/use-select.ts +35 -0
  47. package/src/components/FallbackToolUseRejectedMessage.tsx +15 -0
  48. package/src/components/FileEditToolUpdatedMessage.tsx +66 -0
  49. package/src/components/Help.tsx +215 -0
  50. package/src/components/HighlightedCode.tsx +33 -0
  51. package/src/components/InvalidConfigDialog.tsx +113 -0
  52. package/src/components/Link.tsx +32 -0
  53. package/src/components/LogSelector.tsx +86 -0
  54. package/src/components/Logo.tsx +145 -0
  55. package/src/components/MCPServerApprovalDialog.tsx +100 -0
  56. package/src/components/MCPServerDialogCopy.tsx +25 -0
  57. package/src/components/MCPServerMultiselectDialog.tsx +109 -0
  58. package/src/components/Message.tsx +221 -0
  59. package/src/components/MessageResponse.tsx +15 -0
  60. package/src/components/MessageSelector.tsx +211 -0
  61. package/src/components/ModeIndicator.tsx +88 -0
  62. package/src/components/ModelConfig.tsx +301 -0
  63. package/src/components/ModelListManager.tsx +227 -0
  64. package/src/components/ModelSelector.tsx +3386 -0
  65. package/src/components/ModelStatusDisplay.tsx +230 -0
  66. package/src/components/Onboarding.tsx +274 -0
  67. package/src/components/PressEnterToContinue.tsx +11 -0
  68. package/src/components/PromptInput.tsx +740 -0
  69. package/src/components/SentryErrorBoundary.ts +33 -0
  70. package/src/components/Spinner.tsx +129 -0
  71. package/src/components/StickerRequestForm.tsx +16 -0
  72. package/src/components/StructuredDiff.tsx +191 -0
  73. package/src/components/TextInput.tsx +259 -0
  74. package/src/components/TodoItem.tsx +11 -0
  75. package/src/components/TokenWarning.tsx +31 -0
  76. package/src/components/ToolUseLoader.tsx +40 -0
  77. package/src/components/TrustDialog.tsx +106 -0
  78. package/src/components/binary-feedback/BinaryFeedback.tsx +63 -0
  79. package/src/components/binary-feedback/BinaryFeedbackOption.tsx +111 -0
  80. package/src/components/binary-feedback/BinaryFeedbackView.tsx +172 -0
  81. package/src/components/binary-feedback/utils.ts +220 -0
  82. package/src/components/messages/AssistantBashOutputMessage.tsx +22 -0
  83. package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +49 -0
  84. package/src/components/messages/AssistantRedactedThinkingMessage.tsx +19 -0
  85. package/src/components/messages/AssistantTextMessage.tsx +144 -0
  86. package/src/components/messages/AssistantThinkingMessage.tsx +40 -0
  87. package/src/components/messages/AssistantToolUseMessage.tsx +133 -0
  88. package/src/components/messages/TaskProgressMessage.tsx +32 -0
  89. package/src/components/messages/TaskToolMessage.tsx +58 -0
  90. package/src/components/messages/UserBashInputMessage.tsx +28 -0
  91. package/src/components/messages/UserCommandMessage.tsx +30 -0
  92. package/src/components/messages/UserKodingInputMessage.tsx +28 -0
  93. package/src/components/messages/UserPromptMessage.tsx +35 -0
  94. package/src/components/messages/UserTextMessage.tsx +39 -0
  95. package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +12 -0
  96. package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +36 -0
  97. package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +31 -0
  98. package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +57 -0
  99. package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +35 -0
  100. package/src/components/messages/UserToolResultMessage/utils.tsx +56 -0
  101. package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +121 -0
  102. package/src/components/permissions/FallbackPermissionRequest.tsx +153 -0
  103. package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +182 -0
  104. package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +77 -0
  105. package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +164 -0
  106. package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +83 -0
  107. package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +240 -0
  108. package/src/components/permissions/PermissionRequest.tsx +101 -0
  109. package/src/components/permissions/PermissionRequestTitle.tsx +69 -0
  110. package/src/components/permissions/hooks.ts +44 -0
  111. package/src/components/permissions/toolUseOptions.ts +59 -0
  112. package/src/components/permissions/utils.ts +23 -0
  113. package/src/constants/betas.ts +5 -0
  114. package/src/constants/claude-asterisk-ascii-art.tsx +238 -0
  115. package/src/constants/figures.ts +4 -0
  116. package/src/constants/keys.ts +3 -0
  117. package/src/constants/macros.ts +8 -0
  118. package/src/constants/modelCapabilities.ts +179 -0
  119. package/src/constants/models.ts +1025 -0
  120. package/src/constants/oauth.ts +18 -0
  121. package/src/constants/product.ts +17 -0
  122. package/src/constants/prompts.ts +177 -0
  123. package/src/constants/releaseNotes.ts +7 -0
  124. package/src/context/PermissionContext.tsx +149 -0
  125. package/src/context.ts +278 -0
  126. package/src/cost-tracker.ts +84 -0
  127. package/src/entrypoints/cli.tsx +1518 -0
  128. package/src/entrypoints/mcp.ts +176 -0
  129. package/src/history.ts +25 -0
  130. package/src/hooks/useApiKeyVerification.ts +59 -0
  131. package/src/hooks/useArrowKeyHistory.ts +55 -0
  132. package/src/hooks/useCanUseTool.ts +138 -0
  133. package/src/hooks/useCancelRequest.ts +39 -0
  134. package/src/hooks/useDoublePress.ts +42 -0
  135. package/src/hooks/useExitOnCtrlCD.ts +31 -0
  136. package/src/hooks/useInterval.ts +25 -0
  137. package/src/hooks/useLogMessages.ts +16 -0
  138. package/src/hooks/useLogStartupTime.ts +12 -0
  139. package/src/hooks/useNotifyAfterTimeout.ts +65 -0
  140. package/src/hooks/usePermissionRequestLogging.ts +44 -0
  141. package/src/hooks/useTerminalSize.ts +49 -0
  142. package/src/hooks/useTextInput.ts +318 -0
  143. package/src/hooks/useUnifiedCompletion.ts +1404 -0
  144. package/src/messages.ts +38 -0
  145. package/src/permissions.ts +268 -0
  146. package/src/query.ts +707 -0
  147. package/src/screens/ConfigureNpmPrefix.tsx +197 -0
  148. package/src/screens/Doctor.tsx +219 -0
  149. package/src/screens/LogList.tsx +68 -0
  150. package/src/screens/REPL.tsx +798 -0
  151. package/src/screens/ResumeConversation.tsx +68 -0
  152. package/src/services/adapters/base.ts +38 -0
  153. package/src/services/adapters/chatCompletions.ts +90 -0
  154. package/src/services/adapters/responsesAPI.ts +170 -0
  155. package/src/services/browserMocks.ts +66 -0
  156. package/src/services/claude.ts +2083 -0
  157. package/src/services/customCommands.ts +704 -0
  158. package/src/services/fileFreshness.ts +377 -0
  159. package/src/services/gpt5ConnectionTest.ts +340 -0
  160. package/src/services/mcpClient.ts +564 -0
  161. package/src/services/mcpServerApproval.tsx +50 -0
  162. package/src/services/mentionProcessor.ts +273 -0
  163. package/src/services/modelAdapterFactory.ts +69 -0
  164. package/src/services/notifier.ts +40 -0
  165. package/src/services/oauth.ts +357 -0
  166. package/src/services/openai.ts +1305 -0
  167. package/src/services/responseStateManager.ts +90 -0
  168. package/src/services/sentry.ts +3 -0
  169. package/src/services/statsig.ts +171 -0
  170. package/src/services/statsigStorage.ts +86 -0
  171. package/src/services/systemReminder.ts +507 -0
  172. package/src/services/vcr.ts +161 -0
  173. package/src/test/testAdapters.ts +96 -0
  174. package/src/tools/ArchitectTool/ArchitectTool.tsx +122 -0
  175. package/src/tools/ArchitectTool/prompt.ts +15 -0
  176. package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +569 -0
  177. package/src/tools/BashTool/BashTool.tsx +243 -0
  178. package/src/tools/BashTool/BashToolResultMessage.tsx +38 -0
  179. package/src/tools/BashTool/OutputLine.tsx +49 -0
  180. package/src/tools/BashTool/prompt.ts +174 -0
  181. package/src/tools/BashTool/utils.ts +56 -0
  182. package/src/tools/FileEditTool/FileEditTool.tsx +315 -0
  183. package/src/tools/FileEditTool/prompt.ts +51 -0
  184. package/src/tools/FileEditTool/utils.ts +58 -0
  185. package/src/tools/FileReadTool/FileReadTool.tsx +404 -0
  186. package/src/tools/FileReadTool/prompt.ts +7 -0
  187. package/src/tools/FileWriteTool/FileWriteTool.tsx +297 -0
  188. package/src/tools/FileWriteTool/prompt.ts +10 -0
  189. package/src/tools/GlobTool/GlobTool.tsx +119 -0
  190. package/src/tools/GlobTool/prompt.ts +8 -0
  191. package/src/tools/GrepTool/GrepTool.tsx +147 -0
  192. package/src/tools/GrepTool/prompt.ts +11 -0
  193. package/src/tools/MCPTool/MCPTool.tsx +107 -0
  194. package/src/tools/MCPTool/prompt.ts +3 -0
  195. package/src/tools/MemoryReadTool/MemoryReadTool.tsx +127 -0
  196. package/src/tools/MemoryReadTool/prompt.ts +3 -0
  197. package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +89 -0
  198. package/src/tools/MemoryWriteTool/prompt.ts +3 -0
  199. package/src/tools/MultiEditTool/MultiEditTool.tsx +366 -0
  200. package/src/tools/MultiEditTool/prompt.ts +45 -0
  201. package/src/tools/NotebookEditTool/NotebookEditTool.tsx +298 -0
  202. package/src/tools/NotebookEditTool/prompt.ts +3 -0
  203. package/src/tools/NotebookReadTool/NotebookReadTool.tsx +258 -0
  204. package/src/tools/NotebookReadTool/prompt.ts +3 -0
  205. package/src/tools/StickerRequestTool/StickerRequestTool.tsx +93 -0
  206. package/src/tools/StickerRequestTool/prompt.ts +19 -0
  207. package/src/tools/TaskTool/TaskTool.tsx +466 -0
  208. package/src/tools/TaskTool/constants.ts +1 -0
  209. package/src/tools/TaskTool/prompt.ts +92 -0
  210. package/src/tools/ThinkTool/ThinkTool.tsx +54 -0
  211. package/src/tools/ThinkTool/prompt.ts +12 -0
  212. package/src/tools/TodoWriteTool/TodoWriteTool.tsx +290 -0
  213. package/src/tools/TodoWriteTool/prompt.ts +63 -0
  214. package/src/tools/lsTool/lsTool.tsx +272 -0
  215. package/src/tools/lsTool/prompt.ts +2 -0
  216. package/src/tools.ts +63 -0
  217. package/src/types/PermissionMode.ts +120 -0
  218. package/src/types/RequestContext.ts +72 -0
  219. package/src/types/conversation.ts +51 -0
  220. package/src/types/logs.ts +58 -0
  221. package/src/types/modelCapabilities.ts +64 -0
  222. package/src/types/notebook.ts +87 -0
  223. package/src/utils/Cursor.ts +436 -0
  224. package/src/utils/PersistentShell.ts +373 -0
  225. package/src/utils/advancedFuzzyMatcher.ts +290 -0
  226. package/src/utils/agentLoader.ts +284 -0
  227. package/src/utils/agentStorage.ts +97 -0
  228. package/src/utils/array.ts +3 -0
  229. package/src/utils/ask.tsx +99 -0
  230. package/src/utils/auth.ts +13 -0
  231. package/src/utils/autoCompactCore.ts +223 -0
  232. package/src/utils/autoUpdater.ts +318 -0
  233. package/src/utils/betas.ts +20 -0
  234. package/src/utils/browser.ts +14 -0
  235. package/src/utils/cleanup.ts +72 -0
  236. package/src/utils/commands.ts +261 -0
  237. package/src/utils/commonUnixCommands.ts +161 -0
  238. package/src/utils/config.ts +942 -0
  239. package/src/utils/conversationRecovery.ts +55 -0
  240. package/src/utils/debugLogger.ts +1123 -0
  241. package/src/utils/diff.ts +42 -0
  242. package/src/utils/env.ts +57 -0
  243. package/src/utils/errors.ts +21 -0
  244. package/src/utils/exampleCommands.ts +109 -0
  245. package/src/utils/execFileNoThrow.ts +51 -0
  246. package/src/utils/expertChatStorage.ts +136 -0
  247. package/src/utils/file.ts +402 -0
  248. package/src/utils/fileRecoveryCore.ts +71 -0
  249. package/src/utils/format.tsx +44 -0
  250. package/src/utils/fuzzyMatcher.ts +328 -0
  251. package/src/utils/generators.ts +62 -0
  252. package/src/utils/git.ts +92 -0
  253. package/src/utils/globalLogger.ts +77 -0
  254. package/src/utils/http.ts +10 -0
  255. package/src/utils/imagePaste.ts +38 -0
  256. package/src/utils/json.ts +13 -0
  257. package/src/utils/log.ts +382 -0
  258. package/src/utils/markdown.ts +213 -0
  259. package/src/utils/messageContextManager.ts +289 -0
  260. package/src/utils/messages.tsx +939 -0
  261. package/src/utils/model.ts +836 -0
  262. package/src/utils/permissions/filesystem.ts +118 -0
  263. package/src/utils/responseState.ts +23 -0
  264. package/src/utils/ripgrep.ts +167 -0
  265. package/src/utils/secureFile.ts +559 -0
  266. package/src/utils/sessionState.ts +49 -0
  267. package/src/utils/state.ts +25 -0
  268. package/src/utils/style.ts +29 -0
  269. package/src/utils/terminal.ts +50 -0
  270. package/src/utils/theme.ts +133 -0
  271. package/src/utils/thinking.ts +144 -0
  272. package/src/utils/todoStorage.ts +431 -0
  273. package/src/utils/tokens.ts +43 -0
  274. package/src/utils/toolExecutionController.ts +163 -0
  275. package/src/utils/unaryLogging.ts +26 -0
  276. package/src/utils/user.ts +37 -0
  277. package/src/utils/validate.ts +165 -0
  278. package/cli.mjs +0 -1803
@@ -0,0 +1,49 @@
1
+ import * as React from 'react'
2
+ import { extractTag } from '../../utils/messages'
3
+ import { getTheme } from '../../utils/theme'
4
+ import { Box, Text } from 'ink'
5
+
6
+ export function AssistantLocalCommandOutputMessage({
7
+ content,
8
+ }: {
9
+ content: string
10
+ }): React.ReactNode[] {
11
+ const stdout = extractTag(content, 'local-command-stdout')
12
+ const stderr = extractTag(content, 'local-command-stderr')
13
+ if (!stdout && !stderr) {
14
+ return []
15
+ }
16
+ const theme = getTheme()
17
+ let insides = [
18
+ format(stdout?.trim(), theme.text),
19
+ format(stderr?.trim(), theme.error),
20
+ ].filter(Boolean)
21
+
22
+ if (insides.length === 0) {
23
+ insides = [
24
+ <React.Fragment key="0">
25
+ <Text>(No output)</Text>
26
+ </React.Fragment>
27
+ ]
28
+ }
29
+
30
+ return [
31
+ <Box key="0" gap={1}>
32
+ <Box>
33
+ <Text color={theme.secondaryText}>{' '}⎿ </Text>
34
+ </Box>
35
+ {insides.map((_, index) => (
36
+ <Box key={index} flexDirection="column">
37
+ {_}
38
+ </Box>
39
+ ))}
40
+ </Box>,
41
+ ]
42
+ }
43
+
44
+ function format(content: string | undefined, color: string): React.ReactNode {
45
+ if (!content) {
46
+ return null
47
+ }
48
+ return <Text color={color}>{content}</Text>
49
+ }
@@ -0,0 +1,19 @@
1
+ import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { getTheme } from '../../utils/theme'
4
+
5
+ type Props = {
6
+ addMargin: boolean
7
+ }
8
+
9
+ export function AssistantRedactedThinkingMessage({
10
+ addMargin = false,
11
+ }: Props): React.ReactNode {
12
+ return (
13
+ <Box marginTop={addMargin ? 1 : 0}>
14
+ <Text color={getTheme().secondaryText} italic>
15
+ ✻ Thinking…
16
+ </Text>
17
+ </Box>
18
+ )
19
+ }
@@ -0,0 +1,144 @@
1
+ import { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
2
+ import React from 'react'
3
+ import { AssistantBashOutputMessage } from './AssistantBashOutputMessage'
4
+ import { AssistantLocalCommandOutputMessage } from './AssistantLocalCommandOutputMessage'
5
+ import { getTheme } from '../../utils/theme'
6
+ import { Box, Text } from 'ink'
7
+ import { Cost } from '../Cost'
8
+ import {
9
+ API_ERROR_MESSAGE_PREFIX,
10
+ CREDIT_BALANCE_TOO_LOW_ERROR_MESSAGE,
11
+ INVALID_API_KEY_ERROR_MESSAGE,
12
+ PROMPT_TOO_LONG_ERROR_MESSAGE,
13
+ } from '../../services/claude.js'
14
+ import {
15
+ CANCEL_MESSAGE,
16
+ INTERRUPT_MESSAGE,
17
+ INTERRUPT_MESSAGE_FOR_TOOL_USE,
18
+ isEmptyMessageText,
19
+ NO_RESPONSE_REQUESTED,
20
+ } from '../../utils/messages.js'
21
+ import { BLACK_CIRCLE } from '../../constants/figures'
22
+ import { applyMarkdown } from '../../utils/markdown'
23
+ import { useTerminalSize } from '../../hooks/useTerminalSize'
24
+
25
+ type Props = {
26
+ param: TextBlockParam
27
+ costUSD: number
28
+ durationMs: number
29
+ debug: boolean
30
+ addMargin: boolean
31
+ shouldShowDot: boolean
32
+ verbose?: boolean
33
+ width?: number | string
34
+ }
35
+
36
+ export function AssistantTextMessage({
37
+ param: { text },
38
+ costUSD,
39
+ durationMs,
40
+ debug,
41
+ addMargin,
42
+ shouldShowDot,
43
+ verbose,
44
+ }: Props): React.ReactNode {
45
+ const { columns } = useTerminalSize()
46
+ if (isEmptyMessageText(text)) {
47
+ return null
48
+ }
49
+
50
+ // Show bash output
51
+ if (text.startsWith('<bash-stdout') || text.startsWith('<bash-stderr')) {
52
+ return <AssistantBashOutputMessage content={text} verbose={verbose} />
53
+ }
54
+
55
+ // Show command output
56
+ if (
57
+ text.startsWith('<local-command-stdout') ||
58
+ text.startsWith('<local-command-stderr')
59
+ ) {
60
+ return <AssistantLocalCommandOutputMessage content={text} />
61
+ }
62
+
63
+ if (text.startsWith(API_ERROR_MESSAGE_PREFIX)) {
64
+ return (
65
+ <Text>
66
+ &nbsp;&nbsp;⎿ &nbsp;
67
+ <Text color={getTheme().error}>
68
+ {text === API_ERROR_MESSAGE_PREFIX
69
+ ? `${API_ERROR_MESSAGE_PREFIX}: Please wait a moment and try again.`
70
+ : text}
71
+ </Text>
72
+ </Text>
73
+ )
74
+ }
75
+
76
+ switch (text) {
77
+ // Local JSX commands don't need a response, but we still want Claude to see them
78
+ // Tool results render their own interrupt messages
79
+ case NO_RESPONSE_REQUESTED:
80
+ case INTERRUPT_MESSAGE_FOR_TOOL_USE:
81
+ return null
82
+
83
+ case INTERRUPT_MESSAGE:
84
+ case CANCEL_MESSAGE:
85
+ return (
86
+ <Text>
87
+ &nbsp;&nbsp;⎿ &nbsp;
88
+ <Text color={getTheme().error}>Interrupted by user</Text>
89
+ </Text>
90
+ )
91
+
92
+ case PROMPT_TOO_LONG_ERROR_MESSAGE:
93
+ return (
94
+ <Text>
95
+ &nbsp;&nbsp;⎿ &nbsp;
96
+ <Text color={getTheme().error}>
97
+ Context low &middot; Run /compact to compact & continue
98
+ </Text>
99
+ </Text>
100
+ )
101
+
102
+ case CREDIT_BALANCE_TOO_LOW_ERROR_MESSAGE:
103
+ return (
104
+ <Text>
105
+ &nbsp;&nbsp;⎿ &nbsp;
106
+ <Text color={getTheme().error}>
107
+ Credit balance too low &middot; Add funds:
108
+ https://console.anthropic.com/settings/billing
109
+ </Text>
110
+ </Text>
111
+ )
112
+
113
+ case INVALID_API_KEY_ERROR_MESSAGE:
114
+ return (
115
+ <Text>
116
+ &nbsp;&nbsp;⎿ &nbsp;
117
+ <Text color={getTheme().error}>{INVALID_API_KEY_ERROR_MESSAGE}</Text>
118
+ </Text>
119
+ )
120
+
121
+ default:
122
+ return (
123
+ <Box
124
+ alignItems="flex-start"
125
+ flexDirection="row"
126
+ justifyContent="space-between"
127
+ marginTop={addMargin ? 1 : 0}
128
+ width="100%"
129
+ >
130
+ <Box flexDirection="row">
131
+ {shouldShowDot && (
132
+ <Box minWidth={2}>
133
+ <Text color={getTheme().text}>{BLACK_CIRCLE}</Text>
134
+ </Box>
135
+ )}
136
+ <Box flexDirection="column" width={columns - 6}>
137
+ <Text>{applyMarkdown(text)}</Text>
138
+ </Box>
139
+ </Box>
140
+ <Cost costUSD={costUSD} durationMs={durationMs} debug={debug} />
141
+ </Box>
142
+ )
143
+ }
144
+ }
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { getTheme } from '../../utils/theme'
4
+ import { applyMarkdown } from '../../utils/markdown'
5
+ import {
6
+ ThinkingBlock,
7
+ ThinkingBlockParam,
8
+ } from '@anthropic-ai/sdk/resources/index.mjs'
9
+
10
+ type Props = {
11
+ param: ThinkingBlock | ThinkingBlockParam
12
+ addMargin: boolean
13
+ }
14
+
15
+ export function AssistantThinkingMessage({
16
+ param: { thinking },
17
+ addMargin = false,
18
+ }: Props): React.ReactNode {
19
+ if (!thinking) {
20
+ return null
21
+ }
22
+
23
+ return (
24
+ <Box
25
+ flexDirection="column"
26
+ gap={1}
27
+ marginTop={addMargin ? 1 : 0}
28
+ width="100%"
29
+ >
30
+ <Text color={getTheme().secondaryText} italic>
31
+ ✻ Thinking…
32
+ </Text>
33
+ <Box paddingLeft={2}>
34
+ <Text color={getTheme().secondaryText} italic>
35
+ {applyMarkdown(thinking)}
36
+ </Text>
37
+ </Box>
38
+ </Box>
39
+ )
40
+ }
@@ -0,0 +1,133 @@
1
+ import { Box, Text } from 'ink'
2
+ import React from 'react'
3
+ import { logError } from '../../utils/log'
4
+ import { ToolUseBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
5
+ import { Tool } from '../../Tool'
6
+ import { Cost } from '../Cost'
7
+ import { ToolUseLoader } from '../ToolUseLoader'
8
+ import { getTheme } from '../../utils/theme'
9
+ import { BLACK_CIRCLE } from '../../constants/figures'
10
+ import { ThinkTool } from '../../tools/ThinkTool/ThinkTool'
11
+ import { AssistantThinkingMessage } from './AssistantThinkingMessage'
12
+ import { TaskToolMessage } from './TaskToolMessage'
13
+
14
+ type Props = {
15
+ param: ToolUseBlockParam
16
+ costUSD: number
17
+ durationMs: number
18
+ addMargin: boolean
19
+ tools: Tool[]
20
+ debug: boolean
21
+ verbose: boolean
22
+ erroredToolUseIDs: Set<string>
23
+ inProgressToolUseIDs: Set<string>
24
+ unresolvedToolUseIDs: Set<string>
25
+ shouldAnimate: boolean
26
+ shouldShowDot: boolean
27
+ }
28
+
29
+ export function AssistantToolUseMessage({
30
+ param,
31
+ costUSD,
32
+ durationMs,
33
+ addMargin,
34
+ tools,
35
+ debug,
36
+ verbose,
37
+ erroredToolUseIDs,
38
+ inProgressToolUseIDs,
39
+ unresolvedToolUseIDs,
40
+ shouldAnimate,
41
+ shouldShowDot,
42
+ }: Props): React.ReactNode {
43
+ const tool = tools.find(_ => _.name === param.name)
44
+ if (!tool) {
45
+ logError(`Tool ${param.name} not found`)
46
+ return null
47
+ }
48
+ const isQueued =
49
+ !inProgressToolUseIDs.has(param.id) && unresolvedToolUseIDs.has(param.id)
50
+ // Keeping color undefined makes the OS use the default color regardless of appearance
51
+ const color = isQueued ? getTheme().secondaryText : undefined
52
+
53
+ // TODO: Avoid this special case
54
+ if (tool === ThinkTool) {
55
+ // params were already validated in query(), so this won't throe
56
+ const { thought } = ThinkTool.inputSchema.parse(param.input)
57
+ return (
58
+ <AssistantThinkingMessage
59
+ param={{ thinking: thought, signature: '', type: 'thinking' }}
60
+ addMargin={addMargin}
61
+ />
62
+ )
63
+ }
64
+
65
+ const userFacingToolName = tool.userFacingName ? tool.userFacingName(param.input) : tool.name
66
+ return (
67
+ <Box
68
+ flexDirection="row"
69
+ justifyContent="space-between"
70
+ marginTop={addMargin ? 1 : 0}
71
+ width="100%"
72
+ >
73
+ <Box>
74
+ <Box
75
+ flexWrap="nowrap"
76
+ minWidth={userFacingToolName.length + (shouldShowDot ? 2 : 0)}
77
+ >
78
+ {shouldShowDot &&
79
+ (isQueued ? (
80
+ <Box minWidth={2}>
81
+ <Text color={color}>{BLACK_CIRCLE}</Text>
82
+ </Box>
83
+ ) : (
84
+ <ToolUseLoader
85
+ shouldAnimate={shouldAnimate}
86
+ isUnresolved={unresolvedToolUseIDs.has(param.id)}
87
+ isError={erroredToolUseIDs.has(param.id)}
88
+ />
89
+ ))}
90
+ {tool.name === 'Task' && param.input ? (
91
+ <TaskToolMessage
92
+ agentType={(param.input as any).subagent_type || 'general-purpose'}
93
+ bold={!isQueued}
94
+ >
95
+ {userFacingToolName}
96
+ </TaskToolMessage>
97
+ ) : (
98
+ <Text color={color} bold={!isQueued}>
99
+ {userFacingToolName}
100
+ </Text>
101
+ )}
102
+ </Box>
103
+ <Box flexWrap="nowrap">
104
+ {Object.keys(param.input as { [key: string]: unknown }).length > 0 &&
105
+ (() => {
106
+ const toolMessage = tool.renderToolUseMessage(
107
+ param.input as never,
108
+ {
109
+ verbose,
110
+ },
111
+ )
112
+
113
+ // If the tool returns a React component, render it directly
114
+ if (React.isValidElement(toolMessage)) {
115
+ return (
116
+ <Box flexDirection="row">
117
+ <Text color={color}>(</Text>
118
+ {toolMessage}
119
+ <Text color={color}>)</Text>
120
+ </Box>
121
+ )
122
+ }
123
+
124
+ // If it's a string, wrap it in Text
125
+ return <Text color={color}>({toolMessage})</Text>
126
+ })()}
127
+ <Text color={color}>…</Text>
128
+ </Box>
129
+ </Box>
130
+ <Cost costUSD={costUSD} durationMs={durationMs} debug={debug} />
131
+ </Box>
132
+ )
133
+ }
@@ -0,0 +1,32 @@
1
+ import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { getTheme } from '../../utils/theme'
4
+
5
+ interface Props {
6
+ agentType: string
7
+ status: string
8
+ toolCount?: number
9
+ }
10
+
11
+ export function TaskProgressMessage({ agentType, status, toolCount }: Props) {
12
+ const theme = getTheme()
13
+
14
+ return (
15
+ <Box flexDirection="column" marginTop={1}>
16
+ <Box flexDirection="row">
17
+ <Text color={theme.claude}>⎯ </Text>
18
+ <Text color={theme.text} bold>
19
+ [{agentType}]
20
+ </Text>
21
+ <Text color={theme.secondaryText}> {status}</Text>
22
+ </Box>
23
+ {toolCount && toolCount > 0 && (
24
+ <Box marginLeft={3}>
25
+ <Text color={theme.secondaryText}>
26
+ Tools used: {toolCount}
27
+ </Text>
28
+ </Box>
29
+ )}
30
+ </Box>
31
+ )
32
+ }
@@ -0,0 +1,58 @@
1
+ import React, { useEffect, useState, useMemo } from 'react'
2
+ import { Text } from 'ink'
3
+ import { getAgentByType } from '../../utils/agentLoader'
4
+ import { getTheme } from '../../utils/theme'
5
+
6
+ interface Props {
7
+ agentType: string
8
+ children: React.ReactNode
9
+ bold?: boolean
10
+ }
11
+
12
+ // Simple cache to prevent re-fetching agent configs
13
+ const agentConfigCache = new Map<string, any>()
14
+
15
+ export function TaskToolMessage({ agentType, children, bold = true }: Props) {
16
+ const theme = getTheme()
17
+ const [agentConfig, setAgentConfig] = useState<any>(() => {
18
+ // Return cached config immediately if available
19
+ return agentConfigCache.get(agentType) || null
20
+ })
21
+
22
+ useEffect(() => {
23
+ // Skip if already cached
24
+ if (agentConfigCache.has(agentType)) {
25
+ setAgentConfig(agentConfigCache.get(agentType))
26
+ return
27
+ }
28
+
29
+ // Load and cache agent configuration
30
+ let mounted = true
31
+ getAgentByType(agentType).then(config => {
32
+ if (mounted) {
33
+ agentConfigCache.set(agentType, config)
34
+ setAgentConfig(config)
35
+ }
36
+ }).catch(() => {
37
+ // Silently handle errors to prevent console noise
38
+ if (mounted) {
39
+ agentConfigCache.set(agentType, null)
40
+ }
41
+ })
42
+
43
+ return () => {
44
+ mounted = false
45
+ }
46
+ }, [agentType])
47
+
48
+ // Memoize color calculation to prevent unnecessary re-renders
49
+ const color = useMemo(() => {
50
+ return agentConfig?.color || theme.text
51
+ }, [agentConfig?.color, theme.text])
52
+
53
+ return (
54
+ <Text color={color} bold={bold}>
55
+ {children}
56
+ </Text>
57
+ )
58
+ }
@@ -0,0 +1,28 @@
1
+ import { Box, Text } from 'ink'
2
+ import * as React from 'react'
3
+ import { extractTag } from '../../utils/messages'
4
+ import { getTheme } from '../../utils/theme'
5
+ import { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
6
+
7
+ type Props = {
8
+ addMargin: boolean
9
+ param: TextBlockParam
10
+ }
11
+
12
+ export function UserBashInputMessage({
13
+ param: { text },
14
+ addMargin,
15
+ }: Props): React.ReactNode {
16
+ const input = extractTag(text, 'bash-input')
17
+ if (!input) {
18
+ return null
19
+ }
20
+ return (
21
+ <Box flexDirection="column" marginTop={addMargin ? 1 : 0} width="100%">
22
+ <Box>
23
+ <Text color={getTheme().bashBorder}>!</Text>
24
+ <Text color={getTheme().secondaryText}> {input}</Text>
25
+ </Box>
26
+ </Box>
27
+ )
28
+ }
@@ -0,0 +1,30 @@
1
+ import { Box, Text } from 'ink'
2
+ import * as React from 'react'
3
+ import { getTheme } from '../../utils/theme'
4
+ import { extractTag } from '../../utils/messages'
5
+ import { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
6
+
7
+ type Props = {
8
+ addMargin: boolean
9
+ param: TextBlockParam
10
+ }
11
+
12
+ export function UserCommandMessage({
13
+ addMargin,
14
+ param: { text },
15
+ }: Props): React.ReactNode {
16
+ const commandMessage = extractTag(text, 'command-message')
17
+ const args = extractTag(text, 'command-args')
18
+ if (!commandMessage) {
19
+ return null
20
+ }
21
+
22
+ const theme = getTheme()
23
+ return (
24
+ <Box flexDirection="column" marginTop={addMargin ? 1 : 0} width="100%">
25
+ <Text color={theme.secondaryText}>
26
+ &gt; /{commandMessage} {args}
27
+ </Text>
28
+ </Box>
29
+ )
30
+ }
@@ -0,0 +1,28 @@
1
+ import { Box, Text } from 'ink'
2
+ import * as React from 'react'
3
+ import { extractTag } from '../../utils/messages'
4
+ import { getTheme } from '../../utils/theme'
5
+ import { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
6
+
7
+ type Props = {
8
+ addMargin: boolean
9
+ param: TextBlockParam
10
+ }
11
+
12
+ export function UserKodingInputMessage({
13
+ param: { text },
14
+ addMargin,
15
+ }: Props): React.ReactNode {
16
+ const input = extractTag(text, 'koding-input')
17
+ if (!input) {
18
+ return null
19
+ }
20
+ return (
21
+ <Box flexDirection="column" marginTop={addMargin ? 1 : 0} width="100%">
22
+ <Box>
23
+ <Text color={getTheme().koding}>#</Text>
24
+ <Text color={getTheme().secondaryText}> {input}</Text>
25
+ </Box>
26
+ </Box>
27
+ )
28
+ }
@@ -0,0 +1,35 @@
1
+ import React from 'react'
2
+ import { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
3
+ import { Box, Text } from 'ink'
4
+ import { getTheme } from '../../utils/theme'
5
+ import { logError } from '../../utils/log'
6
+ import { useTerminalSize } from '../../hooks/useTerminalSize'
7
+
8
+ type Props = {
9
+ addMargin: boolean
10
+ param: TextBlockParam
11
+ }
12
+
13
+ export function UserPromptMessage({
14
+ addMargin,
15
+ param: { text },
16
+ }: Props): React.ReactNode {
17
+ const { columns } = useTerminalSize()
18
+ if (!text) {
19
+ logError('No content found in user prompt message')
20
+ return null
21
+ }
22
+
23
+ return (
24
+ <Box flexDirection="row" marginTop={addMargin ? 1 : 0} width="100%">
25
+ <Box minWidth={2} width={2}>
26
+ <Text color={getTheme().secondaryText}>&gt;</Text>
27
+ </Box>
28
+ <Box flexDirection="column" width={columns - 4}>
29
+ <Text color={getTheme().secondaryText} wrap="wrap">
30
+ {text}
31
+ </Text>
32
+ </Box>
33
+ </Box>
34
+ )
35
+ }
@@ -0,0 +1,39 @@
1
+ import { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
2
+ import { UserBashInputMessage } from './UserBashInputMessage'
3
+ import { UserKodingInputMessage } from './UserKodingInputMessage'
4
+ import { UserCommandMessage } from './UserCommandMessage'
5
+ import { UserPromptMessage } from './UserPromptMessage'
6
+ import * as React from 'react'
7
+ import { NO_CONTENT_MESSAGE } from '../../services/claude'
8
+
9
+ type Props = {
10
+ addMargin: boolean
11
+ param: TextBlockParam
12
+ }
13
+
14
+ export function UserTextMessage({ addMargin, param }: Props): React.ReactNode {
15
+ if (param.text.trim() === NO_CONTENT_MESSAGE) {
16
+ return null
17
+ }
18
+
19
+ // Koding inputs!
20
+ if (param.text.includes('<koding-input>')) {
21
+ return <UserKodingInputMessage addMargin={addMargin} param={param} />
22
+ }
23
+
24
+ // Bash inputs!
25
+ if (param.text.includes('<bash-input>')) {
26
+ return <UserBashInputMessage addMargin={addMargin} param={param} />
27
+ }
28
+
29
+ // Slash commands/
30
+ if (
31
+ param.text.includes('<command-name>') ||
32
+ param.text.includes('<command-message>')
33
+ ) {
34
+ return <UserCommandMessage addMargin={addMargin} param={param} />
35
+ }
36
+
37
+ // User prompts>
38
+ return <UserPromptMessage addMargin={addMargin} param={param} />
39
+ }
@@ -0,0 +1,12 @@
1
+ import { Text } from 'ink'
2
+ import * as React from 'react'
3
+ import { getTheme } from '../../../utils/theme'
4
+
5
+ export function UserToolCanceledMessage(): React.ReactNode {
6
+ return (
7
+ <Text>
8
+ &nbsp;&nbsp;⎿ &nbsp;
9
+ <Text color={getTheme().error}>Interrupted by user</Text>
10
+ </Text>
11
+ )
12
+ }
@@ -0,0 +1,36 @@
1
+ import { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
2
+ import { Box, Text } from 'ink'
3
+ import * as React from 'react'
4
+ import { getTheme } from '../../../utils/theme'
5
+
6
+ const MAX_RENDERED_LINES = 10
7
+
8
+ type Props = {
9
+ param: ToolResultBlockParam
10
+ verbose: boolean
11
+ }
12
+
13
+ export function UserToolErrorMessage({
14
+ param,
15
+ verbose,
16
+ }: Props): React.ReactNode {
17
+ const error =
18
+ typeof param.content === 'string' ? param.content.trim() : 'Error'
19
+ return (
20
+ <Box flexDirection="row" width="100%">
21
+ <Text>&nbsp;&nbsp;⎿ &nbsp;</Text>
22
+ <Box flexDirection="column">
23
+ <Text color={getTheme().error}>
24
+ {verbose
25
+ ? error
26
+ : error.split('\n').slice(0, MAX_RENDERED_LINES).join('\n') || ''}
27
+ </Text>
28
+ {!verbose && error.split('\n').length > MAX_RENDERED_LINES && (
29
+ <Text color={getTheme().secondaryText}>
30
+ ... (+{error.split('\n').length - MAX_RENDERED_LINES} lines)
31
+ </Text>
32
+ )}
33
+ </Box>
34
+ </Box>
35
+ )
36
+ }