@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,100 @@
1
+ import React from 'react'
2
+ import { Box, Text, useInput } from 'ink'
3
+ import { getTheme } from '../utils/theme'
4
+ import { Select } from './CustomSelect/select'
5
+ import {
6
+ saveCurrentProjectConfig,
7
+ getCurrentProjectConfig,
8
+ } from '../utils/config.js'
9
+ import { MCPServerDialogCopy } from './MCPServerDialogCopy'
10
+ import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD'
11
+
12
+ type Props = {
13
+ serverName: string
14
+ onDone(): void
15
+ }
16
+
17
+ export function MCPServerApprovalDialog({
18
+ serverName,
19
+ onDone,
20
+ }: Props): React.ReactNode {
21
+ const theme = getTheme()
22
+ function onChange(value: 'yes' | 'no') {
23
+ const config = getCurrentProjectConfig()
24
+ switch (value) {
25
+ case 'yes': {
26
+ if (!config.approvedMcprcServers) {
27
+ config.approvedMcprcServers = []
28
+ }
29
+ if (!config.approvedMcprcServers.includes(serverName)) {
30
+ config.approvedMcprcServers.push(serverName)
31
+ }
32
+ saveCurrentProjectConfig(config)
33
+ onDone()
34
+ break
35
+ }
36
+ case 'no': {
37
+ if (!config.rejectedMcprcServers) {
38
+ config.rejectedMcprcServers = []
39
+ }
40
+ if (!config.rejectedMcprcServers.includes(serverName)) {
41
+ config.rejectedMcprcServers.push(serverName)
42
+ }
43
+ saveCurrentProjectConfig(config)
44
+ onDone()
45
+ break
46
+ }
47
+ }
48
+ }
49
+
50
+ const exitState = useExitOnCtrlCD(() => process.exit(0))
51
+
52
+ useInput((_input, key) => {
53
+ if (key.escape) {
54
+ onDone()
55
+ return
56
+ }
57
+ })
58
+
59
+ return (
60
+ <>
61
+ <Box
62
+ flexDirection="column"
63
+ gap={1}
64
+ padding={1}
65
+ borderStyle="round"
66
+ borderColor={theme.warning}
67
+ >
68
+ <Text bold color={theme.warning}>
69
+ New MCP Server Detected
70
+ </Text>
71
+ <Text>
72
+ This project contains a .mcprc file with an MCP server that requires
73
+ your approval:
74
+ </Text>
75
+ <Text bold>{serverName}</Text>
76
+
77
+ <MCPServerDialogCopy />
78
+
79
+ <Text>Do you want to approve this MCP server?</Text>
80
+
81
+ <Select
82
+ options={[
83
+ { label: 'Yes, approve this server', value: 'yes' },
84
+ { label: 'No, reject this server', value: 'no' },
85
+ ]}
86
+ onChange={value => onChange(value as 'yes' | 'no')}
87
+ />
88
+ </Box>
89
+ <Box marginLeft={3}>
90
+ <Text dimColor>
91
+ {exitState.pending ? (
92
+ <>Press {exitState.keyName} again to exit</>
93
+ ) : (
94
+ <>Enter to confirm · Esc to reject</>
95
+ )}
96
+ </Text>
97
+ </Box>
98
+ </>
99
+ )
100
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react'
2
+ import { Text } from 'ink'
3
+ import Link from 'ink-link'
4
+ import { PRODUCT_NAME, PRODUCT_COMMAND } from '../constants/product'
5
+
6
+ export function MCPServerDialogCopy(): React.ReactNode {
7
+ return (
8
+ <>
9
+ <Text>
10
+ MCP servers provide additional functionality to {PRODUCT_NAME}. They may
11
+ execute code, make network requests, or access system resources via tool
12
+ calls. All tool calls will require your explicit approval before
13
+ execution. For more information, see{' '}
14
+ <Link url="https://docs.anthropic.com/s/claude-code-mcp">
15
+ MCP documentation
16
+ </Link>
17
+ </Text>
18
+
19
+ <Text dimColor>
20
+ Remember: You can always change these choices later by running `
21
+ {PRODUCT_COMMAND} mcp reset-mcprc-choices`
22
+ </Text>
23
+ </>
24
+ )
25
+ }
@@ -0,0 +1,109 @@
1
+ import React from 'react'
2
+ import { Box, Text, useInput } from 'ink'
3
+ import { getTheme } from '../utils/theme'
4
+ import { MultiSelect } from '@inkjs/ui'
5
+ import {
6
+ saveCurrentProjectConfig,
7
+ getCurrentProjectConfig,
8
+ } from '../utils/config.js'
9
+ import { partition } from 'lodash-es'
10
+ import { MCPServerDialogCopy } from './MCPServerDialogCopy'
11
+ import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD'
12
+
13
+ type Props = {
14
+ serverNames: string[]
15
+ onDone(): void
16
+ }
17
+
18
+ export function MCPServerMultiselectDialog({
19
+ serverNames,
20
+ onDone,
21
+ }: Props): React.ReactNode {
22
+ const theme = getTheme()
23
+ function onSubmit(selectedServers: string[]) {
24
+ const config = getCurrentProjectConfig()
25
+
26
+ // Initialize arrays if they don't exist
27
+ if (!config.approvedMcprcServers) {
28
+ config.approvedMcprcServers = []
29
+ }
30
+ if (!config.rejectedMcprcServers) {
31
+ config.rejectedMcprcServers = []
32
+ }
33
+
34
+ // Use partition to separate approved and rejected servers
35
+ const [approvedServers, rejectedServers] = partition(serverNames, server =>
36
+ selectedServers.includes(server),
37
+ )
38
+
39
+ // Add new servers directly to the respective lists
40
+ config.approvedMcprcServers.push(...approvedServers)
41
+ config.rejectedMcprcServers.push(...rejectedServers)
42
+
43
+ saveCurrentProjectConfig(config)
44
+ onDone()
45
+ }
46
+
47
+ const exitState = useExitOnCtrlCD(() => process.exit())
48
+
49
+ useInput((_input, key) => {
50
+ if (key.escape) {
51
+ // On escape, treat all servers as rejected
52
+ const config = getCurrentProjectConfig()
53
+ if (!config.rejectedMcprcServers) {
54
+ config.rejectedMcprcServers = []
55
+ }
56
+
57
+ for (const server of serverNames) {
58
+ if (!config.rejectedMcprcServers.includes(server)) {
59
+ config.rejectedMcprcServers.push(server)
60
+ }
61
+ }
62
+
63
+ saveCurrentProjectConfig(config)
64
+ onDone()
65
+ return
66
+ }
67
+ })
68
+
69
+ return (
70
+ <>
71
+ <Box
72
+ flexDirection="column"
73
+ gap={1}
74
+ padding={1}
75
+ borderStyle="round"
76
+ borderColor={theme.warning}
77
+ >
78
+ <Text bold color={theme.warning}>
79
+ New MCP Servers Detected
80
+ </Text>
81
+ <Text>
82
+ This project contains a .mcprc file with {serverNames.length} MCP
83
+ servers that require your approval.
84
+ </Text>
85
+ <MCPServerDialogCopy />
86
+
87
+ <Text>Please select the servers you want to enable:</Text>
88
+
89
+ <MultiSelect
90
+ options={serverNames.map(server => ({
91
+ label: server,
92
+ value: server,
93
+ }))}
94
+ defaultValue={serverNames}
95
+ onSubmit={onSubmit}
96
+ />
97
+ </Box>
98
+ <Box marginLeft={3}>
99
+ <Text dimColor>
100
+ {exitState.pending ? (
101
+ <>Press {exitState.keyName} again to exit</>
102
+ ) : (
103
+ <>Space to select · Enter to confirm · Esc to reject all</>
104
+ )}
105
+ </Text>
106
+ </Box>
107
+ </>
108
+ )
109
+ }
@@ -0,0 +1,221 @@
1
+ import { Box } from 'ink'
2
+ import * as React from 'react'
3
+ import type { AssistantMessage, Message, UserMessage } from '../query'
4
+ import type {
5
+ ContentBlock,
6
+ DocumentBlockParam,
7
+ ImageBlockParam,
8
+ TextBlockParam,
9
+ ThinkingBlockParam,
10
+ ToolResultBlockParam,
11
+ ToolUseBlockParam,
12
+ } from '@anthropic-ai/sdk/resources/index.mjs'
13
+ import { Tool } from '../Tool'
14
+ import { logError } from '../utils/log'
15
+ import { UserToolResultMessage } from './messages/UserToolResultMessage/UserToolResultMessage'
16
+ import { AssistantToolUseMessage } from './messages/AssistantToolUseMessage'
17
+ import { AssistantTextMessage } from './messages/AssistantTextMessage'
18
+ import { UserTextMessage } from './messages/UserTextMessage'
19
+ import { NormalizedMessage } from '../utils/messages'
20
+ import { AssistantThinkingMessage } from './messages/AssistantThinkingMessage'
21
+ import { AssistantRedactedThinkingMessage } from './messages/AssistantRedactedThinkingMessage'
22
+ import { useTerminalSize } from '../hooks/useTerminalSize'
23
+
24
+ type Props = {
25
+ message: UserMessage | AssistantMessage
26
+ messages: NormalizedMessage[]
27
+ // TODO: Find a way to remove this, and leave spacing to the consumer
28
+ addMargin: boolean
29
+ tools: Tool[]
30
+ verbose: boolean
31
+ debug: boolean
32
+ erroredToolUseIDs: Set<string>
33
+ inProgressToolUseIDs: Set<string>
34
+ unresolvedToolUseIDs: Set<string>
35
+ shouldAnimate: boolean
36
+ shouldShowDot: boolean
37
+ width?: number | string
38
+ }
39
+
40
+ export function Message({
41
+ message,
42
+ messages,
43
+ addMargin,
44
+ tools,
45
+ verbose,
46
+ debug,
47
+ erroredToolUseIDs,
48
+ inProgressToolUseIDs,
49
+ unresolvedToolUseIDs,
50
+ shouldAnimate,
51
+ shouldShowDot,
52
+ width,
53
+ }: Props): React.ReactNode {
54
+ // Assistant message
55
+ if (message.type === 'assistant') {
56
+ return (
57
+ <Box flexDirection="column" width="100%">
58
+ {message.message.content.map((_, index) => (
59
+ <AssistantMessage
60
+ key={index}
61
+ param={_}
62
+ costUSD={message.costUSD}
63
+ durationMs={message.durationMs}
64
+ addMargin={addMargin}
65
+ tools={tools}
66
+ debug={debug}
67
+ options={{ verbose }}
68
+ erroredToolUseIDs={erroredToolUseIDs}
69
+ inProgressToolUseIDs={inProgressToolUseIDs}
70
+ unresolvedToolUseIDs={unresolvedToolUseIDs}
71
+ shouldAnimate={shouldAnimate}
72
+ shouldShowDot={shouldShowDot}
73
+ width={width}
74
+ />
75
+ ))}
76
+ </Box>
77
+ )
78
+ }
79
+
80
+ // User message
81
+ // TODO: normalize upstream
82
+ const content =
83
+ typeof message.message.content === 'string'
84
+ ? [{ type: 'text', text: message.message.content } as TextBlockParam]
85
+ : message.message.content
86
+ return (
87
+ <Box flexDirection="column" width="100%">
88
+ {content.map((_, index) => (
89
+ <UserMessage
90
+ key={index}
91
+ message={message}
92
+ messages={messages}
93
+ addMargin={addMargin}
94
+ tools={tools}
95
+ param={_ as TextBlockParam}
96
+ options={{ verbose }}
97
+ />
98
+ ))}
99
+ </Box>
100
+ )
101
+ }
102
+
103
+ function UserMessage({
104
+ message,
105
+ messages,
106
+ addMargin,
107
+ tools,
108
+ param,
109
+ options: { verbose },
110
+ }: {
111
+ message: UserMessage
112
+ messages: Message[]
113
+ addMargin: boolean
114
+ tools: Tool[]
115
+ param:
116
+ | TextBlockParam
117
+ | DocumentBlockParam
118
+ | ImageBlockParam
119
+ | ToolUseBlockParam
120
+ | ToolResultBlockParam
121
+ options: {
122
+ verbose: boolean
123
+ }
124
+ key?: React.Key
125
+ }): React.ReactNode {
126
+ const { columns } = useTerminalSize()
127
+ switch (param.type) {
128
+ case 'text':
129
+ return <UserTextMessage addMargin={addMargin} param={param} />
130
+ case 'tool_result':
131
+ return (
132
+ <UserToolResultMessage
133
+ param={param}
134
+ message={message}
135
+ messages={messages}
136
+ tools={tools}
137
+ verbose={verbose}
138
+ width={columns - 5}
139
+ />
140
+ )
141
+ }
142
+ }
143
+
144
+ function AssistantMessage({
145
+ param,
146
+ costUSD,
147
+ durationMs,
148
+ addMargin,
149
+ tools,
150
+ debug,
151
+ options: { verbose },
152
+ erroredToolUseIDs,
153
+ inProgressToolUseIDs,
154
+ unresolvedToolUseIDs,
155
+ shouldAnimate,
156
+ shouldShowDot,
157
+ width,
158
+ }: {
159
+ param:
160
+ | ContentBlock
161
+ | TextBlockParam
162
+ | ImageBlockParam
163
+ | ThinkingBlockParam
164
+ | ToolUseBlockParam
165
+ | ToolResultBlockParam
166
+ costUSD: number
167
+ durationMs: number
168
+ addMargin: boolean
169
+ tools: Tool[]
170
+ debug: boolean
171
+ options: {
172
+ verbose: boolean
173
+ }
174
+ erroredToolUseIDs: Set<string>
175
+ inProgressToolUseIDs: Set<string>
176
+ unresolvedToolUseIDs: Set<string>
177
+ shouldAnimate: boolean
178
+ shouldShowDot: boolean
179
+ width?: number | string
180
+ key?: React.Key
181
+ }): React.ReactNode {
182
+ switch (param.type) {
183
+ case 'tool_use':
184
+ return (
185
+ <AssistantToolUseMessage
186
+ param={param}
187
+ costUSD={costUSD}
188
+ durationMs={durationMs}
189
+ addMargin={addMargin}
190
+ tools={tools}
191
+ debug={debug}
192
+ verbose={verbose}
193
+ erroredToolUseIDs={erroredToolUseIDs}
194
+ inProgressToolUseIDs={inProgressToolUseIDs}
195
+ unresolvedToolUseIDs={unresolvedToolUseIDs}
196
+ shouldAnimate={shouldAnimate}
197
+ shouldShowDot={shouldShowDot}
198
+ />
199
+ )
200
+ case 'text':
201
+ return (
202
+ <AssistantTextMessage
203
+ param={param}
204
+ costUSD={costUSD}
205
+ durationMs={durationMs}
206
+ debug={debug}
207
+ addMargin={addMargin}
208
+ shouldShowDot={shouldShowDot}
209
+ verbose={verbose}
210
+ width={width}
211
+ />
212
+ )
213
+ case 'redacted_thinking':
214
+ return <AssistantRedactedThinkingMessage addMargin={addMargin} />
215
+ case 'thinking':
216
+ return <AssistantThinkingMessage addMargin={addMargin} param={param} />
217
+ default:
218
+ logError(`Unable to render message type: ${param.type}`)
219
+ return null
220
+ }
221
+ }
@@ -0,0 +1,15 @@
1
+ import { Box, Text } from 'ink'
2
+ import * as React from 'react'
3
+
4
+ type Props = {
5
+ children: React.ReactNode
6
+ }
7
+
8
+ export function MessageResponse({ children }: Props): React.ReactNode {
9
+ return (
10
+ <Box flexDirection="row" height={1} overflow="hidden">
11
+ <Text>{' '}⎿ &nbsp;</Text>
12
+ {children}
13
+ </Box>
14
+ )
15
+ }
@@ -0,0 +1,211 @@
1
+ import { Box, Text, useInput } from 'ink'
2
+ import * as React from 'react'
3
+ import { useMemo, useState, useEffect } from 'react'
4
+ import figures from 'figures'
5
+ import { getTheme } from '../utils/theme'
6
+ import { Message as MessageComponent } from './Message'
7
+ import { randomUUID } from 'crypto'
8
+ import { type Tool } from '../Tool'
9
+ import {
10
+ createUserMessage,
11
+ isEmptyMessageText,
12
+ isNotEmptyMessage,
13
+ normalizeMessages,
14
+ } from '../utils/messages.js'
15
+ import { logEvent } from '../services/statsig'
16
+ import type { AssistantMessage, UserMessage } from '../query'
17
+ import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD'
18
+
19
+ type Props = {
20
+ erroredToolUseIDs: Set<string>
21
+ messages: (UserMessage | AssistantMessage)[]
22
+ onSelect: (message: UserMessage) => void
23
+ onEscape: () => void
24
+ tools: Tool[]
25
+ unresolvedToolUseIDs: Set<string>
26
+ }
27
+
28
+ const MAX_VISIBLE_MESSAGES = 7
29
+
30
+ export function MessageSelector({
31
+ erroredToolUseIDs,
32
+ messages,
33
+ onSelect,
34
+ onEscape,
35
+ tools,
36
+ unresolvedToolUseIDs,
37
+ }: Props): React.ReactNode {
38
+ const currentUUID = useMemo(randomUUID, [])
39
+
40
+ // Log when selector is opened
41
+ useEffect(() => {
42
+ logEvent('tengu_message_selector_opened', {})
43
+ }, [])
44
+
45
+ function handleSelect(message: UserMessage) {
46
+ const indexFromEnd = messages.length - 1 - messages.indexOf(message)
47
+ logEvent('tengu_message_selector_selected', {
48
+ index_from_end: indexFromEnd.toString(),
49
+ message_type: message.type,
50
+ is_current_prompt: (message.uuid === currentUUID).toString(),
51
+ })
52
+ onSelect(message)
53
+ }
54
+
55
+ function handleEscape() {
56
+ logEvent('tengu_message_selector_cancelled', {})
57
+ onEscape()
58
+ }
59
+
60
+ // Add current prompt as a virtual message
61
+ const allItems = useMemo(
62
+ () => [
63
+ // Filter out tool results
64
+ ...messages
65
+ .filter(
66
+ _ =>
67
+ !(
68
+ _.type === 'user' &&
69
+ Array.isArray(_.message.content) &&
70
+ _.message.content[0]?.type === 'tool_result'
71
+ ),
72
+ )
73
+ // Filter out assistant messages, until we have a way to kick off the tool use loop from REPL
74
+ .filter(_ => _.type !== 'assistant'),
75
+ { ...createUserMessage(''), uuid: currentUUID } as UserMessage,
76
+ ],
77
+ [messages, currentUUID],
78
+ )
79
+ const [selectedIndex, setSelectedIndex] = useState(allItems.length - 1)
80
+
81
+ const exitState = useExitOnCtrlCD(() => process.exit(0))
82
+
83
+ useInput((input, key) => {
84
+ if (key.tab || key.escape) {
85
+ handleEscape()
86
+ return
87
+ }
88
+ if (key.return) {
89
+ handleSelect(allItems[selectedIndex]!)
90
+ return
91
+ }
92
+ if (key.upArrow) {
93
+ if (key.ctrl || key.shift || key.meta) {
94
+ // Jump to top with any modifier key
95
+ setSelectedIndex(0)
96
+ } else {
97
+ setSelectedIndex(prev => Math.max(0, prev - 1))
98
+ }
99
+ }
100
+ if (key.downArrow) {
101
+ if (key.ctrl || key.shift || key.meta) {
102
+ // Jump to bottom with any modifier key
103
+ setSelectedIndex(allItems.length - 1)
104
+ } else {
105
+ setSelectedIndex(prev => Math.min(allItems.length - 1, prev + 1))
106
+ }
107
+ }
108
+
109
+ // Handle number keys (1-9)
110
+ const num = Number(input)
111
+ if (!isNaN(num) && num >= 1 && num <= Math.min(9, allItems.length)) {
112
+ if (!allItems[num - 1]) {
113
+ return
114
+ }
115
+ handleSelect(allItems[num - 1]!)
116
+ }
117
+ })
118
+
119
+ const firstVisibleIndex = Math.max(
120
+ 0,
121
+ Math.min(
122
+ selectedIndex - Math.floor(MAX_VISIBLE_MESSAGES / 2),
123
+ allItems.length - MAX_VISIBLE_MESSAGES,
124
+ ),
125
+ )
126
+
127
+ const normalizedMessages = useMemo(
128
+ () => normalizeMessages(messages).filter(isNotEmptyMessage),
129
+ [messages],
130
+ )
131
+
132
+ return (
133
+ <>
134
+ <Box
135
+ flexDirection="column"
136
+ borderStyle="round"
137
+ borderColor={getTheme().secondaryBorder}
138
+ height={4 + Math.min(MAX_VISIBLE_MESSAGES, allItems.length) * 2}
139
+ paddingX={1}
140
+ marginTop={1}
141
+ >
142
+ <Box flexDirection="column" minHeight={2} marginBottom={1}>
143
+ <Text bold>Jump to a previous message</Text>
144
+ <Text dimColor>This will fork the conversation</Text>
145
+ </Box>
146
+ {allItems
147
+ .slice(firstVisibleIndex, firstVisibleIndex + MAX_VISIBLE_MESSAGES)
148
+ .map((msg, index) => {
149
+ const actualIndex = firstVisibleIndex + index
150
+ const isSelected = actualIndex === selectedIndex
151
+ const isCurrent = msg.uuid === currentUUID
152
+
153
+ return (
154
+ <Box key={msg.uuid} flexDirection="row" height={2} minHeight={2}>
155
+ <Box width={7}>
156
+ {isSelected ? (
157
+ <Text color="blue" bold>
158
+ {figures.pointer} {firstVisibleIndex + index + 1}{' '}
159
+ </Text>
160
+ ) : (
161
+ <Text>
162
+ {' '}
163
+ {firstVisibleIndex + index + 1}{' '}
164
+ </Text>
165
+ )}
166
+ </Box>
167
+ <Box height={1} overflow="hidden" width={100}>
168
+ {isCurrent ? (
169
+ <Box width="100%">
170
+ <Text dimColor italic>
171
+ {'(current)'}
172
+ </Text>
173
+ </Box>
174
+ ) : Array.isArray(msg.message.content) &&
175
+ msg.message.content[0]?.type === 'text' &&
176
+ isEmptyMessageText(msg.message.content[0].text) ? (
177
+ <Text dimColor italic>
178
+ (empty message)
179
+ </Text>
180
+ ) : (
181
+ <MessageComponent
182
+ message={msg}
183
+ messages={normalizedMessages}
184
+ addMargin={false}
185
+ tools={tools}
186
+ verbose={false}
187
+ debug={false}
188
+ erroredToolUseIDs={erroredToolUseIDs}
189
+ inProgressToolUseIDs={new Set()}
190
+ unresolvedToolUseIDs={unresolvedToolUseIDs}
191
+ shouldAnimate={false}
192
+ shouldShowDot={false}
193
+ />
194
+ )}
195
+ </Box>
196
+ </Box>
197
+ )
198
+ })}
199
+ </Box>
200
+ <Box marginLeft={3}>
201
+ <Text dimColor>
202
+ {exitState.pending ? (
203
+ <>Press {exitState.keyName} again to exit</>
204
+ ) : (
205
+ <>↑/↓ to select · Enter to confirm · Tab/Esc to cancel</>
206
+ )}
207
+ </Text>
208
+ </Box>
209
+ </>
210
+ )
211
+ }