aegiscode 3.1.10 → 3.2.0
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/bin/aegis-cli-linux-x64 +0 -0
- package/bin/cli.js +68 -0
- package/package.json +11 -48
- package/scripts/download-binary.mjs +155 -0
- package/dist/main.js +0 -2171
- package/gui/run-gui.cjs +0 -13
- package/src/agent/Agent.ts +0 -903
- package/src/agent/SimpleAgent.ts +0 -48
- package/src/agent/index.ts +0 -54
- package/src/agent/orchestrator/AppBuilder.ts +0 -443
- package/src/agent/orchestrator/CouncilAgent.ts +0 -310
- package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
- package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
- package/src/agent/orchestrator/index.ts +0 -38
- package/src/agent/orchestrator/utils.ts +0 -397
- package/src/agent/pricing.ts +0 -115
- package/src/agent/router.ts +0 -74
- package/src/agent/routerStats.ts +0 -121
- package/src/agent/types.ts +0 -318
- package/src/auth/login.ts +0 -383
- package/src/cli/config.ts +0 -189
- package/src/cli/index.ts +0 -17
- package/src/cli/middleware.ts +0 -119
- package/src/cli/types.ts +0 -75
- package/src/config/ConfigManager.ts +0 -587
- package/src/config/index.ts +0 -7
- package/src/config/types.ts +0 -584
- package/src/context/CompactionService.ts +0 -300
- package/src/context/ContextManager.ts +0 -450
- package/src/context/FileAnalyzer.ts +0 -267
- package/src/context/TokenCounter.ts +0 -265
- package/src/context/index.ts +0 -27
- package/src/context/storage/CacheStore.ts +0 -176
- package/src/context/storage/JSONLStore.ts +0 -201
- package/src/context/storage/MemoryStore.ts +0 -205
- package/src/context/storage/PersistentStore.ts +0 -327
- package/src/context/storage/index.ts +0 -9
- package/src/context/storage/pathUtils.ts +0 -114
- package/src/context/test.ts +0 -309
- package/src/context/types.ts +0 -268
- package/src/hooks/HookExecutor.ts +0 -434
- package/src/hooks/HookManager.ts +0 -596
- package/src/hooks/HookService.ts +0 -269
- package/src/hooks/Matcher.ts +0 -157
- package/src/hooks/index.ts +0 -63
- package/src/hooks/types.ts +0 -424
- package/src/main.tsx +0 -596
- package/src/mcp/HealthMonitor.ts +0 -150
- package/src/mcp/McpClient.ts +0 -491
- package/src/mcp/McpRegistry.ts +0 -321
- package/src/mcp/createMcpTool.ts +0 -251
- package/src/mcp/index.ts +0 -15
- package/src/mcp/server.ts +0 -334
- package/src/mcp/test-server.ts +0 -88
- package/src/mcp/test.ts +0 -372
- package/src/mcp/types.ts +0 -247
- package/src/memory/AgentMemoryBus.ts +0 -432
- package/src/memory/CloudSync.ts +0 -99
- package/src/memory/DriveSync.ts +0 -106
- package/src/memory/SharedMemory.ts +0 -951
- package/src/memory/index.ts +0 -14
- package/src/memory/machineFingerprint.ts +0 -40
- package/src/orchestrator/SubAgentMetadata.ts +0 -136
- package/src/prompts/builder.ts +0 -213
- package/src/prompts/default.ts +0 -144
- package/src/prompts/index.ts +0 -16
- package/src/prompts/plan.ts +0 -64
- package/src/prompts/test.ts +0 -78
- package/src/services/AnthropicChatService.ts +0 -341
- package/src/services/ChatService.ts +0 -347
- package/src/services/ClaudeCliChatService.ts +0 -256
- package/src/services/CloudSync.ts +0 -168
- package/src/services/CostLedger.ts +0 -211
- package/src/services/Heartbeat.ts +0 -135
- package/src/services/LearningCollector.ts +0 -291
- package/src/services/OllamaInstaller.ts +0 -342
- package/src/services/VersionChecker.ts +0 -445
- package/src/services/index.ts +0 -57
- package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
- package/src/services/streaming/RenderingProfile.ts +0 -90
- package/src/services/streaming/StreamEventParser.ts +0 -181
- package/src/services/streaming/ThrottledRenderer.ts +0 -139
- package/src/services/streaming/TranscriptBuffer.ts +0 -574
- package/src/services/streaming/eventStatusMap.ts +0 -52
- package/src/services/streaming/index.ts +0 -46
- package/src/services/streaming/renderFormatting.ts +0 -79
- package/src/services/streaming/types.ts +0 -234
- package/src/skills/SkillLoader.ts +0 -126
- package/src/skills/SkillRegistry.ts +0 -366
- package/src/skills/index.ts +0 -48
- package/src/skills/types.ts +0 -146
- package/src/slash-commands/billing.ts +0 -70
- package/src/slash-commands/build.ts +0 -413
- package/src/slash-commands/builtinCommands.ts +0 -2733
- package/src/slash-commands/clone.ts +0 -242
- package/src/slash-commands/council.ts +0 -125
- package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
- package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
- package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
- package/src/slash-commands/custom/index.ts +0 -7
- package/src/slash-commands/debate.ts +0 -254
- package/src/slash-commands/gmail.ts +0 -105
- package/src/slash-commands/index.ts +0 -388
- package/src/slash-commands/mcpCommand.ts +0 -205
- package/src/slash-commands/types.ts +0 -201
- package/src/store/index.ts +0 -76
- package/src/store/selectors.ts +0 -246
- package/src/store/slices/appSlice.ts +0 -205
- package/src/store/slices/commandSlice.ts +0 -115
- package/src/store/slices/configSlice.ts +0 -46
- package/src/store/slices/focusSlice.ts +0 -64
- package/src/store/slices/index.ts +0 -9
- package/src/store/slices/sessionSlice.ts +0 -424
- package/src/store/streaming-buffer.ts +0 -425
- package/src/store/test.ts +0 -296
- package/src/store/types.ts +0 -274
- package/src/store/vanilla.ts +0 -186
- package/src/tools/builtin/bash.ts +0 -236
- package/src/tools/builtin/council.ts +0 -105
- package/src/tools/builtin/edit.ts +0 -213
- package/src/tools/builtin/glob.ts +0 -136
- package/src/tools/builtin/grep.ts +0 -263
- package/src/tools/builtin/index.ts +0 -61
- package/src/tools/builtin/memory.ts +0 -66
- package/src/tools/builtin/read.ts +0 -168
- package/src/tools/builtin/skill.ts +0 -97
- package/src/tools/builtin/snapshot.ts +0 -40
- package/src/tools/builtin/task.ts +0 -106
- package/src/tools/builtin/write.ts +0 -134
- package/src/tools/createTool.ts +0 -221
- package/src/tools/execution/ExecutionPipeline.ts +0 -263
- package/src/tools/execution/index.ts +0 -40
- package/src/tools/execution/stages/CacheStage.ts +0 -131
- package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
- package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
- package/src/tools/execution/stages/ExecutionStage.ts +0 -48
- package/src/tools/execution/stages/FormattingStage.ts +0 -44
- package/src/tools/execution/stages/HookStage.ts +0 -72
- package/src/tools/execution/stages/PermissionStage.ts +0 -287
- package/src/tools/execution/stages/PostHookStage.ts +0 -71
- package/src/tools/execution/stages/index.ts +0 -12
- package/src/tools/execution/test.ts +0 -266
- package/src/tools/execution/types.ts +0 -273
- package/src/tools/index.ts +0 -81
- package/src/tools/registry.ts +0 -304
- package/src/tools/schemas.ts +0 -109
- package/src/tools/test.ts +0 -220
- package/src/tools/types.ts +0 -175
- package/src/tools/validation/PermissionChecker.ts +0 -242
- package/src/tools/validation/SensitiveFileDetector.ts +0 -210
- package/src/tools/validation/index.ts +0 -11
- package/src/ui/App.tsx +0 -166
- package/src/ui/components/AegisInterface.tsx +0 -484
- package/src/ui/components/common/ChatSearch.tsx +0 -150
- package/src/ui/components/common/ErrorBoundary.tsx +0 -82
- package/src/ui/components/common/ExitMessage.tsx +0 -120
- package/src/ui/components/common/LoadingIndicator.tsx +0 -49
- package/src/ui/components/common/index.ts +0 -6
- package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
- package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
- package/src/ui/components/dialog/SetupWizard.tsx +0 -297
- package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
- package/src/ui/components/dialog/index.ts +0 -8
- package/src/ui/components/index.ts +0 -28
- package/src/ui/components/input/CommandSuggestions.tsx +0 -139
- package/src/ui/components/input/CustomTextInput.tsx +0 -220
- package/src/ui/components/input/InputArea.tsx +0 -361
- package/src/ui/components/input/PromptSuggestions.tsx +0 -66
- package/src/ui/components/input/index.ts +0 -6
- package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
- package/src/ui/components/layout/ContextBar.tsx +0 -79
- package/src/ui/components/layout/MessageArea.tsx +0 -96
- package/src/ui/components/layout/MessageList.tsx +0 -647
- package/src/ui/components/layout/MessageSeparator.tsx +0 -26
- package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
- package/src/ui/components/layout/index.ts +0 -7
- package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
- package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
- package/src/ui/components/markdown/index.ts +0 -8
- package/src/ui/components/markdown/parser.ts +0 -336
- package/src/ui/components/markdown/types.ts +0 -66
- package/src/ui/focus/FocusManager.ts +0 -137
- package/src/ui/focus/index.ts +0 -13
- package/src/ui/focus/types.ts +0 -54
- package/src/ui/focus/useFocus.ts +0 -75
- package/src/ui/hooks/index.ts +0 -11
- package/src/ui/hooks/useAgent.ts +0 -284
- package/src/ui/hooks/useCommandHistory.ts +0 -87
- package/src/ui/hooks/useCommandProcessor.ts +0 -443
- package/src/ui/hooks/useConfirmation.ts +0 -99
- package/src/ui/hooks/useCtrlCHandler.ts +0 -100
- package/src/ui/hooks/useInputBuffer.ts +0 -122
- package/src/ui/hooks/useTerminalSize.ts +0 -68
- package/src/ui/hooks/useTerminalWidth.ts +0 -5
- package/src/ui/hooks/useWindowedList.ts +0 -118
- package/src/ui/render-debugger.ts +0 -621
- package/src/ui/test.ts +0 -189
- package/src/ui/themes/ThemeManager.ts +0 -332
- package/src/ui/themes/aegisTheme.ts +0 -87
- package/src/ui/themes/darkTheme.ts +0 -87
- package/src/ui/themes/defaultTheme.ts +0 -85
- package/src/ui/themes/index.ts +0 -10
- package/src/ui/themes/lightTheme.ts +0 -89
- package/src/ui/themes/popularThemes.ts +0 -187
- package/src/ui/themes/types.ts +0 -130
- package/src/utils/clipboard.ts +0 -48
- package/src/utils/debug.ts +0 -43
- package/src/utils/environment.ts +0 -68
- package/src/utils/index.ts +0 -10
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CommandSuggestions - Dropdown autocomplete for slash commands
|
|
3
|
-
*
|
|
4
|
-
* Shows fuzzy-matched command suggestions when user types "/..."
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, { useMemo, useEffect, useRef } from 'react';
|
|
8
|
-
import { Box, Text, useInput } from 'ink';
|
|
9
|
-
import { getCommandCompletions } from '../../../slash-commands/index.js';
|
|
10
|
-
import { themeManager } from '../../themes/index.js';
|
|
11
|
-
import { FocusId, focusManager } from '../../focus/index.js';
|
|
12
|
-
|
|
13
|
-
interface CommandSuggestionsProps {
|
|
14
|
-
/** The current input value */
|
|
15
|
-
input: string;
|
|
16
|
-
/** Cursor position */
|
|
17
|
-
cursorPosition: number;
|
|
18
|
-
/** Callback to set input value (for tab-complete) */
|
|
19
|
-
onSelectSuggestion: (suggestion: string) => void;
|
|
20
|
-
/** Whether suggestions are visible */
|
|
21
|
-
visible: boolean;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const CommandSuggestions: React.FC<CommandSuggestionsProps> = ({
|
|
25
|
-
input,
|
|
26
|
-
cursorPosition,
|
|
27
|
-
onSelectSuggestion,
|
|
28
|
-
visible,
|
|
29
|
-
}) => {
|
|
30
|
-
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
|
31
|
-
const theme = themeManager.getTheme();
|
|
32
|
-
|
|
33
|
-
// Reset selection when suggestions change
|
|
34
|
-
const lastInputRef = useRef(input);
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (lastInputRef.current !== input) {
|
|
37
|
-
setSelectedIndex(0);
|
|
38
|
-
lastInputRef.current = input;
|
|
39
|
-
}
|
|
40
|
-
}, [input]);
|
|
41
|
-
|
|
42
|
-
const suggestions = useMemo(() => {
|
|
43
|
-
if (!visible || !input.startsWith('/')) return [];
|
|
44
|
-
|
|
45
|
-
const partial = input.slice(0, cursorPosition);
|
|
46
|
-
const results = getCommandCompletions(partial);
|
|
47
|
-
const isListing = partial.trim() === '/';
|
|
48
|
-
|
|
49
|
-
if (isListing) {
|
|
50
|
-
// Alphabetical when showing all commands
|
|
51
|
-
results.sort((a, b) => a.command.localeCompare(b.command));
|
|
52
|
-
} else {
|
|
53
|
-
results.sort((a, b) => (b.matchScore ?? 0) - (a.matchScore ?? 0));
|
|
54
|
-
}
|
|
55
|
-
return results;
|
|
56
|
-
}, [input, cursorPosition, visible]);
|
|
57
|
-
|
|
58
|
-
// Clamp selected index
|
|
59
|
-
const clampedIndex = Math.min(selectedIndex, Math.max(0, suggestions.length - 1));
|
|
60
|
-
|
|
61
|
-
// Tab key: cycle through suggestions
|
|
62
|
-
useInput(
|
|
63
|
-
(_, key) => {
|
|
64
|
-
if (!visible || suggestions.length === 0) return;
|
|
65
|
-
if (focusManager.getCurrentFocus() !== FocusId.MAIN_INPUT) return;
|
|
66
|
-
|
|
67
|
-
if (key.tab && !key.shift) {
|
|
68
|
-
// Select next suggestion (cycle)
|
|
69
|
-
const next = (clampedIndex + 1) % suggestions.length;
|
|
70
|
-
setSelectedIndex(next);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (key.tab && key.shift) {
|
|
75
|
-
// Select previous suggestion
|
|
76
|
-
const prev = clampedIndex <= 0 ? suggestions.length - 1 : clampedIndex - 1;
|
|
77
|
-
setSelectedIndex(prev);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Enter on a suggestion: select it
|
|
82
|
-
if (key.return && suggestions.length > 0) {
|
|
83
|
-
const selected = suggestions[clampedIndex];
|
|
84
|
-
if (selected) {
|
|
85
|
-
// Replace the command part of the input
|
|
86
|
-
const beforeCursor = input.slice(0, cursorPosition);
|
|
87
|
-
const afterCursor = input.slice(cursorPosition);
|
|
88
|
-
const slashIdx = beforeCursor.lastIndexOf('/');
|
|
89
|
-
|
|
90
|
-
if (slashIdx !== -1) {
|
|
91
|
-
const newBefore = beforeCursor.slice(0, slashIdx);
|
|
92
|
-
const completed = `${newBefore}${selected.command} `;
|
|
93
|
-
onSelectSuggestion(completed + afterCursor);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
{ isActive: visible && suggestions.length > 0 }
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
if (!visible || suggestions.length === 0) return null;
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<Box
|
|
105
|
-
flexDirection="column"
|
|
106
|
-
marginLeft={1}
|
|
107
|
-
marginBottom={0}
|
|
108
|
-
borderStyle="round"
|
|
109
|
-
borderColor={theme.colors.border.light}
|
|
110
|
-
paddingX={1}
|
|
111
|
-
paddingY={0}
|
|
112
|
-
>
|
|
113
|
-
<Text dimColor>Commands ({suggestions.length}):</Text>
|
|
114
|
-
{suggestions.map((s, i) => {
|
|
115
|
-
const isSelected = i === clampedIndex;
|
|
116
|
-
return (
|
|
117
|
-
<Box key={s.command} flexDirection="row">
|
|
118
|
-
<Text>
|
|
119
|
-
{isSelected ? (
|
|
120
|
-
<Text color={theme.colors.primary} bold>{'>'}</Text>
|
|
121
|
-
) : (
|
|
122
|
-
<Text> </Text>
|
|
123
|
-
)}{' '}
|
|
124
|
-
<Text color={isSelected ? theme.colors.primary : undefined} bold={isSelected}>
|
|
125
|
-
{s.command}
|
|
126
|
-
</Text>
|
|
127
|
-
<Text color={theme.colors.text.muted}> — {s.description}</Text>
|
|
128
|
-
</Text>
|
|
129
|
-
</Box>
|
|
130
|
-
);
|
|
131
|
-
})}
|
|
132
|
-
<Text dimColor>
|
|
133
|
-
Tab/Shift+Tab to navigate · Enter to select
|
|
134
|
-
</Text>
|
|
135
|
-
</Box>
|
|
136
|
-
);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
export default CommandSuggestions;
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import React, { useMemo, useCallback, useRef, memo } from 'react';
|
|
2
|
-
import { Text, useInput } from 'ink';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import { useIsFocused, FocusId, focusManager } from '../../focus/index.js';
|
|
5
|
-
|
|
6
|
-
interface CustomTextInputProps {
|
|
7
|
-
value: string;
|
|
8
|
-
cursorPosition: number;
|
|
9
|
-
/** Cursor visibility — computed by parent so this component doesn't re-render every tick */
|
|
10
|
-
cursorOn?: boolean;
|
|
11
|
-
onChange: (value: string) => void;
|
|
12
|
-
onChangeCursorPosition: (pos: number) => void;
|
|
13
|
-
onSubmit?: (value: string) => void;
|
|
14
|
-
onPaste?: (text: string) => { prompt?: string } | void;
|
|
15
|
-
onArrowUp?: () => void;
|
|
16
|
-
onArrowDown?: () => void;
|
|
17
|
-
placeholder?: string;
|
|
18
|
-
focusId?: FocusId;
|
|
19
|
-
disabled?: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const PASTE_CONFIG = {
|
|
23
|
-
TIMEOUT_MS: 100,
|
|
24
|
-
RAPID_INPUT_THRESHOLD_MS: 400,
|
|
25
|
-
LARGE_INPUT_THRESHOLD: 100,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const CustomTextInput: React.FC<CustomTextInputProps> = memo(
|
|
29
|
-
({
|
|
30
|
-
value,
|
|
31
|
-
cursorPosition,
|
|
32
|
-
onChange,
|
|
33
|
-
onChangeCursorPosition,
|
|
34
|
-
onSubmit,
|
|
35
|
-
onPaste,
|
|
36
|
-
onArrowUp,
|
|
37
|
-
onArrowDown,
|
|
38
|
-
placeholder = '',
|
|
39
|
-
focusId = FocusId.MAIN_INPUT,
|
|
40
|
-
disabled = false,
|
|
41
|
-
cursorOn = true,
|
|
42
|
-
}) => {
|
|
43
|
-
const isFocused = useIsFocused(focusId);
|
|
44
|
-
const isActive = isFocused && !disabled;
|
|
45
|
-
|
|
46
|
-
// Refs for all values accessed inside the stable useInput handler.
|
|
47
|
-
// Updated on every render — never trigger re-renders themselves.
|
|
48
|
-
const valueRef = useRef(value);
|
|
49
|
-
const cursorPositionRef = useRef(cursorPosition);
|
|
50
|
-
const onChangeRef = useRef(onChange);
|
|
51
|
-
const onChangeCursorPositionRef = useRef(onChangeCursorPosition);
|
|
52
|
-
const onSubmitRef = useRef(onSubmit);
|
|
53
|
-
const onPasteRef = useRef(onPaste);
|
|
54
|
-
const onArrowUpRef = useRef(onArrowUp);
|
|
55
|
-
const onArrowDownRef = useRef(onArrowDown);
|
|
56
|
-
const isActiveRef = useRef(isActive);
|
|
57
|
-
|
|
58
|
-
valueRef.current = value;
|
|
59
|
-
cursorPositionRef.current = cursorPosition;
|
|
60
|
-
onChangeRef.current = onChange;
|
|
61
|
-
onChangeCursorPositionRef.current = onChangeCursorPosition;
|
|
62
|
-
onSubmitRef.current = onSubmit;
|
|
63
|
-
onPasteRef.current = onPaste;
|
|
64
|
-
onArrowUpRef.current = onArrowUp;
|
|
65
|
-
onArrowDownRef.current = onArrowDown;
|
|
66
|
-
isActiveRef.current = isActive;
|
|
67
|
-
|
|
68
|
-
const renderedValue = useMemo(() => {
|
|
69
|
-
if (!isActive) return value || chalk.dim(placeholder);
|
|
70
|
-
|
|
71
|
-
if (value.length === 0) {
|
|
72
|
-
const block = cursorOn
|
|
73
|
-
? chalk.bgHex('#00c8b4').hex('#000000')(' ')
|
|
74
|
-
: ' ';
|
|
75
|
-
return block + chalk.dim(placeholder);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const before = value.slice(0, cursorPosition);
|
|
79
|
-
const charUnder = value[cursorPosition] ?? ' ';
|
|
80
|
-
const after = value.slice(cursorPosition + 1);
|
|
81
|
-
|
|
82
|
-
const cursorChar = cursorOn
|
|
83
|
-
? chalk.bgHex('#00c8b4').hex('#000000')(charUnder)
|
|
84
|
-
: charUnder;
|
|
85
|
-
|
|
86
|
-
return before + cursorChar + after;
|
|
87
|
-
}, [value, cursorPosition, isActive, placeholder, cursorOn]);
|
|
88
|
-
|
|
89
|
-
const pasteStateRef = useRef({
|
|
90
|
-
chunks: [] as string[],
|
|
91
|
-
timeoutId: null as NodeJS.Timeout | null,
|
|
92
|
-
firstInputTime: null as number | null,
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Stable — accesses all mutable values via refs
|
|
96
|
-
const handlePasteStable = useCallback((text: string) => {
|
|
97
|
-
const val = valueRef.current;
|
|
98
|
-
const pos = cursorPositionRef.current;
|
|
99
|
-
// Claude Code behavior: trim trailing whitespace from pasted text
|
|
100
|
-
const cleanText = text.trimEnd();
|
|
101
|
-
|
|
102
|
-
if (onPasteRef.current) {
|
|
103
|
-
const result = onPasteRef.current(text);
|
|
104
|
-
if (result?.prompt) {
|
|
105
|
-
onChangeRef.current(val + result.prompt);
|
|
106
|
-
onChangeCursorPositionRef.current(val.length + result.prompt.length);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const newValue = val.slice(0, pos) + cleanText + val.slice(pos);
|
|
111
|
-
onChangeRef.current(newValue);
|
|
112
|
-
onChangeCursorPositionRef.current(pos + cleanText.length);
|
|
113
|
-
}, []);
|
|
114
|
-
|
|
115
|
-
// Stable — Ink registers this once and never re-registers unless focusId changes.
|
|
116
|
-
// All mutable state is read from refs at call time.
|
|
117
|
-
const inputHandler = useCallback(
|
|
118
|
-
(input: string, key: Parameters<Parameters<typeof useInput>[0]>[1]) => {
|
|
119
|
-
if (focusManager.getCurrentFocus() !== focusId || !isActiveRef.current) return;
|
|
120
|
-
|
|
121
|
-
const val = valueRef.current;
|
|
122
|
-
const pos = cursorPositionRef.current;
|
|
123
|
-
const now = Date.now();
|
|
124
|
-
const pasteState = pasteStateRef.current;
|
|
125
|
-
const timeSinceFirst = pasteState.firstInputTime
|
|
126
|
-
? now - pasteState.firstInputTime
|
|
127
|
-
: 0;
|
|
128
|
-
|
|
129
|
-
const isPaste =
|
|
130
|
-
input.length > PASTE_CONFIG.LARGE_INPUT_THRESHOLD ||
|
|
131
|
-
input.includes('\n') ||
|
|
132
|
-
(timeSinceFirst < PASTE_CONFIG.RAPID_INPUT_THRESHOLD_MS &&
|
|
133
|
-
pasteState.chunks.length > 0);
|
|
134
|
-
|
|
135
|
-
if (isPaste && input.length > 1) {
|
|
136
|
-
pasteState.chunks.push(input);
|
|
137
|
-
if (!pasteState.firstInputTime) pasteState.firstInputTime = now;
|
|
138
|
-
if (pasteState.timeoutId) clearTimeout(pasteState.timeoutId);
|
|
139
|
-
pasteState.timeoutId = setTimeout(() => {
|
|
140
|
-
const mergedText = pasteState.chunks.join('');
|
|
141
|
-
handlePasteStable(mergedText);
|
|
142
|
-
pasteState.chunks = [];
|
|
143
|
-
pasteState.timeoutId = null;
|
|
144
|
-
pasteState.firstInputTime = null;
|
|
145
|
-
}, PASTE_CONFIG.TIMEOUT_MS);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Multi-agent commands (/multi, /multiyolo, /research, /build, /forge, /council):
|
|
150
|
-
// Enter inserts newline, Shift+Enter submits — enables multi-line prompts.
|
|
151
|
-
// All other commands/messages: Enter submits immediately (as before).
|
|
152
|
-
const MULTI_AGENT_CMDS = ['/multi', '/multiyolo', '/research', '/build', '/forge', '/council'];
|
|
153
|
-
const isMultiAgent = MULTI_AGENT_CMDS.some(cmd => val.trimStart().startsWith(cmd));
|
|
154
|
-
if (key.return) {
|
|
155
|
-
if (isMultiAgent && !key.shift) {
|
|
156
|
-
// Insert newline at cursor
|
|
157
|
-
const newValue = val.slice(0, pos) + '\n' + val.slice(pos);
|
|
158
|
-
onChangeRef.current(newValue);
|
|
159
|
-
onChangeCursorPositionRef.current(pos + 1);
|
|
160
|
-
} else {
|
|
161
|
-
onSubmitRef.current?.(val);
|
|
162
|
-
}
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
if (key.leftArrow) { if (pos > 0) onChangeCursorPositionRef.current(pos - 1); return; }
|
|
166
|
-
if (key.rightArrow) { if (pos < val.length) onChangeCursorPositionRef.current(pos + 1); return; }
|
|
167
|
-
if (key.upArrow) { onArrowUpRef.current?.(); return; }
|
|
168
|
-
if (key.downArrow) { onArrowDownRef.current?.(); return; }
|
|
169
|
-
if (key.backspace || key.delete) {
|
|
170
|
-
if (pos > 0) {
|
|
171
|
-
const newValue = val.slice(0, pos - 1) + val.slice(pos);
|
|
172
|
-
onChangeRef.current(newValue);
|
|
173
|
-
onChangeCursorPositionRef.current(pos - 1);
|
|
174
|
-
}
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
if (key.ctrl && input === 'a') { onChangeCursorPositionRef.current(0); return; }
|
|
178
|
-
if (key.ctrl && input === 'e') { onChangeCursorPositionRef.current(val.length); return; }
|
|
179
|
-
if (key.ctrl && input === 'k') { onChangeRef.current(val.slice(0, pos)); return; }
|
|
180
|
-
if (key.ctrl && input === 'u') {
|
|
181
|
-
onChangeRef.current(val.slice(pos));
|
|
182
|
-
onChangeCursorPositionRef.current(0);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (input && !key.ctrl && !key.meta) {
|
|
186
|
-
const newValue = val.slice(0, pos) + input + val.slice(pos);
|
|
187
|
-
onChangeRef.current(newValue);
|
|
188
|
-
onChangeCursorPositionRef.current(pos + input.length);
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
[focusId, handlePasteStable] // both stable — focusId is a constant, handlePasteStable has empty deps
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
useInput(inputHandler, { isActive });
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<Text>
|
|
198
|
-
{!isActive && value.length === 0 ? (
|
|
199
|
-
<Text dimColor>{placeholder}</Text>
|
|
200
|
-
) : (
|
|
201
|
-
renderedValue
|
|
202
|
-
)}
|
|
203
|
-
</Text>
|
|
204
|
-
);
|
|
205
|
-
},
|
|
206
|
-
// Only re-render when the displayed output would actually change.
|
|
207
|
-
// Callbacks are accessed via refs so they never need to cause a re-render.
|
|
208
|
-
// focus changes trigger re-renders via useIsFocused internally (bypasses this comparator).
|
|
209
|
-
(prev, next) =>
|
|
210
|
-
prev.value === next.value &&
|
|
211
|
-
prev.cursorPosition === next.cursorPosition &&
|
|
212
|
-
prev.cursorOn === next.cursorOn &&
|
|
213
|
-
prev.placeholder === next.placeholder &&
|
|
214
|
-
prev.disabled === next.disabled &&
|
|
215
|
-
prev.focusId === next.focusId
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
CustomTextInput.displayName = 'CustomTextInput';
|
|
219
|
-
|
|
220
|
-
export default CustomTextInput;
|