@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,209 @@
1
+ import type { Command } from '../commands'
2
+ import type { Tool } from '../Tool'
3
+ import Table from 'cli-table3'
4
+ import { getSystemPrompt } from '../constants/prompts'
5
+ import { getContext } from '../context'
6
+ import { zodToJsonSchema } from 'zod-to-json-schema'
7
+ import { getMessagesGetter } from '../messages'
8
+ import { PROJECT_FILE } from '../constants/product'
9
+ // Quick and dirty estimate of bytes per token for rough token counts
10
+ const BYTES_PER_TOKEN = 4
11
+
12
+ interface Section {
13
+ title: string
14
+ content: string
15
+ }
16
+
17
+ interface ToolSummary {
18
+ name: string
19
+ description: string
20
+ }
21
+
22
+ function getContextSections(text: string): Section[] {
23
+ const sections: Section[] = []
24
+
25
+ // Find first <context> tag
26
+ const firstContextIndex = text.indexOf('<context')
27
+
28
+ // Everything before first tag is Core Sysprompt
29
+ if (firstContextIndex > 0) {
30
+ const coreSysprompt = text.slice(0, firstContextIndex).trim()
31
+ if (coreSysprompt) {
32
+ sections.push({
33
+ title: 'Core Sysprompt',
34
+ content: coreSysprompt,
35
+ })
36
+ }
37
+ }
38
+
39
+ let currentPos = firstContextIndex
40
+ let nonContextContent = ''
41
+
42
+ const regex = /<context\s+name="([^"]*)">([\s\S]*?)<\/context>/g
43
+ let match: RegExpExecArray | null
44
+
45
+ while ((match = regex.exec(text)) !== null) {
46
+ // Collect text between context tags
47
+ if (match.index > currentPos) {
48
+ nonContextContent += text.slice(currentPos, match.index)
49
+ }
50
+
51
+ const [, name = 'Unnamed Section', content = ''] = match
52
+ sections.push({
53
+ title: name === 'codeStyle' ? `CodeStyle + ${PROJECT_FILE}'s` : name,
54
+ content: content.trim(),
55
+ })
56
+
57
+ currentPos = match.index + match[0].length
58
+ }
59
+
60
+ // Collect remaining text after last tag
61
+ if (currentPos < text.length) {
62
+ nonContextContent += text.slice(currentPos)
63
+ }
64
+
65
+ // Add non-contextualized content if present
66
+ const trimmedNonContext = nonContextContent.trim()
67
+ if (trimmedNonContext) {
68
+ sections.push({
69
+ title: 'Non-contextualized Content',
70
+ content: trimmedNonContext,
71
+ })
72
+ }
73
+
74
+ return sections
75
+ }
76
+
77
+ function formatTokenCount(bytes: number): string {
78
+ const tokens = bytes / BYTES_PER_TOKEN
79
+ const k = tokens / 1000
80
+ return `${Math.round(k * 10) / 10}k`
81
+ }
82
+
83
+ function formatByteCount(bytes: number): string {
84
+ const kb = bytes / 1024
85
+ return `${Math.round(kb * 10) / 10}kb`
86
+ }
87
+
88
+ function createSummaryTable(
89
+ systemText: string,
90
+ systemSections: Section[],
91
+ tools: ToolSummary[],
92
+ messages: unknown,
93
+ ): string {
94
+ const table = new Table({
95
+ head: ['Component', 'Tokens', 'Size', '% Used'],
96
+ style: { head: ['bold'] },
97
+ chars: {
98
+ mid: '─',
99
+ 'left-mid': '├',
100
+ 'mid-mid': '┼',
101
+ 'right-mid': '┤',
102
+ },
103
+ })
104
+
105
+ const messagesStr = JSON.stringify(messages)
106
+ const toolsStr = JSON.stringify(tools)
107
+
108
+ // Calculate total for percentages
109
+ const total = systemText.length + toolsStr.length + messagesStr.length
110
+ const getPercentage = (n: number) => `${Math.round((n / total) * 100)}%`
111
+
112
+ // System prompt and its sections
113
+ table.push([
114
+ 'System prompt',
115
+ formatTokenCount(systemText.length),
116
+ formatByteCount(systemText.length),
117
+ getPercentage(systemText.length),
118
+ ])
119
+ for (const section of systemSections) {
120
+ table.push([
121
+ ` ${section.title}`,
122
+ formatTokenCount(section.content.length),
123
+ formatByteCount(section.content.length),
124
+ getPercentage(section.content.length),
125
+ ])
126
+ }
127
+
128
+ // Tools
129
+ table.push([
130
+ 'Tool definitions',
131
+ formatTokenCount(toolsStr.length),
132
+ formatByteCount(toolsStr.length),
133
+ getPercentage(toolsStr.length),
134
+ ])
135
+ for (const tool of tools) {
136
+ table.push([
137
+ ` ${tool.name}`,
138
+ formatTokenCount(tool.description.length),
139
+ formatByteCount(tool.description.length),
140
+ getPercentage(tool.description.length),
141
+ ])
142
+ }
143
+
144
+ // Messages and total
145
+ table.push(
146
+ [
147
+ 'Messages',
148
+ formatTokenCount(messagesStr.length),
149
+ formatByteCount(messagesStr.length),
150
+ getPercentage(messagesStr.length),
151
+ ],
152
+ ['Total', formatTokenCount(total), formatByteCount(total), '100%'],
153
+ )
154
+
155
+ return table.toString()
156
+ }
157
+
158
+ const command: Command = {
159
+ name: 'ctx-viz',
160
+ description:
161
+ 'Show token usage breakdown for the current conversation context',
162
+ isEnabled: true,
163
+ isHidden: false,
164
+ type: 'local',
165
+
166
+ userFacingName() {
167
+ return this.name
168
+ },
169
+
170
+ async call(_args: string, cmdContext: { options: { tools: Tool[] } }) {
171
+ // Get tools and system prompt with injected context
172
+ const [systemPromptRaw, sysContext] = await Promise.all([
173
+ getSystemPrompt(),
174
+ getContext(),
175
+ ])
176
+
177
+ const rawTools = cmdContext.options.tools
178
+
179
+ // Full system prompt with context sections injected
180
+ let systemPrompt = systemPromptRaw.join('\n')
181
+ for (const [name, content] of Object.entries(sysContext)) {
182
+ systemPrompt += `\n<context name="${name}">${content}</context>`
183
+ }
184
+
185
+ // Get full tool definitions including prompts and schemas
186
+ const tools = rawTools.map(t => {
187
+ // Get full prompt and schema
188
+ const fullPrompt = t.prompt({ safeMode: false })
189
+ const schema = JSON.stringify(
190
+ 'inputJSONSchema' in t && t.inputJSONSchema
191
+ ? t.inputJSONSchema
192
+ : zodToJsonSchema(t.inputSchema),
193
+ )
194
+
195
+ return {
196
+ name: t.name,
197
+ description: `${fullPrompt}\n\nSchema:\n${schema}`,
198
+ }
199
+ })
200
+
201
+ // Get current messages from REPL
202
+ const messages = getMessagesGetter()()
203
+
204
+ const sections = getContextSections(systemPrompt)
205
+ return createSummaryTable(systemPrompt, sections, tools, messages)
206
+ },
207
+ }
208
+
209
+ export default command
@@ -0,0 +1,24 @@
1
+ import React from 'react'
2
+ import type { Command } from '../commands'
3
+ import { Doctor } from '../screens/Doctor'
4
+ import { PRODUCT_NAME } from '../constants/product'
5
+
6
+ const doctor: Command = {
7
+ name: 'doctor',
8
+ description: `Checks the health of your ${PRODUCT_NAME} installation`,
9
+ isEnabled: true,
10
+ isHidden: false,
11
+ userFacingName() {
12
+ return 'doctor'
13
+ },
14
+ type: 'local-jsx',
15
+ call(onDone) {
16
+ const element = React.createElement(Doctor, {
17
+ onDone,
18
+ doctorMode: true,
19
+ })
20
+ return Promise.resolve(element)
21
+ },
22
+ }
23
+
24
+ export default doctor
@@ -0,0 +1,19 @@
1
+ import { Command } from '../commands'
2
+ import { Help } from '../components/Help'
3
+ import * as React from 'react'
4
+
5
+ const help = {
6
+ type: 'local-jsx',
7
+ name: 'help',
8
+ description: 'Show help and available commands',
9
+ isEnabled: true,
10
+ isHidden: false,
11
+ async call(onDone, { options: { commands } }) {
12
+ return <Help commands={commands} onClose={onDone} />
13
+ },
14
+ userFacingName() {
15
+ return 'help'
16
+ },
17
+ } satisfies Command
18
+
19
+ export default help
@@ -0,0 +1,37 @@
1
+ import type { Command } from '../commands'
2
+ import { markProjectOnboardingComplete } from '../ProjectOnboarding'
3
+ import { PROJECT_FILE } from '../constants/product'
4
+ const command = {
5
+ type: 'prompt',
6
+ name: 'init',
7
+ description: `Initialize a new ${PROJECT_FILE} file with codebase documentation`,
8
+ isEnabled: true,
9
+ isHidden: false,
10
+ progressMessage: 'analyzing your codebase',
11
+ userFacingName() {
12
+ return 'init'
13
+ },
14
+ async getPromptForCommand(_args: string) {
15
+ // Mark onboarding as complete when init command is run
16
+ markProjectOnboardingComplete()
17
+ return [
18
+ {
19
+ role: 'user',
20
+ content: [
21
+ {
22
+ type: 'text',
23
+ text: `Please analyze this codebase and create a ${PROJECT_FILE} file containing:
24
+ 1. Build/lint/test commands - especially for running a single test
25
+ 2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.
26
+
27
+ The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 20 lines long.
28
+ If there's already a ${PROJECT_FILE}, improve it.
29
+ If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include them.`,
30
+ },
31
+ ],
32
+ },
33
+ ]
34
+ },
35
+ } satisfies Command
36
+
37
+ export default command
@@ -0,0 +1,42 @@
1
+ import { Command } from '../commands'
2
+ import { logError } from '../utils/log'
3
+ import { execFileNoThrow } from '../utils/execFileNoThrow'
4
+
5
+ const isEnabled =
6
+ process.platform === 'darwin' &&
7
+ ['iTerm.app', 'Apple_Terminal'].includes(process.env.TERM_PROGRAM || '')
8
+
9
+ const listen: Command = {
10
+ type: 'local',
11
+ name: 'listen',
12
+ description: 'Activates speech recognition and transcribes speech to text',
13
+ isEnabled: isEnabled,
14
+ isHidden: isEnabled,
15
+ userFacingName() {
16
+ return 'listen'
17
+ },
18
+ async call(_, { abortController }) {
19
+ // Start dictation using AppleScript
20
+ const script = `tell application "System Events" to tell ¬
21
+ (the first process whose frontmost is true) to tell ¬
22
+ menu bar 1 to tell ¬
23
+ menu bar item "Edit" to tell ¬
24
+ menu "Edit" to tell ¬
25
+ menu item "Start Dictation" to ¬
26
+ if exists then click it`
27
+
28
+ const { stderr, code } = await execFileNoThrow(
29
+ 'osascript',
30
+ ['-e', script],
31
+ abortController.signal,
32
+ )
33
+
34
+ if (code !== 0) {
35
+ logError(`Failed to start dictation: ${stderr}`)
36
+ return 'Failed to start dictation'
37
+ }
38
+ return 'Dictation started. Press esc to stop.'
39
+ },
40
+ }
41
+
42
+ export default listen
@@ -0,0 +1,51 @@
1
+ import * as React from 'react'
2
+ import type { Command } from '../commands'
3
+ import { ConsoleOAuthFlow } from '../components/ConsoleOAuthFlow'
4
+ import { clearTerminal } from '../utils/terminal'
5
+ import { isLoggedInToAnthropic } from '../utils/auth'
6
+ import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD'
7
+ import { Box, Text } from 'ink'
8
+ import { clearConversation } from './clear'
9
+
10
+ export default () =>
11
+ ({
12
+ type: 'local-jsx',
13
+ name: 'login',
14
+ description: isLoggedInToAnthropic()
15
+ ? 'Switch Anthropic accounts'
16
+ : 'Sign in with your Anthropic account',
17
+ isEnabled: true,
18
+ isHidden: false,
19
+ async call(onDone, context) {
20
+ await clearTerminal()
21
+ return (
22
+ <Login
23
+ onDone={async () => {
24
+ clearConversation(context)
25
+ onDone()
26
+ }}
27
+ />
28
+ )
29
+ },
30
+ userFacingName() {
31
+ return 'login'
32
+ },
33
+ }) satisfies Command
34
+
35
+ function Login(props: { onDone: () => void }) {
36
+ const exitState = useExitOnCtrlCD(props.onDone)
37
+ return (
38
+ <Box flexDirection="column">
39
+ <ConsoleOAuthFlow onDone={props.onDone} />
40
+ <Box marginLeft={3}>
41
+ <Text dimColor>
42
+ {exitState.pending ? (
43
+ <>Press {exitState.keyName} again to exit</>
44
+ ) : (
45
+ ''
46
+ )}
47
+ </Text>
48
+ </Box>
49
+ </Box>
50
+ )
51
+ }
@@ -0,0 +1,40 @@
1
+ import * as React from 'react'
2
+ import type { Command } from '../commands'
3
+ import { getGlobalConfig, saveGlobalConfig } from '../utils/config'
4
+ import { clearTerminal } from '../utils/terminal'
5
+ import { Text } from 'ink'
6
+
7
+ export default {
8
+ type: 'local-jsx',
9
+ name: 'logout',
10
+ description: 'Sign out from your Anthropic account',
11
+ isEnabled: true,
12
+ isHidden: false,
13
+ async call() {
14
+ await clearTerminal()
15
+
16
+ const config = getGlobalConfig()
17
+
18
+ config.oauthAccount = undefined
19
+ config.hasCompletedOnboarding = false
20
+
21
+ if (config.customApiKeyResponses?.approved) {
22
+ config.customApiKeyResponses.approved = []
23
+ }
24
+
25
+ saveGlobalConfig(config)
26
+
27
+ const message = (
28
+ <Text>Successfully logged out from your Anthropic account.</Text>
29
+ )
30
+
31
+ setTimeout(() => {
32
+ process.exit(0)
33
+ }, 200)
34
+
35
+ return message
36
+ },
37
+ userFacingName() {
38
+ return 'logout'
39
+ },
40
+ } satisfies Command
@@ -0,0 +1,41 @@
1
+ import type { Command } from '../commands'
2
+ import { listMCPServers, getClients } from '../services/mcpClient'
3
+ import { PRODUCT_COMMAND } from '../constants/product'
4
+ import chalk from 'chalk'
5
+ import { getTheme } from '../utils/theme'
6
+
7
+ const mcp = {
8
+ type: 'local',
9
+ name: 'mcp',
10
+ description: 'Show MCP server connection status',
11
+ isEnabled: true,
12
+ isHidden: false,
13
+ async call() {
14
+ const servers = listMCPServers()
15
+ const clients = await getClients()
16
+ const theme = getTheme()
17
+
18
+ if (Object.keys(servers).length === 0) {
19
+ return `⎿ No MCP servers configured. Run \`${PRODUCT_COMMAND} mcp\` to learn about how to configure MCP servers.`
20
+ }
21
+
22
+ // Sort servers by name and format status with colors
23
+ const serverStatusLines = clients
24
+ .sort((a, b) => a.name.localeCompare(b.name))
25
+ .map(client => {
26
+ const isConnected = client.type === 'connected'
27
+ const status = isConnected ? 'connected' : 'disconnected'
28
+ const coloredStatus = isConnected
29
+ ? chalk.hex(theme.success)(status)
30
+ : chalk.hex(theme.error)(status)
31
+ return `⎿ • ${client.name}: ${coloredStatus}`
32
+ })
33
+
34
+ return ['⎿ MCP Server Status', ...serverStatusLines].join('\n')
35
+ },
36
+ userFacingName() {
37
+ return 'mcp'
38
+ },
39
+ } satisfies Command
40
+
41
+ export default mcp
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+ import { render } from 'ink'
3
+ import { ModelConfig } from '../components/ModelConfig'
4
+ import { enableConfigs } from '../utils/config'
5
+ import { triggerModelConfigChange } from '../messages'
6
+
7
+ export const help = 'Change your AI provider and model settings'
8
+ export const description = 'Change your AI provider and model settings'
9
+ export const isEnabled = true
10
+ export const isHidden = false
11
+ export const name = 'model'
12
+ export const type = 'local-jsx'
13
+
14
+ export function userFacingName(): string {
15
+ return name
16
+ }
17
+
18
+ export async function call(
19
+ onDone: (result?: string) => void,
20
+ context: any,
21
+ ): Promise<React.ReactNode> {
22
+ const { abortController } = context
23
+ enableConfigs()
24
+ abortController?.abort?.()
25
+ return (
26
+ <ModelConfig
27
+ onClose={() => {
28
+ // Force ModelManager reload to ensure UI sync - wait for completion before closing
29
+ import('../utils/model').then(({ reloadModelManager }) => {
30
+ reloadModelManager()
31
+ // 🔧 Critical fix: Trigger global UI refresh after model config changes
32
+ // This ensures PromptInput component detects ModelManager singleton state changes
33
+ triggerModelConfigChange()
34
+ // Only close after reload is complete to ensure UI synchronization
35
+ onDone()
36
+ })
37
+ }}
38
+ />
39
+ )
40
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import type { Command } from '../commands'
3
+ import { ModelStatusDisplay } from '../components/ModelStatusDisplay'
4
+
5
+ const modelstatus: Command = {
6
+ name: 'modelstatus',
7
+ description: 'Display current model configuration and status',
8
+ aliases: ['ms', 'model-status'],
9
+ isEnabled: true,
10
+ isHidden: false,
11
+ userFacingName() {
12
+ return 'modelstatus'
13
+ },
14
+ type: 'local-jsx',
15
+ call(onDone) {
16
+ return Promise.resolve(<ModelStatusDisplay onClose={onDone} />)
17
+ },
18
+ }
19
+
20
+ export default modelstatus
@@ -0,0 +1,34 @@
1
+ import * as React from 'react'
2
+ import type { Command } from '../commands'
3
+ import { Onboarding } from '../components/Onboarding'
4
+ import { clearTerminal } from '../utils/terminal'
5
+ import { getGlobalConfig, saveGlobalConfig } from '../utils/config'
6
+ import { clearConversation } from './clear'
7
+
8
+ export default {
9
+ type: 'local-jsx',
10
+ name: 'onboarding',
11
+ description: 'Run through the onboarding flow',
12
+ isEnabled: true,
13
+ isHidden: false,
14
+ async call(onDone, context) {
15
+ await clearTerminal()
16
+ const config = getGlobalConfig()
17
+ saveGlobalConfig({
18
+ ...config,
19
+ theme: 'dark',
20
+ })
21
+
22
+ return (
23
+ <Onboarding
24
+ onDone={async () => {
25
+ clearConversation(context)
26
+ onDone()
27
+ }}
28
+ />
29
+ )
30
+ },
31
+ userFacingName() {
32
+ return 'onboarding'
33
+ },
34
+ } satisfies Command
@@ -0,0 +1,59 @@
1
+ import { Command } from '../commands'
2
+
3
+ export default {
4
+ type: 'prompt',
5
+ name: 'pr-comments',
6
+ description: 'Get comments from a GitHub pull request',
7
+ progressMessage: 'fetching PR comments',
8
+ isEnabled: true,
9
+ isHidden: false,
10
+ userFacingName() {
11
+ return 'pr-comments'
12
+ },
13
+ async getPromptForCommand(args: string) {
14
+ return [
15
+ {
16
+ role: 'user',
17
+ content: [
18
+ {
19
+ type: 'text',
20
+ text: `You are an AI assistant integrated into a git-based version control system. Your task is to fetch and display comments from a GitHub pull request.
21
+
22
+ Follow these steps:
23
+
24
+ 1. Use \`gh pr view --json number,headRepository\` to get the PR number and repository info
25
+ 2. Use \`gh api /repos/{owner}/{repo}/issues/{number}/comments\` to get PR-level comments
26
+ 3. Use \`gh api /repos/{owner}/{repo}/pulls/{number}/comments\` to get review comments. Pay particular attention to the following fields: \`body\`, \`diff_hunk\`, \`path\`, \`line\`, etc. If the comment references some code, consider fetching it using eg \`gh api /repos/{owner}/{repo}/contents/{path}?ref={branch} | jq .content -r | base64 -d\`
27
+ 4. Parse and format all comments in a readable way
28
+ 5. Return ONLY the formatted comments, with no additional text
29
+
30
+ Format the comments as:
31
+
32
+ ## Comments
33
+
34
+ [For each comment thread:]
35
+ - @author file.ts#line:
36
+ \`\`\`diff
37
+ [diff_hunk from the API response]
38
+ \`\`\`
39
+ > quoted comment text
40
+
41
+ [any replies indented]
42
+
43
+ If there are no comments, return "No comments found."
44
+
45
+ Remember:
46
+ 1. Only show the actual comments, no explanatory text
47
+ 2. Include both PR-level and code review comments
48
+ 3. Preserve the threading/nesting of comment replies
49
+ 4. Show the file and line number context for code review comments
50
+ 5. Use jq to parse the JSON responses from the GitHub API
51
+
52
+ ${args ? 'Additional user input: ' + args : ''}
53
+ `,
54
+ },
55
+ ],
56
+ },
57
+ ]
58
+ },
59
+ } satisfies Command
@@ -0,0 +1,54 @@
1
+ import { Command } from '../commands'
2
+ import { reloadCustomCommands } from '../services/customCommands'
3
+ import { getCommands } from '../commands'
4
+
5
+ /**
6
+ * Refresh Commands - Reload custom commands from filesystem
7
+ *
8
+ * This command provides a runtime mechanism to refresh the custom commands
9
+ * cache without restarting the application. It's particularly useful during
10
+ * development or when users are actively creating/modifying custom commands.
11
+ *
12
+ * The command follows the standard local command pattern used throughout
13
+ * the project and provides detailed feedback about the refresh operation.
14
+ */
15
+ const refreshCommands = {
16
+ type: 'local',
17
+ name: 'refresh-commands',
18
+ description: 'Reload custom commands from filesystem',
19
+ isEnabled: true,
20
+ isHidden: false,
21
+ async call(_, context) {
22
+ try {
23
+ // Clear custom commands cache to force filesystem rescan
24
+ reloadCustomCommands()
25
+
26
+ // Clear the main commands cache to ensure full reload
27
+ // This ensures that changes to custom commands are reflected in the main command list
28
+ getCommands.cache.clear?.()
29
+
30
+ // Reload commands to get updated count and validate the refresh
31
+ const commands = await getCommands()
32
+ const customCommands = commands.filter(
33
+ cmd => cmd.name.startsWith('project:') || cmd.name.startsWith('user:'),
34
+ )
35
+
36
+ // Provide detailed feedback about the refresh operation
37
+ return `✅ Commands refreshed successfully!
38
+
39
+ Custom commands reloaded: ${customCommands.length}
40
+ - Project commands: ${customCommands.filter(cmd => cmd.name.startsWith('project:')).length}
41
+ - User commands: ${customCommands.filter(cmd => cmd.name.startsWith('user:')).length}
42
+
43
+ Use /help to see updated command list.`
44
+ } catch (error) {
45
+ console.error('Failed to refresh commands:', error)
46
+ return '❌ Failed to refresh commands. Check console for details.'
47
+ }
48
+ },
49
+ userFacingName() {
50
+ return 'refresh-commands'
51
+ },
52
+ } satisfies Command
53
+
54
+ export default refreshCommands