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.
Files changed (209) hide show
  1. package/bin/aegis-cli-linux-x64 +0 -0
  2. package/bin/cli.js +68 -0
  3. package/package.json +11 -48
  4. package/scripts/download-binary.mjs +155 -0
  5. package/dist/main.js +0 -2171
  6. package/gui/run-gui.cjs +0 -13
  7. package/src/agent/Agent.ts +0 -903
  8. package/src/agent/SimpleAgent.ts +0 -48
  9. package/src/agent/index.ts +0 -54
  10. package/src/agent/orchestrator/AppBuilder.ts +0 -443
  11. package/src/agent/orchestrator/CouncilAgent.ts +0 -310
  12. package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
  13. package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
  14. package/src/agent/orchestrator/index.ts +0 -38
  15. package/src/agent/orchestrator/utils.ts +0 -397
  16. package/src/agent/pricing.ts +0 -115
  17. package/src/agent/router.ts +0 -74
  18. package/src/agent/routerStats.ts +0 -121
  19. package/src/agent/types.ts +0 -318
  20. package/src/auth/login.ts +0 -383
  21. package/src/cli/config.ts +0 -189
  22. package/src/cli/index.ts +0 -17
  23. package/src/cli/middleware.ts +0 -119
  24. package/src/cli/types.ts +0 -75
  25. package/src/config/ConfigManager.ts +0 -587
  26. package/src/config/index.ts +0 -7
  27. package/src/config/types.ts +0 -584
  28. package/src/context/CompactionService.ts +0 -300
  29. package/src/context/ContextManager.ts +0 -450
  30. package/src/context/FileAnalyzer.ts +0 -267
  31. package/src/context/TokenCounter.ts +0 -265
  32. package/src/context/index.ts +0 -27
  33. package/src/context/storage/CacheStore.ts +0 -176
  34. package/src/context/storage/JSONLStore.ts +0 -201
  35. package/src/context/storage/MemoryStore.ts +0 -205
  36. package/src/context/storage/PersistentStore.ts +0 -327
  37. package/src/context/storage/index.ts +0 -9
  38. package/src/context/storage/pathUtils.ts +0 -114
  39. package/src/context/test.ts +0 -309
  40. package/src/context/types.ts +0 -268
  41. package/src/hooks/HookExecutor.ts +0 -434
  42. package/src/hooks/HookManager.ts +0 -596
  43. package/src/hooks/HookService.ts +0 -269
  44. package/src/hooks/Matcher.ts +0 -157
  45. package/src/hooks/index.ts +0 -63
  46. package/src/hooks/types.ts +0 -424
  47. package/src/main.tsx +0 -596
  48. package/src/mcp/HealthMonitor.ts +0 -150
  49. package/src/mcp/McpClient.ts +0 -491
  50. package/src/mcp/McpRegistry.ts +0 -321
  51. package/src/mcp/createMcpTool.ts +0 -251
  52. package/src/mcp/index.ts +0 -15
  53. package/src/mcp/server.ts +0 -334
  54. package/src/mcp/test-server.ts +0 -88
  55. package/src/mcp/test.ts +0 -372
  56. package/src/mcp/types.ts +0 -247
  57. package/src/memory/AgentMemoryBus.ts +0 -432
  58. package/src/memory/CloudSync.ts +0 -99
  59. package/src/memory/DriveSync.ts +0 -106
  60. package/src/memory/SharedMemory.ts +0 -951
  61. package/src/memory/index.ts +0 -14
  62. package/src/memory/machineFingerprint.ts +0 -40
  63. package/src/orchestrator/SubAgentMetadata.ts +0 -136
  64. package/src/prompts/builder.ts +0 -213
  65. package/src/prompts/default.ts +0 -144
  66. package/src/prompts/index.ts +0 -16
  67. package/src/prompts/plan.ts +0 -64
  68. package/src/prompts/test.ts +0 -78
  69. package/src/services/AnthropicChatService.ts +0 -341
  70. package/src/services/ChatService.ts +0 -347
  71. package/src/services/ClaudeCliChatService.ts +0 -256
  72. package/src/services/CloudSync.ts +0 -168
  73. package/src/services/CostLedger.ts +0 -211
  74. package/src/services/Heartbeat.ts +0 -135
  75. package/src/services/LearningCollector.ts +0 -291
  76. package/src/services/OllamaInstaller.ts +0 -342
  77. package/src/services/VersionChecker.ts +0 -445
  78. package/src/services/index.ts +0 -57
  79. package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
  80. package/src/services/streaming/RenderingProfile.ts +0 -90
  81. package/src/services/streaming/StreamEventParser.ts +0 -181
  82. package/src/services/streaming/ThrottledRenderer.ts +0 -139
  83. package/src/services/streaming/TranscriptBuffer.ts +0 -574
  84. package/src/services/streaming/eventStatusMap.ts +0 -52
  85. package/src/services/streaming/index.ts +0 -46
  86. package/src/services/streaming/renderFormatting.ts +0 -79
  87. package/src/services/streaming/types.ts +0 -234
  88. package/src/skills/SkillLoader.ts +0 -126
  89. package/src/skills/SkillRegistry.ts +0 -366
  90. package/src/skills/index.ts +0 -48
  91. package/src/skills/types.ts +0 -146
  92. package/src/slash-commands/billing.ts +0 -70
  93. package/src/slash-commands/build.ts +0 -413
  94. package/src/slash-commands/builtinCommands.ts +0 -2733
  95. package/src/slash-commands/clone.ts +0 -242
  96. package/src/slash-commands/council.ts +0 -125
  97. package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
  98. package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
  99. package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
  100. package/src/slash-commands/custom/index.ts +0 -7
  101. package/src/slash-commands/debate.ts +0 -254
  102. package/src/slash-commands/gmail.ts +0 -105
  103. package/src/slash-commands/index.ts +0 -388
  104. package/src/slash-commands/mcpCommand.ts +0 -205
  105. package/src/slash-commands/types.ts +0 -201
  106. package/src/store/index.ts +0 -76
  107. package/src/store/selectors.ts +0 -246
  108. package/src/store/slices/appSlice.ts +0 -205
  109. package/src/store/slices/commandSlice.ts +0 -115
  110. package/src/store/slices/configSlice.ts +0 -46
  111. package/src/store/slices/focusSlice.ts +0 -64
  112. package/src/store/slices/index.ts +0 -9
  113. package/src/store/slices/sessionSlice.ts +0 -424
  114. package/src/store/streaming-buffer.ts +0 -425
  115. package/src/store/test.ts +0 -296
  116. package/src/store/types.ts +0 -274
  117. package/src/store/vanilla.ts +0 -186
  118. package/src/tools/builtin/bash.ts +0 -236
  119. package/src/tools/builtin/council.ts +0 -105
  120. package/src/tools/builtin/edit.ts +0 -213
  121. package/src/tools/builtin/glob.ts +0 -136
  122. package/src/tools/builtin/grep.ts +0 -263
  123. package/src/tools/builtin/index.ts +0 -61
  124. package/src/tools/builtin/memory.ts +0 -66
  125. package/src/tools/builtin/read.ts +0 -168
  126. package/src/tools/builtin/skill.ts +0 -97
  127. package/src/tools/builtin/snapshot.ts +0 -40
  128. package/src/tools/builtin/task.ts +0 -106
  129. package/src/tools/builtin/write.ts +0 -134
  130. package/src/tools/createTool.ts +0 -221
  131. package/src/tools/execution/ExecutionPipeline.ts +0 -263
  132. package/src/tools/execution/index.ts +0 -40
  133. package/src/tools/execution/stages/CacheStage.ts +0 -131
  134. package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
  135. package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
  136. package/src/tools/execution/stages/ExecutionStage.ts +0 -48
  137. package/src/tools/execution/stages/FormattingStage.ts +0 -44
  138. package/src/tools/execution/stages/HookStage.ts +0 -72
  139. package/src/tools/execution/stages/PermissionStage.ts +0 -287
  140. package/src/tools/execution/stages/PostHookStage.ts +0 -71
  141. package/src/tools/execution/stages/index.ts +0 -12
  142. package/src/tools/execution/test.ts +0 -266
  143. package/src/tools/execution/types.ts +0 -273
  144. package/src/tools/index.ts +0 -81
  145. package/src/tools/registry.ts +0 -304
  146. package/src/tools/schemas.ts +0 -109
  147. package/src/tools/test.ts +0 -220
  148. package/src/tools/types.ts +0 -175
  149. package/src/tools/validation/PermissionChecker.ts +0 -242
  150. package/src/tools/validation/SensitiveFileDetector.ts +0 -210
  151. package/src/tools/validation/index.ts +0 -11
  152. package/src/ui/App.tsx +0 -166
  153. package/src/ui/components/AegisInterface.tsx +0 -484
  154. package/src/ui/components/common/ChatSearch.tsx +0 -150
  155. package/src/ui/components/common/ErrorBoundary.tsx +0 -82
  156. package/src/ui/components/common/ExitMessage.tsx +0 -120
  157. package/src/ui/components/common/LoadingIndicator.tsx +0 -49
  158. package/src/ui/components/common/index.ts +0 -6
  159. package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
  160. package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
  161. package/src/ui/components/dialog/SetupWizard.tsx +0 -297
  162. package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
  163. package/src/ui/components/dialog/index.ts +0 -8
  164. package/src/ui/components/index.ts +0 -28
  165. package/src/ui/components/input/CommandSuggestions.tsx +0 -139
  166. package/src/ui/components/input/CustomTextInput.tsx +0 -220
  167. package/src/ui/components/input/InputArea.tsx +0 -361
  168. package/src/ui/components/input/PromptSuggestions.tsx +0 -66
  169. package/src/ui/components/input/index.ts +0 -6
  170. package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
  171. package/src/ui/components/layout/ContextBar.tsx +0 -79
  172. package/src/ui/components/layout/MessageArea.tsx +0 -96
  173. package/src/ui/components/layout/MessageList.tsx +0 -647
  174. package/src/ui/components/layout/MessageSeparator.tsx +0 -26
  175. package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
  176. package/src/ui/components/layout/index.ts +0 -7
  177. package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
  178. package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
  179. package/src/ui/components/markdown/index.ts +0 -8
  180. package/src/ui/components/markdown/parser.ts +0 -336
  181. package/src/ui/components/markdown/types.ts +0 -66
  182. package/src/ui/focus/FocusManager.ts +0 -137
  183. package/src/ui/focus/index.ts +0 -13
  184. package/src/ui/focus/types.ts +0 -54
  185. package/src/ui/focus/useFocus.ts +0 -75
  186. package/src/ui/hooks/index.ts +0 -11
  187. package/src/ui/hooks/useAgent.ts +0 -284
  188. package/src/ui/hooks/useCommandHistory.ts +0 -87
  189. package/src/ui/hooks/useCommandProcessor.ts +0 -443
  190. package/src/ui/hooks/useConfirmation.ts +0 -99
  191. package/src/ui/hooks/useCtrlCHandler.ts +0 -100
  192. package/src/ui/hooks/useInputBuffer.ts +0 -122
  193. package/src/ui/hooks/useTerminalSize.ts +0 -68
  194. package/src/ui/hooks/useTerminalWidth.ts +0 -5
  195. package/src/ui/hooks/useWindowedList.ts +0 -118
  196. package/src/ui/render-debugger.ts +0 -621
  197. package/src/ui/test.ts +0 -189
  198. package/src/ui/themes/ThemeManager.ts +0 -332
  199. package/src/ui/themes/aegisTheme.ts +0 -87
  200. package/src/ui/themes/darkTheme.ts +0 -87
  201. package/src/ui/themes/defaultTheme.ts +0 -85
  202. package/src/ui/themes/index.ts +0 -10
  203. package/src/ui/themes/lightTheme.ts +0 -89
  204. package/src/ui/themes/popularThemes.ts +0 -187
  205. package/src/ui/themes/types.ts +0 -130
  206. package/src/utils/clipboard.ts +0 -48
  207. package/src/utils/debug.ts +0 -43
  208. package/src/utils/environment.ts +0 -68
  209. package/src/utils/index.ts +0 -10
@@ -1,284 +0,0 @@
1
- /**
2
- * useAgent - Agent lifecycle hook
3
- *
4
- * Extracts agent initialization, context management, and model switching
5
- * from AegisInterface into a focused, testable hook.
6
- */
7
-
8
- import { useEffect, useRef, useCallback, useState } from 'react';
9
- import { Agent } from '../../agent/Agent.js';
10
- import { ContextManager } from '../../context/index.js';
11
- import { sessionActions, appActions, configActions, getState, subscribe } from '../../store/index.js';
12
- import type { ModelConfig } from '../../config/types.js';
13
-
14
- export interface UseAgentOptions {
15
- apiKey: string;
16
- baseURL?: string;
17
- model?: string;
18
- debug?: boolean;
19
- resumeSessionId?: string;
20
- }
21
-
22
- export interface UseAgentResult {
23
- agentRef: React.MutableRefObject<Agent | null>;
24
- contextManagerRef: React.MutableRefObject<ContextManager | null>;
25
- isInitializing: boolean;
26
- initError: string | null;
27
- currentModel: string | undefined;
28
- setCurrentModel: React.Dispatch<React.SetStateAction<string | undefined>>;
29
- handleSetupComplete: () => Promise<void>;
30
- getAgent: () => Agent | null;
31
- getContextManager: () => ContextManager | null;
32
- }
33
-
34
- async function initHooks(sessionId: string): Promise<void> {
35
- try {
36
- const { ConfigManager } = await import('../../config/index.js');
37
- const cm = ConfigManager.getInstance();
38
- const { initializeHooks, onSessionStart } = await import('../../hooks/index.js');
39
- initializeHooks(cm.getConfig().hooks || {});
40
- await onSessionStart(sessionId, process.cwd());
41
- } catch { /* non-fatal */ }
42
- }
43
-
44
- export function useAgent(options: UseAgentOptions): UseAgentResult {
45
- const { apiKey, baseURL, model, debug, resumeSessionId } = options;
46
-
47
- const agentRef = useRef<Agent | null>(null);
48
- const contextManagerRef = useRef<ContextManager | null>(null);
49
- const [isInitializing, setIsInitializing] = useState(true);
50
- const [initError, setInitError] = useState<string | null>(null);
51
-
52
- const currentModelIdRef = useRef(model);
53
- const [currentModel, setCurrentModel] = useState(model);
54
-
55
- const getAgent = useCallback(() => agentRef.current, []);
56
- const getContextManager = useCallback(() => contextManagerRef.current, []);
57
-
58
- // Stable option refs (prevents recreation of callbacks)
59
- const debugRef = useRef(debug);
60
- debugRef.current = debug;
61
- const modelRef = useRef(model);
62
- modelRef.current = model;
63
-
64
- // ==================== Setup Completion ====================
65
- const handleSetupComplete = useCallback(async () => {
66
- setInitError(null);
67
- setIsInitializing(true);
68
- try {
69
- const { ConfigManager } = await import('../../config/index.js');
70
- const cm = ConfigManager.getInstance();
71
- await cm.initialize();
72
- const newModel = cm.getDefaultModel();
73
-
74
- // If we already have a ContextManager (e.g. session was loaded earlier),
75
- // preserve it instead of creating a new one
76
- if (!contextManagerRef.current) {
77
- contextManagerRef.current = new ContextManager({ compressionThreshold: 100000 });
78
- }
79
- const ctxManager = contextManagerRef.current;
80
-
81
- // Only create a new session if none exists
82
- let sid = ctxManager.getCurrentSessionId();
83
- if (!sid) {
84
- sid = await ctxManager.createSession();
85
- }
86
- sessionActions().setSessionId(sid);
87
-
88
- agentRef.current = await Agent.create({
89
- apiKey: newModel.apiKey!,
90
- baseURL: newModel.baseURL,
91
- model: newModel.model!,
92
- requireConfirmation: newModel.requireConfirmation,
93
- });
94
-
95
- const { initializeCustomCommands } = await import('../../slash-commands/index.js');
96
- await initializeCustomCommands(process.cwd());
97
-
98
- import('../../skills/index.js').then(({ initializeSkills }) => {
99
- initializeSkills(process.cwd()).catch(() => {});
100
- }).catch(() => {});
101
-
102
- await initHooks(sid);
103
-
104
- setIsInitializing(false);
105
- } catch (err) {
106
- setInitError(err instanceof Error ? err.message : String(err));
107
- setIsInitializing(false);
108
- }
109
- }, []);
110
-
111
- // ==================== Initialization ====================
112
- useEffect(() => {
113
- const initAgent = async () => {
114
- if (getState().app.initializationStatus === 'needsSetup') return;
115
- try {
116
- if (debugRef.current) {
117
- console.log('[DEBUG] Initializing Agent and ContextManager...');
118
- }
119
-
120
- const ctxManager = new ContextManager({ compressionThreshold: 100000 });
121
- contextManagerRef.current = ctxManager;
122
-
123
- let currentSessionId: string;
124
-
125
- if (resumeSessionId) {
126
- const loaded = await ctxManager.loadSession(resumeSessionId);
127
- if (loaded) {
128
- currentSessionId = resumeSessionId;
129
- const contextMessages = ctxManager.getMessages();
130
- // Batch-restore all messages in a single store update so MessageList's
131
- // scrollLineOffset initializes correctly (showing the bottom of history).
132
- // Adding messages one-by-one via addUserMessage/addAssistantMessage skips
133
- // auto-scroll for assistant messages, leaving scrollLineOffset at 0 (top).
134
- const restoredMessages: import('../../store/types.js').SessionMessage[] =
135
- contextMessages
136
- .filter(m => m.role === 'user' || m.role === 'assistant')
137
- .map((m, i) => ({
138
- id: `restored-${i}-${Date.now()}`,
139
- role: m.role as 'user' | 'assistant',
140
- content: m.content,
141
- timestamp: m.timestamp || Date.now(),
142
- }));
143
- sessionActions().restoreSession(currentSessionId, restoredMessages);
144
- if (debugRef.current) {
145
- console.log('[DEBUG] Loaded session with', contextMessages.length, 'messages');
146
- }
147
- } else {
148
- if (debugRef.current) {
149
- console.log('[DEBUG] Failed to load session, creating new one');
150
- }
151
- currentSessionId = await ctxManager.createSession();
152
- }
153
- } else {
154
- currentSessionId = await ctxManager.createSession();
155
- }
156
-
157
- sessionActions().setSessionId(currentSessionId);
158
-
159
- const { ConfigManager } = await import('../../config/index.js');
160
- const cm = ConfigManager.getInstance();
161
- await cm.initialize();
162
- const requireConfirmation = cm.getDefaultModel().requireConfirmation;
163
-
164
- agentRef.current = await Agent.create({ apiKey, baseURL, model, requireConfirmation });
165
-
166
- const { initializeCustomCommands } = await import('../../slash-commands/index.js');
167
- const customCmdResult = await initializeCustomCommands(process.cwd());
168
- if (debugRef.current && customCmdResult.count > 0) {
169
- console.log('[DEBUG] Loaded', customCmdResult.count, 'custom commands');
170
- }
171
-
172
- import('../../skills/index.js').then(({ initializeSkills }) => {
173
- initializeSkills(process.cwd()).catch(() => {});
174
- }).catch(() => {});
175
-
176
- await initHooks(currentSessionId);
177
-
178
- setIsInitializing(false);
179
-
180
- if (debugRef.current) {
181
- console.log('[DEBUG] Agent initialized successfully, sessionId:', currentSessionId);
182
- }
183
- } catch (error) {
184
- setInitError(error instanceof Error ? error.message : '');
185
- setIsInitializing(false);
186
- }
187
- };
188
-
189
- initAgent();
190
-
191
- return () => {
192
- contextManagerRef.current?.cleanup().catch(() => {});
193
- };
194
- }, [apiKey, baseURL, model, debug, resumeSessionId]);
195
-
196
- // ==================== Model Switch Subscription ====================
197
- // Tracks both which model is active AND that model's own settings
198
- // (requireConfirmation/allowedTools/disallowedTools), since the running
199
- // Agent instance freezes these at creation time. Without watching the
200
- // settings too, toggling e.g. `/confirm off` for the *currently active*
201
- // model would silently have no effect until the user switched models away
202
- // and back.
203
- const currentModelSettingsRef = useRef<string>('');
204
- useEffect(() => {
205
- // Seed the snapshot from current state so the first store change after
206
- // mount doesn't look like a settings change and trigger a spurious rebuild.
207
- const initialModels = getState().config.config?.models || [];
208
- const initialFound = currentModelIdRef.current
209
- ? initialModels.find((m: ModelConfig) => m.id === currentModelIdRef.current)
210
- : undefined;
211
- if (initialFound) {
212
- currentModelSettingsRef.current = JSON.stringify({
213
- requireConfirmation: initialFound.requireConfirmation,
214
- allowedTools: initialFound.allowedTools,
215
- disallowedTools: initialFound.disallowedTools,
216
- });
217
- }
218
-
219
- const rebuildAgent = (found: ModelConfig, displayName: string) => {
220
- if (!agentRef.current) return;
221
- import('../../agent/Agent.js').then(({ Agent }) => {
222
- const apiKey = found.apiKey || process.env.OPENAI_API_KEY || '';
223
- if (!apiKey) return;
224
- Agent.create({
225
- apiKey,
226
- baseURL: found.baseURL || (found as ModelConfig & { baseUrl?: string }).baseUrl,
227
- model: displayName,
228
- requireConfirmation: found.requireConfirmation,
229
- }).then(agent => {
230
- agentRef.current = agent;
231
- }).catch(() => {});
232
- }).catch(() => {});
233
- };
234
-
235
- const unsubscribe = subscribe((state) => {
236
- const newModelId = state.config.config?.currentModelId;
237
- if (!newModelId) return;
238
- const models = state.config.config?.models || [];
239
- const found = models.find((m: ModelConfig) => m.id === newModelId);
240
- if (!found) return;
241
-
242
- const settingsSnapshot = JSON.stringify({
243
- requireConfirmation: found.requireConfirmation,
244
- allowedTools: found.allowedTools,
245
- disallowedTools: found.disallowedTools,
246
- });
247
-
248
- const modelChanged = newModelId !== currentModelIdRef.current;
249
- const settingsChanged = settingsSnapshot !== currentModelSettingsRef.current;
250
-
251
- if (!modelChanged && !settingsChanged) return;
252
-
253
- currentModelIdRef.current = newModelId;
254
- currentModelSettingsRef.current = settingsSnapshot;
255
-
256
- const displayName = found.model || found.id || newModelId;
257
- setCurrentModel(displayName);
258
- modelRef.current = displayName;
259
-
260
- // Apply requireConfirmation synchronously on the live Agent instance so
261
- // toggling /confirm takes effect immediately — the full rebuild below
262
- // is async and would otherwise leave a window where agentRef.current
263
- // still has the stale value if a message is sent before it resolves.
264
- if (!modelChanged && agentRef.current) {
265
- agentRef.current.setRequireConfirmation(found.requireConfirmation);
266
- }
267
-
268
- rebuildAgent(found, displayName);
269
- });
270
- return unsubscribe;
271
- }, []);
272
-
273
- return {
274
- agentRef,
275
- contextManagerRef,
276
- isInitializing,
277
- initError,
278
- currentModel,
279
- setCurrentModel,
280
- handleSetupComplete,
281
- getAgent,
282
- getContextManager,
283
- };
284
- }
@@ -1,87 +0,0 @@
1
- /**
2
- * useCommandHistory - 命令历史管理
3
- */
4
-
5
- import { useState, useCallback } from 'react';
6
-
7
- interface CommandHistoryResult {
8
- /** 添加命令到历史 */
9
- addToHistory: (command: string) => void;
10
- /** 获取上一条命令 */
11
- getPreviousCommand: () => string | null;
12
- /** 获取下一条命令 */
13
- getNextCommand: () => string | null;
14
- /** 重置历史索引 */
15
- resetIndex: () => void;
16
- /** 历史记录 */
17
- history: string[];
18
- /** 当前索引 */
19
- historyIndex: number;
20
- }
21
-
22
- /**
23
- *
24
- *
25
- */
26
- export const useCommandHistory = (maxHistory = 100): CommandHistoryResult => {
27
- const [history, setHistory] = useState<string[]>([]);
28
- const [historyIndex, setHistoryIndex] = useState(-1);
29
-
30
- const addToHistory = useCallback((command: string) => {
31
- if (command.trim()) {
32
- setHistory(prev => {
33
- // 避免重复添加相同命
34
- if (prev[prev.length - 1] === command) {
35
- return prev;
36
- }
37
- // 限制历史记录数
38
- const newHistory = [...prev, command];
39
- if (newHistory.length > maxHistory) {
40
- newHistory.shift();
41
- }
42
- return newHistory;
43
- });
44
- }
45
- setHistoryIndex(-1);
46
- }, [maxHistory]);
47
-
48
- const getPreviousCommand = useCallback(() => {
49
- if (history.length === 0) {
50
- return null;
51
- }
52
-
53
- if (historyIndex < history.length - 1) {
54
- const newIndex = historyIndex + 1;
55
- setHistoryIndex(newIndex);
56
- return history[history.length - 1 - newIndex];
57
- }
58
-
59
- // 已经到达最早的命
60
- return history[0];
61
- }, [history, historyIndex]);
62
-
63
- const getNextCommand = useCallback(() => {
64
- if (historyIndex > 0) {
65
- const newIndex = historyIndex - 1;
66
- setHistoryIndex(newIndex);
67
- return history[history.length - 1 - newIndex];
68
- }
69
-
70
- // 返回到最新(清空输
71
- setHistoryIndex(-1);
72
- return '';
73
- }, [history, historyIndex]);
74
-
75
- const resetIndex = useCallback(() => {
76
- setHistoryIndex(-1);
77
- }, []);
78
-
79
- return {
80
- addToHistory,
81
- getPreviousCommand,
82
- getNextCommand,
83
- resetIndex,
84
- history,
85
- historyIndex,
86
- };
87
- };