@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,33 @@
1
+ import * as React from 'react'
2
+ import { captureException } from '../services/sentry'
3
+
4
+ interface Props {
5
+ children: React.ReactNode
6
+ }
7
+
8
+ interface State {
9
+ hasError: boolean
10
+ }
11
+
12
+ export class SentryErrorBoundary extends React.Component<Props, State> {
13
+ constructor(props: Props) {
14
+ super(props)
15
+ ;(this as any).state = { hasError: false }
16
+ }
17
+
18
+ static getDerivedStateFromError(): State {
19
+ return { hasError: true }
20
+ }
21
+
22
+ componentDidCatch(error: Error): void {
23
+ captureException(error)
24
+ }
25
+
26
+ render(): React.ReactNode {
27
+ if ((this as any).state.hasError) {
28
+ return null
29
+ }
30
+
31
+ return (this as any).props.children
32
+ }
33
+ }
@@ -0,0 +1,129 @@
1
+ import { Box, Text } from 'ink'
2
+ import * as React from 'react'
3
+ import { useEffect, useRef, useState } from 'react'
4
+ import { getTheme } from '../utils/theme'
5
+ import { sample } from 'lodash-es'
6
+ import { getSessionState } from '../utils/sessionState'
7
+ // NB: The third character in this string is an emoji that
8
+ // renders on Windows consoles with a green background
9
+ const CHARACTERS =
10
+ process.platform === 'darwin'
11
+ ? ['·', '✢', '✳', '∗', '✻', '✽']
12
+ : ['·', '✢', '*', '∗', '✻', '✽']
13
+
14
+ const MESSAGES = [
15
+ 'Accomplishing',
16
+ 'Actioning',
17
+ 'Actualizing',
18
+ 'Baking',
19
+ 'Brewing',
20
+ 'Calculating',
21
+ 'Cerebrating',
22
+ 'Churning',
23
+ 'Coding',
24
+ 'Coalescing',
25
+ 'Cogitating',
26
+ 'Computing',
27
+ 'Conjuring',
28
+ 'Considering',
29
+ 'Cooking',
30
+ 'Crafting',
31
+ 'Creating',
32
+ 'Crunching',
33
+ 'Deliberating',
34
+ 'Determining',
35
+ 'Doing',
36
+ 'Effecting',
37
+ 'Finagling',
38
+ 'Forging',
39
+ 'Forming',
40
+ 'Generating',
41
+ 'Hatching',
42
+ 'Herding',
43
+ 'Honking',
44
+ 'Hustling',
45
+ 'Ideating',
46
+ 'Inferring',
47
+ 'Manifesting',
48
+ 'Marinating',
49
+ 'Moseying',
50
+ 'Mulling',
51
+ 'Mustering',
52
+ 'Musing',
53
+ 'Noodling',
54
+ 'Percolating',
55
+ 'Pondering',
56
+ 'Processing',
57
+ 'Puttering',
58
+ 'Reticulating',
59
+ 'Ruminating',
60
+ 'Schlepping',
61
+ 'Shucking',
62
+ 'Simmering',
63
+ 'Smooshing',
64
+ 'Spinning',
65
+ 'Stewing',
66
+ 'Synthesizing',
67
+ 'Thinking',
68
+ 'Transmuting',
69
+ 'Vibing',
70
+ 'Working',
71
+ ]
72
+
73
+ export function Spinner(): React.ReactNode {
74
+ const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]
75
+ const [frame, setFrame] = useState(0)
76
+ const [elapsedTime, setElapsedTime] = useState(0)
77
+ const message = useRef(sample(MESSAGES))
78
+ const startTime = useRef(Date.now())
79
+
80
+ useEffect(() => {
81
+ const timer = setInterval(() => {
82
+ setFrame(f => (f + 1) % frames.length)
83
+ }, 120)
84
+
85
+ return () => clearInterval(timer)
86
+ }, [frames.length])
87
+
88
+ useEffect(() => {
89
+ const timer = setInterval(() => {
90
+ setElapsedTime(Math.floor((Date.now() - startTime.current) / 1000))
91
+ }, 1000)
92
+
93
+ return () => clearInterval(timer)
94
+ }, [])
95
+
96
+ return (
97
+ <Box flexDirection="row" marginTop={1}>
98
+ <Box flexWrap="nowrap" height={1} width={2}>
99
+ <Text color={getTheme().claude}>{frames[frame]}</Text>
100
+ </Box>
101
+ <Text color={getTheme().claude}>{message.current}… </Text>
102
+ <Text color={getTheme().secondaryText}>
103
+ ({elapsedTime}s · <Text bold>esc</Text> to interrupt)
104
+ </Text>
105
+ <Text color={getTheme().secondaryText}>
106
+ · {getSessionState('currentError')}
107
+ </Text>
108
+ </Box>
109
+ )
110
+ }
111
+
112
+ export function SimpleSpinner(): React.ReactNode {
113
+ const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]
114
+ const [frame, setFrame] = useState(0)
115
+
116
+ useEffect(() => {
117
+ const timer = setInterval(() => {
118
+ setFrame(f => (f + 1) % frames.length)
119
+ }, 120)
120
+
121
+ return () => clearInterval(timer)
122
+ }, [frames.length])
123
+
124
+ return (
125
+ <Box flexWrap="nowrap" height={1} width={2}>
126
+ <Text color={getTheme().claude}>{frames[frame]}</Text>
127
+ </Box>
128
+ )
129
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react'
2
+
3
+ export interface FormData {
4
+ // Define form data structure as needed
5
+ [key: string]: any
6
+ }
7
+
8
+ export interface StickerRequestFormProps {
9
+ // Define props as needed
10
+ onSubmit?: (data: FormData) => void
11
+ }
12
+
13
+ export const StickerRequestForm: React.FC<StickerRequestFormProps> = () => {
14
+ // Minimal component implementation
15
+ return null
16
+ }
@@ -0,0 +1,191 @@
1
+ import { Box, Text } from 'ink'
2
+ import * as React from 'react'
3
+ import { Hunk } from 'diff'
4
+ import { getTheme, ThemeNames } from '../utils/theme'
5
+ import { useMemo } from 'react'
6
+ import { wrapText } from '../utils/format'
7
+
8
+ type Props = {
9
+ patch: Hunk
10
+ dim: boolean
11
+ width: number
12
+ overrideTheme?: ThemeNames // custom theme for previews
13
+ key?: React.Key
14
+ }
15
+
16
+ export function StructuredDiff({
17
+ patch,
18
+ dim,
19
+ width,
20
+ overrideTheme,
21
+ }: Props): React.ReactNode {
22
+ const diff = useMemo(
23
+ () => formatDiff(patch.lines, patch.oldStart, width, dim, overrideTheme),
24
+ [patch.lines, patch.oldStart, width, dim, overrideTheme],
25
+ )
26
+
27
+ return diff.map((_, i) => <Box key={i}>{_}</Box>)
28
+ }
29
+
30
+ function formatDiff(
31
+ lines: string[],
32
+ startingLineNumber: number,
33
+ width: number,
34
+ dim: boolean,
35
+ overrideTheme?: ThemeNames,
36
+ ): React.ReactNode[] {
37
+ const theme = getTheme(overrideTheme)
38
+
39
+ const ls = numberDiffLines(
40
+ lines.map(code => {
41
+ if (code.startsWith('+')) {
42
+ return {
43
+ code: ' ' + code.slice(1),
44
+ i: 0,
45
+ type: 'add',
46
+ }
47
+ }
48
+ if (code.startsWith('-')) {
49
+ return {
50
+ code: ' ' + code.slice(1),
51
+ i: 0,
52
+ type: 'remove',
53
+ }
54
+ }
55
+ return { code, i: 0, type: 'nochange' }
56
+ }),
57
+ startingLineNumber,
58
+ )
59
+
60
+ const maxLineNumber = Math.max(...ls.map(({ i }) => i))
61
+ const maxWidth = maxLineNumber.toString().length
62
+
63
+ return ls.flatMap(({ type, code, i }) => {
64
+ const wrappedLines = wrapText(code, width - maxWidth)
65
+ return wrappedLines.map((line, lineIndex) => {
66
+ const key = `${type}-${i}-${lineIndex}`
67
+ switch (type) {
68
+ case 'add':
69
+ return (
70
+ <React.Fragment key={key}>
71
+ <Text>
72
+ <LineNumber
73
+ i={lineIndex === 0 ? i : undefined}
74
+ width={maxWidth}
75
+ />
76
+ <Text
77
+ color={overrideTheme ? theme.text : undefined}
78
+ backgroundColor={
79
+ dim ? theme.diff.addedDimmed : theme.diff.added
80
+ }
81
+ dimColor={dim}
82
+ >
83
+ {line}
84
+ </Text>
85
+ </Text>
86
+ </React.Fragment>
87
+ )
88
+ case 'remove':
89
+ return (
90
+ <React.Fragment key={key}>
91
+ <Text>
92
+ <LineNumber
93
+ i={lineIndex === 0 ? i : undefined}
94
+ width={maxWidth}
95
+ />
96
+ <Text
97
+ color={overrideTheme ? theme.text : undefined}
98
+ backgroundColor={
99
+ dim ? theme.diff.removedDimmed : theme.diff.removed
100
+ }
101
+ dimColor={dim}
102
+ >
103
+ {line}
104
+ </Text>
105
+ </Text>
106
+ </React.Fragment>
107
+ )
108
+ case 'nochange':
109
+ return (
110
+ <React.Fragment key={key}>
111
+ <Text>
112
+ <LineNumber
113
+ i={lineIndex === 0 ? i : undefined}
114
+ width={maxWidth}
115
+ />
116
+ <Text
117
+ color={overrideTheme ? theme.text : undefined}
118
+ dimColor={dim}
119
+ >
120
+ {line}
121
+ </Text>
122
+ </Text>
123
+ </React.Fragment>
124
+ )
125
+ }
126
+ })
127
+ })
128
+ }
129
+
130
+ function LineNumber({
131
+ i,
132
+ width,
133
+ }: {
134
+ i: number | undefined
135
+ width: number
136
+ }): React.ReactNode {
137
+ return (
138
+ <Text color={getTheme().secondaryText}>
139
+ {i !== undefined ? i.toString().padStart(width) : ' '.repeat(width)}{' '}
140
+ </Text>
141
+ )
142
+ }
143
+
144
+ function numberDiffLines(
145
+ diff: { code: string; type: string }[],
146
+ startLine: number,
147
+ ): { code: string; type: string; i: number }[] {
148
+ let i = startLine
149
+ const result: { code: string; type: string; i: number }[] = []
150
+ const queue = [...diff]
151
+
152
+ while (queue.length > 0) {
153
+ const { code, type } = queue.shift()!
154
+ const line = {
155
+ code: code,
156
+ type,
157
+ i,
158
+ }
159
+
160
+ // Update counters based on change type
161
+ switch (type) {
162
+ case 'nochange':
163
+ i++
164
+ result.push(line)
165
+ break
166
+ case 'add':
167
+ i++
168
+ result.push(line)
169
+ break
170
+ case 'remove': {
171
+ result.push(line)
172
+ let numRemoved = 0
173
+ while (queue[0]?.type === 'remove') {
174
+ i++
175
+ const { code, type } = queue.shift()!
176
+ const line = {
177
+ code: code,
178
+ type,
179
+ i,
180
+ }
181
+ result.push(line)
182
+ numRemoved++
183
+ }
184
+ i -= numRemoved
185
+ break
186
+ }
187
+ }
188
+ }
189
+
190
+ return result
191
+ }
@@ -0,0 +1,259 @@
1
+ import React from 'react'
2
+ import { Text, useInput } from 'ink'
3
+ import chalk from 'chalk'
4
+ import { useTextInput } from '../hooks/useTextInput'
5
+ import { getTheme } from '../utils/theme'
6
+ import { type Key } from 'ink'
7
+
8
+ export type Props = {
9
+ /**
10
+ * Optional callback for handling history navigation on up arrow at start of input
11
+ */
12
+ readonly onHistoryUp?: () => void
13
+
14
+ /**
15
+ * Optional callback for handling history navigation on down arrow at end of input
16
+ */
17
+ readonly onHistoryDown?: () => void
18
+
19
+ /**
20
+ * Text to display when `value` is empty.
21
+ */
22
+ readonly placeholder?: string
23
+
24
+ /**
25
+ * Allow multi-line input via line ending with backslash (default: `true`)
26
+ */
27
+ readonly multiline?: boolean
28
+
29
+ /**
30
+ * Listen to user's input. Useful in case there are multiple input components
31
+ * at the same time and input must be "routed" to a specific component.
32
+ */
33
+ readonly focus?: boolean
34
+
35
+ /**
36
+ * Replace all chars and mask the value. Useful for password inputs.
37
+ */
38
+ readonly mask?: string
39
+
40
+ /**
41
+ * Whether to show cursor and allow navigation inside text input with arrow keys.
42
+ */
43
+ readonly showCursor?: boolean
44
+
45
+ /**
46
+ * Highlight pasted text
47
+ */
48
+ readonly highlightPastedText?: boolean
49
+
50
+ /**
51
+ * Value to display in a text input.
52
+ */
53
+ readonly value: string
54
+
55
+ /**
56
+ * Function to call when value updates.
57
+ */
58
+ readonly onChange: (value: string) => void
59
+
60
+ /**
61
+ * Function to call when `Enter` is pressed, where first argument is a value of the input.
62
+ */
63
+ readonly onSubmit?: (value: string) => void
64
+
65
+ /**
66
+ * Function to call when Ctrl+C is pressed to exit.
67
+ */
68
+ readonly onExit?: () => void
69
+
70
+ /**
71
+ * Optional callback to show exit message
72
+ */
73
+ readonly onExitMessage?: (show: boolean, key?: string) => void
74
+
75
+ /**
76
+ * Optional callback to show custom message
77
+ */
78
+ readonly onMessage?: (show: boolean, message?: string) => void
79
+
80
+ /**
81
+ * Optional callback to reset history position
82
+ */
83
+ readonly onHistoryReset?: () => void
84
+
85
+ /**
86
+ * Number of columns to wrap text at
87
+ */
88
+ readonly columns: number
89
+
90
+ /**
91
+ * Optional callback when an image is pasted
92
+ */
93
+ readonly onImagePaste?: (base64Image: string) => void
94
+
95
+ /**
96
+ * Optional callback when a large text (over 800 chars) is pasted
97
+ */
98
+ readonly onPaste?: (text: string) => void
99
+
100
+ /**
101
+ * Whether the input is dimmed and non-interactive
102
+ */
103
+ readonly isDimmed?: boolean
104
+
105
+ /**
106
+ * Whether to disable cursor movement for up/down arrow keys
107
+ */
108
+ readonly disableCursorMovementForUpDownKeys?: boolean
109
+
110
+ /**
111
+ * Optional callback to handle special key combinations before input processing
112
+ * Return true to prevent default handling
113
+ */
114
+ readonly onSpecialKey?: (input: string, key: Key) => boolean
115
+
116
+ readonly cursorOffset: number
117
+
118
+ /**
119
+ * Callback to set the offset of the cursor
120
+ */
121
+ onChangeCursorOffset: (offset: number) => void
122
+ }
123
+
124
+ export default function TextInput({
125
+ value: originalValue,
126
+ placeholder = '',
127
+ focus = true,
128
+ mask,
129
+ multiline = false,
130
+ highlightPastedText = false,
131
+ showCursor = true,
132
+ onChange,
133
+ onSubmit,
134
+ onExit,
135
+ onHistoryUp,
136
+ onHistoryDown,
137
+ onExitMessage,
138
+ onMessage,
139
+ onHistoryReset,
140
+ columns,
141
+ onImagePaste,
142
+ onPaste,
143
+ isDimmed = false,
144
+ disableCursorMovementForUpDownKeys = false,
145
+ onSpecialKey,
146
+ cursorOffset,
147
+ onChangeCursorOffset,
148
+ }: Props) {
149
+ const { onInput, renderedValue } = useTextInput({
150
+ value: originalValue,
151
+ onChange,
152
+ onSubmit,
153
+ onExit,
154
+ onExitMessage,
155
+ onMessage,
156
+ onHistoryReset,
157
+ onHistoryUp,
158
+ onHistoryDown,
159
+ focus,
160
+ mask,
161
+ multiline,
162
+ cursorChar: showCursor ? ' ' : '',
163
+ highlightPastedText,
164
+ invert: chalk.inverse,
165
+ themeText: (text: string) => chalk.hex(getTheme().text)(text),
166
+ columns,
167
+ onImagePaste,
168
+ disableCursorMovementForUpDownKeys,
169
+ externalOffset: cursorOffset,
170
+ onOffsetChange: onChangeCursorOffset,
171
+ })
172
+
173
+ // Paste detection state
174
+ const [pasteState, setPasteState] = React.useState<{
175
+ chunks: string[]
176
+ timeoutId: ReturnType<typeof setTimeout> | null
177
+ }>({ chunks: [], timeoutId: null })
178
+
179
+ const resetPasteTimeout = (
180
+ currentTimeoutId: ReturnType<typeof setTimeout> | null,
181
+ ) => {
182
+ if (currentTimeoutId) {
183
+ clearTimeout(currentTimeoutId)
184
+ }
185
+ return setTimeout(() => {
186
+ setPasteState(({ chunks }) => {
187
+ const pastedText = chunks.join('')
188
+ // Schedule callback after current render to avoid state updates during render
189
+ Promise.resolve().then(() => onPaste!(pastedText))
190
+ return { chunks: [], timeoutId: null }
191
+ })
192
+ }, 100)
193
+ }
194
+
195
+ const wrappedOnInput = (input: string, key: Key): void => {
196
+ // Check for special key combinations first
197
+ if (onSpecialKey && onSpecialKey(input, key)) {
198
+ // Special key was handled, don't process further
199
+ return
200
+ }
201
+
202
+ // Special handling for backspace or delete
203
+ if (
204
+ key.backspace ||
205
+ key.delete ||
206
+ input === '\b' ||
207
+ input === '\x7f' ||
208
+ input === '\x08'
209
+ ) {
210
+ // Ensure backspace is handled directly
211
+ onInput(input, {
212
+ ...key,
213
+ backspace: true,
214
+ })
215
+ return
216
+ }
217
+
218
+ // Handle pastes (>800 chars)
219
+ // Usually we get one or two input characters at a time. If we
220
+ // get a bunch, the user has probably pasted.
221
+ // Unfortunately node batches long pastes, so it's possible
222
+ // that we would see e.g. 1024 characters and then just a few
223
+ // more in the next frame that belong with the original paste.
224
+ // This batching number is not consistent.
225
+ if (onPaste && (input.length > 800 || pasteState.timeoutId)) {
226
+ setPasteState(({ chunks, timeoutId }) => {
227
+ return {
228
+ chunks: [...chunks, input],
229
+ timeoutId: resetPasteTimeout(timeoutId),
230
+ }
231
+ })
232
+ return
233
+ }
234
+
235
+ onInput(input, key)
236
+ }
237
+
238
+ useInput(wrappedOnInput, { isActive: focus })
239
+
240
+ let renderedPlaceholder = placeholder
241
+ ? chalk.hex(getTheme().secondaryText)(placeholder)
242
+ : undefined
243
+
244
+ // Fake mouse cursor, because we like punishment
245
+ if (showCursor && focus) {
246
+ renderedPlaceholder =
247
+ placeholder.length > 0
248
+ ? chalk.inverse(placeholder[0]) +
249
+ chalk.hex(getTheme().secondaryText)(placeholder.slice(1))
250
+ : chalk.inverse(' ')
251
+ }
252
+
253
+ const showPlaceholder = originalValue.length == 0 && placeholder
254
+ return (
255
+ <Text wrap="truncate-end" dimColor={isDimmed}>
256
+ {showPlaceholder ? renderedPlaceholder : renderedValue}
257
+ </Text>
258
+ )
259
+ }
@@ -0,0 +1,11 @@
1
+ import React from 'react'
2
+
3
+ export interface TodoItemProps {
4
+ // Define props as needed
5
+ children?: React.ReactNode
6
+ }
7
+
8
+ export const TodoItem: React.FC<TodoItemProps> = ({ children }) => {
9
+ // Minimal component implementation
10
+ return <>{children}</>
11
+ }
@@ -0,0 +1,31 @@
1
+ import { Box, Text } from 'ink'
2
+ import * as React from 'react'
3
+ import { getTheme } from '../utils/theme'
4
+
5
+ type Props = {
6
+ tokenUsage: number
7
+ }
8
+
9
+ const MAX_TOKENS = 190_000
10
+ export const WARNING_THRESHOLD = MAX_TOKENS * 0.6
11
+ const ERROR_THRESHOLD = MAX_TOKENS * 0.8
12
+
13
+ export function TokenWarning({ tokenUsage }: Props): React.ReactNode {
14
+ const theme = getTheme()
15
+
16
+ if (tokenUsage < WARNING_THRESHOLD) {
17
+ return null
18
+ }
19
+
20
+ const isError = tokenUsage >= ERROR_THRESHOLD
21
+
22
+ return (
23
+ <Box flexDirection="row">
24
+ <Text color={isError ? theme.error : theme.warning}>
25
+ Context low (
26
+ {Math.max(0, 100 - Math.round((tokenUsage / MAX_TOKENS) * 100))}%
27
+ remaining) &middot; Run /compact to compact & continue
28
+ </Text>
29
+ </Box>
30
+ )
31
+ }
@@ -0,0 +1,40 @@
1
+ import { Box, Text } from 'ink'
2
+ import React from 'react'
3
+ import { useInterval } from '../hooks/useInterval'
4
+ import { getTheme } from '../utils/theme'
5
+ import { BLACK_CIRCLE } from '../constants/figures'
6
+
7
+ type Props = {
8
+ isError: boolean
9
+ isUnresolved: boolean
10
+ shouldAnimate: boolean
11
+ }
12
+
13
+ export function ToolUseLoader({
14
+ isError,
15
+ isUnresolved,
16
+ shouldAnimate,
17
+ }: Props): React.ReactNode {
18
+ const [isVisible, setIsVisible] = React.useState(true)
19
+
20
+ useInterval(() => {
21
+ if (!shouldAnimate) {
22
+ return
23
+ }
24
+ // To avoid flickering when the tool use confirm is visible, we set the loader to be visible
25
+ // when the tool use confirm is visible.
26
+ setIsVisible(_ => !_)
27
+ }, 600)
28
+
29
+ const color = isUnresolved
30
+ ? getTheme().secondaryText
31
+ : isError
32
+ ? getTheme().error
33
+ : getTheme().success
34
+
35
+ return (
36
+ <Box minWidth={2}>
37
+ <Text color={color}>{isVisible ? BLACK_CIRCLE : ' '}</Text>
38
+ </Box>
39
+ )
40
+ }