@shareai-lab/kode 1.0.71 → 1.0.73
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 +142 -1
- package/README.zh-CN.md +47 -1
- package/package.json +5 -1
- package/src/ProjectOnboarding.tsx +47 -29
- package/src/Tool.ts +33 -4
- package/src/commands/agents.tsx +3401 -0
- package/src/commands/help.tsx +2 -2
- package/src/commands/resume.tsx +2 -1
- package/src/commands/terminalSetup.ts +4 -4
- package/src/commands.ts +3 -0
- package/src/components/ApproveApiKey.tsx +1 -1
- package/src/components/Config.tsx +10 -6
- package/src/components/ConsoleOAuthFlow.tsx +5 -4
- package/src/components/CustomSelect/select-option.tsx +28 -2
- package/src/components/CustomSelect/select.tsx +14 -5
- package/src/components/CustomSelect/theme.ts +45 -0
- package/src/components/Help.tsx +4 -4
- package/src/components/InvalidConfigDialog.tsx +1 -1
- package/src/components/LogSelector.tsx +1 -1
- package/src/components/MCPServerApprovalDialog.tsx +1 -1
- package/src/components/Message.tsx +2 -0
- package/src/components/ModelListManager.tsx +10 -6
- package/src/components/ModelSelector.tsx +201 -23
- package/src/components/ModelStatusDisplay.tsx +7 -5
- package/src/components/PromptInput.tsx +117 -87
- package/src/components/SentryErrorBoundary.ts +3 -3
- package/src/components/StickerRequestForm.tsx +16 -0
- package/src/components/StructuredDiff.tsx +36 -29
- package/src/components/TextInput.tsx +13 -0
- package/src/components/TodoItem.tsx +11 -0
- package/src/components/TrustDialog.tsx +1 -1
- package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +5 -1
- package/src/components/messages/AssistantToolUseMessage.tsx +14 -4
- package/src/components/messages/TaskProgressMessage.tsx +32 -0
- package/src/components/messages/TaskToolMessage.tsx +58 -0
- package/src/components/permissions/FallbackPermissionRequest.tsx +2 -4
- package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +1 -1
- package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +5 -3
- package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +1 -1
- package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +5 -3
- package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +2 -4
- package/src/components/permissions/PermissionRequest.tsx +3 -5
- package/src/constants/macros.ts +2 -0
- package/src/constants/modelCapabilities.ts +179 -0
- package/src/constants/models.ts +90 -0
- package/src/constants/product.ts +1 -1
- package/src/context.ts +7 -7
- package/src/entrypoints/cli.tsx +23 -3
- package/src/entrypoints/mcp.ts +10 -10
- package/src/hooks/useCanUseTool.ts +1 -1
- package/src/hooks/useTextInput.ts +5 -2
- package/src/hooks/useUnifiedCompletion.ts +1404 -0
- package/src/messages.ts +1 -0
- package/src/query.ts +3 -0
- package/src/screens/ConfigureNpmPrefix.tsx +1 -1
- package/src/screens/Doctor.tsx +1 -1
- package/src/screens/REPL.tsx +15 -9
- package/src/services/adapters/base.ts +38 -0
- package/src/services/adapters/chatCompletions.ts +90 -0
- package/src/services/adapters/responsesAPI.ts +170 -0
- package/src/services/claude.ts +198 -62
- package/src/services/customCommands.ts +43 -22
- package/src/services/gpt5ConnectionTest.ts +340 -0
- package/src/services/mcpClient.ts +1 -1
- package/src/services/mentionProcessor.ts +273 -0
- package/src/services/modelAdapterFactory.ts +69 -0
- package/src/services/openai.ts +521 -12
- package/src/services/responseStateManager.ts +90 -0
- package/src/services/systemReminder.ts +113 -12
- package/src/test/testAdapters.ts +96 -0
- package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +120 -56
- package/src/tools/BashTool/BashTool.tsx +4 -31
- package/src/tools/BashTool/BashToolResultMessage.tsx +1 -1
- package/src/tools/BashTool/OutputLine.tsx +1 -0
- package/src/tools/FileEditTool/FileEditTool.tsx +4 -5
- package/src/tools/FileReadTool/FileReadTool.tsx +43 -10
- package/src/tools/MCPTool/MCPTool.tsx +2 -1
- package/src/tools/MultiEditTool/MultiEditTool.tsx +2 -2
- package/src/tools/NotebookReadTool/NotebookReadTool.tsx +15 -23
- package/src/tools/StickerRequestTool/StickerRequestTool.tsx +1 -1
- package/src/tools/TaskTool/TaskTool.tsx +170 -86
- package/src/tools/TaskTool/prompt.ts +61 -25
- package/src/tools/ThinkTool/ThinkTool.tsx +1 -3
- package/src/tools/TodoWriteTool/TodoWriteTool.tsx +11 -10
- package/src/tools/lsTool/lsTool.tsx +5 -2
- package/src/tools.ts +16 -16
- package/src/types/conversation.ts +51 -0
- package/src/types/logs.ts +58 -0
- package/src/types/modelCapabilities.ts +64 -0
- package/src/types/notebook.ts +87 -0
- package/src/utils/advancedFuzzyMatcher.ts +290 -0
- package/src/utils/agentLoader.ts +284 -0
- package/src/utils/ask.tsx +1 -0
- package/src/utils/commands.ts +1 -1
- package/src/utils/commonUnixCommands.ts +161 -0
- package/src/utils/config.ts +173 -2
- package/src/utils/conversationRecovery.ts +1 -0
- package/src/utils/debugLogger.ts +13 -13
- package/src/utils/exampleCommands.ts +1 -0
- package/src/utils/fuzzyMatcher.ts +328 -0
- package/src/utils/messages.tsx +6 -5
- package/src/utils/responseState.ts +23 -0
- package/src/utils/secureFile.ts +559 -0
- package/src/utils/terminal.ts +1 -0
- package/src/utils/theme.ts +11 -0
- package/src/hooks/useSlashCommandTypeahead.ts +0 -137
|
@@ -185,7 +185,7 @@ export function ModelStatusDisplay({ onClose }: Props): React.ReactNode {
|
|
|
185
185
|
</Text>
|
|
186
186
|
<Text color={theme.secondaryText}>
|
|
187
187
|
{' '}
|
|
188
|
-
DefaultModelId: {config.defaultModelId || 'not set'}
|
|
188
|
+
DefaultModelId: {(config as any).defaultModelId || 'not set'}
|
|
189
189
|
</Text>
|
|
190
190
|
{config.modelPointers && (
|
|
191
191
|
<>
|
|
@@ -195,10 +195,12 @@ export function ModelStatusDisplay({ onClose }: Props): React.ReactNode {
|
|
|
195
195
|
{Object.keys(config.modelPointers).length > 0 ? 'Yes' : 'No'}
|
|
196
196
|
</Text>
|
|
197
197
|
{Object.entries(config.modelPointers).map(([pointer, modelId]) => (
|
|
198
|
-
<
|
|
199
|
-
{
|
|
200
|
-
|
|
201
|
-
|
|
198
|
+
<React.Fragment key={pointer}>
|
|
199
|
+
<Text color={theme.secondaryText}>
|
|
200
|
+
{' '}
|
|
201
|
+
{pointer}: {modelId || 'not set'}
|
|
202
|
+
</Text>
|
|
203
|
+
</React.Fragment>
|
|
202
204
|
))}
|
|
203
205
|
</>
|
|
204
206
|
)}
|
|
@@ -5,7 +5,7 @@ import * as React from 'react'
|
|
|
5
5
|
import { type Message } from '../query'
|
|
6
6
|
import { processUserInput } from '../utils/messages'
|
|
7
7
|
import { useArrowKeyHistory } from '../hooks/useArrowKeyHistory'
|
|
8
|
-
import {
|
|
8
|
+
import { useUnifiedCompletion } from '../hooks/useUnifiedCompletion'
|
|
9
9
|
import { addToHistory } from '../history'
|
|
10
10
|
import TextInput from './TextInput'
|
|
11
11
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
|
@@ -96,7 +96,6 @@ type Props = {
|
|
|
96
96
|
) => void
|
|
97
97
|
readFileTimestamps: { [filename: string]: number }
|
|
98
98
|
abortController: AbortController | null
|
|
99
|
-
setAbortController: (abortController: AbortController | null) => void
|
|
100
99
|
onModelChange?: () => void
|
|
101
100
|
}
|
|
102
101
|
|
|
@@ -166,19 +165,53 @@ function PromptInput({
|
|
|
166
165
|
[commands],
|
|
167
166
|
)
|
|
168
167
|
|
|
168
|
+
// Unified completion system - one hook to rule them all (now with terminal behavior)
|
|
169
169
|
const {
|
|
170
170
|
suggestions,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
} =
|
|
175
|
-
|
|
171
|
+
selectedIndex,
|
|
172
|
+
isActive: completionActive,
|
|
173
|
+
emptyDirMessage,
|
|
174
|
+
} = useUnifiedCompletion({
|
|
175
|
+
input,
|
|
176
|
+
cursorOffset,
|
|
176
177
|
onInputChange,
|
|
177
|
-
onSubmit,
|
|
178
178
|
setCursorOffset,
|
|
179
|
-
|
|
179
|
+
commands,
|
|
180
|
+
onSubmit,
|
|
180
181
|
})
|
|
181
182
|
|
|
183
|
+
// Get theme early for memoized rendering
|
|
184
|
+
const theme = getTheme()
|
|
185
|
+
|
|
186
|
+
// Memoized completion suggestions rendering - after useUnifiedCompletion
|
|
187
|
+
const renderedSuggestions = useMemo(() => {
|
|
188
|
+
if (suggestions.length === 0) return null
|
|
189
|
+
|
|
190
|
+
return suggestions.map((suggestion, index) => {
|
|
191
|
+
const isSelected = index === selectedIndex
|
|
192
|
+
const isAgent = suggestion.type === 'agent'
|
|
193
|
+
|
|
194
|
+
// Simple color logic without complex lookups
|
|
195
|
+
const displayColor = isSelected
|
|
196
|
+
? theme.suggestion
|
|
197
|
+
: (isAgent && suggestion.metadata?.color)
|
|
198
|
+
? suggestion.metadata.color
|
|
199
|
+
: undefined
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<Box key={`${suggestion.type}-${suggestion.value}-${index}`} flexDirection="row">
|
|
203
|
+
<Text
|
|
204
|
+
color={displayColor}
|
|
205
|
+
dimColor={!isSelected && !displayColor}
|
|
206
|
+
>
|
|
207
|
+
{isSelected ? '◆ ' : ' '}
|
|
208
|
+
{suggestion.displayValue}
|
|
209
|
+
</Text>
|
|
210
|
+
</Box>
|
|
211
|
+
)
|
|
212
|
+
})
|
|
213
|
+
}, [suggestions, selectedIndex, theme.suggestion])
|
|
214
|
+
|
|
182
215
|
const onChange = useCallback(
|
|
183
216
|
(value: string) => {
|
|
184
217
|
if (value.startsWith('!')) {
|
|
@@ -189,10 +222,9 @@ function PromptInput({
|
|
|
189
222
|
onModeChange('koding')
|
|
190
223
|
return
|
|
191
224
|
}
|
|
192
|
-
updateSuggestions(value)
|
|
193
225
|
onInputChange(value)
|
|
194
226
|
},
|
|
195
|
-
[onModeChange, onInputChange
|
|
227
|
+
[onModeChange, onInputChange],
|
|
196
228
|
)
|
|
197
229
|
|
|
198
230
|
// Handle Tab key model switching with simple context check
|
|
@@ -238,15 +270,15 @@ function PromptInput({
|
|
|
238
270
|
input,
|
|
239
271
|
)
|
|
240
272
|
|
|
241
|
-
// Only use history navigation when there are
|
|
273
|
+
// Only use history navigation when there are no suggestions
|
|
242
274
|
const handleHistoryUp = () => {
|
|
243
|
-
if (
|
|
275
|
+
if (!completionActive) {
|
|
244
276
|
onHistoryUp()
|
|
245
277
|
}
|
|
246
278
|
}
|
|
247
279
|
|
|
248
280
|
const handleHistoryDown = () => {
|
|
249
|
-
if (
|
|
281
|
+
if (!completionActive) {
|
|
250
282
|
onHistoryDown()
|
|
251
283
|
}
|
|
252
284
|
}
|
|
@@ -270,7 +302,7 @@ function PromptInput({
|
|
|
270
302
|
|
|
271
303
|
// Create additional context to inform Claude this is for KODING.md
|
|
272
304
|
const kodingContext =
|
|
273
|
-
'The user is using Koding mode. Format your response as a comprehensive, well-structured document suitable for adding to
|
|
305
|
+
'The user is using Koding mode. Format your response as a comprehensive, well-structured document suitable for adding to AGENTS.md. Use proper markdown formatting with headings, lists, code blocks, etc. The response should be complete and ready to add to AGENTS.md documentation.'
|
|
274
306
|
|
|
275
307
|
// Switch to prompt mode but tag the submission for later capture
|
|
276
308
|
onModeChange('prompt')
|
|
@@ -326,7 +358,7 @@ function PromptInput({
|
|
|
326
358
|
}
|
|
327
359
|
}
|
|
328
360
|
|
|
329
|
-
// If in koding mode or input starts with '#', interpret it using AI before appending to
|
|
361
|
+
// If in koding mode or input starts with '#', interpret it using AI before appending to AGENTS.md
|
|
330
362
|
else if (mode === 'koding' || input.startsWith('#')) {
|
|
331
363
|
try {
|
|
332
364
|
// Strip the # if we're in koding mode and the user didn't type it (since it's implied)
|
|
@@ -354,7 +386,12 @@ function PromptInput({
|
|
|
354
386
|
if (isLoading) {
|
|
355
387
|
return
|
|
356
388
|
}
|
|
357
|
-
|
|
389
|
+
|
|
390
|
+
// Handle Enter key when completions are active
|
|
391
|
+
// If there are suggestions showing, Enter should complete the selection, not send the message
|
|
392
|
+
if (suggestions.length > 0 && completionActive) {
|
|
393
|
+
// The completion is handled by useUnifiedCompletion hook
|
|
394
|
+
// Just return to prevent message sending
|
|
358
395
|
return
|
|
359
396
|
}
|
|
360
397
|
|
|
@@ -373,7 +410,7 @@ function PromptInput({
|
|
|
373
410
|
}
|
|
374
411
|
onInputChange('')
|
|
375
412
|
onModeChange('prompt')
|
|
376
|
-
|
|
413
|
+
// Suggestions are now handled by unified completion
|
|
377
414
|
setPastedImage(null)
|
|
378
415
|
setPastedText(null)
|
|
379
416
|
onSubmitCountChange(_ => _ + 1)
|
|
@@ -446,8 +483,29 @@ function PromptInput({
|
|
|
446
483
|
setPastedText(text)
|
|
447
484
|
}
|
|
448
485
|
|
|
449
|
-
useInput((
|
|
450
|
-
|
|
486
|
+
useInput((inputChar, key) => {
|
|
487
|
+
// For bash mode, only exit when deleting the last character (which would be the '!' character)
|
|
488
|
+
if (mode === 'bash' && (key.backspace || key.delete)) {
|
|
489
|
+
// Check the current input state, not the inputChar parameter
|
|
490
|
+
// If current input is empty, we're about to delete the '!' character, so exit bash mode
|
|
491
|
+
if (input === '') {
|
|
492
|
+
onModeChange('prompt')
|
|
493
|
+
}
|
|
494
|
+
return
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// For koding mode, only exit when deleting the last character (which would be the '#' character)
|
|
498
|
+
if (mode === 'koding' && (key.backspace || key.delete)) {
|
|
499
|
+
// Check the current input state, not the inputChar parameter
|
|
500
|
+
// If current input is empty, we're about to delete the '#' character, so exit koding mode
|
|
501
|
+
if (input === '') {
|
|
502
|
+
onModeChange('prompt')
|
|
503
|
+
}
|
|
504
|
+
return
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// For other modes, keep the original behavior
|
|
508
|
+
if (inputChar === '' && (key.escape || key.backspace || key.delete)) {
|
|
451
509
|
onModeChange('prompt')
|
|
452
510
|
}
|
|
453
511
|
// esc is a little overloaded:
|
|
@@ -464,22 +522,15 @@ function PromptInput({
|
|
|
464
522
|
return true // Explicitly handled
|
|
465
523
|
}
|
|
466
524
|
|
|
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
525
|
return false // Not handled, allow other hooks
|
|
474
526
|
})
|
|
475
527
|
|
|
476
528
|
const textInputColumns = useTerminalSize().columns - 6
|
|
477
529
|
const tokenUsage = useMemo(() => countTokens(messages), [messages])
|
|
478
|
-
const theme = getTheme()
|
|
479
530
|
|
|
480
531
|
// 🔧 Fix: Track model ID changes to detect external config updates
|
|
481
532
|
const modelManager = getModelManager()
|
|
482
|
-
const currentModelId = modelManager.getModel('main')?.id || null
|
|
533
|
+
const currentModelId = (modelManager.getModel('main') as any)?.id || null
|
|
483
534
|
|
|
484
535
|
const modelInfo = useMemo(() => {
|
|
485
536
|
// Force fresh ModelManager instance to detect config changes
|
|
@@ -491,7 +542,7 @@ function PromptInput({
|
|
|
491
542
|
|
|
492
543
|
return {
|
|
493
544
|
name: currentModel.modelName, // 🔧 Fix: Use actual model name, not display name
|
|
494
|
-
id: currentModel.id, // 添加模型ID用于调试
|
|
545
|
+
id: (currentModel as any).id, // 添加模型ID用于调试
|
|
495
546
|
provider: currentModel.provider, // 添加提供商信息
|
|
496
547
|
contextLength: currentModel.contextLength,
|
|
497
548
|
currentTokens: tokenUsage,
|
|
@@ -559,14 +610,22 @@ function PromptInput({
|
|
|
559
610
|
onImagePaste={onImagePaste}
|
|
560
611
|
columns={textInputColumns}
|
|
561
612
|
isDimmed={isDisabled || isLoading}
|
|
562
|
-
disableCursorMovementForUpDownKeys={
|
|
613
|
+
disableCursorMovementForUpDownKeys={completionActive}
|
|
563
614
|
cursorOffset={cursorOffset}
|
|
564
615
|
onChangeCursorOffset={setCursorOffset}
|
|
565
616
|
onPaste={onTextPaste}
|
|
617
|
+
onSpecialKey={(input, key) => {
|
|
618
|
+
// Handle Shift+M for model switching
|
|
619
|
+
if (key.shift && (input === 'M' || input === 'm')) {
|
|
620
|
+
handleQuickModelSwitch()
|
|
621
|
+
return true // Prevent the 'M' from being typed
|
|
622
|
+
}
|
|
623
|
+
return false
|
|
624
|
+
}}
|
|
566
625
|
/>
|
|
567
626
|
</Box>
|
|
568
627
|
</Box>
|
|
569
|
-
{suggestions.length === 0 && (
|
|
628
|
+
{!completionActive && suggestions.length === 0 && (
|
|
570
629
|
<Box
|
|
571
630
|
flexDirection="row"
|
|
572
631
|
justifyContent="space-between"
|
|
@@ -592,15 +651,15 @@ function PromptInput({
|
|
|
592
651
|
color={mode === 'koding' ? theme.koding : undefined}
|
|
593
652
|
dimColor={mode !== 'koding'}
|
|
594
653
|
>
|
|
595
|
-
· # for
|
|
654
|
+
· # for AGENTS.md
|
|
596
655
|
</Text>
|
|
597
656
|
<Text dimColor>
|
|
598
|
-
· / for commands ·
|
|
657
|
+
· / for commands · shift+m to switch model · esc to undo
|
|
599
658
|
</Text>
|
|
600
659
|
</>
|
|
601
660
|
)}
|
|
602
661
|
</Box>
|
|
603
|
-
<SentryErrorBoundary
|
|
662
|
+
<SentryErrorBoundary children={
|
|
604
663
|
<Box justifyContent="flex-end" gap={1}>
|
|
605
664
|
{!autoUpdaterResult &&
|
|
606
665
|
!isAutoUpdating &&
|
|
@@ -622,9 +681,10 @@ function PromptInput({
|
|
|
622
681
|
onChangeIsUpdating={setIsAutoUpdating}
|
|
623
682
|
/> */}
|
|
624
683
|
</Box>
|
|
625
|
-
|
|
684
|
+
} />
|
|
626
685
|
</Box>
|
|
627
686
|
)}
|
|
687
|
+
{/* Unified completion suggestions - optimized rendering */}
|
|
628
688
|
{suggestions.length > 0 && (
|
|
629
689
|
<Box
|
|
630
690
|
flexDirection="row"
|
|
@@ -633,58 +693,28 @@ function PromptInput({
|
|
|
633
693
|
paddingY={0}
|
|
634
694
|
>
|
|
635
695
|
<Box flexDirection="column">
|
|
636
|
-
{
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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
|
-
})}
|
|
696
|
+
{renderedSuggestions}
|
|
697
|
+
|
|
698
|
+
{/* 简洁操作提示框 */}
|
|
699
|
+
<Box marginTop={1} paddingX={3} borderStyle="round" borderColor="gray">
|
|
700
|
+
<Text dimColor={!emptyDirMessage} color={emptyDirMessage ? "yellow" : undefined}>
|
|
701
|
+
{emptyDirMessage || (() => {
|
|
702
|
+
const selected = suggestions[selectedIndex]
|
|
703
|
+
if (!selected) {
|
|
704
|
+
return '↑↓ navigate • → accept • Tab cycle • Esc close'
|
|
705
|
+
}
|
|
706
|
+
if (selected?.value.endsWith('/')) {
|
|
707
|
+
return '→ enter directory • ↑↓ navigate • Tab cycle • Esc close'
|
|
708
|
+
} else if (selected?.type === 'agent') {
|
|
709
|
+
return '→ select agent • ↑↓ navigate • Tab cycle • Esc close'
|
|
710
|
+
} else {
|
|
711
|
+
return '→ insert reference • ↑↓ navigate • Tab cycle • Esc close'
|
|
712
|
+
}
|
|
713
|
+
})()}
|
|
714
|
+
</Text>
|
|
715
|
+
</Box>
|
|
686
716
|
</Box>
|
|
687
|
-
<SentryErrorBoundary
|
|
717
|
+
<SentryErrorBoundary children={
|
|
688
718
|
<Box justifyContent="flex-end" gap={1}>
|
|
689
719
|
<TokenWarning tokenUsage={countTokens(messages)} />
|
|
690
720
|
<AutoUpdater
|
|
@@ -695,7 +725,7 @@ function PromptInput({
|
|
|
695
725
|
onChangeIsUpdating={setIsAutoUpdating}
|
|
696
726
|
/>
|
|
697
727
|
</Box>
|
|
698
|
-
|
|
728
|
+
} />
|
|
699
729
|
</Box>
|
|
700
730
|
)}
|
|
701
731
|
</Box>
|
|
@@ -12,7 +12,7 @@ interface State {
|
|
|
12
12
|
export class SentryErrorBoundary extends React.Component<Props, State> {
|
|
13
13
|
constructor(props: Props) {
|
|
14
14
|
super(props)
|
|
15
|
-
this.state = { hasError: false }
|
|
15
|
+
;(this as any).state = { hasError: false }
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
static getDerivedStateFromError(): State {
|
|
@@ -24,10 +24,10 @@ export class SentryErrorBoundary extends React.Component<Props, State> {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
render(): React.ReactNode {
|
|
27
|
-
if (this.state.hasError) {
|
|
27
|
+
if ((this as any).state.hasError) {
|
|
28
28
|
return null
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
return this.props.children
|
|
31
|
+
return (this as any).props.children
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
export interface FormData {
|
|
4
|
+
// Define form data structure as needed
|
|
5
|
+
[key: string]: any
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface StickerRequestFormProps {
|
|
9
|
+
// Define props as needed
|
|
10
|
+
onSubmit?: (data: FormData) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const StickerRequestForm: React.FC<StickerRequestFormProps> = () => {
|
|
14
|
+
// Minimal component implementation
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
@@ -10,6 +10,7 @@ type Props = {
|
|
|
10
10
|
dim: boolean
|
|
11
11
|
width: number
|
|
12
12
|
overrideTheme?: ThemeNames // custom theme for previews
|
|
13
|
+
key?: React.Key
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function StructuredDiff({
|
|
@@ -66,43 +67,48 @@ function formatDiff(
|
|
|
66
67
|
switch (type) {
|
|
67
68
|
case 'add':
|
|
68
69
|
return (
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
70
|
+
<React.Fragment key={key}>
|
|
71
|
+
<Text>
|
|
72
|
+
<LineNumber
|
|
73
|
+
i={lineIndex === 0 ? i : undefined}
|
|
74
|
+
width={maxWidth}
|
|
75
|
+
/>
|
|
76
|
+
<Text
|
|
77
|
+
color={overrideTheme ? theme.text : undefined}
|
|
78
|
+
backgroundColor={
|
|
79
|
+
dim ? theme.diff.addedDimmed : theme.diff.added
|
|
80
|
+
}
|
|
81
|
+
dimColor={dim}
|
|
82
|
+
>
|
|
83
|
+
{line}
|
|
84
|
+
</Text>
|
|
82
85
|
</Text>
|
|
83
|
-
</
|
|
86
|
+
</React.Fragment>
|
|
84
87
|
)
|
|
85
88
|
case 'remove':
|
|
86
89
|
return (
|
|
87
|
-
<
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
<React.Fragment key={key}>
|
|
91
|
+
<Text>
|
|
92
|
+
<LineNumber
|
|
93
|
+
i={lineIndex === 0 ? i : undefined}
|
|
94
|
+
width={maxWidth}
|
|
95
|
+
/>
|
|
96
|
+
<Text
|
|
97
|
+
color={overrideTheme ? theme.text : undefined}
|
|
98
|
+
backgroundColor={
|
|
99
|
+
dim ? theme.diff.removedDimmed : theme.diff.removed
|
|
100
|
+
}
|
|
101
|
+
dimColor={dim}
|
|
102
|
+
>
|
|
103
|
+
{line}
|
|
104
|
+
</Text>
|
|
100
105
|
</Text>
|
|
101
|
-
</
|
|
106
|
+
</React.Fragment>
|
|
102
107
|
)
|
|
103
108
|
case 'nochange':
|
|
104
109
|
return (
|
|
105
|
-
<
|
|
110
|
+
<React.Fragment key={key}>
|
|
111
|
+
<Text>
|
|
106
112
|
<LineNumber
|
|
107
113
|
i={lineIndex === 0 ? i : undefined}
|
|
108
114
|
width={maxWidth}
|
|
@@ -114,6 +120,7 @@ function formatDiff(
|
|
|
114
120
|
{line}
|
|
115
121
|
</Text>
|
|
116
122
|
</Text>
|
|
123
|
+
</React.Fragment>
|
|
117
124
|
)
|
|
118
125
|
}
|
|
119
126
|
})
|
|
@@ -106,6 +106,12 @@ export type Props = {
|
|
|
106
106
|
* Whether to disable cursor movement for up/down arrow keys
|
|
107
107
|
*/
|
|
108
108
|
readonly disableCursorMovementForUpDownKeys?: boolean
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Optional callback to handle special key combinations before input processing
|
|
112
|
+
* Return true to prevent default handling
|
|
113
|
+
*/
|
|
114
|
+
readonly onSpecialKey?: (input: string, key: Key) => boolean
|
|
109
115
|
|
|
110
116
|
readonly cursorOffset: number
|
|
111
117
|
|
|
@@ -136,6 +142,7 @@ export default function TextInput({
|
|
|
136
142
|
onPaste,
|
|
137
143
|
isDimmed = false,
|
|
138
144
|
disableCursorMovementForUpDownKeys = false,
|
|
145
|
+
onSpecialKey,
|
|
139
146
|
cursorOffset,
|
|
140
147
|
onChangeCursorOffset,
|
|
141
148
|
}: Props) {
|
|
@@ -186,6 +193,12 @@ export default function TextInput({
|
|
|
186
193
|
}
|
|
187
194
|
|
|
188
195
|
const wrappedOnInput = (input: string, key: Key): void => {
|
|
196
|
+
// Check for special key combinations first
|
|
197
|
+
if (onSpecialKey && onSpecialKey(input, key)) {
|
|
198
|
+
// Special key was handled, don't process further
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
189
202
|
// Special handling for backspace or delete
|
|
190
203
|
if (
|
|
191
204
|
key.backspace ||
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
export interface TodoItemProps {
|
|
4
|
+
// Define props as needed
|
|
5
|
+
children?: React.ReactNode
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const TodoItem: React.FC<TodoItemProps> = ({ children }) => {
|
|
9
|
+
// Minimal component implementation
|
|
10
|
+
return <>{children}</>
|
|
11
|
+
}
|
|
@@ -20,7 +20,11 @@ export function AssistantLocalCommandOutputMessage({
|
|
|
20
20
|
].filter(Boolean)
|
|
21
21
|
|
|
22
22
|
if (insides.length === 0) {
|
|
23
|
-
insides = [
|
|
23
|
+
insides = [
|
|
24
|
+
<React.Fragment key="0">
|
|
25
|
+
<Text>(No output)</Text>
|
|
26
|
+
</React.Fragment>
|
|
27
|
+
]
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
return [
|
|
@@ -9,6 +9,7 @@ import { getTheme } from '../../utils/theme'
|
|
|
9
9
|
import { BLACK_CIRCLE } from '../../constants/figures'
|
|
10
10
|
import { ThinkTool } from '../../tools/ThinkTool/ThinkTool'
|
|
11
11
|
import { AssistantThinkingMessage } from './AssistantThinkingMessage'
|
|
12
|
+
import { TaskToolMessage } from './TaskToolMessage'
|
|
12
13
|
|
|
13
14
|
type Props = {
|
|
14
15
|
param: ToolUseBlockParam
|
|
@@ -61,7 +62,7 @@ export function AssistantToolUseMessage({
|
|
|
61
62
|
)
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
const userFacingToolName = tool.userFacingName(param.input
|
|
65
|
+
const userFacingToolName = tool.userFacingName ? tool.userFacingName(param.input) : tool.name
|
|
65
66
|
return (
|
|
66
67
|
<Box
|
|
67
68
|
flexDirection="row"
|
|
@@ -86,9 +87,18 @@ export function AssistantToolUseMessage({
|
|
|
86
87
|
isError={erroredToolUseIDs.has(param.id)}
|
|
87
88
|
/>
|
|
88
89
|
))}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
{tool.name === 'Task' && param.input ? (
|
|
91
|
+
<TaskToolMessage
|
|
92
|
+
agentType={(param.input as any).subagent_type || 'general-purpose'}
|
|
93
|
+
bold={!isQueued}
|
|
94
|
+
>
|
|
95
|
+
{userFacingToolName}
|
|
96
|
+
</TaskToolMessage>
|
|
97
|
+
) : (
|
|
98
|
+
<Text color={color} bold={!isQueued}>
|
|
99
|
+
{userFacingToolName}
|
|
100
|
+
</Text>
|
|
101
|
+
)}
|
|
92
102
|
</Box>
|
|
93
103
|
<Box flexWrap="nowrap">
|
|
94
104
|
{Object.keys(param.input as { [key: string]: unknown }).length > 0 &&
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { getTheme } from '../../utils/theme'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
agentType: string
|
|
7
|
+
status: string
|
|
8
|
+
toolCount?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function TaskProgressMessage({ agentType, status, toolCount }: Props) {
|
|
12
|
+
const theme = getTheme()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Box flexDirection="column" marginTop={1}>
|
|
16
|
+
<Box flexDirection="row">
|
|
17
|
+
<Text color={theme.claude}>⎯ </Text>
|
|
18
|
+
<Text color={theme.text} bold>
|
|
19
|
+
[{agentType}]
|
|
20
|
+
</Text>
|
|
21
|
+
<Text color={theme.secondaryText}> {status}</Text>
|
|
22
|
+
</Box>
|
|
23
|
+
{toolCount && toolCount > 0 && (
|
|
24
|
+
<Box marginLeft={3}>
|
|
25
|
+
<Text color={theme.secondaryText}>
|
|
26
|
+
Tools used: {toolCount}
|
|
27
|
+
</Text>
|
|
28
|
+
</Box>
|
|
29
|
+
)}
|
|
30
|
+
</Box>
|
|
31
|
+
)
|
|
32
|
+
}
|