aegiscode 3.1.10 → 3.4.2

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.js +165 -0
  2. package/package.json +18 -86
  3. package/LICENSE +0 -21
  4. package/README.md +0 -394
  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
package/src/main.tsx DELETED
@@ -1,596 +0,0 @@
1
- /**
2
- * AEGIS CLI - 主入口
3
- *
4
- *
5
- * 1. 早期解析 --debug 参数(确保日志可用)
6
- * 2. 启动版本检查(不等待,与后续流程并行)
7
- * 3. 创建 yargs CLI 实例
8
- * 4. 注册全局选项和命令
9
- * 5. 执行中间件链(validatePermissions → loadConfiguration → validateOutput)
10
- * 6. 执行默认命令 → 启动 React UI(传递 versionCheckPromise)
11
- *
12
- *
13
- * 1. 默认配置
14
- * 2. 用户配置 (~/.aegiscode/config.json)
15
- * 3. 项目配置 (./.aegiscode/config.json)
16
- * 4. 环境变量 (OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL)
17
- * 5. CLI 参数 (--api-key, --base-url, --model)
18
- */
19
-
20
- // RAF polyfill — MUST run before any module that uses requestAnimationFrame.
21
- // Pass Date.now() as the timestamp so RAF callbacks that throttle by timestamp
22
- // (e.g. MessageList's RAF_INTERVAL_MS check) work correctly. Without this,
23
- // `now` is undefined and `undefined - ref < 30` is always false, causing the
24
- // streaming render loop to fire at the raw setTimeout rate (~16ms) instead of 30ms.
25
- if (typeof globalThis.requestAnimationFrame === 'undefined') {
26
- (globalThis as any).requestAnimationFrame = (cb: FrameRequestCallback) => setTimeout(() => cb(Date.now()), 16);
27
- (globalThis as any).cancelAnimationFrame = (id: number) => clearTimeout(id);
28
- }
29
-
30
- import { config as dotenvConfig } from 'dotenv';
31
- import { resolve } from 'path';
32
- import { homedir } from 'os';
33
- // --print's whole point is clean, pipeable stdout (text or JSON) — the
34
- // dotenvx promo banner would otherwise land on stdout ahead of the result.
35
- const isPrintMode = process.argv.includes('--print') || process.argv.includes('-p');
36
- // SetupWizard saves keys to ~/.aegiscode/.env (the only place a globally-installed
37
- // `aegis` binary can reliably find them — process.cwd() is wherever the user happens
38
- // to invoke it from). Load that first, then ./.env so a project-local file can override it.
39
- dotenvConfig({ path: resolve(homedir(), '.aegiscode', '.env'), quiet: isPrintMode });
40
- dotenvConfig({ path: resolve(process.cwd(), '.env'), quiet: isPrintMode, override: true });
41
- import React from 'react';
42
- import yargs from 'yargs';
43
- import { hideBin } from 'yargs/helpers';
44
- import { App } from './ui/App.js';
45
- import { configManager } from './config/index.js';
46
- import { cliConfig, globalOptions, middlewareChain } from './cli/index.js';
47
- import { checkVersionOnStartup } from './services/index.js';
48
- import { runCouncil } from './slash-commands/council.js';
49
- import { getLatestSessionFile } from './context/index.js';
50
- import { themeManager } from './ui/themes/index.js';
51
- import { setGlobalDebug } from './utils/debug.js';
52
- import type { CliArguments } from './cli/types.js';
53
- import type { VersionCheckResult } from './services/VersionChecker.js';
54
- import * as path from 'node:path';
55
-
56
- // Rendering debugger (auto-starts with --debug-rendering flag)
57
- const ENABLE_RENDER_DEBUG = process.argv.includes('--debug-rendering');
58
-
59
- // ========== 全局状
60
- let isDebugMode = false;
61
- let versionCheckPromise: Promise<VersionCheckResult | null> | undefined;
62
-
63
- /**
64
- *
65
- *
66
- *
67
- * - Logger 在各模块中被创建
68
- * - 如果等 yargs 解析完再设置 debug,部分初始化日志会丢失
69
- * - 早期解析确保所有日志都能正确输出
70
- */
71
- function parseDebugEarly(): void {
72
- const rawArgs = hideBin(process.argv)
73
- const debugIndex = rawArgs.indexOf('--debug')
74
- const shortDebugIndex = rawArgs.indexOf('-d')
75
-
76
- if (debugIndex !== -1 || shortDebugIndex !== -1) {
77
- isDebugMode = true
78
- setGlobalDebug(true) // 设置全局 debug 状态,供其他模块使
79
- console.log('[DEBUG] Debug mode enabled via early parsing')
80
- }
81
- }
82
-
83
- /**
84
- *
85
- */
86
- async function main(): Promise<void> {
87
- // 1. 早期解
88
- parseDebugEarly();
89
-
90
- // One-shot memory introspection flags for external callers (e.g. aegiscode-gui's
91
- // Electron main process) — single source of truth instead of duplicating the
92
- // SQLite schema/read logic in another codebase. MUST run before anything below
93
- // imports SharedMemory (its singleton constructor fires init() — TTL eviction +
94
- // cloud sync — synchronously on first import) or makes network calls (version
95
- // check, token verification) that a fast read-only call has no business paying for.
96
- const earlyArgs = process.argv.slice(2);
97
- // One-shot account re-verification for external callers (aegiscode-gui) — same
98
- // single-source-of-truth reasoning as the memory flags below.
99
- if (earlyArgs[0] === '--verify-account-json') {
100
- const { verifyAccount } = await import('./auth/login.js');
101
- console.log(JSON.stringify(await verifyAccount()));
102
- process.exit(0);
103
- }
104
-
105
- if (earlyArgs[0] === '--memory-stats-json' || earlyArgs[0] === '--memory-search-json' || earlyArgs[0] === '--memory-clear-json' || earlyArgs[0] === '--memory-upload-json' || earlyArgs[0] === '--memory-download-json') {
106
- process.env.AEGIS_MEMORY_READONLY = '1';
107
- const { sharedMemory } = await import('./memory/SharedMemory.js');
108
- await sharedMemory.whenReady();
109
-
110
- if (earlyArgs[0] === '--memory-stats-json') {
111
- console.log(JSON.stringify(sharedMemory.getStats()));
112
- } else if (earlyArgs[0] === '--memory-search-json') {
113
- const query = earlyArgs[1] || '';
114
- const limit = parseInt(earlyArgs[2] || '50', 10);
115
- const results = query ? await sharedMemory.search(query, limit) : sharedMemory.recent(limit);
116
- console.log(JSON.stringify(results));
117
- } else if (earlyArgs[0] === '--memory-upload-json') {
118
- console.log(JSON.stringify(await sharedMemory.pushAll()));
119
- } else if (earlyArgs[0] === '--memory-download-json') {
120
- console.log(JSON.stringify(await sharedMemory.pullAll()));
121
- } else {
122
- sharedMemory.clear();
123
- console.log(JSON.stringify({ ok: true }));
124
- }
125
- process.exit(0);
126
- }
127
-
128
- // 2. 启动版本检查(不等待,与后续流程并行执
129
- versionCheckPromise = checkVersionOnStartup();
130
-
131
- // Memory is available for all logged-in users — no separate token verification needed
132
-
133
- // Handle --model flag: aegis --model deepseek "question"
134
- // or aegis --model council "question" for council vote
135
- const modelArg = process.argv.find(a => a.startsWith('--model='))?.split('=')[1]
136
- || (() => { const i = process.argv.indexOf('--model'); return i > -1 ? process.argv[i+1] : null; })();
137
-
138
- if (modelArg) {
139
- const { readFileSync } = await import('fs');
140
- const { homedir } = await import('os');
141
- try {
142
- const cfg = JSON.parse(readFileSync(`${homedir()}/.aegiscode/config.json`, 'utf8'));
143
- const found = (cfg.models || []).find((m: any) => m.id === modelArg);
144
- if (found) {
145
- process.env.OPENAI_API_KEY = found.apiKey;
146
- process.env.OPENAI_BASE_URL = found.baseUrl || found.baseURL;
147
- // Sätt currentModelId så getDefaultModel() väljer rätt
148
- cfg.currentModelId = found.id;
149
- const { writeFileSync } = await import('fs');
150
- writeFileSync(`${homedir()}/.aegiscode/config.json`, JSON.stringify(cfg, null, 2));
151
- console.log(`\x1b[38;2;0;229;192m[AEGIS] Model: ${found.name || found.id}\x1b[0m`);
152
- }
153
- } catch {}
154
- }
155
-
156
- // Handle /council command
157
- const args = process.argv.slice(2);
158
-
159
- if (args[0] === '/council' || args[0] === 'council') {
160
- const question = args.slice(1).join(' ');
161
- if (!question) {
162
- console.error('Usage: aegis /council <question>');
163
- process.exit(1);
164
- }
165
- runCouncil(question).then(() => process.exit(0));
166
- return;
167
- }
168
- if (isDebugMode) {
169
- console.log('[DEBUG] Version check started (running in parallel)');
170
- }
171
-
172
- // 3. 创建 yargs CLI 实
173
- const cli = yargs(hideBin(process.argv))
174
- .scriptName(cliConfig.scriptName)
175
- .usage(cliConfig.usage)
176
- .version(cliConfig.version)
177
- // 注册全局选
178
- .options(globalOptions)
179
-
180
- // 注册中间
181
- .middleware(middlewareChain)
182
-
183
- // ── auth commands ──────────────────────────────────────────────────────
184
- .command(
185
- 'login',
186
- 'Log in to aegiscloud (Google or username/password), or Claude Code Pro/Max',
187
- (y) => y
188
- .option('password', {
189
- alias: 'p',
190
- type: 'boolean',
191
- describe: 'Log in with username and password instead of browser',
192
- default: false,
193
- })
194
- .option('claude-pro', {
195
- type: 'boolean',
196
- describe: 'Authenticate with a Claude Code Pro/Max subscription token instead of an API key',
197
- default: false,
198
- }),
199
- async (argv) => {
200
- const { runLogin, runLoginPassword, runLoginClaudePro } = await import('./auth/login.js');
201
- try {
202
- if ((argv as any).claudePro) {
203
- await runLoginClaudePro();
204
- process.exit(0);
205
- } else if ((argv as any).password) {
206
- await runLoginPassword();
207
- } else {
208
- await runLogin();
209
- }
210
- console.log('\n\x1b[32m✓ Logged in successfully.\x1b[0m');
211
- console.log('\nRun \x1b[1maegis\x1b[0m to start coding.\n');
212
- } catch (err) {
213
- console.error('\n\x1b[31m✗ Login failed:\x1b[0m', (err as Error).message);
214
- process.exit(1);
215
- }
216
- process.exit(0);
217
- },
218
- )
219
-
220
- .command(
221
- 'logout',
222
- 'Log out and remove stored aegiscloud credentials',
223
- () => {},
224
- async () => {
225
- const { runLogout } = await import('./auth/login.js');
226
- runLogout();
227
- process.exit(0);
228
- },
229
- )
230
-
231
- // 示
232
- .example('$0', 'Start interactive mode')
233
- .example('$0 login', 'Log in via browser')
234
- .example('$0 continue', 'Continue the most recent conversation')
235
- .example('$0 resume <session-id>', 'Resume a specific conversation by ID')
236
- .example('$0 "帮我分析这个项目"', 'Start with an initial message')
237
- .example('$0 --model gpt-4', 'Use a specific model')
238
- .example('$0 --router', 'Start with the auto-router on')
239
- .example('$0 --debug', 'Enable debug mode')
240
- .example('$0 --init', 'Create default config file')
241
-
242
- // 帮助
243
- .help()
244
- .alias('h', 'help')
245
-
246
- // 7. 错误处
247
- .fail((msg, err, yargsInstance) => {
248
- if (err) {
249
- console.error('💥 An error occurred:')
250
- console.error(err.message)
251
- if (isDebugMode && err.stack) {
252
- console.error('\nStack trace:')
253
- console.error(err.stack)
254
- }
255
- process.exit(1)
256
- }
257
-
258
- if (msg) {
259
- console.error('❌ Invalid arguments:')
260
- console.error(msg)
261
- console.error('')
262
- yargsInstance.showHelp()
263
- process.exit(1)
264
- }
265
- })
266
-
267
- // 8. 严格模式(禁止未知选
268
- .strict()
269
-
270
- // 9. 默认命
271
- .command(
272
- '$0 [message..]',
273
- 'Start interactive mode',
274
- (yargs) => {
275
- return yargs.positional('message', {
276
- type: 'string',
277
- describe: 'Initial message to send (can be multiple words)',
278
- array: true,
279
- })
280
- },
281
- async (argv) => {
282
- const args = argv as CliArguments
283
-
284
- // 处理 --init 命
285
- if (args.init) {
286
- const configPath = await configManager.createDefaultConfig()
287
- console.log(`✅ Created default config at: ${configPath}`)
288
- console.log('')
289
- console.log('Please edit the file and add your API key:')
290
- console.log(` vim ${configPath}`)
291
- process.exit(0)
292
- }
293
-
294
- // ── Mandatory login check (Aegiscode Pro) ────────────────────────
295
- // Aegiscode Pro requires login. No login = no API access.
296
- const { readFileSync } = await import('node:fs');
297
- const { homedir } = await import('node:os');
298
- let hasCredentials = false;
299
- try {
300
- const cfg = JSON.parse(readFileSync(`${homedir()}/.aegiscode/config.json`, 'utf8'));
301
- hasCredentials = !!(cfg?.aegiscloud?.api_key);
302
- } catch {}
303
-
304
- const needsLogin = hasCredentials
305
- ? !(await (await import('./auth/login.js')).ensureAccountValid())
306
- : true;
307
-
308
- if (needsLogin) {
309
- const { runLogin } = await import('./auth/login.js');
310
- try {
311
- await runLogin();
312
- console.log('\n\x1b[32m✓ Logged in.\x1b[0m Starting ÆGIS...\n');
313
- await configManager.initialize(process.cwd());
314
- } catch (err) {
315
- console.error('\n\x1b[31m✗ Login failed:\x1b[0m', (err as Error).message);
316
- console.error('Run \x1b[1maegis login\x1b[0m to try again.');
317
- process.exit(1);
318
- }
319
- }
320
-
321
- const modelConfig = configManager.getDefaultModel()
322
-
323
- // No API key yet — don't exit here. App.tsx already detects this on mount
324
- // (initializeStoreState) and renders the interactive SetupWizard, which
325
- // creates ~/.aegiscode/.env for the user. Exiting here with print-only
326
- // instructions skipped that wizard entirely and left fresh installs
327
- // stuck telling users to hand-create a hidden folder themselves.
328
- // --print mode has no UI to fall back on, so it still needs to fail fast.
329
- if (!modelConfig.apiKey && isPrintMode) {
330
- process.stderr.write(
331
- 'No API key configured. Run `aegis` once (without --print) to set one up, ' +
332
- 'or set an environment variable: export OPENAI_API_KEY=sk-...\n',
333
- );
334
- process.exit(1);
335
- }
336
-
337
- // 获取初始消息(支持多个单
338
- const messageArray = argv.message as string[] | undefined
339
- let initialMessage =
340
- messageArray && messageArray.length > 0
341
- ? messageArray.join(' ')
342
- : undefined
343
-
344
- if (isDebugMode && initialMessage) {
345
- console.log('[DEBUG] Initial message:', initialMessage)
346
- }
347
-
348
- // Handle `aegis continue` and `aegis resume <id>` without `--` prefix
349
- // so they behave like `aegis --continue` and `aegis --resume <id>`.
350
- if (!args.continue && !args.resume && initialMessage && !initialMessage.startsWith('/')) {
351
- const trimmed = initialMessage.trim();
352
- if (trimmed === 'continue') {
353
- args.continue = true;
354
- initialMessage = undefined;
355
- } else if (trimmed.startsWith('resume ') || trimmed.startsWith('resume\t')) {
356
- const parts = trimmed.split(/\s+/);
357
- if (parts.length >= 2) {
358
- args.resume = parts[1];
359
- initialMessage = undefined;
360
- }
361
- }
362
- }
363
-
364
- // 处理 --continue 和 --resume 参
365
- let resumeSessionId: string | undefined;
366
-
367
- if (args.continue) {
368
- // 获取最近的会话文
369
- const latestSession = await getLatestSessionFile(process.cwd());
370
- if (latestSession) {
371
- // 从文件路径提取 sessionId(去掉 .jsonl 扩展
372
- resumeSessionId = path.basename(latestSession, '.jsonl');
373
- if (isDebugMode) {
374
- console.log('[DEBUG] Continuing session:', resumeSessionId);
375
- }
376
- } else {
377
- console.log('No previous session found. Starting a new conversation.');
378
- }
379
- } else if (args.resume && typeof args.resume === 'string') {
380
- resumeSessionId = args.resume;
381
- if (isDebugMode) {
382
- console.log('[DEBUG] Resuming session:', resumeSessionId);
383
- }
384
- }
385
-
386
- // --print: headless mode, no Ink. Runs one turn, writes the result to
387
- // stdout (text or JSON per --output-format), and exits — no TUI chrome,
388
- // no spinners, no escape codes, so the output is safe to pipe/script.
389
- if (args.print) {
390
- if (!initialMessage) {
391
- process.stderr.write('Error: --print requires a message, e.g. aegis --print "your question"\n');
392
- process.exit(1);
393
- }
394
-
395
- const { startCostLedger } = await import('./services/CostLedger.js');
396
- startCostLedger();
397
- const { startHeartbeat } = await import('./services/Heartbeat.js');
398
- startHeartbeat();
399
-
400
- const { ContextManager } = await import('./context/index.js');
401
- const { Agent } = await import('./agent/Agent.js');
402
-
403
- const ctxManager = new ContextManager({ compressionThreshold: 100000 });
404
- let sessionId: string;
405
- let priorMessages: { role: 'system' | 'user' | 'assistant' | 'tool'; content: string }[] = [];
406
-
407
- if (resumeSessionId) {
408
- const loaded = await ctxManager.loadSession(resumeSessionId);
409
- sessionId = loaded ? resumeSessionId : await ctxManager.createSession();
410
- if (loaded) {
411
- priorMessages = ctxManager.getMessages()
412
- .filter(m => m.role === 'user' || m.role === 'assistant')
413
- .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));
414
- }
415
- } else {
416
- sessionId = await ctxManager.createSession();
417
- }
418
-
419
- try {
420
- const agent = await Agent.create({
421
- apiKey: modelConfig.apiKey ?? '',
422
- baseURL: modelConfig.baseURL,
423
- model: modelConfig.model,
424
- requireConfirmation: false,
425
- });
426
-
427
- const result = await agent.chatWithMetadata(initialMessage, {
428
- sessionId,
429
- messages: priorMessages,
430
- });
431
-
432
- if (!result.success) {
433
- throw new Error(result.error?.message || 'Agent execution failed');
434
- }
435
- const finalMessage = result.finalMessage || '';
436
-
437
- await ctxManager.addMessage('user', initialMessage);
438
- await ctxManager.addMessage('assistant', finalMessage);
439
- await ctxManager.flush();
440
-
441
- if (args.outputFormat === 'json') {
442
- process.stdout.write(JSON.stringify({
443
- result: finalMessage,
444
- session_id: sessionId,
445
- num_turns: result.metadata?.turnsCount,
446
- num_tool_calls: result.metadata?.toolCallsCount,
447
- total_tokens: result.metadata?.totalTokens,
448
- }) + '\n');
449
- } else {
450
- process.stdout.write(finalMessage + '\n');
451
- }
452
- process.exit(0);
453
- } catch (error) {
454
- process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
455
- process.exit(1);
456
- }
457
- }
458
-
459
- // 初始化主题(从用户配置加载,或自动检测终端颜色模
460
- themeManager.initializeFromConfig();
461
-
462
- // CLI 参数覆盖(如果指定
463
- if (args.theme && themeManager.hasTheme(args.theme)) {
464
- themeManager.setTheme(args.theme);
465
- if (isDebugMode) {
466
- console.log('[DEBUG] Theme overridden by CLI to:', args.theme);
467
- }
468
- } else if (isDebugMode) {
469
- console.log('[DEBUG] Theme:', themeManager.getCurrentThemeName());
470
- }
471
-
472
- // Check for --plain flag (plain text mode, no Ink)
473
- const isPlain = args.plain === true;
474
- if (isDebugMode) console.log('[DEBUG] Plain mode:', isPlain);
475
-
476
- if (isPlain) {
477
- // Plain text mode — just relay input/output
478
- if (initialMessage) {
479
- console.log(initialMessage);
480
- }
481
- const { createInterface } = await import('node:readline');
482
- const rl = createInterface({ input: process.stdin, output: process.stdout });
483
- rl.on('line', (line) => {
484
- if (line.trim()) console.log(line);
485
- });
486
- rl.on('close', () => process.exit(0));
487
- return;
488
- }
489
-
490
- // Ink rendering — with fallback for non-TTY environments
491
- const isTTY = process.stdin.isTTY === true && process.stdout.isTTY === true;
492
- if (isDebugMode) console.log('[DEBUG] TTY:', isTTY, '(stdin:', process.stdin.isTTY, 'stdout:', process.stdout.isTTY + ')');
493
-
494
- // Auto-start rendering debugger if --debug-rendering is set
495
- if (ENABLE_RENDER_DEBUG) {
496
- const { startRenderDebugger } = await import('./ui/render-debugger.js');
497
- startRenderDebugger({ reportInterval: 5000, verbose: false });
498
- }
499
-
500
- // Create proper stdin/stdout for Ink (mock if needed to prevent raw mode errors)
501
- let renderStdin = process.stdin;
502
- let renderStdout = process.stdout;
503
-
504
- // Force isTTY on stdout so Ink renders properly in all terminal environments
505
- if (!process.stdout.isTTY) {
506
- (process.stdout as any).isTTY = true;
507
- if (isDebugMode) console.log('[DEBUG] Forcing isTTY=true on stdout');
508
- }
509
-
510
- if (!isTTY) {
511
- const { PassThrough } = await import('node:stream');
512
- const mockStdin = new PassThrough();
513
- (mockStdin as any).isTTY = true;
514
- (mockStdin as any).setRawMode = () => {};
515
- (mockStdin as any).ref = () => {};
516
- (mockStdin as any).unref = () => {};
517
- (mockStdin as any).setEncoding = () => {};
518
- renderStdin = mockStdin as unknown as typeof process.stdin;
519
- if (isDebugMode) console.log('[DEBUG] Using mock stdin for Ink render');
520
- }
521
-
522
- // Start background services (fire-and-forget, silent on failure)
523
- const { startCostLedger } = await import('./services/CostLedger.js');
524
- startCostLedger();
525
- const { startHeartbeat } = await import('./services/Heartbeat.js');
526
- startHeartbeat();
527
-
528
- try {
529
- const { render } = await import('ink');
530
- render(
531
- <App
532
- apiKey={modelConfig.apiKey ?? ''}
533
- baseURL={modelConfig.baseURL}
534
- model={modelConfig.model}
535
- initialMessage={initialMessage}
536
- debug={args.debug}
537
- permissionMode={args.permissionMode}
538
- versionCheckPromise={versionCheckPromise}
539
- resumeSessionId={resumeSessionId}
540
- routerEnabled={args.router}
541
- />,
542
- {
543
- exitOnCtrlC: false,
544
- patchConsole: true,
545
- stdin: renderStdin,
546
- stdout: process.stdout,
547
- // Disabled: the alt screen buffer blocks normal mouse text selection/copy
548
- // in most terminal emulators. Real Claude Code renders inline instead.
549
- alternateScreen: false,
550
- maxFps: 30,
551
- // Without this, Ink's default log-update mode erases and rewrites the
552
- // ENTIRE terminal output on every re-render, even when only a single
553
- // line changed (e.g. the input cursor blink, every ~530ms, forever).
554
- // That full erase+rewrite invalidates any in-progress mouse text
555
- // selection in terminals like Kitty — selecting a message becomes
556
- // impossible because the screen keeps getting wiped out from under it.
557
- // incrementalRendering does real line-level diffing and only rewrites
558
- // lines that actually changed, leaving untouched lines (and any
559
- // selection on them) alone.
560
- incrementalRendering: true,
561
- },
562
- );
563
-
564
- // Handle EOF (Ctrl+D) on real stdin to allow normal terminal closing
565
- if (isTTY && process.stdin.isTTY) {
566
- process.stdin.on('end', () => {
567
- if (isDebugMode) console.log('[DEBUG] EOF received on stdin');
568
- process.exit(0);
569
- });
570
- }
571
- } catch (renderError) {
572
- // Ink rendering failed, fall back to simple text mode
573
- if (isDebugMode) console.log('[DEBUG] Ink rendering failed:', (renderError as Error).message, '- falling back to text mode');
574
- if (initialMessage) {
575
- console.log(initialMessage);
576
- }
577
- const { createInterface } = await import('node:readline');
578
- const rl = createInterface({ input: process.stdin, output: process.stdout });
579
- rl.on('line', (line) => {
580
- if (line.trim()) console.log(line);
581
- });
582
- rl.on('close', () => process.exit(0));
583
- }
584
- })
585
- await cli.parse()
586
- }
587
-
588
- // 运行主函
589
- main().catch((error) => {
590
- console.error('Fatal error:', error.message)
591
- if (isDebugMode && error.stack) {
592
- console.error('\nStack trace:')
593
- console.error(error.stack)
594
- }
595
- process.exit(1)
596
- })