@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.
- package/README.md +202 -76
- package/README.zh-CN.md +246 -0
- package/cli.js +62 -0
- package/package.json +45 -25
- package/scripts/postinstall.js +56 -0
- package/src/ProjectOnboarding.tsx +180 -0
- package/src/Tool.ts +53 -0
- package/src/commands/approvedTools.ts +53 -0
- package/src/commands/bug.tsx +20 -0
- package/src/commands/clear.ts +43 -0
- package/src/commands/compact.ts +120 -0
- package/src/commands/config.tsx +19 -0
- package/src/commands/cost.ts +18 -0
- package/src/commands/ctx_viz.ts +209 -0
- package/src/commands/doctor.ts +24 -0
- package/src/commands/help.tsx +19 -0
- package/src/commands/init.ts +37 -0
- package/src/commands/listen.ts +42 -0
- package/src/commands/login.tsx +51 -0
- package/src/commands/logout.tsx +40 -0
- package/src/commands/mcp.ts +41 -0
- package/src/commands/model.tsx +40 -0
- package/src/commands/modelstatus.tsx +20 -0
- package/src/commands/onboarding.tsx +34 -0
- package/src/commands/pr_comments.ts +59 -0
- package/src/commands/refreshCommands.ts +54 -0
- package/src/commands/release-notes.ts +34 -0
- package/src/commands/resume.tsx +30 -0
- package/src/commands/review.ts +49 -0
- package/src/commands/terminalSetup.ts +221 -0
- package/src/commands.ts +136 -0
- package/src/components/ApproveApiKey.tsx +93 -0
- package/src/components/AsciiLogo.tsx +13 -0
- package/src/components/AutoUpdater.tsx +148 -0
- package/src/components/Bug.tsx +367 -0
- package/src/components/Config.tsx +289 -0
- package/src/components/ConsoleOAuthFlow.tsx +326 -0
- package/src/components/Cost.tsx +23 -0
- package/src/components/CostThresholdDialog.tsx +46 -0
- package/src/components/CustomSelect/option-map.ts +42 -0
- package/src/components/CustomSelect/select-option.tsx +52 -0
- package/src/components/CustomSelect/select.tsx +143 -0
- package/src/components/CustomSelect/use-select-state.ts +414 -0
- package/src/components/CustomSelect/use-select.ts +35 -0
- package/src/components/FallbackToolUseRejectedMessage.tsx +15 -0
- package/src/components/FileEditToolUpdatedMessage.tsx +66 -0
- package/src/components/Help.tsx +215 -0
- package/src/components/HighlightedCode.tsx +33 -0
- package/src/components/InvalidConfigDialog.tsx +113 -0
- package/src/components/Link.tsx +32 -0
- package/src/components/LogSelector.tsx +86 -0
- package/src/components/Logo.tsx +145 -0
- package/src/components/MCPServerApprovalDialog.tsx +100 -0
- package/src/components/MCPServerDialogCopy.tsx +25 -0
- package/src/components/MCPServerMultiselectDialog.tsx +109 -0
- package/src/components/Message.tsx +219 -0
- package/src/components/MessageResponse.tsx +15 -0
- package/src/components/MessageSelector.tsx +211 -0
- package/src/components/ModeIndicator.tsx +88 -0
- package/src/components/ModelConfig.tsx +301 -0
- package/src/components/ModelListManager.tsx +223 -0
- package/src/components/ModelSelector.tsx +3208 -0
- package/src/components/ModelStatusDisplay.tsx +228 -0
- package/src/components/Onboarding.tsx +274 -0
- package/src/components/PressEnterToContinue.tsx +11 -0
- package/src/components/PromptInput.tsx +710 -0
- package/src/components/SentryErrorBoundary.ts +33 -0
- package/src/components/Spinner.tsx +129 -0
- package/src/components/StructuredDiff.tsx +184 -0
- package/src/components/TextInput.tsx +246 -0
- package/src/components/TokenWarning.tsx +31 -0
- package/src/components/ToolUseLoader.tsx +40 -0
- package/src/components/TrustDialog.tsx +106 -0
- package/src/components/binary-feedback/BinaryFeedback.tsx +63 -0
- package/src/components/binary-feedback/BinaryFeedbackOption.tsx +111 -0
- package/src/components/binary-feedback/BinaryFeedbackView.tsx +172 -0
- package/src/components/binary-feedback/utils.ts +220 -0
- package/src/components/messages/AssistantBashOutputMessage.tsx +22 -0
- package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +45 -0
- package/src/components/messages/AssistantRedactedThinkingMessage.tsx +19 -0
- package/src/components/messages/AssistantTextMessage.tsx +144 -0
- package/src/components/messages/AssistantThinkingMessage.tsx +40 -0
- package/src/components/messages/AssistantToolUseMessage.tsx +123 -0
- package/src/components/messages/UserBashInputMessage.tsx +28 -0
- package/src/components/messages/UserCommandMessage.tsx +30 -0
- package/src/components/messages/UserKodingInputMessage.tsx +28 -0
- package/src/components/messages/UserPromptMessage.tsx +35 -0
- package/src/components/messages/UserTextMessage.tsx +39 -0
- package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +12 -0
- package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +36 -0
- package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +31 -0
- package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +57 -0
- package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +35 -0
- package/src/components/messages/UserToolResultMessage/utils.tsx +56 -0
- package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +121 -0
- package/src/components/permissions/FallbackPermissionRequest.tsx +155 -0
- package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +182 -0
- package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +75 -0
- package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +164 -0
- package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +81 -0
- package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +242 -0
- package/src/components/permissions/PermissionRequest.tsx +103 -0
- package/src/components/permissions/PermissionRequestTitle.tsx +69 -0
- package/src/components/permissions/hooks.ts +44 -0
- package/src/components/permissions/toolUseOptions.ts +59 -0
- package/src/components/permissions/utils.ts +23 -0
- package/src/constants/betas.ts +5 -0
- package/src/constants/claude-asterisk-ascii-art.tsx +238 -0
- package/src/constants/figures.ts +4 -0
- package/src/constants/keys.ts +3 -0
- package/src/constants/macros.ts +6 -0
- package/src/constants/models.ts +935 -0
- package/src/constants/oauth.ts +18 -0
- package/src/constants/product.ts +17 -0
- package/src/constants/prompts.ts +177 -0
- package/src/constants/releaseNotes.ts +7 -0
- package/src/context/PermissionContext.tsx +149 -0
- package/src/context.ts +278 -0
- package/src/cost-tracker.ts +84 -0
- package/src/entrypoints/cli.tsx +1498 -0
- package/src/entrypoints/mcp.ts +176 -0
- package/src/history.ts +25 -0
- package/src/hooks/useApiKeyVerification.ts +59 -0
- package/src/hooks/useArrowKeyHistory.ts +55 -0
- package/src/hooks/useCanUseTool.ts +138 -0
- package/src/hooks/useCancelRequest.ts +39 -0
- package/src/hooks/useDoublePress.ts +42 -0
- package/src/hooks/useExitOnCtrlCD.ts +31 -0
- package/src/hooks/useInterval.ts +25 -0
- package/src/hooks/useLogMessages.ts +16 -0
- package/src/hooks/useLogStartupTime.ts +12 -0
- package/src/hooks/useNotifyAfterTimeout.ts +65 -0
- package/src/hooks/usePermissionRequestLogging.ts +44 -0
- package/src/hooks/useSlashCommandTypeahead.ts +137 -0
- package/src/hooks/useTerminalSize.ts +49 -0
- package/src/hooks/useTextInput.ts +315 -0
- package/src/messages.ts +37 -0
- package/src/permissions.ts +268 -0
- package/src/query.ts +704 -0
- package/src/screens/ConfigureNpmPrefix.tsx +197 -0
- package/src/screens/Doctor.tsx +219 -0
- package/src/screens/LogList.tsx +68 -0
- package/src/screens/REPL.tsx +792 -0
- package/src/screens/ResumeConversation.tsx +68 -0
- package/src/services/browserMocks.ts +66 -0
- package/src/services/claude.ts +1947 -0
- package/src/services/customCommands.ts +683 -0
- package/src/services/fileFreshness.ts +377 -0
- package/src/services/mcpClient.ts +564 -0
- package/src/services/mcpServerApproval.tsx +50 -0
- package/src/services/notifier.ts +40 -0
- package/src/services/oauth.ts +357 -0
- package/src/services/openai.ts +796 -0
- package/src/services/sentry.ts +3 -0
- package/src/services/statsig.ts +171 -0
- package/src/services/statsigStorage.ts +86 -0
- package/src/services/systemReminder.ts +406 -0
- package/src/services/vcr.ts +161 -0
- package/src/tools/ArchitectTool/ArchitectTool.tsx +122 -0
- package/src/tools/ArchitectTool/prompt.ts +15 -0
- package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +505 -0
- package/src/tools/BashTool/BashTool.tsx +270 -0
- package/src/tools/BashTool/BashToolResultMessage.tsx +38 -0
- package/src/tools/BashTool/OutputLine.tsx +48 -0
- package/src/tools/BashTool/prompt.ts +174 -0
- package/src/tools/BashTool/utils.ts +56 -0
- package/src/tools/FileEditTool/FileEditTool.tsx +316 -0
- package/src/tools/FileEditTool/prompt.ts +51 -0
- package/src/tools/FileEditTool/utils.ts +58 -0
- package/src/tools/FileReadTool/FileReadTool.tsx +371 -0
- package/src/tools/FileReadTool/prompt.ts +7 -0
- package/src/tools/FileWriteTool/FileWriteTool.tsx +297 -0
- package/src/tools/FileWriteTool/prompt.ts +10 -0
- package/src/tools/GlobTool/GlobTool.tsx +119 -0
- package/src/tools/GlobTool/prompt.ts +8 -0
- package/src/tools/GrepTool/GrepTool.tsx +147 -0
- package/src/tools/GrepTool/prompt.ts +11 -0
- package/src/tools/MCPTool/MCPTool.tsx +106 -0
- package/src/tools/MCPTool/prompt.ts +3 -0
- package/src/tools/MemoryReadTool/MemoryReadTool.tsx +127 -0
- package/src/tools/MemoryReadTool/prompt.ts +3 -0
- package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +89 -0
- package/src/tools/MemoryWriteTool/prompt.ts +3 -0
- package/src/tools/MultiEditTool/MultiEditTool.tsx +366 -0
- package/src/tools/MultiEditTool/prompt.ts +45 -0
- package/src/tools/NotebookEditTool/NotebookEditTool.tsx +298 -0
- package/src/tools/NotebookEditTool/prompt.ts +3 -0
- package/src/tools/NotebookReadTool/NotebookReadTool.tsx +266 -0
- package/src/tools/NotebookReadTool/prompt.ts +3 -0
- package/src/tools/StickerRequestTool/StickerRequestTool.tsx +93 -0
- package/src/tools/StickerRequestTool/prompt.ts +19 -0
- package/src/tools/TaskTool/TaskTool.tsx +382 -0
- package/src/tools/TaskTool/constants.ts +1 -0
- package/src/tools/TaskTool/prompt.ts +56 -0
- package/src/tools/ThinkTool/ThinkTool.tsx +56 -0
- package/src/tools/ThinkTool/prompt.ts +12 -0
- package/src/tools/TodoWriteTool/TodoWriteTool.tsx +289 -0
- package/src/tools/TodoWriteTool/prompt.ts +63 -0
- package/src/tools/lsTool/lsTool.tsx +269 -0
- package/src/tools/lsTool/prompt.ts +2 -0
- package/src/tools.ts +63 -0
- package/src/types/PermissionMode.ts +120 -0
- package/src/types/RequestContext.ts +72 -0
- package/src/utils/Cursor.ts +436 -0
- package/src/utils/PersistentShell.ts +373 -0
- package/src/utils/agentStorage.ts +97 -0
- package/src/utils/array.ts +3 -0
- package/src/utils/ask.tsx +98 -0
- package/src/utils/auth.ts +13 -0
- package/src/utils/autoCompactCore.ts +223 -0
- package/src/utils/autoUpdater.ts +318 -0
- package/src/utils/betas.ts +20 -0
- package/src/utils/browser.ts +14 -0
- package/src/utils/cleanup.ts +72 -0
- package/src/utils/commands.ts +261 -0
- package/src/utils/config.ts +771 -0
- package/src/utils/conversationRecovery.ts +54 -0
- package/src/utils/debugLogger.ts +1123 -0
- package/src/utils/diff.ts +42 -0
- package/src/utils/env.ts +57 -0
- package/src/utils/errors.ts +21 -0
- package/src/utils/exampleCommands.ts +108 -0
- package/src/utils/execFileNoThrow.ts +51 -0
- package/src/utils/expertChatStorage.ts +136 -0
- package/src/utils/file.ts +402 -0
- package/src/utils/fileRecoveryCore.ts +71 -0
- package/src/utils/format.tsx +44 -0
- package/src/utils/generators.ts +62 -0
- package/src/utils/git.ts +92 -0
- package/src/utils/globalLogger.ts +77 -0
- package/src/utils/http.ts +10 -0
- package/src/utils/imagePaste.ts +38 -0
- package/src/utils/json.ts +13 -0
- package/src/utils/log.ts +382 -0
- package/src/utils/markdown.ts +213 -0
- package/src/utils/messageContextManager.ts +289 -0
- package/src/utils/messages.tsx +938 -0
- package/src/utils/model.ts +836 -0
- package/src/utils/permissions/filesystem.ts +118 -0
- package/src/utils/ripgrep.ts +167 -0
- package/src/utils/sessionState.ts +49 -0
- package/src/utils/state.ts +25 -0
- package/src/utils/style.ts +29 -0
- package/src/utils/terminal.ts +49 -0
- package/src/utils/theme.ts +122 -0
- package/src/utils/thinking.ts +144 -0
- package/src/utils/todoStorage.ts +431 -0
- package/src/utils/tokens.ts +43 -0
- package/src/utils/toolExecutionController.ts +163 -0
- package/src/utils/unaryLogging.ts +26 -0
- package/src/utils/user.ts +37 -0
- package/src/utils/validate.ts +165 -0
- 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
|
+
}
|
package/src/commands.ts
ADDED
|
@@ -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
|
+
}
|