@shareai-lab/kode 1.1.14 → 1.1.16-dev.1

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 (289) hide show
  1. package/cli.js +77 -82
  2. package/dist/entrypoints/cli.js +59 -38
  3. package/dist/entrypoints/cli.js.map +3 -3
  4. package/dist/index.js +5 -26
  5. package/dist/package.json +4 -1
  6. package/package.json +11 -104
  7. package/dist/test/testAdapters.js +0 -88
  8. package/dist/test/testAdapters.js.map +0 -1
  9. package/src/ProjectOnboarding.tsx +0 -198
  10. package/src/Tool.ts +0 -83
  11. package/src/commands/agents.tsx +0 -3416
  12. package/src/commands/approvedTools.ts +0 -53
  13. package/src/commands/bug.tsx +0 -20
  14. package/src/commands/clear.ts +0 -43
  15. package/src/commands/compact.ts +0 -120
  16. package/src/commands/config.tsx +0 -19
  17. package/src/commands/cost.ts +0 -18
  18. package/src/commands/ctx_viz.ts +0 -209
  19. package/src/commands/doctor.ts +0 -24
  20. package/src/commands/help.tsx +0 -19
  21. package/src/commands/init.ts +0 -37
  22. package/src/commands/listen.ts +0 -42
  23. package/src/commands/login.tsx +0 -51
  24. package/src/commands/logout.tsx +0 -40
  25. package/src/commands/mcp.ts +0 -41
  26. package/src/commands/model.tsx +0 -40
  27. package/src/commands/modelstatus.tsx +0 -20
  28. package/src/commands/onboarding.tsx +0 -34
  29. package/src/commands/pr_comments.ts +0 -59
  30. package/src/commands/refreshCommands.ts +0 -54
  31. package/src/commands/release-notes.ts +0 -34
  32. package/src/commands/resume.tsx +0 -31
  33. package/src/commands/review.ts +0 -49
  34. package/src/commands/terminalSetup.ts +0 -221
  35. package/src/commands.ts +0 -139
  36. package/src/components/ApproveApiKey.tsx +0 -93
  37. package/src/components/AsciiLogo.tsx +0 -13
  38. package/src/components/AutoUpdater.tsx +0 -148
  39. package/src/components/Bug.tsx +0 -367
  40. package/src/components/Config.tsx +0 -293
  41. package/src/components/ConsoleOAuthFlow.tsx +0 -327
  42. package/src/components/Cost.tsx +0 -23
  43. package/src/components/CostThresholdDialog.tsx +0 -46
  44. package/src/components/CustomSelect/option-map.ts +0 -42
  45. package/src/components/CustomSelect/select-option.tsx +0 -78
  46. package/src/components/CustomSelect/select.tsx +0 -152
  47. package/src/components/CustomSelect/theme.ts +0 -45
  48. package/src/components/CustomSelect/use-select-state.ts +0 -414
  49. package/src/components/CustomSelect/use-select.ts +0 -35
  50. package/src/components/FallbackToolUseRejectedMessage.tsx +0 -15
  51. package/src/components/FileEditToolUpdatedMessage.tsx +0 -66
  52. package/src/components/Help.tsx +0 -215
  53. package/src/components/HighlightedCode.tsx +0 -33
  54. package/src/components/InvalidConfigDialog.tsx +0 -113
  55. package/src/components/Link.tsx +0 -32
  56. package/src/components/LogSelector.tsx +0 -86
  57. package/src/components/Logo.tsx +0 -170
  58. package/src/components/MCPServerApprovalDialog.tsx +0 -100
  59. package/src/components/MCPServerDialogCopy.tsx +0 -25
  60. package/src/components/MCPServerMultiselectDialog.tsx +0 -109
  61. package/src/components/Message.tsx +0 -221
  62. package/src/components/MessageResponse.tsx +0 -15
  63. package/src/components/MessageSelector.tsx +0 -211
  64. package/src/components/ModeIndicator.tsx +0 -88
  65. package/src/components/ModelConfig.tsx +0 -301
  66. package/src/components/ModelListManager.tsx +0 -227
  67. package/src/components/ModelSelector.tsx +0 -3387
  68. package/src/components/ModelStatusDisplay.tsx +0 -230
  69. package/src/components/Onboarding.tsx +0 -274
  70. package/src/components/PressEnterToContinue.tsx +0 -11
  71. package/src/components/PromptInput.tsx +0 -760
  72. package/src/components/SentryErrorBoundary.ts +0 -39
  73. package/src/components/Spinner.tsx +0 -129
  74. package/src/components/StickerRequestForm.tsx +0 -16
  75. package/src/components/StructuredDiff.tsx +0 -191
  76. package/src/components/TextInput.tsx +0 -259
  77. package/src/components/TodoItem.tsx +0 -47
  78. package/src/components/TokenWarning.tsx +0 -31
  79. package/src/components/ToolUseLoader.tsx +0 -40
  80. package/src/components/TrustDialog.tsx +0 -106
  81. package/src/components/binary-feedback/BinaryFeedback.tsx +0 -63
  82. package/src/components/binary-feedback/BinaryFeedbackOption.tsx +0 -111
  83. package/src/components/binary-feedback/BinaryFeedbackView.tsx +0 -172
  84. package/src/components/binary-feedback/utils.ts +0 -220
  85. package/src/components/messages/AssistantBashOutputMessage.tsx +0 -22
  86. package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +0 -49
  87. package/src/components/messages/AssistantRedactedThinkingMessage.tsx +0 -19
  88. package/src/components/messages/AssistantTextMessage.tsx +0 -144
  89. package/src/components/messages/AssistantThinkingMessage.tsx +0 -40
  90. package/src/components/messages/AssistantToolUseMessage.tsx +0 -132
  91. package/src/components/messages/TaskProgressMessage.tsx +0 -32
  92. package/src/components/messages/TaskToolMessage.tsx +0 -58
  93. package/src/components/messages/UserBashInputMessage.tsx +0 -28
  94. package/src/components/messages/UserCommandMessage.tsx +0 -30
  95. package/src/components/messages/UserKodingInputMessage.tsx +0 -28
  96. package/src/components/messages/UserPromptMessage.tsx +0 -35
  97. package/src/components/messages/UserTextMessage.tsx +0 -39
  98. package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +0 -12
  99. package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +0 -36
  100. package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +0 -31
  101. package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +0 -57
  102. package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +0 -35
  103. package/src/components/messages/UserToolResultMessage/utils.tsx +0 -56
  104. package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +0 -121
  105. package/src/components/permissions/FallbackPermissionRequest.tsx +0 -153
  106. package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +0 -182
  107. package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +0 -77
  108. package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +0 -164
  109. package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +0 -83
  110. package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +0 -240
  111. package/src/components/permissions/PermissionRequest.tsx +0 -101
  112. package/src/components/permissions/PermissionRequestTitle.tsx +0 -69
  113. package/src/components/permissions/hooks.ts +0 -44
  114. package/src/components/permissions/toolUseOptions.ts +0 -59
  115. package/src/components/permissions/utils.ts +0 -23
  116. package/src/constants/betas.ts +0 -5
  117. package/src/constants/claude-asterisk-ascii-art.tsx +0 -238
  118. package/src/constants/figures.ts +0 -4
  119. package/src/constants/keys.ts +0 -3
  120. package/src/constants/macros.ts +0 -11
  121. package/src/constants/modelCapabilities.ts +0 -179
  122. package/src/constants/models.ts +0 -1025
  123. package/src/constants/oauth.ts +0 -18
  124. package/src/constants/product.ts +0 -17
  125. package/src/constants/prompts.ts +0 -168
  126. package/src/constants/releaseNotes.ts +0 -7
  127. package/src/context/PermissionContext.tsx +0 -149
  128. package/src/context.ts +0 -278
  129. package/src/cost-tracker.ts +0 -84
  130. package/src/entrypoints/cli.tsx +0 -1561
  131. package/src/entrypoints/mcp.ts +0 -175
  132. package/src/history.ts +0 -25
  133. package/src/hooks/useApiKeyVerification.ts +0 -59
  134. package/src/hooks/useArrowKeyHistory.ts +0 -55
  135. package/src/hooks/useCanUseTool.ts +0 -138
  136. package/src/hooks/useCancelRequest.ts +0 -39
  137. package/src/hooks/useDoublePress.ts +0 -41
  138. package/src/hooks/useExitOnCtrlCD.ts +0 -31
  139. package/src/hooks/useInterval.ts +0 -25
  140. package/src/hooks/useLogMessages.ts +0 -16
  141. package/src/hooks/useLogStartupTime.ts +0 -12
  142. package/src/hooks/useNotifyAfterTimeout.ts +0 -65
  143. package/src/hooks/usePermissionRequestLogging.ts +0 -44
  144. package/src/hooks/useTerminalSize.ts +0 -49
  145. package/src/hooks/useTextInput.ts +0 -317
  146. package/src/hooks/useUnifiedCompletion.ts +0 -1405
  147. package/src/index.ts +0 -34
  148. package/src/messages.ts +0 -38
  149. package/src/permissions.ts +0 -268
  150. package/src/query.ts +0 -720
  151. package/src/screens/ConfigureNpmPrefix.tsx +0 -197
  152. package/src/screens/Doctor.tsx +0 -219
  153. package/src/screens/LogList.tsx +0 -68
  154. package/src/screens/REPL.tsx +0 -813
  155. package/src/screens/ResumeConversation.tsx +0 -68
  156. package/src/services/adapters/base.ts +0 -38
  157. package/src/services/adapters/chatCompletions.ts +0 -90
  158. package/src/services/adapters/responsesAPI.ts +0 -170
  159. package/src/services/browserMocks.ts +0 -66
  160. package/src/services/claude.ts +0 -2197
  161. package/src/services/customCommands.ts +0 -704
  162. package/src/services/fileFreshness.ts +0 -377
  163. package/src/services/gpt5ConnectionTest.ts +0 -340
  164. package/src/services/mcpClient.ts +0 -564
  165. package/src/services/mcpServerApproval.tsx +0 -50
  166. package/src/services/mentionProcessor.ts +0 -273
  167. package/src/services/modelAdapterFactory.ts +0 -69
  168. package/src/services/notifier.ts +0 -40
  169. package/src/services/oauth.ts +0 -357
  170. package/src/services/openai.ts +0 -1359
  171. package/src/services/responseStateManager.ts +0 -90
  172. package/src/services/sentry.ts +0 -3
  173. package/src/services/statsig.ts +0 -172
  174. package/src/services/statsigStorage.ts +0 -86
  175. package/src/services/systemReminder.ts +0 -507
  176. package/src/services/vcr.ts +0 -161
  177. package/src/test/testAdapters.ts +0 -96
  178. package/src/tools/ArchitectTool/ArchitectTool.tsx +0 -135
  179. package/src/tools/ArchitectTool/prompt.ts +0 -15
  180. package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +0 -576
  181. package/src/tools/BashTool/BashTool.tsx +0 -243
  182. package/src/tools/BashTool/BashToolResultMessage.tsx +0 -38
  183. package/src/tools/BashTool/OutputLine.tsx +0 -49
  184. package/src/tools/BashTool/prompt.ts +0 -174
  185. package/src/tools/BashTool/utils.ts +0 -56
  186. package/src/tools/FileEditTool/FileEditTool.tsx +0 -319
  187. package/src/tools/FileEditTool/prompt.ts +0 -51
  188. package/src/tools/FileEditTool/utils.ts +0 -58
  189. package/src/tools/FileReadTool/FileReadTool.tsx +0 -404
  190. package/src/tools/FileReadTool/prompt.ts +0 -7
  191. package/src/tools/FileWriteTool/FileWriteTool.tsx +0 -301
  192. package/src/tools/FileWriteTool/prompt.ts +0 -10
  193. package/src/tools/GlobTool/GlobTool.tsx +0 -119
  194. package/src/tools/GlobTool/prompt.ts +0 -8
  195. package/src/tools/GrepTool/GrepTool.tsx +0 -147
  196. package/src/tools/GrepTool/prompt.ts +0 -11
  197. package/src/tools/MCPTool/MCPTool.tsx +0 -107
  198. package/src/tools/MCPTool/prompt.ts +0 -3
  199. package/src/tools/MemoryReadTool/MemoryReadTool.tsx +0 -127
  200. package/src/tools/MemoryReadTool/prompt.ts +0 -3
  201. package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +0 -89
  202. package/src/tools/MemoryWriteTool/prompt.ts +0 -3
  203. package/src/tools/MultiEditTool/MultiEditTool.tsx +0 -388
  204. package/src/tools/MultiEditTool/prompt.ts +0 -45
  205. package/src/tools/NotebookEditTool/NotebookEditTool.tsx +0 -298
  206. package/src/tools/NotebookEditTool/prompt.ts +0 -3
  207. package/src/tools/NotebookReadTool/NotebookReadTool.tsx +0 -258
  208. package/src/tools/NotebookReadTool/prompt.ts +0 -3
  209. package/src/tools/StickerRequestTool/StickerRequestTool.tsx +0 -107
  210. package/src/tools/StickerRequestTool/prompt.ts +0 -19
  211. package/src/tools/TaskTool/TaskTool.tsx +0 -438
  212. package/src/tools/TaskTool/constants.ts +0 -1
  213. package/src/tools/TaskTool/prompt.ts +0 -92
  214. package/src/tools/ThinkTool/ThinkTool.tsx +0 -54
  215. package/src/tools/ThinkTool/prompt.ts +0 -12
  216. package/src/tools/TodoWriteTool/TodoWriteTool.tsx +0 -313
  217. package/src/tools/TodoWriteTool/prompt.ts +0 -63
  218. package/src/tools/URLFetcherTool/URLFetcherTool.tsx +0 -178
  219. package/src/tools/URLFetcherTool/cache.ts +0 -55
  220. package/src/tools/URLFetcherTool/htmlToMarkdown.ts +0 -55
  221. package/src/tools/URLFetcherTool/prompt.ts +0 -17
  222. package/src/tools/WebSearchTool/WebSearchTool.tsx +0 -103
  223. package/src/tools/WebSearchTool/prompt.ts +0 -13
  224. package/src/tools/WebSearchTool/searchProviders.ts +0 -66
  225. package/src/tools/lsTool/lsTool.tsx +0 -272
  226. package/src/tools/lsTool/prompt.ts +0 -2
  227. package/src/tools.ts +0 -67
  228. package/src/types/PermissionMode.ts +0 -120
  229. package/src/types/RequestContext.ts +0 -72
  230. package/src/types/common.d.ts +0 -2
  231. package/src/types/conversation.ts +0 -51
  232. package/src/types/logs.ts +0 -58
  233. package/src/types/modelCapabilities.ts +0 -64
  234. package/src/types/notebook.ts +0 -87
  235. package/src/utils/Cursor.ts +0 -436
  236. package/src/utils/PersistentShell.ts +0 -552
  237. package/src/utils/advancedFuzzyMatcher.ts +0 -290
  238. package/src/utils/agentLoader.ts +0 -278
  239. package/src/utils/agentStorage.ts +0 -97
  240. package/src/utils/array.ts +0 -3
  241. package/src/utils/ask.tsx +0 -99
  242. package/src/utils/auth.ts +0 -13
  243. package/src/utils/autoCompactCore.ts +0 -223
  244. package/src/utils/autoUpdater.ts +0 -458
  245. package/src/utils/betas.ts +0 -20
  246. package/src/utils/browser.ts +0 -14
  247. package/src/utils/cleanup.ts +0 -72
  248. package/src/utils/commands.ts +0 -261
  249. package/src/utils/commonUnixCommands.ts +0 -161
  250. package/src/utils/config.ts +0 -945
  251. package/src/utils/conversationRecovery.ts +0 -55
  252. package/src/utils/debugLogger.ts +0 -1235
  253. package/src/utils/diff.ts +0 -42
  254. package/src/utils/env.ts +0 -57
  255. package/src/utils/errors.ts +0 -21
  256. package/src/utils/exampleCommands.ts +0 -109
  257. package/src/utils/execFileNoThrow.ts +0 -51
  258. package/src/utils/expertChatStorage.ts +0 -136
  259. package/src/utils/file.ts +0 -405
  260. package/src/utils/fileRecoveryCore.ts +0 -71
  261. package/src/utils/format.tsx +0 -44
  262. package/src/utils/fuzzyMatcher.ts +0 -328
  263. package/src/utils/generators.ts +0 -62
  264. package/src/utils/git.ts +0 -92
  265. package/src/utils/globalLogger.ts +0 -77
  266. package/src/utils/http.ts +0 -10
  267. package/src/utils/imagePaste.ts +0 -38
  268. package/src/utils/json.ts +0 -13
  269. package/src/utils/log.ts +0 -382
  270. package/src/utils/markdown.ts +0 -213
  271. package/src/utils/messageContextManager.ts +0 -294
  272. package/src/utils/messages.tsx +0 -945
  273. package/src/utils/model.ts +0 -914
  274. package/src/utils/permissions/filesystem.ts +0 -127
  275. package/src/utils/responseState.ts +0 -23
  276. package/src/utils/ripgrep.ts +0 -167
  277. package/src/utils/secureFile.ts +0 -564
  278. package/src/utils/sessionState.ts +0 -49
  279. package/src/utils/state.ts +0 -25
  280. package/src/utils/style.ts +0 -29
  281. package/src/utils/terminal.ts +0 -50
  282. package/src/utils/theme.ts +0 -127
  283. package/src/utils/thinking.ts +0 -144
  284. package/src/utils/todoStorage.ts +0 -431
  285. package/src/utils/tokens.ts +0 -43
  286. package/src/utils/toolExecutionController.ts +0 -163
  287. package/src/utils/unaryLogging.ts +0 -26
  288. package/src/utils/user.ts +0 -37
  289. package/src/utils/validate.ts +0 -165
@@ -1,404 +0,0 @@
1
- import { ImageBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
2
- import { statSync } from 'node:fs'
3
- import { Box, Text } from 'ink'
4
- import * as path from 'node:path'
5
- import { extname, relative } from 'node:path'
6
- import * as React from 'react'
7
- import { z } from 'zod'
8
- import { FallbackToolUseRejectedMessage } from '../../components/FallbackToolUseRejectedMessage'
9
- import { HighlightedCode } from '../../components/HighlightedCode'
10
- import type { Tool } from '../../Tool'
11
- import { getCwd } from '../../utils/state'
12
- import {
13
- addLineNumbers,
14
- findSimilarFile,
15
- normalizeFilePath,
16
- readTextContent,
17
- } from '../../utils/file.js'
18
- import { logError } from '../../utils/log'
19
- import { getTheme } from '../../utils/theme'
20
- import { emitReminderEvent } from '../../services/systemReminder'
21
- import {
22
- recordFileRead,
23
- generateFileModificationReminder,
24
- } from '../../services/fileFreshness'
25
- import { DESCRIPTION, PROMPT } from './prompt'
26
- import { hasReadPermission } from '../../utils/permissions/filesystem'
27
- import { secureFileService } from '../../utils/secureFile'
28
-
29
- const MAX_LINES_TO_RENDER = 5
30
- const MAX_OUTPUT_SIZE = 0.25 * 1024 * 1024 // 0.25MB in bytes
31
-
32
- // Common image extensions
33
- const IMAGE_EXTENSIONS = new Set([
34
- '.png',
35
- '.jpg',
36
- '.jpeg',
37
- '.gif',
38
- '.bmp',
39
- '.webp',
40
- ])
41
-
42
- // Maximum dimensions for images
43
- const MAX_WIDTH = 2000
44
- const MAX_HEIGHT = 2000
45
- const MAX_IMAGE_SIZE = 3.75 * 1024 * 1024 // 5MB in bytes, with base64 encoding
46
-
47
- const inputSchema = z.strictObject({
48
- file_path: z.string().describe('The absolute path to the file to read'),
49
- offset: z
50
- .number()
51
- .optional()
52
- .describe(
53
- 'The line number to start reading from. Only provide if the file is too large to read at once',
54
- ),
55
- limit: z
56
- .number()
57
- .optional()
58
- .describe(
59
- 'The number of lines to read. Only provide if the file is too large to read at once.',
60
- ),
61
- })
62
-
63
- export const FileReadTool = {
64
- name: 'View',
65
- async description() {
66
- return DESCRIPTION
67
- },
68
- async prompt() {
69
- return PROMPT
70
- },
71
- inputSchema,
72
- isReadOnly() {
73
- return true
74
- },
75
- isConcurrencySafe() {
76
- return true // FileRead is read-only, safe for concurrent execution
77
- },
78
- userFacingName() {
79
- return 'Read'
80
- },
81
- async isEnabled() {
82
- return true
83
- },
84
- needsPermissions({ file_path }) {
85
- return !hasReadPermission(file_path || getCwd())
86
- },
87
- renderToolUseMessage(input, { verbose }) {
88
- const { file_path, ...rest } = input
89
- const entries = [
90
- ['file_path', verbose ? file_path : relative(getCwd(), file_path)],
91
- ...Object.entries(rest),
92
- ]
93
- return entries
94
- .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
95
- .join(', ')
96
- },
97
- renderToolResultMessage(output) {
98
- const verbose = false // Set default value for verbose
99
- // TODO: Render recursively
100
- switch (output.type) {
101
- case 'image':
102
- return (
103
- <Box justifyContent="space-between" overflowX="hidden" width="100%">
104
- <Box flexDirection="row">
105
- <Text>&nbsp;&nbsp;⎿ &nbsp;</Text>
106
- <Text>Read image</Text>
107
- </Box>
108
- </Box>
109
- )
110
- case 'text': {
111
- const { filePath, content, numLines } = output.file
112
- const contentWithFallback = content || '(No content)'
113
- return (
114
- <Box justifyContent="space-between" overflowX="hidden" width="100%">
115
- <Box flexDirection="row">
116
- <Text>&nbsp;&nbsp;⎿ &nbsp;</Text>
117
- <Box flexDirection="column">
118
- <HighlightedCode
119
- code={
120
- verbose
121
- ? contentWithFallback
122
- : contentWithFallback
123
- .split('\n')
124
- .slice(0, MAX_LINES_TO_RENDER)
125
- .filter(_ => _.trim() !== '')
126
- .join('\n')
127
- }
128
- language={extname(filePath).slice(1)}
129
- />
130
- {!verbose && numLines > MAX_LINES_TO_RENDER && (
131
- <Text color={getTheme().secondaryText}>
132
- ... (+{numLines - MAX_LINES_TO_RENDER} lines)
133
- </Text>
134
- )}
135
- </Box>
136
- </Box>
137
- </Box>
138
- )
139
- }
140
- }
141
- },
142
- renderToolUseRejectedMessage() {
143
- return <FallbackToolUseRejectedMessage />
144
- },
145
- async validateInput({ file_path, offset, limit }) {
146
- const fullFilePath = normalizeFilePath(file_path)
147
-
148
- // Use secure file service to check if file exists and get file info
149
- const fileCheck = secureFileService.safeGetFileInfo(fullFilePath)
150
- if (!fileCheck.success) {
151
- // Try to find a similar file with a different extension
152
- const similarFilename = findSimilarFile(fullFilePath)
153
- let message = 'File does not exist.'
154
-
155
- // If we found a similar file, suggest it to the assistant
156
- if (similarFilename) {
157
- message += ` Did you mean ${similarFilename}?`
158
- }
159
-
160
- return {
161
- result: false,
162
- message,
163
- }
164
- }
165
-
166
- const stats = fileCheck.stats!
167
- const fileSize = stats.size
168
- const ext = path.extname(fullFilePath).toLowerCase()
169
-
170
- // Skip size check for image files - they have their own size limits
171
- if (!IMAGE_EXTENSIONS.has(ext)) {
172
- // If file is too large and no offset/limit provided
173
- if (fileSize > MAX_OUTPUT_SIZE && !offset && !limit) {
174
- return {
175
- result: false,
176
- message: formatFileSizeError(fileSize),
177
- meta: { fileSize },
178
- }
179
- }
180
- }
181
-
182
- return { result: true }
183
- },
184
- async *call(
185
- { file_path, offset = 1, limit = undefined },
186
- { readFileTimestamps },
187
- ) {
188
- const ext = path.extname(file_path).toLowerCase()
189
- const fullFilePath = normalizeFilePath(file_path)
190
-
191
- // Record file read for freshness tracking
192
- recordFileRead(fullFilePath)
193
-
194
- // Emit file read event for system reminders
195
- emitReminderEvent('file:read', {
196
- filePath: fullFilePath,
197
- extension: ext,
198
- timestamp: Date.now(),
199
- })
200
-
201
- // Update read timestamp, to invalidate stale writes
202
- readFileTimestamps[fullFilePath] = Date.now()
203
-
204
- // Check for file modifications and generate reminder if needed
205
- const modificationReminder = generateFileModificationReminder(fullFilePath)
206
- if (modificationReminder) {
207
- emitReminderEvent('file:modified', {
208
- filePath: fullFilePath,
209
- reminder: modificationReminder,
210
- timestamp: Date.now(),
211
- })
212
- }
213
-
214
- // If it's an image file, process and return base64 encoded contents
215
- if (IMAGE_EXTENSIONS.has(ext)) {
216
- const data = await readImage(fullFilePath, ext)
217
- yield {
218
- type: 'result',
219
- data,
220
- resultForAssistant: this.renderResultForAssistant(data),
221
- }
222
- return
223
- }
224
-
225
- // Handle offset properly - if offset is 0, don't subtract 1
226
- const lineOffset = offset === 0 ? 0 : offset - 1
227
- const { content, lineCount, totalLines } = readTextContent(
228
- fullFilePath,
229
- lineOffset,
230
- limit,
231
- )
232
-
233
- // Add size validation after reading for non-image files
234
- if (!IMAGE_EXTENSIONS.has(ext) && content.length > MAX_OUTPUT_SIZE) {
235
- throw new Error(formatFileSizeError(content.length))
236
- }
237
-
238
- const data = {
239
- type: 'text' as const,
240
- file: {
241
- filePath: file_path,
242
- content: content,
243
- numLines: lineCount,
244
- startLine: offset,
245
- totalLines,
246
- },
247
- }
248
-
249
- yield {
250
- type: 'result',
251
- data,
252
- resultForAssistant: this.renderResultForAssistant(data),
253
- }
254
- },
255
- renderResultForAssistant(data) {
256
- switch (data.type) {
257
- case 'image':
258
- return [
259
- {
260
- type: 'image',
261
- source: {
262
- type: 'base64',
263
- data: data.file.base64,
264
- media_type: data.file.type,
265
- },
266
- },
267
- ]
268
- case 'text':
269
- return addLineNumbers(data.file)
270
- }
271
- },
272
- } satisfies Tool<
273
- typeof inputSchema,
274
- | {
275
- type: 'text'
276
- file: {
277
- filePath: string
278
- content: string
279
- numLines: number
280
- startLine: number
281
- totalLines: number
282
- }
283
- }
284
- | {
285
- type: 'image'
286
- file: { base64: string; type: ImageBlockParam.Source['media_type'] }
287
- }
288
- >
289
-
290
- const formatFileSizeError = (sizeInBytes: number) =>
291
- `File content (${Math.round(sizeInBytes / 1024)}KB) exceeds maximum allowed size (${Math.round(MAX_OUTPUT_SIZE / 1024)}KB). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content.`
292
-
293
- function createImageResponse(
294
- buffer: Buffer,
295
- ext: string,
296
- ): {
297
- type: 'image'
298
- file: { base64: string; type: ImageBlockParam.Source['media_type'] }
299
- } {
300
- return {
301
- type: 'image',
302
- file: {
303
- base64: buffer.toString('base64'),
304
- type: `image/${ext.slice(1)}` as ImageBlockParam.Source['media_type'],
305
- },
306
- }
307
- }
308
-
309
- async function readImage(
310
- filePath: string,
311
- ext: string,
312
- ): Promise<{
313
- type: 'image'
314
- file: { base64: string; type: ImageBlockParam.Source['media_type'] }
315
- }> {
316
- try {
317
- const stats = statSync(filePath)
318
- const sharp = (
319
- (await import('sharp')) as unknown as { default: typeof import('sharp') }
320
- ).default
321
-
322
- // Use secure file service to read the file
323
- const fileReadResult = secureFileService.safeReadFile(filePath, {
324
- encoding: 'buffer' as BufferEncoding,
325
- maxFileSize: MAX_IMAGE_SIZE
326
- })
327
-
328
- if (!fileReadResult.success) {
329
- throw new Error(`Failed to read image file: ${fileReadResult.error}`)
330
- }
331
-
332
- const image = sharp(fileReadResult.content as Buffer)
333
- const metadata = await image.metadata()
334
-
335
- if (!metadata.width || !metadata.height) {
336
- if (stats.size > MAX_IMAGE_SIZE) {
337
- const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()
338
- return createImageResponse(compressedBuffer, 'jpeg')
339
- }
340
- }
341
-
342
- // Calculate dimensions while maintaining aspect ratio
343
- let width = metadata.width || 0
344
- let height = metadata.height || 0
345
-
346
- // Check if the original file just works
347
- if (
348
- stats.size <= MAX_IMAGE_SIZE &&
349
- width <= MAX_WIDTH &&
350
- height <= MAX_HEIGHT
351
- ) {
352
- // Use secure file service to read the file
353
- const fileReadResult = secureFileService.safeReadFile(filePath, {
354
- encoding: 'buffer' as BufferEncoding,
355
- maxFileSize: MAX_IMAGE_SIZE
356
- })
357
-
358
- if (!fileReadResult.success) {
359
- throw new Error(`Failed to read image file: ${fileReadResult.error}`)
360
- }
361
-
362
- return createImageResponse(fileReadResult.content as Buffer, ext)
363
- }
364
-
365
- if (width > MAX_WIDTH) {
366
- height = Math.round((height * MAX_WIDTH) / width)
367
- width = MAX_WIDTH
368
- }
369
-
370
- if (height > MAX_HEIGHT) {
371
- width = Math.round((width * MAX_HEIGHT) / height)
372
- height = MAX_HEIGHT
373
- }
374
-
375
- // Resize image and convert to buffer
376
- const resizedImageBuffer = await image
377
- .resize(width, height, {
378
- fit: 'inside',
379
- withoutEnlargement: true,
380
- })
381
- .toBuffer()
382
-
383
- // If still too large after resize, compress quality
384
- if (resizedImageBuffer.length > MAX_IMAGE_SIZE) {
385
- const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()
386
- return createImageResponse(compressedBuffer, 'jpeg')
387
- }
388
-
389
- return createImageResponse(resizedImageBuffer, ext)
390
- } catch (e) {
391
- logError(e)
392
- // If any error occurs during processing, return original image
393
- const fileReadResult = secureFileService.safeReadFile(filePath, {
394
- encoding: 'buffer' as BufferEncoding,
395
- maxFileSize: MAX_IMAGE_SIZE
396
- })
397
-
398
- if (!fileReadResult.success) {
399
- throw new Error(`Failed to read image file: ${fileReadResult.error}`)
400
- }
401
-
402
- return createImageResponse(fileReadResult.content as Buffer, ext)
403
- }
404
- }
@@ -1,7 +0,0 @@
1
- import { NotebookReadTool } from '../NotebookReadTool/NotebookReadTool'
2
-
3
- const MAX_LINES_TO_READ = 2000
4
- const MAX_LINE_LENGTH = 2000
5
-
6
- export const DESCRIPTION = 'Read a file from the local filesystem.'
7
- export const PROMPT = `Reads a file from the local filesystem. The file_path parameter must be an absolute path, not a relative path. By default, it reads up to ${MAX_LINES_TO_READ} lines starting from the beginning of the file. You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters. Any lines longer than ${MAX_LINE_LENGTH} characters will be truncated. For image files, the tool will display the image for you. For Jupyter notebooks (.ipynb files), use the ${NotebookReadTool.name} instead.`
@@ -1,301 +0,0 @@
1
- import { Hunk } from 'diff'
2
- import { existsSync, mkdirSync, readFileSync, statSync } from 'fs'
3
- import { Box, Text } from 'ink'
4
- import { EOL } from 'os'
5
- import { dirname, extname, isAbsolute, relative, resolve, sep } from 'path'
6
- import * as React from 'react'
7
- import { z } from 'zod'
8
- import { FileEditToolUpdatedMessage } from '../../components/FileEditToolUpdatedMessage'
9
- import { HighlightedCode } from '../../components/HighlightedCode'
10
- import { StructuredDiff } from '../../components/StructuredDiff'
11
- import { FallbackToolUseRejectedMessage } from '../../components/FallbackToolUseRejectedMessage'
12
- import { logEvent } from '../../services/statsig'
13
- import type { Tool } from '../../Tool'
14
- import { intersperse } from '../../utils/array'
15
- import {
16
- addLineNumbers,
17
- detectFileEncoding,
18
- detectLineEndings,
19
- detectRepoLineEndings,
20
- writeTextContent,
21
- } from '../../utils/file.js'
22
- import { logError } from '../../utils/log'
23
- import { getCwd } from '../../utils/state'
24
- import { getTheme } from '../../utils/theme'
25
- import { PROMPT } from './prompt'
26
- import { hasWritePermission } from '../../utils/permissions/filesystem'
27
- import { getPatch } from '../../utils/diff'
28
- import { PROJECT_FILE } from '../../constants/product'
29
- import { emitReminderEvent } from '../../services/systemReminder'
30
- import { recordFileEdit } from '../../services/fileFreshness'
31
-
32
- const MAX_LINES_TO_RENDER = 5
33
- const MAX_LINES_TO_RENDER_FOR_ASSISTANT = 16000
34
- const TRUNCATED_MESSAGE =
35
- '<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with Grep in order to find the line numbers of what you are looking for.</NOTE>'
36
-
37
- const inputSchema = z.strictObject({
38
- file_path: z
39
- .string()
40
- .describe(
41
- 'The absolute path to the file to write (must be absolute, not relative)',
42
- ),
43
- content: z.string().describe('The content to write to the file'),
44
- })
45
-
46
- export const FileWriteTool = {
47
- name: 'Replace',
48
- async description() {
49
- return 'Write a file to the local filesystem.'
50
- },
51
- userFacingName: () => 'Write',
52
- async prompt() {
53
- return PROMPT
54
- },
55
- inputSchema,
56
- async isEnabled() {
57
- return true
58
- },
59
- isReadOnly() {
60
- return false
61
- },
62
- isConcurrencySafe() {
63
- return false // FileWriteTool modifies state/files, not safe for concurrent execution
64
- },
65
- needsPermissions({ file_path }) {
66
- return !hasWritePermission(file_path)
67
- },
68
- renderToolUseMessage(input, { verbose }) {
69
- return `file_path: ${verbose ? input.file_path : relative(getCwd(), input.file_path)}`
70
- },
71
- renderToolUseRejectedMessage({ file_path, content }: any = {}, { columns, verbose }: any = {}) {
72
- try {
73
- if (!file_path) {
74
- return <FallbackToolUseRejectedMessage />
75
- }
76
- const fullFilePath = isAbsolute(file_path)
77
- ? file_path
78
- : resolve(getCwd(), file_path)
79
- const oldFileExists = existsSync(fullFilePath)
80
- const enc = oldFileExists ? detectFileEncoding(fullFilePath) : 'utf-8'
81
- const oldContent = oldFileExists ? readFileSync(fullFilePath, enc) : null
82
- const type = oldContent ? 'update' : 'create'
83
- const patch = getPatch({
84
- filePath: file_path,
85
- fileContents: oldContent ?? '',
86
- oldStr: oldContent ?? '',
87
- newStr: content,
88
- })
89
-
90
- return (
91
- <Box flexDirection="column">
92
- <Text>
93
- {' '}⎿{' '}
94
- <Text color={getTheme().error}>
95
- User rejected {type === 'update' ? 'update' : 'write'} to{' '}
96
- </Text>
97
- <Text bold>
98
- {verbose ? file_path : relative(getCwd(), file_path)}
99
- </Text>
100
- </Text>
101
- {intersperse(
102
- patch.map(_ => (
103
- <Box flexDirection="column" paddingLeft={5} key={_.newStart}>
104
- <StructuredDiff patch={_} dim={true} width={columns - 12} />
105
- </Box>
106
- )),
107
- i => (
108
- <Box paddingLeft={5} key={`ellipsis-${i}`}>
109
- <Text color={getTheme().secondaryText}>...</Text>
110
- </Box>
111
- ),
112
- )}
113
- </Box>
114
- )
115
- } catch (e) {
116
- // Handle the case where while we were showing the diff, the user manually made the change.
117
- // TODO: Find a way to show the diff in this case
118
- logError(e)
119
- return (
120
- <Box flexDirection="column">
121
- <Text>{' '}⎿ (No changes)</Text>
122
- </Box>
123
- )
124
- }
125
- },
126
- renderToolResultMessage(
127
- { filePath, content, structuredPatch, type }
128
- ) {
129
- const verbose = false // Default to false since verbose is no longer passed
130
- switch (type) {
131
- case 'create': {
132
- const contentWithFallback = content || '(No content)'
133
- const numLines = content.split(EOL).length
134
-
135
- return (
136
- <Box flexDirection="column">
137
- <Text>
138
- {' '}⎿ Wrote {numLines} lines to{' '}
139
- <Text bold>
140
- {verbose ? filePath : relative(getCwd(), filePath)}
141
- </Text>
142
- </Text>
143
- <Box flexDirection="column" paddingLeft={5}>
144
- <HighlightedCode
145
- code={
146
- verbose
147
- ? contentWithFallback
148
- : contentWithFallback
149
- .split('\n')
150
- .slice(0, MAX_LINES_TO_RENDER)
151
- .filter(_ => _.trim() !== '')
152
- .join('\n')
153
- }
154
- language={extname(filePath).slice(1)}
155
- />
156
- {!verbose && numLines > MAX_LINES_TO_RENDER && (
157
- <Text color={getTheme().secondaryText}>
158
- ... (+{numLines - MAX_LINES_TO_RENDER} lines)
159
- </Text>
160
- )}
161
- </Box>
162
- </Box>
163
- )
164
- }
165
- case 'update':
166
- return (
167
- <FileEditToolUpdatedMessage
168
- filePath={filePath}
169
- structuredPatch={structuredPatch}
170
- verbose={verbose}
171
- />
172
- )
173
- }
174
- },
175
- async validateInput({ file_path }, { readFileTimestamps }) {
176
- const fullFilePath = isAbsolute(file_path)
177
- ? file_path
178
- : resolve(getCwd(), file_path)
179
- if (!existsSync(fullFilePath)) {
180
- return { result: true }
181
- }
182
-
183
- const readTimestamp = readFileTimestamps[fullFilePath]
184
- if (!readTimestamp) {
185
- return {
186
- result: false,
187
- message:
188
- 'File has not been read yet. Read it first before writing to it.',
189
- }
190
- }
191
-
192
- // Check if file exists and get its last modified time
193
- const stats = statSync(fullFilePath)
194
- const lastWriteTime = stats.mtimeMs
195
- if (lastWriteTime > readTimestamp) {
196
- return {
197
- result: false,
198
- message:
199
- 'File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.',
200
- }
201
- }
202
-
203
- return { result: true }
204
- },
205
- async *call({ file_path, content }, { readFileTimestamps }) {
206
- const fullFilePath = isAbsolute(file_path)
207
- ? file_path
208
- : resolve(getCwd(), file_path)
209
- const dir = dirname(fullFilePath)
210
- const oldFileExists = existsSync(fullFilePath)
211
- const enc = oldFileExists ? detectFileEncoding(fullFilePath) : 'utf-8'
212
- const oldContent = oldFileExists ? readFileSync(fullFilePath, enc) : null
213
-
214
- const endings = oldFileExists
215
- ? detectLineEndings(fullFilePath)
216
- : await detectRepoLineEndings(getCwd())
217
-
218
- mkdirSync(dir, { recursive: true })
219
- writeTextContent(fullFilePath, content, enc, endings!)
220
-
221
- // Record Agent edit operation for file freshness tracking
222
- recordFileEdit(fullFilePath, content)
223
-
224
- // Update read timestamp, to invalidate stale writes
225
- readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs
226
-
227
- // Log when writing to CLAUDE.md
228
- if (fullFilePath.endsWith(`${sep}${PROJECT_FILE}`)) {
229
- logEvent('tengu_write_claudemd', {})
230
- }
231
-
232
- // Emit file edited event for system reminders
233
- emitReminderEvent('file:edited', {
234
- filePath: fullFilePath,
235
- content,
236
- oldContent: oldContent || '',
237
- timestamp: Date.now(),
238
- operation: oldFileExists ? 'update' : 'create',
239
- })
240
-
241
- if (oldContent) {
242
- const patch = getPatch({
243
- filePath: file_path,
244
- fileContents: oldContent,
245
- oldStr: oldContent,
246
- newStr: content,
247
- })
248
-
249
- const data = {
250
- type: 'update' as const,
251
- filePath: file_path,
252
- content,
253
- structuredPatch: patch,
254
- }
255
- yield {
256
- type: 'result',
257
- data,
258
- resultForAssistant: this.renderResultForAssistant(data),
259
- }
260
- return
261
- }
262
-
263
- const data = {
264
- type: 'create' as const,
265
- filePath: file_path,
266
- content,
267
- structuredPatch: [],
268
- }
269
- yield {
270
- type: 'result',
271
- data,
272
- resultForAssistant: this.renderResultForAssistant(data),
273
- }
274
- },
275
- renderResultForAssistant({ filePath, content, type }) {
276
- switch (type) {
277
- case 'create':
278
- return `File created successfully at: ${filePath}`
279
- case 'update':
280
- return `The file ${filePath} has been updated. Here's the result of running \`cat -n\` on a snippet of the edited file:
281
- ${addLineNumbers({
282
- content:
283
- content.split(/\r?\n/).length > MAX_LINES_TO_RENDER_FOR_ASSISTANT
284
- ? content
285
- .split(/\r?\n/)
286
- .slice(0, MAX_LINES_TO_RENDER_FOR_ASSISTANT)
287
- .join('\n') + TRUNCATED_MESSAGE
288
- : content,
289
- startLine: 1,
290
- })}`
291
- }
292
- },
293
- } satisfies Tool<
294
- typeof inputSchema,
295
- {
296
- type: 'create' | 'update'
297
- filePath: string
298
- content: string
299
- structuredPatch: Hunk[]
300
- }
301
- >