@shareai-lab/kode 1.1.13 → 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 (288) hide show
  1. package/dist/entrypoints/cli.js +59 -38
  2. package/dist/entrypoints/cli.js.map +3 -3
  3. package/dist/index.js +5 -26
  4. package/dist/package.json +4 -1
  5. package/package.json +11 -104
  6. package/dist/test/testAdapters.js +0 -88
  7. package/dist/test/testAdapters.js.map +0 -1
  8. package/src/ProjectOnboarding.tsx +0 -198
  9. package/src/Tool.ts +0 -83
  10. package/src/commands/agents.tsx +0 -3416
  11. package/src/commands/approvedTools.ts +0 -53
  12. package/src/commands/bug.tsx +0 -20
  13. package/src/commands/clear.ts +0 -43
  14. package/src/commands/compact.ts +0 -120
  15. package/src/commands/config.tsx +0 -19
  16. package/src/commands/cost.ts +0 -18
  17. package/src/commands/ctx_viz.ts +0 -209
  18. package/src/commands/doctor.ts +0 -24
  19. package/src/commands/help.tsx +0 -19
  20. package/src/commands/init.ts +0 -37
  21. package/src/commands/listen.ts +0 -42
  22. package/src/commands/login.tsx +0 -51
  23. package/src/commands/logout.tsx +0 -40
  24. package/src/commands/mcp.ts +0 -41
  25. package/src/commands/model.tsx +0 -40
  26. package/src/commands/modelstatus.tsx +0 -20
  27. package/src/commands/onboarding.tsx +0 -34
  28. package/src/commands/pr_comments.ts +0 -59
  29. package/src/commands/refreshCommands.ts +0 -54
  30. package/src/commands/release-notes.ts +0 -34
  31. package/src/commands/resume.tsx +0 -31
  32. package/src/commands/review.ts +0 -49
  33. package/src/commands/terminalSetup.ts +0 -221
  34. package/src/commands.ts +0 -139
  35. package/src/components/ApproveApiKey.tsx +0 -93
  36. package/src/components/AsciiLogo.tsx +0 -13
  37. package/src/components/AutoUpdater.tsx +0 -148
  38. package/src/components/Bug.tsx +0 -367
  39. package/src/components/Config.tsx +0 -293
  40. package/src/components/ConsoleOAuthFlow.tsx +0 -327
  41. package/src/components/Cost.tsx +0 -23
  42. package/src/components/CostThresholdDialog.tsx +0 -46
  43. package/src/components/CustomSelect/option-map.ts +0 -42
  44. package/src/components/CustomSelect/select-option.tsx +0 -78
  45. package/src/components/CustomSelect/select.tsx +0 -152
  46. package/src/components/CustomSelect/theme.ts +0 -45
  47. package/src/components/CustomSelect/use-select-state.ts +0 -414
  48. package/src/components/CustomSelect/use-select.ts +0 -35
  49. package/src/components/FallbackToolUseRejectedMessage.tsx +0 -15
  50. package/src/components/FileEditToolUpdatedMessage.tsx +0 -66
  51. package/src/components/Help.tsx +0 -215
  52. package/src/components/HighlightedCode.tsx +0 -33
  53. package/src/components/InvalidConfigDialog.tsx +0 -113
  54. package/src/components/Link.tsx +0 -32
  55. package/src/components/LogSelector.tsx +0 -86
  56. package/src/components/Logo.tsx +0 -170
  57. package/src/components/MCPServerApprovalDialog.tsx +0 -100
  58. package/src/components/MCPServerDialogCopy.tsx +0 -25
  59. package/src/components/MCPServerMultiselectDialog.tsx +0 -109
  60. package/src/components/Message.tsx +0 -221
  61. package/src/components/MessageResponse.tsx +0 -15
  62. package/src/components/MessageSelector.tsx +0 -211
  63. package/src/components/ModeIndicator.tsx +0 -88
  64. package/src/components/ModelConfig.tsx +0 -301
  65. package/src/components/ModelListManager.tsx +0 -227
  66. package/src/components/ModelSelector.tsx +0 -3387
  67. package/src/components/ModelStatusDisplay.tsx +0 -230
  68. package/src/components/Onboarding.tsx +0 -274
  69. package/src/components/PressEnterToContinue.tsx +0 -11
  70. package/src/components/PromptInput.tsx +0 -760
  71. package/src/components/SentryErrorBoundary.ts +0 -39
  72. package/src/components/Spinner.tsx +0 -129
  73. package/src/components/StickerRequestForm.tsx +0 -16
  74. package/src/components/StructuredDiff.tsx +0 -191
  75. package/src/components/TextInput.tsx +0 -259
  76. package/src/components/TodoItem.tsx +0 -47
  77. package/src/components/TokenWarning.tsx +0 -31
  78. package/src/components/ToolUseLoader.tsx +0 -40
  79. package/src/components/TrustDialog.tsx +0 -106
  80. package/src/components/binary-feedback/BinaryFeedback.tsx +0 -63
  81. package/src/components/binary-feedback/BinaryFeedbackOption.tsx +0 -111
  82. package/src/components/binary-feedback/BinaryFeedbackView.tsx +0 -172
  83. package/src/components/binary-feedback/utils.ts +0 -220
  84. package/src/components/messages/AssistantBashOutputMessage.tsx +0 -22
  85. package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +0 -49
  86. package/src/components/messages/AssistantRedactedThinkingMessage.tsx +0 -19
  87. package/src/components/messages/AssistantTextMessage.tsx +0 -144
  88. package/src/components/messages/AssistantThinkingMessage.tsx +0 -40
  89. package/src/components/messages/AssistantToolUseMessage.tsx +0 -132
  90. package/src/components/messages/TaskProgressMessage.tsx +0 -32
  91. package/src/components/messages/TaskToolMessage.tsx +0 -58
  92. package/src/components/messages/UserBashInputMessage.tsx +0 -28
  93. package/src/components/messages/UserCommandMessage.tsx +0 -30
  94. package/src/components/messages/UserKodingInputMessage.tsx +0 -28
  95. package/src/components/messages/UserPromptMessage.tsx +0 -35
  96. package/src/components/messages/UserTextMessage.tsx +0 -39
  97. package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +0 -12
  98. package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +0 -36
  99. package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +0 -31
  100. package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +0 -57
  101. package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +0 -35
  102. package/src/components/messages/UserToolResultMessage/utils.tsx +0 -56
  103. package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +0 -121
  104. package/src/components/permissions/FallbackPermissionRequest.tsx +0 -153
  105. package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +0 -182
  106. package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +0 -77
  107. package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +0 -164
  108. package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +0 -83
  109. package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +0 -240
  110. package/src/components/permissions/PermissionRequest.tsx +0 -101
  111. package/src/components/permissions/PermissionRequestTitle.tsx +0 -69
  112. package/src/components/permissions/hooks.ts +0 -44
  113. package/src/components/permissions/toolUseOptions.ts +0 -59
  114. package/src/components/permissions/utils.ts +0 -23
  115. package/src/constants/betas.ts +0 -5
  116. package/src/constants/claude-asterisk-ascii-art.tsx +0 -238
  117. package/src/constants/figures.ts +0 -4
  118. package/src/constants/keys.ts +0 -3
  119. package/src/constants/macros.ts +0 -11
  120. package/src/constants/modelCapabilities.ts +0 -179
  121. package/src/constants/models.ts +0 -1025
  122. package/src/constants/oauth.ts +0 -18
  123. package/src/constants/product.ts +0 -17
  124. package/src/constants/prompts.ts +0 -168
  125. package/src/constants/releaseNotes.ts +0 -7
  126. package/src/context/PermissionContext.tsx +0 -149
  127. package/src/context.ts +0 -278
  128. package/src/cost-tracker.ts +0 -84
  129. package/src/entrypoints/cli.tsx +0 -1561
  130. package/src/entrypoints/mcp.ts +0 -175
  131. package/src/history.ts +0 -25
  132. package/src/hooks/useApiKeyVerification.ts +0 -59
  133. package/src/hooks/useArrowKeyHistory.ts +0 -55
  134. package/src/hooks/useCanUseTool.ts +0 -138
  135. package/src/hooks/useCancelRequest.ts +0 -39
  136. package/src/hooks/useDoublePress.ts +0 -41
  137. package/src/hooks/useExitOnCtrlCD.ts +0 -31
  138. package/src/hooks/useInterval.ts +0 -25
  139. package/src/hooks/useLogMessages.ts +0 -16
  140. package/src/hooks/useLogStartupTime.ts +0 -12
  141. package/src/hooks/useNotifyAfterTimeout.ts +0 -65
  142. package/src/hooks/usePermissionRequestLogging.ts +0 -44
  143. package/src/hooks/useTerminalSize.ts +0 -49
  144. package/src/hooks/useTextInput.ts +0 -317
  145. package/src/hooks/useUnifiedCompletion.ts +0 -1405
  146. package/src/index.ts +0 -34
  147. package/src/messages.ts +0 -38
  148. package/src/permissions.ts +0 -268
  149. package/src/query.ts +0 -720
  150. package/src/screens/ConfigureNpmPrefix.tsx +0 -197
  151. package/src/screens/Doctor.tsx +0 -219
  152. package/src/screens/LogList.tsx +0 -68
  153. package/src/screens/REPL.tsx +0 -813
  154. package/src/screens/ResumeConversation.tsx +0 -68
  155. package/src/services/adapters/base.ts +0 -38
  156. package/src/services/adapters/chatCompletions.ts +0 -90
  157. package/src/services/adapters/responsesAPI.ts +0 -170
  158. package/src/services/browserMocks.ts +0 -66
  159. package/src/services/claude.ts +0 -2197
  160. package/src/services/customCommands.ts +0 -704
  161. package/src/services/fileFreshness.ts +0 -377
  162. package/src/services/gpt5ConnectionTest.ts +0 -340
  163. package/src/services/mcpClient.ts +0 -564
  164. package/src/services/mcpServerApproval.tsx +0 -50
  165. package/src/services/mentionProcessor.ts +0 -273
  166. package/src/services/modelAdapterFactory.ts +0 -69
  167. package/src/services/notifier.ts +0 -40
  168. package/src/services/oauth.ts +0 -357
  169. package/src/services/openai.ts +0 -1359
  170. package/src/services/responseStateManager.ts +0 -90
  171. package/src/services/sentry.ts +0 -3
  172. package/src/services/statsig.ts +0 -172
  173. package/src/services/statsigStorage.ts +0 -86
  174. package/src/services/systemReminder.ts +0 -507
  175. package/src/services/vcr.ts +0 -161
  176. package/src/test/testAdapters.ts +0 -96
  177. package/src/tools/ArchitectTool/ArchitectTool.tsx +0 -135
  178. package/src/tools/ArchitectTool/prompt.ts +0 -15
  179. package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +0 -576
  180. package/src/tools/BashTool/BashTool.tsx +0 -243
  181. package/src/tools/BashTool/BashToolResultMessage.tsx +0 -38
  182. package/src/tools/BashTool/OutputLine.tsx +0 -49
  183. package/src/tools/BashTool/prompt.ts +0 -174
  184. package/src/tools/BashTool/utils.ts +0 -56
  185. package/src/tools/FileEditTool/FileEditTool.tsx +0 -319
  186. package/src/tools/FileEditTool/prompt.ts +0 -51
  187. package/src/tools/FileEditTool/utils.ts +0 -58
  188. package/src/tools/FileReadTool/FileReadTool.tsx +0 -404
  189. package/src/tools/FileReadTool/prompt.ts +0 -7
  190. package/src/tools/FileWriteTool/FileWriteTool.tsx +0 -301
  191. package/src/tools/FileWriteTool/prompt.ts +0 -10
  192. package/src/tools/GlobTool/GlobTool.tsx +0 -119
  193. package/src/tools/GlobTool/prompt.ts +0 -8
  194. package/src/tools/GrepTool/GrepTool.tsx +0 -147
  195. package/src/tools/GrepTool/prompt.ts +0 -11
  196. package/src/tools/MCPTool/MCPTool.tsx +0 -107
  197. package/src/tools/MCPTool/prompt.ts +0 -3
  198. package/src/tools/MemoryReadTool/MemoryReadTool.tsx +0 -127
  199. package/src/tools/MemoryReadTool/prompt.ts +0 -3
  200. package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +0 -89
  201. package/src/tools/MemoryWriteTool/prompt.ts +0 -3
  202. package/src/tools/MultiEditTool/MultiEditTool.tsx +0 -388
  203. package/src/tools/MultiEditTool/prompt.ts +0 -45
  204. package/src/tools/NotebookEditTool/NotebookEditTool.tsx +0 -298
  205. package/src/tools/NotebookEditTool/prompt.ts +0 -3
  206. package/src/tools/NotebookReadTool/NotebookReadTool.tsx +0 -258
  207. package/src/tools/NotebookReadTool/prompt.ts +0 -3
  208. package/src/tools/StickerRequestTool/StickerRequestTool.tsx +0 -107
  209. package/src/tools/StickerRequestTool/prompt.ts +0 -19
  210. package/src/tools/TaskTool/TaskTool.tsx +0 -438
  211. package/src/tools/TaskTool/constants.ts +0 -1
  212. package/src/tools/TaskTool/prompt.ts +0 -92
  213. package/src/tools/ThinkTool/ThinkTool.tsx +0 -54
  214. package/src/tools/ThinkTool/prompt.ts +0 -12
  215. package/src/tools/TodoWriteTool/TodoWriteTool.tsx +0 -313
  216. package/src/tools/TodoWriteTool/prompt.ts +0 -63
  217. package/src/tools/URLFetcherTool/URLFetcherTool.tsx +0 -178
  218. package/src/tools/URLFetcherTool/cache.ts +0 -55
  219. package/src/tools/URLFetcherTool/htmlToMarkdown.ts +0 -55
  220. package/src/tools/URLFetcherTool/prompt.ts +0 -17
  221. package/src/tools/WebSearchTool/WebSearchTool.tsx +0 -103
  222. package/src/tools/WebSearchTool/prompt.ts +0 -13
  223. package/src/tools/WebSearchTool/searchProviders.ts +0 -66
  224. package/src/tools/lsTool/lsTool.tsx +0 -272
  225. package/src/tools/lsTool/prompt.ts +0 -2
  226. package/src/tools.ts +0 -67
  227. package/src/types/PermissionMode.ts +0 -120
  228. package/src/types/RequestContext.ts +0 -72
  229. package/src/types/common.d.ts +0 -2
  230. package/src/types/conversation.ts +0 -51
  231. package/src/types/logs.ts +0 -58
  232. package/src/types/modelCapabilities.ts +0 -64
  233. package/src/types/notebook.ts +0 -87
  234. package/src/utils/Cursor.ts +0 -436
  235. package/src/utils/PersistentShell.ts +0 -552
  236. package/src/utils/advancedFuzzyMatcher.ts +0 -290
  237. package/src/utils/agentLoader.ts +0 -278
  238. package/src/utils/agentStorage.ts +0 -97
  239. package/src/utils/array.ts +0 -3
  240. package/src/utils/ask.tsx +0 -99
  241. package/src/utils/auth.ts +0 -13
  242. package/src/utils/autoCompactCore.ts +0 -223
  243. package/src/utils/autoUpdater.ts +0 -458
  244. package/src/utils/betas.ts +0 -20
  245. package/src/utils/browser.ts +0 -14
  246. package/src/utils/cleanup.ts +0 -72
  247. package/src/utils/commands.ts +0 -261
  248. package/src/utils/commonUnixCommands.ts +0 -161
  249. package/src/utils/config.ts +0 -945
  250. package/src/utils/conversationRecovery.ts +0 -55
  251. package/src/utils/debugLogger.ts +0 -1235
  252. package/src/utils/diff.ts +0 -42
  253. package/src/utils/env.ts +0 -57
  254. package/src/utils/errors.ts +0 -21
  255. package/src/utils/exampleCommands.ts +0 -109
  256. package/src/utils/execFileNoThrow.ts +0 -51
  257. package/src/utils/expertChatStorage.ts +0 -136
  258. package/src/utils/file.ts +0 -405
  259. package/src/utils/fileRecoveryCore.ts +0 -71
  260. package/src/utils/format.tsx +0 -44
  261. package/src/utils/fuzzyMatcher.ts +0 -328
  262. package/src/utils/generators.ts +0 -62
  263. package/src/utils/git.ts +0 -92
  264. package/src/utils/globalLogger.ts +0 -77
  265. package/src/utils/http.ts +0 -10
  266. package/src/utils/imagePaste.ts +0 -38
  267. package/src/utils/json.ts +0 -13
  268. package/src/utils/log.ts +0 -382
  269. package/src/utils/markdown.ts +0 -213
  270. package/src/utils/messageContextManager.ts +0 -294
  271. package/src/utils/messages.tsx +0 -945
  272. package/src/utils/model.ts +0 -914
  273. package/src/utils/permissions/filesystem.ts +0 -127
  274. package/src/utils/responseState.ts +0 -23
  275. package/src/utils/ripgrep.ts +0 -167
  276. package/src/utils/secureFile.ts +0 -564
  277. package/src/utils/sessionState.ts +0 -49
  278. package/src/utils/state.ts +0 -25
  279. package/src/utils/style.ts +0 -29
  280. package/src/utils/terminal.ts +0 -50
  281. package/src/utils/theme.ts +0 -127
  282. package/src/utils/thinking.ts +0 -144
  283. package/src/utils/todoStorage.ts +0 -431
  284. package/src/utils/tokens.ts +0 -43
  285. package/src/utils/toolExecutionController.ts +0 -163
  286. package/src/utils/unaryLogging.ts +0 -26
  287. package/src/utils/user.ts +0 -37
  288. 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
- >