@shareai-lab/kode 1.0.69 → 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 +205 -72
- 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,710 @@
|
|
|
1
|
+
import { Box, Text, useInput } from 'ink'
|
|
2
|
+
import { sample } from 'lodash-es'
|
|
3
|
+
import { getExampleCommands } from '../utils/exampleCommands'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
import { type Message } from '../query'
|
|
6
|
+
import { processUserInput } from '../utils/messages'
|
|
7
|
+
import { useArrowKeyHistory } from '../hooks/useArrowKeyHistory'
|
|
8
|
+
import { useSlashCommandTypeahead } from '../hooks/useSlashCommandTypeahead'
|
|
9
|
+
import { addToHistory } from '../history'
|
|
10
|
+
import TextInput from './TextInput'
|
|
11
|
+
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
|
12
|
+
import { countTokens } from '../utils/tokens'
|
|
13
|
+
import { SentryErrorBoundary } from './SentryErrorBoundary'
|
|
14
|
+
import { AutoUpdater } from './AutoUpdater'
|
|
15
|
+
import type { AutoUpdaterResult } from '../utils/autoUpdater'
|
|
16
|
+
import type { Command } from '../commands'
|
|
17
|
+
import type { SetToolJSXFn, Tool } from '../Tool'
|
|
18
|
+
import { TokenWarning, WARNING_THRESHOLD } from './TokenWarning'
|
|
19
|
+
import { useTerminalSize } from '../hooks/useTerminalSize'
|
|
20
|
+
import { getTheme } from '../utils/theme'
|
|
21
|
+
import { getModelManager, reloadModelManager } from '../utils/model'
|
|
22
|
+
import { saveGlobalConfig } from '../utils/config'
|
|
23
|
+
import { setTerminalTitle } from '../utils/terminal'
|
|
24
|
+
import terminalSetup, {
|
|
25
|
+
isShiftEnterKeyBindingInstalled,
|
|
26
|
+
handleHashCommand,
|
|
27
|
+
} from '../commands/terminalSetup'
|
|
28
|
+
import { usePermissionContext } from '../context/PermissionContext'
|
|
29
|
+
|
|
30
|
+
// Async function to interpret the '#' command input using AI
|
|
31
|
+
async function interpretHashCommand(input: string): Promise<string> {
|
|
32
|
+
// Use the AI to interpret the input
|
|
33
|
+
try {
|
|
34
|
+
const { queryQuick } = await import('../services/claude')
|
|
35
|
+
|
|
36
|
+
// Create a prompt for the model to interpret the hash command
|
|
37
|
+
const systemPrompt = [
|
|
38
|
+
"You're helping the user structure notes that will be added to their KODING.md file.",
|
|
39
|
+
"Format the user's input into a well-structured note that will be useful for later reference.",
|
|
40
|
+
'Add appropriate markdown formatting, headings, bullet points, or other structural elements as needed.',
|
|
41
|
+
'The goal is to transform the raw note into something that will be more useful when reviewed later.',
|
|
42
|
+
'You should keep the original meaning but make the structure clear.',
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
// Send the request to the AI
|
|
46
|
+
const result = await queryQuick({
|
|
47
|
+
systemPrompt,
|
|
48
|
+
userPrompt: `Transform this note for KODING.md: ${input}`,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Extract the content from the response
|
|
52
|
+
if (typeof result.message.content === 'string') {
|
|
53
|
+
return result.message.content
|
|
54
|
+
} else if (Array.isArray(result.message.content)) {
|
|
55
|
+
return result.message.content
|
|
56
|
+
.filter(block => block.type === 'text')
|
|
57
|
+
.map(block => (block.type === 'text' ? block.text : ''))
|
|
58
|
+
.join('\n')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return `# ${input}\n\n_Added on ${new Date().toLocaleString()}_`
|
|
62
|
+
} catch (e) {
|
|
63
|
+
// If interpretation fails, return the input with minimal formatting
|
|
64
|
+
return `# ${input}\n\n_Added on ${new Date().toLocaleString()}_`
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type Props = {
|
|
69
|
+
commands: Command[]
|
|
70
|
+
forkNumber: number
|
|
71
|
+
messageLogName: string
|
|
72
|
+
isDisabled: boolean
|
|
73
|
+
isLoading: boolean
|
|
74
|
+
onQuery: (
|
|
75
|
+
newMessages: Message[],
|
|
76
|
+
abortController?: AbortController,
|
|
77
|
+
) => Promise<void>
|
|
78
|
+
debug: boolean
|
|
79
|
+
verbose: boolean
|
|
80
|
+
messages: Message[]
|
|
81
|
+
setToolJSX: SetToolJSXFn
|
|
82
|
+
onAutoUpdaterResult: (result: AutoUpdaterResult) => void
|
|
83
|
+
autoUpdaterResult: AutoUpdaterResult | null
|
|
84
|
+
tools: Tool[]
|
|
85
|
+
input: string
|
|
86
|
+
onInputChange: (value: string) => void
|
|
87
|
+
mode: 'bash' | 'prompt' | 'koding'
|
|
88
|
+
onModeChange: (mode: 'bash' | 'prompt' | 'koding') => void
|
|
89
|
+
submitCount: number
|
|
90
|
+
onSubmitCountChange: (updater: (prev: number) => number) => void
|
|
91
|
+
setIsLoading: (isLoading: boolean) => void
|
|
92
|
+
setAbortController: (abortController: AbortController | null) => void
|
|
93
|
+
onShowMessageSelector: () => void
|
|
94
|
+
setForkConvoWithMessagesOnTheNextRender: (
|
|
95
|
+
forkConvoWithMessages: Message[],
|
|
96
|
+
) => void
|
|
97
|
+
readFileTimestamps: { [filename: string]: number }
|
|
98
|
+
abortController: AbortController | null
|
|
99
|
+
setAbortController: (abortController: AbortController | null) => void
|
|
100
|
+
onModelChange?: () => void
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getPastedTextPrompt(text: string): string {
|
|
104
|
+
const newlineCount = (text.match(/\r\n|\r|\n/g) || []).length
|
|
105
|
+
return `[Pasted text +${newlineCount} lines] `
|
|
106
|
+
}
|
|
107
|
+
function PromptInput({
|
|
108
|
+
commands,
|
|
109
|
+
forkNumber,
|
|
110
|
+
messageLogName,
|
|
111
|
+
isDisabled,
|
|
112
|
+
isLoading,
|
|
113
|
+
onQuery,
|
|
114
|
+
debug,
|
|
115
|
+
verbose,
|
|
116
|
+
messages,
|
|
117
|
+
setToolJSX,
|
|
118
|
+
onAutoUpdaterResult,
|
|
119
|
+
autoUpdaterResult,
|
|
120
|
+
tools,
|
|
121
|
+
input,
|
|
122
|
+
onInputChange,
|
|
123
|
+
mode,
|
|
124
|
+
onModeChange,
|
|
125
|
+
submitCount,
|
|
126
|
+
onSubmitCountChange,
|
|
127
|
+
setIsLoading,
|
|
128
|
+
abortController,
|
|
129
|
+
setAbortController,
|
|
130
|
+
onShowMessageSelector,
|
|
131
|
+
setForkConvoWithMessagesOnTheNextRender,
|
|
132
|
+
readFileTimestamps,
|
|
133
|
+
onModelChange,
|
|
134
|
+
}: Props): React.ReactNode {
|
|
135
|
+
const [isAutoUpdating, setIsAutoUpdating] = useState(false)
|
|
136
|
+
const [exitMessage, setExitMessage] = useState<{
|
|
137
|
+
show: boolean
|
|
138
|
+
key?: string
|
|
139
|
+
}>({ show: false })
|
|
140
|
+
const [message, setMessage] = useState<{ show: boolean; text?: string }>({
|
|
141
|
+
show: false,
|
|
142
|
+
})
|
|
143
|
+
const [modelSwitchMessage, setModelSwitchMessage] = useState<{
|
|
144
|
+
show: boolean
|
|
145
|
+
text?: string
|
|
146
|
+
}>({
|
|
147
|
+
show: false,
|
|
148
|
+
})
|
|
149
|
+
const [pastedImage, setPastedImage] = useState<string | null>(null)
|
|
150
|
+
const [placeholder, setPlaceholder] = useState('')
|
|
151
|
+
const [cursorOffset, setCursorOffset] = useState<number>(input.length)
|
|
152
|
+
const [pastedText, setPastedText] = useState<string | null>(null)
|
|
153
|
+
|
|
154
|
+
// Permission context for mode management
|
|
155
|
+
const { cycleMode, currentMode } = usePermissionContext()
|
|
156
|
+
|
|
157
|
+
// useEffect(() => {
|
|
158
|
+
// getExampleCommands().then(commands => {
|
|
159
|
+
// setPlaceholder(`Try "${sample(commands)}"`)
|
|
160
|
+
// })
|
|
161
|
+
// }, [])
|
|
162
|
+
const { columns } = useTerminalSize()
|
|
163
|
+
|
|
164
|
+
const commandWidth = useMemo(
|
|
165
|
+
() => Math.max(...commands.map(cmd => cmd.userFacingName().length)) + 5,
|
|
166
|
+
[commands],
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
const {
|
|
170
|
+
suggestions,
|
|
171
|
+
selectedSuggestion,
|
|
172
|
+
updateSuggestions,
|
|
173
|
+
clearSuggestions,
|
|
174
|
+
} = useSlashCommandTypeahead({
|
|
175
|
+
commands,
|
|
176
|
+
onInputChange,
|
|
177
|
+
onSubmit,
|
|
178
|
+
setCursorOffset,
|
|
179
|
+
currentInput: input,
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
const onChange = useCallback(
|
|
183
|
+
(value: string) => {
|
|
184
|
+
if (value.startsWith('!')) {
|
|
185
|
+
onModeChange('bash')
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
if (value.startsWith('#')) {
|
|
189
|
+
onModeChange('koding')
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
updateSuggestions(value)
|
|
193
|
+
onInputChange(value)
|
|
194
|
+
},
|
|
195
|
+
[onModeChange, onInputChange, updateSuggestions],
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
// Handle Tab key model switching with simple context check
|
|
199
|
+
const handleQuickModelSwitch = useCallback(async () => {
|
|
200
|
+
const modelManager = getModelManager()
|
|
201
|
+
const currentTokens = countTokens(messages)
|
|
202
|
+
|
|
203
|
+
const switchResult = modelManager.switchToNextModel(currentTokens)
|
|
204
|
+
|
|
205
|
+
if (switchResult.success && switchResult.modelName) {
|
|
206
|
+
// Successful switch
|
|
207
|
+
onSubmitCountChange(prev => prev + 1)
|
|
208
|
+
const newModel = modelManager.getModel('main')
|
|
209
|
+
setModelSwitchMessage({
|
|
210
|
+
show: true,
|
|
211
|
+
text: `✅ Switched to ${switchResult.modelName} (${newModel?.provider || 'Unknown'} | Model: ${newModel?.modelName || 'N/A'})`,
|
|
212
|
+
})
|
|
213
|
+
setTimeout(() => setModelSwitchMessage({ show: false }), 3000)
|
|
214
|
+
} else if (switchResult.blocked && switchResult.message) {
|
|
215
|
+
// Context overflow - show detailed message
|
|
216
|
+
setModelSwitchMessage({
|
|
217
|
+
show: true,
|
|
218
|
+
text: switchResult.message,
|
|
219
|
+
})
|
|
220
|
+
setTimeout(() => setModelSwitchMessage({ show: false }), 5000)
|
|
221
|
+
} else {
|
|
222
|
+
// No other models available or other error
|
|
223
|
+
setModelSwitchMessage({
|
|
224
|
+
show: true,
|
|
225
|
+
text:
|
|
226
|
+
switchResult.message ||
|
|
227
|
+
'⚠️ No other models configured. Use /model to add more models',
|
|
228
|
+
})
|
|
229
|
+
setTimeout(() => setModelSwitchMessage({ show: false }), 3000)
|
|
230
|
+
}
|
|
231
|
+
}, [onSubmitCountChange, messages])
|
|
232
|
+
|
|
233
|
+
const { resetHistory, onHistoryUp, onHistoryDown } = useArrowKeyHistory(
|
|
234
|
+
(value: string, mode: 'bash' | 'prompt' | 'koding') => {
|
|
235
|
+
onChange(value)
|
|
236
|
+
onModeChange(mode)
|
|
237
|
+
},
|
|
238
|
+
input,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
// Only use history navigation when there are 0 or 1 slash command suggestions
|
|
242
|
+
const handleHistoryUp = () => {
|
|
243
|
+
if (suggestions.length <= 1) {
|
|
244
|
+
onHistoryUp()
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const handleHistoryDown = () => {
|
|
249
|
+
if (suggestions.length <= 1) {
|
|
250
|
+
onHistoryDown()
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function onSubmit(input: string, isSubmittingSlashCommand = false) {
|
|
255
|
+
// Special handling for "put a verbose summary" and similar action prompts in koding mode
|
|
256
|
+
if (
|
|
257
|
+
(mode === 'koding' || input.startsWith('#')) &&
|
|
258
|
+
input.match(/^(#\s*)?(put|create|generate|write|give|provide)/i)
|
|
259
|
+
) {
|
|
260
|
+
try {
|
|
261
|
+
// Store the original input for history
|
|
262
|
+
const originalInput = input
|
|
263
|
+
|
|
264
|
+
// Strip the # prefix if present
|
|
265
|
+
const cleanInput = mode === 'koding' ? input : input.substring(1).trim()
|
|
266
|
+
|
|
267
|
+
// Add to history and clear input field
|
|
268
|
+
addToHistory(mode === 'koding' ? `#${input}` : input)
|
|
269
|
+
onInputChange('')
|
|
270
|
+
|
|
271
|
+
// Create additional context to inform Claude this is for KODING.md
|
|
272
|
+
const kodingContext =
|
|
273
|
+
'The user is using Koding mode. Format your response as a comprehensive, well-structured document suitable for adding to KODE.md. Use proper markdown formatting with headings, lists, code blocks, etc. The response should be complete and ready to add to KODE.md documentation.'
|
|
274
|
+
|
|
275
|
+
// Switch to prompt mode but tag the submission for later capture
|
|
276
|
+
onModeChange('prompt')
|
|
277
|
+
|
|
278
|
+
// 🔧 Fix Koding mode: clean up previous state
|
|
279
|
+
if (abortController) {
|
|
280
|
+
abortController.abort()
|
|
281
|
+
}
|
|
282
|
+
setIsLoading(false)
|
|
283
|
+
await new Promise(resolve => setTimeout(resolve, 0))
|
|
284
|
+
|
|
285
|
+
// Set loading state - AbortController now created in onQuery
|
|
286
|
+
setIsLoading(true)
|
|
287
|
+
|
|
288
|
+
// Process as a normal user input but with special handling
|
|
289
|
+
const messages = await processUserInput(
|
|
290
|
+
cleanInput,
|
|
291
|
+
'prompt', // Use prompt mode for processing
|
|
292
|
+
setToolJSX,
|
|
293
|
+
{
|
|
294
|
+
options: {
|
|
295
|
+
commands,
|
|
296
|
+
forkNumber,
|
|
297
|
+
messageLogName,
|
|
298
|
+
tools,
|
|
299
|
+
verbose,
|
|
300
|
+
maxThinkingTokens: 0,
|
|
301
|
+
// Add context flag for koding mode
|
|
302
|
+
isKodingRequest: true,
|
|
303
|
+
kodingContext,
|
|
304
|
+
},
|
|
305
|
+
messageId: undefined,
|
|
306
|
+
abortController: abortController || new AbortController(), // Temporary controller, actual one created in onQuery
|
|
307
|
+
readFileTimestamps,
|
|
308
|
+
setForkConvoWithMessagesOnTheNextRender,
|
|
309
|
+
},
|
|
310
|
+
pastedImage ?? null,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
// Send query and capture response
|
|
314
|
+
if (messages.length) {
|
|
315
|
+
await onQuery(messages)
|
|
316
|
+
|
|
317
|
+
// After query completes, the last message should be Claude's response
|
|
318
|
+
// We'll set up a one-time listener to capture and save Claude's response
|
|
319
|
+
// This will be handled by the REPL component or message handler
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return
|
|
323
|
+
} catch (e) {
|
|
324
|
+
// If something fails, log the error
|
|
325
|
+
console.error('Error processing Koding request:', e)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// If in koding mode or input starts with '#', interpret it using AI before appending to KODE.md
|
|
330
|
+
else if (mode === 'koding' || input.startsWith('#')) {
|
|
331
|
+
try {
|
|
332
|
+
// Strip the # if we're in koding mode and the user didn't type it (since it's implied)
|
|
333
|
+
const contentToInterpret =
|
|
334
|
+
mode === 'koding' && !input.startsWith('#')
|
|
335
|
+
? input.trim()
|
|
336
|
+
: input.substring(1).trim()
|
|
337
|
+
|
|
338
|
+
const interpreted = await interpretHashCommand(contentToInterpret)
|
|
339
|
+
handleHashCommand(interpreted)
|
|
340
|
+
} catch (e) {
|
|
341
|
+
// If interpretation fails, log the error
|
|
342
|
+
}
|
|
343
|
+
onInputChange('')
|
|
344
|
+
addToHistory(mode === 'koding' ? `#${input}` : input)
|
|
345
|
+
onModeChange('prompt')
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
if (input === '') {
|
|
349
|
+
return
|
|
350
|
+
}
|
|
351
|
+
if (isDisabled) {
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
if (isLoading) {
|
|
355
|
+
return
|
|
356
|
+
}
|
|
357
|
+
if (suggestions.length > 0 && !isSubmittingSlashCommand) {
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Handle exit commands
|
|
362
|
+
if (['exit', 'quit', ':q', ':q!', ':wq', ':wq!'].includes(input.trim())) {
|
|
363
|
+
exit()
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let finalInput = input
|
|
367
|
+
if (pastedText) {
|
|
368
|
+
// Create the prompt pattern that would have been used for this pasted text
|
|
369
|
+
const pastedPrompt = getPastedTextPrompt(pastedText)
|
|
370
|
+
if (finalInput.includes(pastedPrompt)) {
|
|
371
|
+
finalInput = finalInput.replace(pastedPrompt, pastedText)
|
|
372
|
+
} // otherwise, ignore the pastedText if the user has modified the prompt
|
|
373
|
+
}
|
|
374
|
+
onInputChange('')
|
|
375
|
+
onModeChange('prompt')
|
|
376
|
+
clearSuggestions()
|
|
377
|
+
setPastedImage(null)
|
|
378
|
+
setPastedText(null)
|
|
379
|
+
onSubmitCountChange(_ => _ + 1)
|
|
380
|
+
|
|
381
|
+
setIsLoading(true)
|
|
382
|
+
|
|
383
|
+
const newAbortController = new AbortController()
|
|
384
|
+
setAbortController(newAbortController)
|
|
385
|
+
|
|
386
|
+
const messages = await processUserInput(
|
|
387
|
+
finalInput,
|
|
388
|
+
mode,
|
|
389
|
+
setToolJSX,
|
|
390
|
+
{
|
|
391
|
+
options: {
|
|
392
|
+
commands,
|
|
393
|
+
forkNumber,
|
|
394
|
+
messageLogName,
|
|
395
|
+
tools,
|
|
396
|
+
verbose,
|
|
397
|
+
maxThinkingTokens: 0,
|
|
398
|
+
},
|
|
399
|
+
messageId: undefined,
|
|
400
|
+
abortController: newAbortController,
|
|
401
|
+
readFileTimestamps,
|
|
402
|
+
setForkConvoWithMessagesOnTheNextRender,
|
|
403
|
+
},
|
|
404
|
+
pastedImage ?? null,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
if (messages.length) {
|
|
408
|
+
onQuery(messages, newAbortController)
|
|
409
|
+
} else {
|
|
410
|
+
// Local JSX commands
|
|
411
|
+
addToHistory(input)
|
|
412
|
+
resetHistory()
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
for (const message of messages) {
|
|
417
|
+
if (message.type === 'user') {
|
|
418
|
+
const inputToAdd = mode === 'bash' ? `!${input}` : input
|
|
419
|
+
addToHistory(inputToAdd)
|
|
420
|
+
resetHistory()
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function onImagePaste(image: string) {
|
|
426
|
+
onModeChange('prompt')
|
|
427
|
+
setPastedImage(image)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function onTextPaste(rawText: string) {
|
|
431
|
+
// Replace any \r with \n first to match useTextInput's conversion behavior
|
|
432
|
+
const text = rawText.replace(/\r/g, '\n')
|
|
433
|
+
|
|
434
|
+
// Get prompt with newline count
|
|
435
|
+
const pastedPrompt = getPastedTextPrompt(text)
|
|
436
|
+
|
|
437
|
+
// Update the input with a visual indicator that text has been pasted
|
|
438
|
+
const newInput =
|
|
439
|
+
input.slice(0, cursorOffset) + pastedPrompt + input.slice(cursorOffset)
|
|
440
|
+
onInputChange(newInput)
|
|
441
|
+
|
|
442
|
+
// Update cursor position to be after the inserted indicator
|
|
443
|
+
setCursorOffset(cursorOffset + pastedPrompt.length)
|
|
444
|
+
|
|
445
|
+
// Still set the pastedText state for actual submission
|
|
446
|
+
setPastedText(text)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
useInput((input, key) => {
|
|
450
|
+
if (input === '' && (key.escape || key.backspace || key.delete)) {
|
|
451
|
+
onModeChange('prompt')
|
|
452
|
+
}
|
|
453
|
+
// esc is a little overloaded:
|
|
454
|
+
// - when we're loading a response, it's used to cancel the request
|
|
455
|
+
// - otherwise, it's used to show the message selector
|
|
456
|
+
// - when double pressed, it's used to clear the input
|
|
457
|
+
if (key.escape && messages.length > 0 && !input && !isLoading) {
|
|
458
|
+
onShowMessageSelector()
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Shift+Tab for mode cycling (matching original Claude Code implementation)
|
|
462
|
+
if (key.shift && key.tab) {
|
|
463
|
+
cycleMode()
|
|
464
|
+
return true // Explicitly handled
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Tab key for model switching (simple and non-conflicting)
|
|
468
|
+
if (key.tab && !key.shift) {
|
|
469
|
+
handleQuickModelSwitch()
|
|
470
|
+
return true // Explicitly handled
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return false // Not handled, allow other hooks
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
const textInputColumns = useTerminalSize().columns - 6
|
|
477
|
+
const tokenUsage = useMemo(() => countTokens(messages), [messages])
|
|
478
|
+
const theme = getTheme()
|
|
479
|
+
|
|
480
|
+
// 🔧 Fix: Track model ID changes to detect external config updates
|
|
481
|
+
const modelManager = getModelManager()
|
|
482
|
+
const currentModelId = modelManager.getModel('main')?.id || null
|
|
483
|
+
|
|
484
|
+
const modelInfo = useMemo(() => {
|
|
485
|
+
// Force fresh ModelManager instance to detect config changes
|
|
486
|
+
const freshModelManager = getModelManager()
|
|
487
|
+
const currentModel = freshModelManager.getModel('main')
|
|
488
|
+
if (!currentModel) {
|
|
489
|
+
return null
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return {
|
|
493
|
+
name: currentModel.modelName, // 🔧 Fix: Use actual model name, not display name
|
|
494
|
+
id: currentModel.id, // 添加模型ID用于调试
|
|
495
|
+
provider: currentModel.provider, // 添加提供商信息
|
|
496
|
+
contextLength: currentModel.contextLength,
|
|
497
|
+
currentTokens: tokenUsage,
|
|
498
|
+
}
|
|
499
|
+
}, [tokenUsage, modelSwitchMessage.show, submitCount, currentModelId]) // Track model ID to detect config changes
|
|
500
|
+
|
|
501
|
+
return (
|
|
502
|
+
<Box flexDirection="column">
|
|
503
|
+
{/* Model info in top-right corner */}
|
|
504
|
+
{modelInfo && (
|
|
505
|
+
<Box justifyContent="flex-end" marginBottom={1}>
|
|
506
|
+
<Text dimColor>
|
|
507
|
+
[{modelInfo.provider}] {modelInfo.name}:{' '}
|
|
508
|
+
{Math.round(modelInfo.currentTokens / 1000)}k /{' '}
|
|
509
|
+
{Math.round(modelInfo.contextLength / 1000)}k
|
|
510
|
+
</Text>
|
|
511
|
+
</Box>
|
|
512
|
+
)}
|
|
513
|
+
|
|
514
|
+
<Box
|
|
515
|
+
alignItems="flex-start"
|
|
516
|
+
justifyContent="flex-start"
|
|
517
|
+
borderColor={
|
|
518
|
+
mode === 'bash'
|
|
519
|
+
? theme.bashBorder
|
|
520
|
+
: mode === 'koding'
|
|
521
|
+
? theme.koding
|
|
522
|
+
: theme.secondaryBorder
|
|
523
|
+
}
|
|
524
|
+
borderDimColor
|
|
525
|
+
borderStyle="round"
|
|
526
|
+
marginTop={1}
|
|
527
|
+
width="100%"
|
|
528
|
+
>
|
|
529
|
+
<Box
|
|
530
|
+
alignItems="flex-start"
|
|
531
|
+
alignSelf="flex-start"
|
|
532
|
+
flexWrap="nowrap"
|
|
533
|
+
justifyContent="flex-start"
|
|
534
|
+
width={3}
|
|
535
|
+
>
|
|
536
|
+
{mode === 'bash' ? (
|
|
537
|
+
<Text color={theme.bashBorder}> ! </Text>
|
|
538
|
+
) : mode === 'koding' ? (
|
|
539
|
+
<Text color={theme.koding}> # </Text>
|
|
540
|
+
) : (
|
|
541
|
+
<Text color={isLoading ? theme.secondaryText : undefined}>
|
|
542
|
+
>
|
|
543
|
+
</Text>
|
|
544
|
+
)}
|
|
545
|
+
</Box>
|
|
546
|
+
<Box paddingRight={1}>
|
|
547
|
+
<TextInput
|
|
548
|
+
multiline
|
|
549
|
+
onSubmit={onSubmit}
|
|
550
|
+
onChange={onChange}
|
|
551
|
+
value={input}
|
|
552
|
+
onHistoryUp={handleHistoryUp}
|
|
553
|
+
onHistoryDown={handleHistoryDown}
|
|
554
|
+
onHistoryReset={() => resetHistory()}
|
|
555
|
+
placeholder={submitCount > 0 ? undefined : placeholder}
|
|
556
|
+
onExit={() => process.exit(0)}
|
|
557
|
+
onExitMessage={(show, key) => setExitMessage({ show, key })}
|
|
558
|
+
onMessage={(show, text) => setMessage({ show, text })}
|
|
559
|
+
onImagePaste={onImagePaste}
|
|
560
|
+
columns={textInputColumns}
|
|
561
|
+
isDimmed={isDisabled || isLoading}
|
|
562
|
+
disableCursorMovementForUpDownKeys={suggestions.length > 0}
|
|
563
|
+
cursorOffset={cursorOffset}
|
|
564
|
+
onChangeCursorOffset={setCursorOffset}
|
|
565
|
+
onPaste={onTextPaste}
|
|
566
|
+
/>
|
|
567
|
+
</Box>
|
|
568
|
+
</Box>
|
|
569
|
+
{suggestions.length === 0 && (
|
|
570
|
+
<Box
|
|
571
|
+
flexDirection="row"
|
|
572
|
+
justifyContent="space-between"
|
|
573
|
+
paddingX={2}
|
|
574
|
+
paddingY={0}
|
|
575
|
+
>
|
|
576
|
+
<Box justifyContent="flex-start" gap={1}>
|
|
577
|
+
{exitMessage.show ? (
|
|
578
|
+
<Text dimColor>Press {exitMessage.key} again to exit</Text>
|
|
579
|
+
) : message.show ? (
|
|
580
|
+
<Text dimColor>{message.text}</Text>
|
|
581
|
+
) : modelSwitchMessage.show ? (
|
|
582
|
+
<Text color={theme.success}>{modelSwitchMessage.text}</Text>
|
|
583
|
+
) : (
|
|
584
|
+
<>
|
|
585
|
+
<Text
|
|
586
|
+
color={mode === 'bash' ? theme.bashBorder : undefined}
|
|
587
|
+
dimColor={mode !== 'bash'}
|
|
588
|
+
>
|
|
589
|
+
! for bash mode
|
|
590
|
+
</Text>
|
|
591
|
+
<Text
|
|
592
|
+
color={mode === 'koding' ? theme.koding : undefined}
|
|
593
|
+
dimColor={mode !== 'koding'}
|
|
594
|
+
>
|
|
595
|
+
· # for KODE.md
|
|
596
|
+
</Text>
|
|
597
|
+
<Text dimColor>
|
|
598
|
+
· / for commands · tab to switch model · esc to undo
|
|
599
|
+
</Text>
|
|
600
|
+
</>
|
|
601
|
+
)}
|
|
602
|
+
</Box>
|
|
603
|
+
<SentryErrorBoundary>
|
|
604
|
+
<Box justifyContent="flex-end" gap={1}>
|
|
605
|
+
{!autoUpdaterResult &&
|
|
606
|
+
!isAutoUpdating &&
|
|
607
|
+
!debug &&
|
|
608
|
+
tokenUsage < WARNING_THRESHOLD && (
|
|
609
|
+
<Text dimColor>
|
|
610
|
+
{terminalSetup.isEnabled &&
|
|
611
|
+
isShiftEnterKeyBindingInstalled()
|
|
612
|
+
? 'shift + ⏎ for newline'
|
|
613
|
+
: '\\⏎ for newline'}
|
|
614
|
+
</Text>
|
|
615
|
+
)}
|
|
616
|
+
<TokenWarning tokenUsage={tokenUsage} />
|
|
617
|
+
{/* <AutoUpdater
|
|
618
|
+
debug={debug}
|
|
619
|
+
onAutoUpdaterResult={onAutoUpdaterResult}
|
|
620
|
+
autoUpdaterResult={autoUpdaterResult}
|
|
621
|
+
isUpdating={isAutoUpdating}
|
|
622
|
+
onChangeIsUpdating={setIsAutoUpdating}
|
|
623
|
+
/> */}
|
|
624
|
+
</Box>
|
|
625
|
+
</SentryErrorBoundary>
|
|
626
|
+
</Box>
|
|
627
|
+
)}
|
|
628
|
+
{suggestions.length > 0 && (
|
|
629
|
+
<Box
|
|
630
|
+
flexDirection="row"
|
|
631
|
+
justifyContent="space-between"
|
|
632
|
+
paddingX={2}
|
|
633
|
+
paddingY={0}
|
|
634
|
+
>
|
|
635
|
+
<Box flexDirection="column">
|
|
636
|
+
{suggestions.map((suggestion, index) => {
|
|
637
|
+
const command = commands.find(
|
|
638
|
+
cmd => cmd.userFacingName() === suggestion.replace('/', ''),
|
|
639
|
+
)
|
|
640
|
+
return (
|
|
641
|
+
<Box
|
|
642
|
+
key={suggestion}
|
|
643
|
+
flexDirection={columns < 80 ? 'column' : 'row'}
|
|
644
|
+
>
|
|
645
|
+
<Box width={columns < 80 ? undefined : commandWidth}>
|
|
646
|
+
<Text
|
|
647
|
+
color={
|
|
648
|
+
index === selectedSuggestion
|
|
649
|
+
? theme.suggestion
|
|
650
|
+
: undefined
|
|
651
|
+
}
|
|
652
|
+
dimColor={index !== selectedSuggestion}
|
|
653
|
+
>
|
|
654
|
+
/{suggestion}
|
|
655
|
+
{command?.aliases && command.aliases.length > 0 && (
|
|
656
|
+
<Text dimColor> ({command.aliases.join(', ')})</Text>
|
|
657
|
+
)}
|
|
658
|
+
</Text>
|
|
659
|
+
</Box>
|
|
660
|
+
{command && (
|
|
661
|
+
<Box
|
|
662
|
+
width={columns - (columns < 80 ? 4 : commandWidth + 4)}
|
|
663
|
+
paddingLeft={columns < 80 ? 4 : 0}
|
|
664
|
+
>
|
|
665
|
+
<Text
|
|
666
|
+
color={
|
|
667
|
+
index === selectedSuggestion
|
|
668
|
+
? theme.suggestion
|
|
669
|
+
: undefined
|
|
670
|
+
}
|
|
671
|
+
dimColor={index !== selectedSuggestion}
|
|
672
|
+
wrap="wrap"
|
|
673
|
+
>
|
|
674
|
+
<Text dimColor={index !== selectedSuggestion}>
|
|
675
|
+
{command.description}
|
|
676
|
+
{command.type === 'prompt' && command.argNames?.length
|
|
677
|
+
? ` (arguments: ${command.argNames.join(', ')})`
|
|
678
|
+
: null}
|
|
679
|
+
</Text>
|
|
680
|
+
</Text>
|
|
681
|
+
</Box>
|
|
682
|
+
)}
|
|
683
|
+
</Box>
|
|
684
|
+
)
|
|
685
|
+
})}
|
|
686
|
+
</Box>
|
|
687
|
+
<SentryErrorBoundary>
|
|
688
|
+
<Box justifyContent="flex-end" gap={1}>
|
|
689
|
+
<TokenWarning tokenUsage={countTokens(messages)} />
|
|
690
|
+
<AutoUpdater
|
|
691
|
+
debug={debug}
|
|
692
|
+
onAutoUpdaterResult={onAutoUpdaterResult}
|
|
693
|
+
autoUpdaterResult={autoUpdaterResult}
|
|
694
|
+
isUpdating={isAutoUpdating}
|
|
695
|
+
onChangeIsUpdating={setIsAutoUpdating}
|
|
696
|
+
/>
|
|
697
|
+
</Box>
|
|
698
|
+
</SentryErrorBoundary>
|
|
699
|
+
</Box>
|
|
700
|
+
)}
|
|
701
|
+
</Box>
|
|
702
|
+
)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
export default memo(PromptInput)
|
|
706
|
+
|
|
707
|
+
function exit(): never {
|
|
708
|
+
setTerminalTitle('')
|
|
709
|
+
process.exit(0)
|
|
710
|
+
}
|