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.
- package/bin/aegis.js +165 -0
- package/package.json +18 -86
- package/LICENSE +0 -21
- package/README.md +0 -394
- 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,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* /connect gmail — Link Gmail to AEGIS conversations
|
|
3
|
-
* Saves conversations as Gmail labels/threads
|
|
4
|
-
*/
|
|
5
|
-
import { exec } from 'child_process';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import * as os from 'os';
|
|
9
|
-
import * as http from 'http';
|
|
10
|
-
|
|
11
|
-
const CONFIG_PATH = path.join(os.homedir(), '.aegiscode', 'config.json');
|
|
12
|
-
const C = {
|
|
13
|
-
teal: '\x1b[38;2;0;229;192m',
|
|
14
|
-
purple: '\x1b[38;2;124;111;212m',
|
|
15
|
-
green: '\x1b[38;2;34;197;94m',
|
|
16
|
-
red: '\x1b[38;2;239;68;68m',
|
|
17
|
-
muted: '\x1b[38;2;68;64;90m',
|
|
18
|
-
bold: '\x1b[1m',
|
|
19
|
-
reset: '\x1b[0m',
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function getConfig() {
|
|
23
|
-
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); }
|
|
24
|
-
catch { return {}; }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function saveConfig(cfg: any) {
|
|
28
|
-
fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export async function runGmail(args: string) {
|
|
32
|
-
const W = process.stdout.columns || 80;
|
|
33
|
-
const line = '─'.repeat(W);
|
|
34
|
-
const cmd = args.trim().toLowerCase();
|
|
35
|
-
const cfg = getConfig();
|
|
36
|
-
|
|
37
|
-
// Status
|
|
38
|
-
if (!cmd || cmd === 'status') {
|
|
39
|
-
console.log(`\n${C.teal}${line}${C.reset}`);
|
|
40
|
-
console.log(`${C.teal}${C.bold} Gmail Integration${C.reset}`);
|
|
41
|
-
console.log(`${C.teal}${line}${C.reset}\n`);
|
|
42
|
-
|
|
43
|
-
if (cfg.gmail?.connected) {
|
|
44
|
-
console.log(` ${C.green}✓ Connected${C.reset} ${C.muted}${cfg.gmail.email}${C.reset}`);
|
|
45
|
-
console.log(` ${C.muted}Conversations saved to Gmail label: AEGIS${C.reset}\n`);
|
|
46
|
-
console.log(` /connect gmail disconnect — remove Gmail access`);
|
|
47
|
-
} else {
|
|
48
|
-
console.log(` ${C.muted}Not connected${C.reset}\n`);
|
|
49
|
-
console.log(` ${C.teal}What you get:${C.reset}`);
|
|
50
|
-
console.log(` ${C.muted}• Conversations saved to Gmail as searchable threads${C.reset}`);
|
|
51
|
-
console.log(` ${C.muted}• Access your AEGIS history from any device${C.reset}`);
|
|
52
|
-
console.log(` ${C.muted}• Gmail search: label:AEGIS${C.reset}\n`);
|
|
53
|
-
console.log(` /connect gmail connect — link your Gmail`);
|
|
54
|
-
}
|
|
55
|
-
console.log(`\n${C.muted}${line}${C.reset}\n`);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Connect
|
|
60
|
-
if (cmd === 'connect') {
|
|
61
|
-
const authUrl = 'https://aegiscloud.org/auth/google?scope=gmail&redirect=cli';
|
|
62
|
-
console.log(`\n ${C.teal}Opening Gmail authorization...${C.reset}`);
|
|
63
|
-
console.log(` ${C.muted}${authUrl}${C.reset}\n`);
|
|
64
|
-
exec(`xdg-open "${authUrl}"`, () => {});
|
|
65
|
-
|
|
66
|
-
// Start local callback server
|
|
67
|
-
console.log(` ${C.muted}Waiting for authorization...${C.reset}`);
|
|
68
|
-
const token = await waitForCallback();
|
|
69
|
-
if (token) {
|
|
70
|
-
cfg.gmail = { connected: true, token, email: token.email || 'connected' };
|
|
71
|
-
saveConfig(cfg);
|
|
72
|
-
console.log(`\n ${C.green}✓ Gmail connected!${C.reset}`);
|
|
73
|
-
console.log(` ${C.muted}Conversations will be saved to Gmail label: AEGIS${C.reset}\n`);
|
|
74
|
-
} else {
|
|
75
|
-
console.log(`\n ${C.red}Authorization failed or timed out${C.reset}\n`);
|
|
76
|
-
}
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Disconnect
|
|
81
|
-
if (cmd === 'disconnect') {
|
|
82
|
-
delete cfg.gmail;
|
|
83
|
-
saveConfig(cfg);
|
|
84
|
-
console.log(`\n ${C.muted}Gmail disconnected${C.reset}\n`);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function waitForCallback(): Promise<any> {
|
|
90
|
-
return new Promise((resolve) => {
|
|
91
|
-
const server = http.createServer((req, res) => {
|
|
92
|
-
const url = new URL(req.url || '/', 'http://localhost:9876');
|
|
93
|
-
if (url.pathname === '/callback') {
|
|
94
|
-
const token = url.searchParams.get('token');
|
|
95
|
-
const email = url.searchParams.get('email');
|
|
96
|
-
res.writeHead(200, {'Content-Type': 'text/html'});
|
|
97
|
-
res.end('<html><body><h2>✓ AEGIS Gmail connected!</h2><p>You can close this tab.</p></body></html>');
|
|
98
|
-
server.close();
|
|
99
|
-
resolve(token ? { token, email } : null);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
server.listen(9876, () => {});
|
|
103
|
-
setTimeout(() => { server.close(); resolve(null); }, 120000);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slash 命令模块
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import Fuse from 'fuse.js';
|
|
8
|
-
import type {
|
|
9
|
-
SlashCommand,
|
|
10
|
-
SlashCommandContext,
|
|
11
|
-
SlashCommandResult,
|
|
12
|
-
CommandSuggestion,
|
|
13
|
-
SlashCommandRegistry,
|
|
14
|
-
CustomCommand,
|
|
15
|
-
} from './types.js';
|
|
16
|
-
import { builtinCommands } from './builtinCommands.js';
|
|
17
|
-
import { mcpCommand } from './mcpCommand.js';
|
|
18
|
-
import { CustomCommandRegistry } from './custom/CustomCommandRegistry.js';
|
|
19
|
-
|
|
20
|
-
// 导出类
|
|
21
|
-
export * from './types.js';
|
|
22
|
-
export { mcpCommand } from './mcpCommand.js';
|
|
23
|
-
export { builtinCommands } from './builtinCommands.js';
|
|
24
|
-
export { CustomCommandRegistry } from './custom/CustomCommandRegistry.js';
|
|
25
|
-
|
|
26
|
-
// ==================== 命令注册
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
*
|
|
30
|
-
*/
|
|
31
|
-
const commandRegistry: SlashCommandRegistry = {};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
-
*/
|
|
36
|
-
let customCommandsInitialized = false;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
*
|
|
40
|
-
*/
|
|
41
|
-
function initializeRegistry(): void {
|
|
42
|
-
// 注册内置命
|
|
43
|
-
for (const cmd of builtinCommands) {
|
|
44
|
-
commandRegistry[cmd.name] = cmd;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 注册 MCP 命
|
|
48
|
-
commandRegistry[mcpCommand.name] = mcpCommand;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 初始化内置命
|
|
52
|
-
initializeRegistry();
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* - ~/.aegis/commands/
|
|
59
|
-
* - ~/.claude/commands/
|
|
60
|
-
* - .aegis/commands/
|
|
61
|
-
* - .claude/commands/
|
|
62
|
-
*/
|
|
63
|
-
export async function initializeCustomCommands(workspaceRoot?: string): Promise<{
|
|
64
|
-
count: number;
|
|
65
|
-
warnings: string[];
|
|
66
|
-
}> {
|
|
67
|
-
if (customCommandsInitialized) {
|
|
68
|
-
return { count: 0, warnings: [] };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const cwd = workspaceRoot || process.cwd();
|
|
72
|
-
const registry = CustomCommandRegistry.getInstance();
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const result = await registry.initialize(cwd);
|
|
76
|
-
|
|
77
|
-
// 将自定义命令转换为 SlashCommand 并注
|
|
78
|
-
for (const customCmd of result.commands) {
|
|
79
|
-
const slashCmd = convertCustomToSlashCommand(customCmd, registry);
|
|
80
|
-
commandRegistry[customCmd.name] = slashCmd;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
customCommandsInitialized = true;
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
count: result.commands.length,
|
|
87
|
-
warnings: result.warnings,
|
|
88
|
-
};
|
|
89
|
-
} catch (error) {
|
|
90
|
-
return {
|
|
91
|
-
count: 0,
|
|
92
|
-
warnings: [`自定义命令初始化失败: ${error instanceof Error ? error.message : String(error)}`],
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
*
|
|
99
|
-
*/
|
|
100
|
-
function convertCustomToSlashCommand(
|
|
101
|
-
customCmd: CustomCommand,
|
|
102
|
-
registry: CustomCommandRegistry
|
|
103
|
-
): SlashCommand {
|
|
104
|
-
const label = registry.getCommandLabel(customCmd);
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
name: customCmd.name,
|
|
108
|
-
description: customCmd.config.description || `自定义命令 ${label}`,
|
|
109
|
-
fullDescription: customCmd.config.description,
|
|
110
|
-
usage: customCmd.config.argumentHint
|
|
111
|
-
? `/${customCmd.name} ${customCmd.config.argumentHint}`
|
|
112
|
-
: `/${customCmd.name}`,
|
|
113
|
-
category: 'custom',
|
|
114
|
-
|
|
115
|
-
async handler(args: string, context: SlashCommandContext): Promise<SlashCommandResult> {
|
|
116
|
-
try {
|
|
117
|
-
// 解析参
|
|
118
|
-
const argArray = args.trim() ? args.trim().split(/\s+/) : [];
|
|
119
|
-
|
|
120
|
-
// 执行自定义命
|
|
121
|
-
const content = await registry.executeCommand(customCmd.name, {
|
|
122
|
-
args: argArray,
|
|
123
|
-
workspaceRoot: context.cwd,
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
if (content === null) {
|
|
127
|
-
return {
|
|
128
|
-
success: false,
|
|
129
|
-
type: 'error',
|
|
130
|
-
error: `命令 /${customCmd.name} 未找到`,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 自定义命令:内容发送给 Agent 执行(对齐 Claude Code 设
|
|
135
|
-
return {
|
|
136
|
-
success: true,
|
|
137
|
-
type: 'success',
|
|
138
|
-
content: content,
|
|
139
|
-
sendToAgent: true,
|
|
140
|
-
};
|
|
141
|
-
} catch (error) {
|
|
142
|
-
return {
|
|
143
|
-
success: false,
|
|
144
|
-
type: 'error',
|
|
145
|
-
error: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
*
|
|
154
|
-
*/
|
|
155
|
-
export async function reloadCustomCommands(workspaceRoot?: string): Promise<{
|
|
156
|
-
count: number;
|
|
157
|
-
warnings: string[];
|
|
158
|
-
}> {
|
|
159
|
-
// 移除现有的自定义命
|
|
160
|
-
for (const [name, cmd] of Object.entries(commandRegistry)) {
|
|
161
|
-
if (cmd.category === 'custom') {
|
|
162
|
-
delete commandRegistry[name];
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
customCommandsInitialized = false;
|
|
167
|
-
return initializeCustomCommands(workspaceRoot);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// ==================== 命令查
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
*
|
|
174
|
-
*/
|
|
175
|
-
export function getCommand(name: string): SlashCommand | undefined {
|
|
176
|
-
const normalizedName = name.toLowerCase();
|
|
177
|
-
|
|
178
|
-
// 直接匹
|
|
179
|
-
if (commandRegistry[normalizedName]) {
|
|
180
|
-
return commandRegistry[normalizedName];
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// 按别名查
|
|
184
|
-
for (const cmd of Object.values(commandRegistry)) {
|
|
185
|
-
if (cmd.aliases?.includes(normalizedName)) {
|
|
186
|
-
return cmd;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return undefined;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
*
|
|
195
|
-
*/
|
|
196
|
-
export function getRegisteredCommands(): SlashCommand[] {
|
|
197
|
-
return Object.values(commandRegistry);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
*
|
|
202
|
-
*/
|
|
203
|
-
export function registerSlashCommand(command: SlashCommand): void {
|
|
204
|
-
commandRegistry[command.name] = command;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
*
|
|
209
|
-
*/
|
|
210
|
-
export function unregisterSlashCommand(name: string): boolean {
|
|
211
|
-
if (commandRegistry[name]) {
|
|
212
|
-
delete commandRegistry[name];
|
|
213
|
-
return true;
|
|
214
|
-
}
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// ==================== 命令解析与执
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
*
|
|
222
|
-
*/
|
|
223
|
-
export function isSlashCommand(input: string): boolean {
|
|
224
|
-
const trimmed = input.trim();
|
|
225
|
-
if (!trimmed.startsWith('/')) return false;
|
|
226
|
-
|
|
227
|
-
// A real command name is a single word ("/help", "/model"); filesystem
|
|
228
|
-
// paths ("/home/neo/foo") also start with '/' but contain further '/'
|
|
229
|
-
// in that first token — treat those as plain chat text, not a command.
|
|
230
|
-
const firstToken = trimmed.slice(1).split(/\s/)[0];
|
|
231
|
-
return firstToken.length > 0 && !firstToken.includes('/');
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
*
|
|
236
|
-
*/
|
|
237
|
-
export function parseSlashCommand(input: string): { name: string; args: string } | null {
|
|
238
|
-
const trimmed = input.trim();
|
|
239
|
-
if (!trimmed.startsWith('/')) {
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const withoutSlash = trimmed.slice(1);
|
|
244
|
-
const wsIndex = withoutSlash.search(/\s/);
|
|
245
|
-
|
|
246
|
-
if (wsIndex === -1) {
|
|
247
|
-
return { name: withoutSlash, args: '' };
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
name: withoutSlash.slice(0, wsIndex),
|
|
252
|
-
args: withoutSlash.slice(wsIndex + 1),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
*
|
|
258
|
-
*/
|
|
259
|
-
export async function executeSlashCommand(
|
|
260
|
-
input: string,
|
|
261
|
-
context: SlashCommandContext
|
|
262
|
-
): Promise<SlashCommandResult> {
|
|
263
|
-
const parsed = parseSlashCommand(input);
|
|
264
|
-
|
|
265
|
-
if (!parsed) {
|
|
266
|
-
return {
|
|
267
|
-
success: false,
|
|
268
|
-
type: 'error',
|
|
269
|
-
error: '无效的命令格式',
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const { name, args } = parsed;
|
|
274
|
-
const command = getCommand(name);
|
|
275
|
-
|
|
276
|
-
if (!command) {
|
|
277
|
-
// 尝试模糊匹配建
|
|
278
|
-
const suggestions = getFuzzyCommandSuggestions(name);
|
|
279
|
-
let errorMsg = `Unknown command: /${name}`;
|
|
280
|
-
|
|
281
|
-
if (suggestions.length > 0) {
|
|
282
|
-
errorMsg += `\n\nDid you mean:\n`;
|
|
283
|
-
for (const s of suggestions.slice(0, 3)) {
|
|
284
|
-
errorMsg += `- \`${s.command}\` - ${s.description}\n`;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
errorMsg += `\nUse \`/help\` to see available commands`;
|
|
289
|
-
|
|
290
|
-
return {
|
|
291
|
-
success: false,
|
|
292
|
-
type: 'error',
|
|
293
|
-
error: errorMsg,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
try {
|
|
298
|
-
return await command.handler(args, context);
|
|
299
|
-
} catch (error) {
|
|
300
|
-
return {
|
|
301
|
-
success: false,
|
|
302
|
-
type: 'error',
|
|
303
|
-
error: `Command failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// ==================== 模糊匹
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
*
|
|
312
|
-
*/
|
|
313
|
-
export function getFuzzyCommandSuggestions(input: string): CommandSuggestion[] {
|
|
314
|
-
const query = (input.startsWith('/') ? input.slice(1) : input).trim().toLowerCase();
|
|
315
|
-
|
|
316
|
-
const searchableCommands = Object.values(commandRegistry).map((cmd) => ({
|
|
317
|
-
name: cmd.name,
|
|
318
|
-
description: cmd.description,
|
|
319
|
-
aliases: cmd.aliases || [],
|
|
320
|
-
}));
|
|
321
|
-
|
|
322
|
-
// 没有输入,返回所有命
|
|
323
|
-
if (!query) {
|
|
324
|
-
return searchableCommands.map((item) => ({
|
|
325
|
-
command: `/${item.name}`,
|
|
326
|
-
description: item.description,
|
|
327
|
-
matchScore: 50,
|
|
328
|
-
}));
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// 精确前缀匹配优
|
|
332
|
-
const prefixMatches = searchableCommands.filter(
|
|
333
|
-
(cmd) => cmd.name.startsWith(query) || cmd.aliases.some((a) => a.startsWith(query))
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
if (prefixMatches.length > 0) {
|
|
337
|
-
return prefixMatches.map((item) => ({
|
|
338
|
-
command: `/${item.name}`,
|
|
339
|
-
description: item.description,
|
|
340
|
-
matchScore: 90,
|
|
341
|
-
}));
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// 使用 Fuse.js 模糊匹
|
|
345
|
-
const fuse = new Fuse(searchableCommands, {
|
|
346
|
-
keys: [
|
|
347
|
-
{ name: 'name', weight: 3 },
|
|
348
|
-
{ name: 'aliases', weight: 2.5 },
|
|
349
|
-
{ name: 'description', weight: 0.5 },
|
|
350
|
-
],
|
|
351
|
-
threshold: 0.4,
|
|
352
|
-
includeScore: true,
|
|
353
|
-
ignoreLocation: true,
|
|
354
|
-
minMatchCharLength: 1,
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
const results = fuse.search(query);
|
|
358
|
-
|
|
359
|
-
return results
|
|
360
|
-
.map((result) => ({
|
|
361
|
-
command: `/${result.item.name}`,
|
|
362
|
-
description: result.item.description,
|
|
363
|
-
matchScore: Math.round((1 - (result.score ?? 1)) * 100),
|
|
364
|
-
}))
|
|
365
|
-
.filter((s) => (s.matchScore || 0) >= 40);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
*
|
|
370
|
-
*/
|
|
371
|
-
export function getCommandCompletions(partialInput: string): CommandSuggestion[] {
|
|
372
|
-
if (!partialInput.startsWith('/')) {
|
|
373
|
-
return [];
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const query = partialInput.slice(1).toLowerCase();
|
|
377
|
-
|
|
378
|
-
if (!query) {
|
|
379
|
-
// 返回所有命
|
|
380
|
-
return Object.values(commandRegistry).map((cmd) => ({
|
|
381
|
-
command: `/${cmd.name}`,
|
|
382
|
-
description: cmd.description,
|
|
383
|
-
matchScore: 50,
|
|
384
|
-
}));
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return getFuzzyCommandSuggestions(query);
|
|
388
|
-
}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* /mcp 命令 - 显示 MCP 服务器状态和工具
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { SlashCommand, SlashCommandResult } from './types.js';
|
|
6
|
-
import { McpRegistry, McpConnectionStatus } from '../mcp/index.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
function getStatusMark(status: McpConnectionStatus): string {
|
|
12
|
-
switch (status) {
|
|
13
|
-
case McpConnectionStatus.CONNECTED:
|
|
14
|
-
return '+';
|
|
15
|
-
case McpConnectionStatus.CONNECTING:
|
|
16
|
-
return '~';
|
|
17
|
-
case McpConnectionStatus.ERROR:
|
|
18
|
-
return 'x';
|
|
19
|
-
case McpConnectionStatus.DISCONNECTED:
|
|
20
|
-
default:
|
|
21
|
-
return '-';
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
27
|
-
*/
|
|
28
|
-
function formatTime(date: Date | undefined): string {
|
|
29
|
-
if (!date) return '-';
|
|
30
|
-
return date.toLocaleTimeString();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* /mcp 命令实现
|
|
35
|
-
*/
|
|
36
|
-
export const mcpCommand: SlashCommand = {
|
|
37
|
-
name: 'mcp',
|
|
38
|
-
description: 'Show MCP server status and tools',
|
|
39
|
-
usage: '/mcp [tools|<server-name>]',
|
|
40
|
-
|
|
41
|
-
async handler(args): Promise<SlashCommandResult> {
|
|
42
|
-
const mcpRegistry = McpRegistry.getInstance();
|
|
43
|
-
const stats = mcpRegistry.getStatistics();
|
|
44
|
-
const servers = mcpRegistry.getAllServers();
|
|
45
|
-
|
|
46
|
-
// 没有配置任何 MCP 服务
|
|
47
|
-
if (stats.totalServers === 0) {
|
|
48
|
-
return {
|
|
49
|
-
success: true,
|
|
50
|
-
type: 'info',
|
|
51
|
-
content: `## mcp
|
|
52
|
-
|
|
53
|
-
no servers configured.
|
|
54
|
-
|
|
55
|
-
add \`mcpServers\` to config:
|
|
56
|
-
|
|
57
|
-
\`\`\`json
|
|
58
|
-
// ~/.aegis/config.json
|
|
59
|
-
{
|
|
60
|
-
"mcpServers": {
|
|
61
|
-
"github": {
|
|
62
|
-
"type": "stdio",
|
|
63
|
-
"command": "npx",
|
|
64
|
-
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
65
|
-
"env": {
|
|
66
|
-
"GITHUB_TOKEN": "\${GITHUB_TOKEN}"
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
\`\`\`
|
|
72
|
-
`,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const trimmedArgs = args.trim().toLowerCase();
|
|
77
|
-
|
|
78
|
-
// /mcp tools - 列出所有工具
|
|
79
|
-
if (trimmedArgs === 'tools') {
|
|
80
|
-
return await handleToolsSubcommand(mcpRegistry);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// /mcp <server-name> - 显示特定服务器详情
|
|
84
|
-
if (trimmedArgs && trimmedArgs !== '') {
|
|
85
|
-
const serverInfo = mcpRegistry.getServer(trimmedArgs);
|
|
86
|
-
if (serverInfo) {
|
|
87
|
-
return handleServerDetail(trimmedArgs, serverInfo);
|
|
88
|
-
}
|
|
89
|
-
// 服务器不存在,显示概
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 默认:显示概
|
|
93
|
-
let output = '## mcp\n\n';
|
|
94
|
-
output += `| key | value |\n`;
|
|
95
|
-
output += `|-----|-------|\n`;
|
|
96
|
-
output += `| servers | ${stats.totalServers} |\n`;
|
|
97
|
-
output += `| connected | ${stats.connectedServers} |\n`;
|
|
98
|
-
output += `| errors | ${stats.errorServers} |\n`;
|
|
99
|
-
output += `| tools | ${stats.totalTools} |\n`;
|
|
100
|
-
output += '\n';
|
|
101
|
-
|
|
102
|
-
output += '### servers\n\n';
|
|
103
|
-
output += '| st | server | tools | connected |\n';
|
|
104
|
-
output += '|----|--------|-------|-----------|\n';
|
|
105
|
-
|
|
106
|
-
for (const [name, info] of servers) {
|
|
107
|
-
const mark = getStatusMark(info.status);
|
|
108
|
-
const toolCount = info.status === McpConnectionStatus.CONNECTED ? info.tools.length : '-';
|
|
109
|
-
const connectedAt = formatTime(info.connectedAt);
|
|
110
|
-
output += `| ${mark} | ${name} | ${toolCount} | ${connectedAt} |\n`;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
output += `\n\`/mcp tools\` to list all tools · \`/mcp <name>\` for details\n`;
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
success: true,
|
|
117
|
-
type: 'success',
|
|
118
|
-
content: output,
|
|
119
|
-
};
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
*
|
|
125
|
-
*/
|
|
126
|
-
async function handleToolsSubcommand(registry: McpRegistry): Promise<SlashCommandResult> {
|
|
127
|
-
const tools = await registry.getAvailableTools();
|
|
128
|
-
|
|
129
|
-
if (tools.length === 0) {
|
|
130
|
-
return {
|
|
131
|
-
success: true,
|
|
132
|
-
type: 'info',
|
|
133
|
-
content: '## mcp tools\n\nno tools available. ensure at least one server is connected.',
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
let output = `## mcp tools (${tools.length})\n\n`;
|
|
138
|
-
output += '| tool | description | type |\n';
|
|
139
|
-
output += '|------|-------------|------|\n';
|
|
140
|
-
|
|
141
|
-
for (const tool of tools) {
|
|
142
|
-
const description = tool.description?.short || '-';
|
|
143
|
-
const category = tool.category || 'mcp';
|
|
144
|
-
const shortDesc = description.length > 50 ? description.slice(0, 47) + '...' : description;
|
|
145
|
-
output += `| \`${tool.name}\` | ${shortDesc} | ${category} |\n`;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
success: true,
|
|
150
|
-
type: 'success',
|
|
151
|
-
content: output,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
*
|
|
157
|
-
*/
|
|
158
|
-
function handleServerDetail(name: string, info: any): SlashCommandResult {
|
|
159
|
-
let output = `## mcp: ${name}\n\n`;
|
|
160
|
-
|
|
161
|
-
output += `| key | value |\n`;
|
|
162
|
-
output += `|-----|-------|\n`;
|
|
163
|
-
output += `| status | ${info.status} |\n`;
|
|
164
|
-
output += `| type | ${info.config.type} |\n`;
|
|
165
|
-
|
|
166
|
-
if (info.serverName) {
|
|
167
|
-
output += `| name | ${info.serverName} |\n`;
|
|
168
|
-
}
|
|
169
|
-
if (info.serverVersion) {
|
|
170
|
-
output += `| version | ${info.serverVersion} |\n`;
|
|
171
|
-
}
|
|
172
|
-
if (info.connectedAt) {
|
|
173
|
-
output += `| connected | ${info.connectedAt.toLocaleString()} |\n`;
|
|
174
|
-
}
|
|
175
|
-
if (info.lastError) {
|
|
176
|
-
output += `| error | ${info.lastError.message} |\n`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
output += '\n### config\n\n';
|
|
180
|
-
output += '```json\n';
|
|
181
|
-
output += JSON.stringify(
|
|
182
|
-
{
|
|
183
|
-
type: info.config.type,
|
|
184
|
-
command: info.config.command,
|
|
185
|
-
args: info.config.args,
|
|
186
|
-
url: info.config.url,
|
|
187
|
-
},
|
|
188
|
-
null,
|
|
189
|
-
2
|
|
190
|
-
);
|
|
191
|
-
output += '\n```\n';
|
|
192
|
-
|
|
193
|
-
if (info.status === McpConnectionStatus.CONNECTED && info.tools.length > 0) {
|
|
194
|
-
output += '\n### tools\n\n';
|
|
195
|
-
for (const tool of info.tools) {
|
|
196
|
-
output += `- \`${tool.name}\` ${tool.description || '-'}\n`;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
success: true,
|
|
202
|
-
type: 'success',
|
|
203
|
-
content: output,
|
|
204
|
-
};
|
|
205
|
-
}
|