@shareai-lab/kode 1.0.70 → 1.0.71

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 (253) hide show
  1. package/README.md +202 -76
  2. package/README.zh-CN.md +246 -0
  3. package/cli.js +62 -0
  4. package/package.json +45 -25
  5. package/scripts/postinstall.js +56 -0
  6. package/src/ProjectOnboarding.tsx +180 -0
  7. package/src/Tool.ts +53 -0
  8. package/src/commands/approvedTools.ts +53 -0
  9. package/src/commands/bug.tsx +20 -0
  10. package/src/commands/clear.ts +43 -0
  11. package/src/commands/compact.ts +120 -0
  12. package/src/commands/config.tsx +19 -0
  13. package/src/commands/cost.ts +18 -0
  14. package/src/commands/ctx_viz.ts +209 -0
  15. package/src/commands/doctor.ts +24 -0
  16. package/src/commands/help.tsx +19 -0
  17. package/src/commands/init.ts +37 -0
  18. package/src/commands/listen.ts +42 -0
  19. package/src/commands/login.tsx +51 -0
  20. package/src/commands/logout.tsx +40 -0
  21. package/src/commands/mcp.ts +41 -0
  22. package/src/commands/model.tsx +40 -0
  23. package/src/commands/modelstatus.tsx +20 -0
  24. package/src/commands/onboarding.tsx +34 -0
  25. package/src/commands/pr_comments.ts +59 -0
  26. package/src/commands/refreshCommands.ts +54 -0
  27. package/src/commands/release-notes.ts +34 -0
  28. package/src/commands/resume.tsx +30 -0
  29. package/src/commands/review.ts +49 -0
  30. package/src/commands/terminalSetup.ts +221 -0
  31. package/src/commands.ts +136 -0
  32. package/src/components/ApproveApiKey.tsx +93 -0
  33. package/src/components/AsciiLogo.tsx +13 -0
  34. package/src/components/AutoUpdater.tsx +148 -0
  35. package/src/components/Bug.tsx +367 -0
  36. package/src/components/Config.tsx +289 -0
  37. package/src/components/ConsoleOAuthFlow.tsx +326 -0
  38. package/src/components/Cost.tsx +23 -0
  39. package/src/components/CostThresholdDialog.tsx +46 -0
  40. package/src/components/CustomSelect/option-map.ts +42 -0
  41. package/src/components/CustomSelect/select-option.tsx +52 -0
  42. package/src/components/CustomSelect/select.tsx +143 -0
  43. package/src/components/CustomSelect/use-select-state.ts +414 -0
  44. package/src/components/CustomSelect/use-select.ts +35 -0
  45. package/src/components/FallbackToolUseRejectedMessage.tsx +15 -0
  46. package/src/components/FileEditToolUpdatedMessage.tsx +66 -0
  47. package/src/components/Help.tsx +215 -0
  48. package/src/components/HighlightedCode.tsx +33 -0
  49. package/src/components/InvalidConfigDialog.tsx +113 -0
  50. package/src/components/Link.tsx +32 -0
  51. package/src/components/LogSelector.tsx +86 -0
  52. package/src/components/Logo.tsx +145 -0
  53. package/src/components/MCPServerApprovalDialog.tsx +100 -0
  54. package/src/components/MCPServerDialogCopy.tsx +25 -0
  55. package/src/components/MCPServerMultiselectDialog.tsx +109 -0
  56. package/src/components/Message.tsx +219 -0
  57. package/src/components/MessageResponse.tsx +15 -0
  58. package/src/components/MessageSelector.tsx +211 -0
  59. package/src/components/ModeIndicator.tsx +88 -0
  60. package/src/components/ModelConfig.tsx +301 -0
  61. package/src/components/ModelListManager.tsx +223 -0
  62. package/src/components/ModelSelector.tsx +3208 -0
  63. package/src/components/ModelStatusDisplay.tsx +228 -0
  64. package/src/components/Onboarding.tsx +274 -0
  65. package/src/components/PressEnterToContinue.tsx +11 -0
  66. package/src/components/PromptInput.tsx +710 -0
  67. package/src/components/SentryErrorBoundary.ts +33 -0
  68. package/src/components/Spinner.tsx +129 -0
  69. package/src/components/StructuredDiff.tsx +184 -0
  70. package/src/components/TextInput.tsx +246 -0
  71. package/src/components/TokenWarning.tsx +31 -0
  72. package/src/components/ToolUseLoader.tsx +40 -0
  73. package/src/components/TrustDialog.tsx +106 -0
  74. package/src/components/binary-feedback/BinaryFeedback.tsx +63 -0
  75. package/src/components/binary-feedback/BinaryFeedbackOption.tsx +111 -0
  76. package/src/components/binary-feedback/BinaryFeedbackView.tsx +172 -0
  77. package/src/components/binary-feedback/utils.ts +220 -0
  78. package/src/components/messages/AssistantBashOutputMessage.tsx +22 -0
  79. package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +45 -0
  80. package/src/components/messages/AssistantRedactedThinkingMessage.tsx +19 -0
  81. package/src/components/messages/AssistantTextMessage.tsx +144 -0
  82. package/src/components/messages/AssistantThinkingMessage.tsx +40 -0
  83. package/src/components/messages/AssistantToolUseMessage.tsx +123 -0
  84. package/src/components/messages/UserBashInputMessage.tsx +28 -0
  85. package/src/components/messages/UserCommandMessage.tsx +30 -0
  86. package/src/components/messages/UserKodingInputMessage.tsx +28 -0
  87. package/src/components/messages/UserPromptMessage.tsx +35 -0
  88. package/src/components/messages/UserTextMessage.tsx +39 -0
  89. package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +12 -0
  90. package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +36 -0
  91. package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +31 -0
  92. package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +57 -0
  93. package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +35 -0
  94. package/src/components/messages/UserToolResultMessage/utils.tsx +56 -0
  95. package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +121 -0
  96. package/src/components/permissions/FallbackPermissionRequest.tsx +155 -0
  97. package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +182 -0
  98. package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +75 -0
  99. package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +164 -0
  100. package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +81 -0
  101. package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +242 -0
  102. package/src/components/permissions/PermissionRequest.tsx +103 -0
  103. package/src/components/permissions/PermissionRequestTitle.tsx +69 -0
  104. package/src/components/permissions/hooks.ts +44 -0
  105. package/src/components/permissions/toolUseOptions.ts +59 -0
  106. package/src/components/permissions/utils.ts +23 -0
  107. package/src/constants/betas.ts +5 -0
  108. package/src/constants/claude-asterisk-ascii-art.tsx +238 -0
  109. package/src/constants/figures.ts +4 -0
  110. package/src/constants/keys.ts +3 -0
  111. package/src/constants/macros.ts +6 -0
  112. package/src/constants/models.ts +935 -0
  113. package/src/constants/oauth.ts +18 -0
  114. package/src/constants/product.ts +17 -0
  115. package/src/constants/prompts.ts +177 -0
  116. package/src/constants/releaseNotes.ts +7 -0
  117. package/src/context/PermissionContext.tsx +149 -0
  118. package/src/context.ts +278 -0
  119. package/src/cost-tracker.ts +84 -0
  120. package/src/entrypoints/cli.tsx +1498 -0
  121. package/src/entrypoints/mcp.ts +176 -0
  122. package/src/history.ts +25 -0
  123. package/src/hooks/useApiKeyVerification.ts +59 -0
  124. package/src/hooks/useArrowKeyHistory.ts +55 -0
  125. package/src/hooks/useCanUseTool.ts +138 -0
  126. package/src/hooks/useCancelRequest.ts +39 -0
  127. package/src/hooks/useDoublePress.ts +42 -0
  128. package/src/hooks/useExitOnCtrlCD.ts +31 -0
  129. package/src/hooks/useInterval.ts +25 -0
  130. package/src/hooks/useLogMessages.ts +16 -0
  131. package/src/hooks/useLogStartupTime.ts +12 -0
  132. package/src/hooks/useNotifyAfterTimeout.ts +65 -0
  133. package/src/hooks/usePermissionRequestLogging.ts +44 -0
  134. package/src/hooks/useSlashCommandTypeahead.ts +137 -0
  135. package/src/hooks/useTerminalSize.ts +49 -0
  136. package/src/hooks/useTextInput.ts +315 -0
  137. package/src/messages.ts +37 -0
  138. package/src/permissions.ts +268 -0
  139. package/src/query.ts +704 -0
  140. package/src/screens/ConfigureNpmPrefix.tsx +197 -0
  141. package/src/screens/Doctor.tsx +219 -0
  142. package/src/screens/LogList.tsx +68 -0
  143. package/src/screens/REPL.tsx +792 -0
  144. package/src/screens/ResumeConversation.tsx +68 -0
  145. package/src/services/browserMocks.ts +66 -0
  146. package/src/services/claude.ts +1947 -0
  147. package/src/services/customCommands.ts +683 -0
  148. package/src/services/fileFreshness.ts +377 -0
  149. package/src/services/mcpClient.ts +564 -0
  150. package/src/services/mcpServerApproval.tsx +50 -0
  151. package/src/services/notifier.ts +40 -0
  152. package/src/services/oauth.ts +357 -0
  153. package/src/services/openai.ts +796 -0
  154. package/src/services/sentry.ts +3 -0
  155. package/src/services/statsig.ts +171 -0
  156. package/src/services/statsigStorage.ts +86 -0
  157. package/src/services/systemReminder.ts +406 -0
  158. package/src/services/vcr.ts +161 -0
  159. package/src/tools/ArchitectTool/ArchitectTool.tsx +122 -0
  160. package/src/tools/ArchitectTool/prompt.ts +15 -0
  161. package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +505 -0
  162. package/src/tools/BashTool/BashTool.tsx +270 -0
  163. package/src/tools/BashTool/BashToolResultMessage.tsx +38 -0
  164. package/src/tools/BashTool/OutputLine.tsx +48 -0
  165. package/src/tools/BashTool/prompt.ts +174 -0
  166. package/src/tools/BashTool/utils.ts +56 -0
  167. package/src/tools/FileEditTool/FileEditTool.tsx +316 -0
  168. package/src/tools/FileEditTool/prompt.ts +51 -0
  169. package/src/tools/FileEditTool/utils.ts +58 -0
  170. package/src/tools/FileReadTool/FileReadTool.tsx +371 -0
  171. package/src/tools/FileReadTool/prompt.ts +7 -0
  172. package/src/tools/FileWriteTool/FileWriteTool.tsx +297 -0
  173. package/src/tools/FileWriteTool/prompt.ts +10 -0
  174. package/src/tools/GlobTool/GlobTool.tsx +119 -0
  175. package/src/tools/GlobTool/prompt.ts +8 -0
  176. package/src/tools/GrepTool/GrepTool.tsx +147 -0
  177. package/src/tools/GrepTool/prompt.ts +11 -0
  178. package/src/tools/MCPTool/MCPTool.tsx +106 -0
  179. package/src/tools/MCPTool/prompt.ts +3 -0
  180. package/src/tools/MemoryReadTool/MemoryReadTool.tsx +127 -0
  181. package/src/tools/MemoryReadTool/prompt.ts +3 -0
  182. package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +89 -0
  183. package/src/tools/MemoryWriteTool/prompt.ts +3 -0
  184. package/src/tools/MultiEditTool/MultiEditTool.tsx +366 -0
  185. package/src/tools/MultiEditTool/prompt.ts +45 -0
  186. package/src/tools/NotebookEditTool/NotebookEditTool.tsx +298 -0
  187. package/src/tools/NotebookEditTool/prompt.ts +3 -0
  188. package/src/tools/NotebookReadTool/NotebookReadTool.tsx +266 -0
  189. package/src/tools/NotebookReadTool/prompt.ts +3 -0
  190. package/src/tools/StickerRequestTool/StickerRequestTool.tsx +93 -0
  191. package/src/tools/StickerRequestTool/prompt.ts +19 -0
  192. package/src/tools/TaskTool/TaskTool.tsx +382 -0
  193. package/src/tools/TaskTool/constants.ts +1 -0
  194. package/src/tools/TaskTool/prompt.ts +56 -0
  195. package/src/tools/ThinkTool/ThinkTool.tsx +56 -0
  196. package/src/tools/ThinkTool/prompt.ts +12 -0
  197. package/src/tools/TodoWriteTool/TodoWriteTool.tsx +289 -0
  198. package/src/tools/TodoWriteTool/prompt.ts +63 -0
  199. package/src/tools/lsTool/lsTool.tsx +269 -0
  200. package/src/tools/lsTool/prompt.ts +2 -0
  201. package/src/tools.ts +63 -0
  202. package/src/types/PermissionMode.ts +120 -0
  203. package/src/types/RequestContext.ts +72 -0
  204. package/src/utils/Cursor.ts +436 -0
  205. package/src/utils/PersistentShell.ts +373 -0
  206. package/src/utils/agentStorage.ts +97 -0
  207. package/src/utils/array.ts +3 -0
  208. package/src/utils/ask.tsx +98 -0
  209. package/src/utils/auth.ts +13 -0
  210. package/src/utils/autoCompactCore.ts +223 -0
  211. package/src/utils/autoUpdater.ts +318 -0
  212. package/src/utils/betas.ts +20 -0
  213. package/src/utils/browser.ts +14 -0
  214. package/src/utils/cleanup.ts +72 -0
  215. package/src/utils/commands.ts +261 -0
  216. package/src/utils/config.ts +771 -0
  217. package/src/utils/conversationRecovery.ts +54 -0
  218. package/src/utils/debugLogger.ts +1123 -0
  219. package/src/utils/diff.ts +42 -0
  220. package/src/utils/env.ts +57 -0
  221. package/src/utils/errors.ts +21 -0
  222. package/src/utils/exampleCommands.ts +108 -0
  223. package/src/utils/execFileNoThrow.ts +51 -0
  224. package/src/utils/expertChatStorage.ts +136 -0
  225. package/src/utils/file.ts +402 -0
  226. package/src/utils/fileRecoveryCore.ts +71 -0
  227. package/src/utils/format.tsx +44 -0
  228. package/src/utils/generators.ts +62 -0
  229. package/src/utils/git.ts +92 -0
  230. package/src/utils/globalLogger.ts +77 -0
  231. package/src/utils/http.ts +10 -0
  232. package/src/utils/imagePaste.ts +38 -0
  233. package/src/utils/json.ts +13 -0
  234. package/src/utils/log.ts +382 -0
  235. package/src/utils/markdown.ts +213 -0
  236. package/src/utils/messageContextManager.ts +289 -0
  237. package/src/utils/messages.tsx +938 -0
  238. package/src/utils/model.ts +836 -0
  239. package/src/utils/permissions/filesystem.ts +118 -0
  240. package/src/utils/ripgrep.ts +167 -0
  241. package/src/utils/sessionState.ts +49 -0
  242. package/src/utils/state.ts +25 -0
  243. package/src/utils/style.ts +29 -0
  244. package/src/utils/terminal.ts +49 -0
  245. package/src/utils/theme.ts +122 -0
  246. package/src/utils/thinking.ts +144 -0
  247. package/src/utils/todoStorage.ts +431 -0
  248. package/src/utils/tokens.ts +43 -0
  249. package/src/utils/toolExecutionController.ts +163 -0
  250. package/src/utils/unaryLogging.ts +26 -0
  251. package/src/utils/user.ts +37 -0
  252. package/src/utils/validate.ts +165 -0
  253. package/cli.mjs +0 -1803
@@ -0,0 +1,34 @@
1
+ import { MACRO } from '../constants/macros.js'
2
+ import type { Command } from '../commands'
3
+ import { RELEASE_NOTES } from '../constants/releaseNotes'
4
+
5
+ const releaseNotes: Command = {
6
+ description: 'Show release notes for the current or specified version',
7
+ isEnabled: false,
8
+ isHidden: false,
9
+ name: 'release-notes',
10
+ userFacingName() {
11
+ return 'release-notes'
12
+ },
13
+ type: 'local',
14
+ async call(args) {
15
+ const currentVersion = MACRO.VERSION
16
+
17
+ // If a specific version is requested, show that version's notes
18
+ const requestedVersion = args ? args.trim() : currentVersion
19
+
20
+ // Get the requested version's notes
21
+ const notes = RELEASE_NOTES[requestedVersion]
22
+
23
+ if (!notes || notes.length === 0) {
24
+ return `No release notes available for version ${requestedVersion}.`
25
+ }
26
+
27
+ const header = `Release notes for version ${requestedVersion}:`
28
+ const formattedNotes = notes.map(note => `• ${note}`).join('\n')
29
+
30
+ return `${header}\n\n${formattedNotes}`
31
+ },
32
+ }
33
+
34
+ export default releaseNotes
@@ -0,0 +1,30 @@
1
+ import * as React from 'react'
2
+ import type { Command } from '../commands'
3
+ import { ResumeConversation } from '../screens/ResumeConversation'
4
+ import { render } from 'ink'
5
+ import { CACHE_PATHS, loadLogList } from '../utils/log'
6
+
7
+ export default {
8
+ type: 'local-jsx',
9
+ name: 'resume',
10
+ description: 'Resume a previous conversation',
11
+ isEnabled: true,
12
+ isHidden: false,
13
+ userFacingName() {
14
+ return 'resume'
15
+ },
16
+ async call(onDone, { options: { commands, tools, verbose } }) {
17
+ const logs = await loadLogList(CACHE_PATHS.messages())
18
+ render(
19
+ <ResumeConversation
20
+ commands={commands}
21
+ context={{ unmount: onDone }}
22
+ logs={logs}
23
+ tools={tools}
24
+ verbose={verbose}
25
+ />,
26
+ )
27
+ // This return is here for type only
28
+ return null
29
+ },
30
+ } satisfies Command
@@ -0,0 +1,49 @@
1
+ import { Command } from '../commands'
2
+ import { BashTool } from '../tools/BashTool/BashTool'
3
+
4
+ export default {
5
+ type: 'prompt',
6
+ name: 'review',
7
+ description: 'Review a pull request',
8
+ isEnabled: true,
9
+ isHidden: false,
10
+ progressMessage: 'reviewing pull request',
11
+ userFacingName() {
12
+ return 'review'
13
+ },
14
+ async getPromptForCommand(args) {
15
+ return [
16
+ {
17
+ role: 'user',
18
+ content: [
19
+ {
20
+ type: 'text',
21
+ text: `
22
+ You are an expert code reviewer. Follow these steps:
23
+
24
+ 1. If no PR number is provided in the args, use ${BashTool.name}("gh pr list") to show open PRs
25
+ 2. If a PR number is provided, use ${BashTool.name}("gh pr view <number>") to get PR details
26
+ 3. Use ${BashTool.name}("gh pr diff <number>") to get the diff
27
+ 4. Analyze the changes and provide a thorough code review that includes:
28
+ - Overview of what the PR does
29
+ - Analysis of code quality and style
30
+ - Specific suggestions for improvements
31
+ - Any potential issues or risks
32
+
33
+ Keep your review concise but thorough. Focus on:
34
+ - Code correctness
35
+ - Following project conventions
36
+ - Performance implications
37
+ - Test coverage
38
+ - Security considerations
39
+
40
+ Format your review with clear sections and bullet points.
41
+
42
+ PR number: ${args}
43
+ `,
44
+ },
45
+ ],
46
+ },
47
+ ]
48
+ },
49
+ } satisfies Command
@@ -0,0 +1,221 @@
1
+ import { Command } from '../commands'
2
+ import { EOL, platform, homedir } from 'os'
3
+ import { execFileNoThrow } from '../utils/execFileNoThrow'
4
+ import chalk from 'chalk'
5
+ import { getTheme } from '../utils/theme'
6
+ import { env } from '../utils/env'
7
+ import { getGlobalConfig, saveGlobalConfig } from '../utils/config'
8
+ import { markProjectOnboardingComplete } from '../ProjectOnboarding'
9
+ import { readFileSync, writeFileSync } from 'fs'
10
+ import { join } from 'path'
11
+ import { safeParseJSON } from '../utils/json'
12
+ import { logError } from '../utils/log'
13
+
14
+ const terminalSetup: Command = {
15
+ type: 'local',
16
+ name: 'terminal-setup',
17
+ userFacingName() {
18
+ return 'terminal-setup'
19
+ },
20
+ description:
21
+ 'Install Shift+Enter key binding for newlines (iTerm2 and VSCode only)',
22
+ isEnabled:
23
+ (platform() === 'darwin' && env.terminal === 'iTerm.app') ||
24
+ env.terminal === 'vscode',
25
+ isHidden: false,
26
+ async call() {
27
+ let result = ''
28
+
29
+ switch (env.terminal) {
30
+ case 'iTerm.app':
31
+ result = await installBindingsForITerm2()
32
+ break
33
+ case 'vscode':
34
+ result = installBindingsForVSCodeTerminal()
35
+ break
36
+ }
37
+
38
+ // Update global config to indicate Shift+Enter key binding is installed
39
+ const config = getGlobalConfig()
40
+ config.shiftEnterKeyBindingInstalled = true
41
+ saveGlobalConfig(config)
42
+
43
+ // Mark onboarding as complete
44
+ markProjectOnboardingComplete()
45
+
46
+ return result
47
+ },
48
+ }
49
+
50
+ export function isShiftEnterKeyBindingInstalled(): boolean {
51
+ return getGlobalConfig().shiftEnterKeyBindingInstalled === true
52
+ }
53
+
54
+ export function handleHashCommand(interpreted: string): void {
55
+ // Appends the AI-interpreted content to both KODE.md and CLAUDE.md (if exists)
56
+ try {
57
+ const cwd = process.cwd()
58
+ const codeContextPath = join(cwd, 'KODE.md')
59
+ const claudePath = join(cwd, 'CLAUDE.md')
60
+
61
+ // Check which files exist and update them
62
+ const filesToUpdate = []
63
+
64
+ // Always try to update KODE.md (create if not exists)
65
+ filesToUpdate.push({ path: codeContextPath, name: 'KODE.md' })
66
+
67
+ // Update CLAUDE.md only if it exists
68
+ try {
69
+ readFileSync(claudePath, 'utf-8')
70
+ filesToUpdate.push({ path: claudePath, name: 'CLAUDE.md' })
71
+ } catch {
72
+ // CLAUDE.md doesn't exist, skip it
73
+ }
74
+
75
+ const now = new Date()
76
+ const timezoneMatch = now.toString().match(/\(([A-Z]+)\)/)
77
+ const timezone = timezoneMatch
78
+ ? timezoneMatch[1]
79
+ : now
80
+ .toLocaleTimeString('en-us', { timeZoneName: 'short' })
81
+ .split(' ')
82
+ .pop()
83
+
84
+ const timestamp = interpreted.includes(now.getFullYear().toString())
85
+ ? ''
86
+ : `\n\n_Added on ${now.toLocaleString()} ${timezone}_`
87
+
88
+ const updatedFiles = []
89
+
90
+ for (const file of filesToUpdate) {
91
+ try {
92
+ // Check if file exists, if not create it
93
+ let existingContent = ''
94
+ try {
95
+ existingContent = readFileSync(file.path, 'utf-8').trim()
96
+ } catch (error) {
97
+ // File doesn't exist yet, that's fine
98
+ }
99
+
100
+ // Add a separator if the file already has content
101
+ const separator = existingContent ? '\n\n' : ''
102
+
103
+ // Combine everything and write to file
104
+ const newContent = `${existingContent}${separator}${interpreted}${timestamp}`
105
+ writeFileSync(file.path, newContent, 'utf-8')
106
+ updatedFiles.push(file.name)
107
+ } catch (error) {
108
+ logError(error)
109
+ console.error(
110
+ chalk.hex(getTheme().error)(
111
+ `Failed to update ${file.name}: ${error.message}`,
112
+ ),
113
+ )
114
+ }
115
+ }
116
+
117
+ if (updatedFiles.length > 0) {
118
+ console.log(
119
+ chalk.hex(getTheme().success)(
120
+ `Added note to ${updatedFiles.join(' and ')}`,
121
+ ),
122
+ )
123
+ }
124
+ } catch (e) {
125
+ logError(e)
126
+ console.error(
127
+ chalk.hex(getTheme().error)(`Failed to add note: ${e.message}`),
128
+ )
129
+ }
130
+ }
131
+
132
+ export default terminalSetup
133
+
134
+ async function installBindingsForITerm2(): Promise<string> {
135
+ const { code } = await execFileNoThrow('defaults', [
136
+ 'write',
137
+ 'com.googlecode.iterm2',
138
+ 'GlobalKeyMap',
139
+ '-dict-add',
140
+ '0xd-0x20000-0x24',
141
+ `<dict>
142
+ <key>Text</key>
143
+ <string>\\n</string>
144
+ <key>Action</key>
145
+ <integer>12</integer>
146
+ <key>Version</key>
147
+ <integer>1</integer>
148
+ <key>Keycode</key>
149
+ <integer>13</integer>
150
+ <key>Modifiers</key>
151
+ <integer>131072</integer>
152
+ </dict>`,
153
+ ])
154
+
155
+ if (code !== 0) {
156
+ throw new Error('Failed to install iTerm2 Shift+Enter key binding')
157
+ }
158
+
159
+ return `${chalk.hex(getTheme().success)(
160
+ 'Installed iTerm2 Shift+Enter key binding',
161
+ )}${EOL}${chalk.dim('See iTerm2 → Preferences → Keys')}${EOL}`
162
+ }
163
+
164
+ type VSCodeKeybinding = {
165
+ key: string
166
+ command: string
167
+ args: { text: string }
168
+ when: string
169
+ }
170
+
171
+ function installBindingsForVSCodeTerminal(): string {
172
+ const vscodeKeybindingsPath = join(
173
+ homedir(),
174
+ platform() === 'win32'
175
+ ? join('AppData', 'Roaming', 'Code', 'User')
176
+ : platform() === 'darwin'
177
+ ? join('Library', 'Application Support', 'Code', 'User')
178
+ : join('.config', 'Code', 'User'),
179
+ 'keybindings.json',
180
+ )
181
+
182
+ try {
183
+ const content = readFileSync(vscodeKeybindingsPath, 'utf-8')
184
+ const keybindings: VSCodeKeybinding[] =
185
+ (safeParseJSON(content) as VSCodeKeybinding[]) ?? []
186
+
187
+ // Check if keybinding already exists
188
+ const existingBinding = keybindings.find(
189
+ binding =>
190
+ binding.key === 'shift+enter' &&
191
+ binding.command === 'workbench.action.terminal.sendSequence' &&
192
+ binding.when === 'terminalFocus',
193
+ )
194
+ if (existingBinding) {
195
+ return `${chalk.hex(getTheme().warning)(
196
+ 'Found existing VSCode terminal Shift+Enter key binding. Remove it to continue.',
197
+ )}${EOL}${chalk.dim(`See ${vscodeKeybindingsPath}`)}${EOL}`
198
+ }
199
+
200
+ // Add the keybinding
201
+ keybindings.push({
202
+ key: 'shift+enter',
203
+ command: 'workbench.action.terminal.sendSequence',
204
+ args: { text: '\\\r\n' },
205
+ when: 'terminalFocus',
206
+ })
207
+
208
+ writeFileSync(
209
+ vscodeKeybindingsPath,
210
+ JSON.stringify(keybindings, null, 4),
211
+ 'utf-8',
212
+ )
213
+
214
+ return `${chalk.hex(getTheme().success)(
215
+ 'Installed VSCode terminal Shift+Enter key binding',
216
+ )}${EOL}${chalk.dim(`See ${vscodeKeybindingsPath}`)}${EOL}`
217
+ } catch (e) {
218
+ logError(e)
219
+ throw new Error('Failed to install VSCode terminal Shift+Enter key binding')
220
+ }
221
+ }
@@ -0,0 +1,136 @@
1
+ import bug from './commands/bug'
2
+ import clear from './commands/clear'
3
+ import compact from './commands/compact'
4
+ import config from './commands/config'
5
+ import cost from './commands/cost'
6
+ import ctx_viz from './commands/ctx_viz'
7
+ import doctor from './commands/doctor'
8
+ import help from './commands/help'
9
+ import init from './commands/init'
10
+ import listen from './commands/listen'
11
+ import login from './commands/login'
12
+ import logout from './commands/logout'
13
+ import mcp from './commands/mcp'
14
+ import * as model from './commands/model'
15
+ import modelstatus from './commands/modelstatus'
16
+ import onboarding from './commands/onboarding'
17
+ import pr_comments from './commands/pr_comments'
18
+ import refreshCommands from './commands/refreshCommands'
19
+ import releaseNotes from './commands/release-notes'
20
+ import review from './commands/review'
21
+ import terminalSetup from './commands/terminalSetup'
22
+ import { Tool, ToolUseContext } from './Tool'
23
+ import resume from './commands/resume'
24
+ import { getMCPCommands } from './services/mcpClient'
25
+ import { loadCustomCommands } from './services/customCommands'
26
+ import type { MessageParam } from '@anthropic-ai/sdk/resources/index.mjs'
27
+ import { memoize } from 'lodash-es'
28
+ import type { Message } from './query'
29
+ import { isAnthropicAuthEnabled } from './utils/auth'
30
+
31
+ type PromptCommand = {
32
+ type: 'prompt'
33
+ progressMessage: string
34
+ argNames?: string[]
35
+ getPromptForCommand(args: string): Promise<MessageParam[]>
36
+ }
37
+
38
+ type LocalCommand = {
39
+ type: 'local'
40
+ call(
41
+ args: string,
42
+ context: {
43
+ options: {
44
+ commands: Command[]
45
+ tools: Tool[]
46
+ slowAndCapableModel: string
47
+ }
48
+ abortController: AbortController
49
+ setForkConvoWithMessagesOnTheNextRender: (
50
+ forkConvoWithMessages: Message[],
51
+ ) => void
52
+ },
53
+ ): Promise<string>
54
+ }
55
+
56
+ type LocalJSXCommand = {
57
+ type: 'local-jsx'
58
+ call(
59
+ onDone: (result?: string) => void,
60
+ context: ToolUseContext & {
61
+ setForkConvoWithMessagesOnTheNextRender: (
62
+ forkConvoWithMessages: Message[],
63
+ ) => void
64
+ },
65
+ ): Promise<React.ReactNode>
66
+ }
67
+
68
+ export type Command = {
69
+ description: string
70
+ isEnabled: boolean
71
+ isHidden: boolean
72
+ name: string
73
+ aliases?: string[]
74
+ userFacingName(): string
75
+ } & (PromptCommand | LocalCommand | LocalJSXCommand)
76
+
77
+ const INTERNAL_ONLY_COMMANDS = [ctx_viz, resume, listen]
78
+
79
+ // Declared as a function so that we don't run this until getCommands is called,
80
+ // since underlying functions read from config, which can't be read at module initialization time
81
+ const COMMANDS = memoize((): Command[] => [
82
+ clear,
83
+ compact,
84
+ config,
85
+ cost,
86
+ doctor,
87
+ help,
88
+ init,
89
+ mcp,
90
+ model,
91
+ modelstatus,
92
+ onboarding,
93
+ pr_comments,
94
+ refreshCommands,
95
+ releaseNotes,
96
+ bug,
97
+ review,
98
+ terminalSetup,
99
+ ...(isAnthropicAuthEnabled() ? [logout, login()] : []),
100
+ ...INTERNAL_ONLY_COMMANDS,
101
+ ])
102
+
103
+ export const getCommands = memoize(async (): Promise<Command[]> => {
104
+ const [mcpCommands, customCommands] = await Promise.all([
105
+ getMCPCommands(),
106
+ loadCustomCommands(),
107
+ ])
108
+
109
+ return [...mcpCommands, ...customCommands, ...COMMANDS()].filter(
110
+ _ => _.isEnabled,
111
+ )
112
+ })
113
+
114
+ export function hasCommand(commandName: string, commands: Command[]): boolean {
115
+ return commands.some(
116
+ _ => _.userFacingName() === commandName || _.aliases?.includes(commandName),
117
+ )
118
+ }
119
+
120
+ export function getCommand(commandName: string, commands: Command[]): Command {
121
+ const command = commands.find(
122
+ _ => _.userFacingName() === commandName || _.aliases?.includes(commandName),
123
+ ) as Command | undefined
124
+ if (!command) {
125
+ throw ReferenceError(
126
+ `Command ${commandName} not found. Available commands: ${commands
127
+ .map(_ => {
128
+ const name = _.userFacingName()
129
+ return _.aliases ? `${name} (aliases: ${_.aliases.join(', ')})` : name
130
+ })
131
+ .join(', ')}`,
132
+ )
133
+ }
134
+
135
+ return command
136
+ }
@@ -0,0 +1,93 @@
1
+ import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { getGlobalConfig, saveGlobalConfig } from '../utils/config'
4
+ import { getTheme } from '../utils/theme'
5
+ import { Select } from '@inkjs/ui'
6
+ import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD'
7
+ import chalk from 'chalk'
8
+
9
+ type Props = {
10
+ customApiKeyTruncated: string
11
+ onDone(): void
12
+ }
13
+
14
+ export function ApproveApiKey({
15
+ customApiKeyTruncated,
16
+ onDone,
17
+ }: Props): React.ReactNode {
18
+ const theme = getTheme()
19
+
20
+ function onChange(value: 'yes' | 'no') {
21
+ const config = getGlobalConfig()
22
+ switch (value) {
23
+ case 'yes': {
24
+ saveGlobalConfig({
25
+ ...config,
26
+ customApiKeyResponses: {
27
+ ...config.customApiKeyResponses,
28
+ approved: [
29
+ ...(config.customApiKeyResponses?.approved ?? []),
30
+ customApiKeyTruncated,
31
+ ],
32
+ },
33
+ })
34
+ onDone()
35
+ break
36
+ }
37
+ case 'no': {
38
+ saveGlobalConfig({
39
+ ...config,
40
+ customApiKeyResponses: {
41
+ ...config.customApiKeyResponses,
42
+ rejected: [
43
+ ...(config.customApiKeyResponses?.rejected ?? []),
44
+ customApiKeyTruncated,
45
+ ],
46
+ },
47
+ })
48
+ onDone()
49
+ break
50
+ }
51
+ }
52
+ }
53
+
54
+ const exitState = useExitOnCtrlCD(() => process.exit(0))
55
+
56
+ return (
57
+ <>
58
+ <Box
59
+ flexDirection="column"
60
+ gap={1}
61
+ padding={1}
62
+ borderStyle="round"
63
+ borderColor={theme.warning}
64
+ >
65
+ <Text bold color={theme.warning}>
66
+ Detected a custom API key in your environment
67
+ </Text>
68
+ <Text>
69
+ Your environment sets{' '}
70
+ <Text color={theme.warning}>ANTHROPIC_API_KEY</Text>:{' '}
71
+ <Text bold>sk-ant-...{customApiKeyTruncated}</Text>
72
+ </Text>
73
+ <Text>Do you want to use this API key?</Text>
74
+ <Select
75
+ options={[
76
+ { label: `No (${chalk.bold('recommended')})`, value: 'no' },
77
+ { label: 'Yes', value: 'yes' },
78
+ ]}
79
+ onChange={value => onChange(value as 'yes' | 'no')}
80
+ />
81
+ </Box>
82
+ <Box marginLeft={3}>
83
+ <Text dimColor>
84
+ {exitState.pending ? (
85
+ <>Press {exitState.keyName} again to exit</>
86
+ ) : (
87
+ <>Enter to confirm</>
88
+ )}
89
+ </Text>
90
+ </Box>
91
+ </>
92
+ )
93
+ }
@@ -0,0 +1,13 @@
1
+ import { Box, Text } from 'ink'
2
+ import React from 'react'
3
+ import { getTheme } from '../utils/theme'
4
+ import { ASCII_LOGO } from '../constants/product'
5
+
6
+ export function AsciiLogo(): React.ReactNode {
7
+ const theme = getTheme()
8
+ return (
9
+ <Box flexDirection="column" alignItems="flex-start">
10
+ <Text color={theme.claude}>{ASCII_LOGO}</Text>
11
+ </Box>
12
+ )
13
+ }