aegiscode 3.1.8 → 3.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -15
- package/dist/main.js +549 -552
- package/package.json +4 -2
- package/src/agent/Agent.ts +903 -0
- package/src/agent/SimpleAgent.ts +48 -0
- package/src/agent/index.ts +54 -0
- package/src/agent/orchestrator/AppBuilder.ts +443 -0
- package/src/agent/orchestrator/CouncilAgent.ts +310 -0
- package/src/agent/orchestrator/DiscussionRoom.ts +462 -0
- package/src/agent/orchestrator/OrchestratorAgent.ts +637 -0
- package/src/agent/orchestrator/index.ts +38 -0
- package/src/agent/orchestrator/utils.ts +397 -0
- package/src/agent/pricing.ts +115 -0
- package/src/agent/router.ts +74 -0
- package/src/agent/routerStats.ts +121 -0
- package/src/agent/types.ts +318 -0
- package/src/auth/login.ts +383 -0
- package/src/cli/config.ts +189 -0
- package/src/cli/index.ts +17 -0
- package/src/cli/middleware.ts +119 -0
- package/src/cli/types.ts +75 -0
- package/src/config/ConfigManager.ts +587 -0
- package/src/config/index.ts +7 -0
- package/src/config/types.ts +584 -0
- package/src/context/CompactionService.ts +300 -0
- package/src/context/ContextManager.ts +450 -0
- package/src/context/FileAnalyzer.ts +267 -0
- package/src/context/TokenCounter.ts +265 -0
- package/src/context/index.ts +27 -0
- package/src/context/storage/CacheStore.ts +176 -0
- package/src/context/storage/JSONLStore.ts +201 -0
- package/src/context/storage/MemoryStore.ts +205 -0
- package/src/context/storage/PersistentStore.ts +327 -0
- package/src/context/storage/index.ts +9 -0
- package/src/context/storage/pathUtils.ts +114 -0
- package/src/context/test.ts +309 -0
- package/src/context/types.ts +268 -0
- package/src/hooks/HookExecutor.ts +434 -0
- package/src/hooks/HookManager.ts +596 -0
- package/src/hooks/HookService.ts +269 -0
- package/src/hooks/Matcher.ts +157 -0
- package/src/hooks/index.ts +63 -0
- package/src/hooks/types.ts +424 -0
- package/src/main.tsx +596 -0
- package/src/mcp/HealthMonitor.ts +150 -0
- package/src/mcp/McpClient.ts +491 -0
- package/src/mcp/McpRegistry.ts +321 -0
- package/src/mcp/createMcpTool.ts +251 -0
- package/src/mcp/index.ts +15 -0
- package/src/mcp/server.ts +334 -0
- package/src/mcp/test-server.ts +88 -0
- package/src/mcp/test.ts +372 -0
- package/src/mcp/types.ts +247 -0
- package/src/memory/AgentMemoryBus.ts +432 -0
- package/src/memory/CloudSync.ts +99 -0
- package/src/memory/DriveSync.ts +106 -0
- package/src/memory/SharedMemory.ts +951 -0
- package/src/memory/index.ts +14 -0
- package/src/memory/machineFingerprint.ts +40 -0
- package/src/orchestrator/SubAgentMetadata.ts +136 -0
- package/src/prompts/builder.ts +213 -0
- package/src/prompts/default.ts +144 -0
- package/src/prompts/index.ts +16 -0
- package/src/prompts/plan.ts +64 -0
- package/src/prompts/test.ts +78 -0
- package/src/services/AnthropicChatService.ts +341 -0
- package/src/services/ChatService.ts +347 -0
- package/src/services/ClaudeCliChatService.ts +256 -0
- package/src/services/CloudSync.ts +168 -0
- package/src/services/CostLedger.ts +211 -0
- package/src/services/Heartbeat.ts +135 -0
- package/src/services/LearningCollector.ts +291 -0
- package/src/services/OllamaInstaller.ts +342 -0
- package/src/services/VersionChecker.ts +445 -0
- package/src/services/index.ts +57 -0
- package/src/services/streaming/OpenAIEventAdapter.ts +126 -0
- package/src/services/streaming/RenderingProfile.ts +90 -0
- package/src/services/streaming/StreamEventParser.ts +181 -0
- package/src/services/streaming/ThrottledRenderer.ts +139 -0
- package/src/services/streaming/TranscriptBuffer.ts +574 -0
- package/src/services/streaming/eventStatusMap.ts +52 -0
- package/src/services/streaming/index.ts +46 -0
- package/src/services/streaming/renderFormatting.ts +79 -0
- package/src/services/streaming/types.ts +234 -0
- package/src/skills/SkillLoader.ts +126 -0
- package/src/skills/SkillRegistry.ts +366 -0
- package/src/skills/index.ts +48 -0
- package/src/skills/types.ts +146 -0
- package/src/slash-commands/billing.ts +70 -0
- package/src/slash-commands/build.ts +413 -0
- package/src/slash-commands/builtinCommands.ts +2733 -0
- package/src/slash-commands/clone.ts +242 -0
- package/src/slash-commands/council.ts +125 -0
- package/src/slash-commands/custom/CustomCommandExecutor.ts +143 -0
- package/src/slash-commands/custom/CustomCommandLoader.ts +251 -0
- package/src/slash-commands/custom/CustomCommandRegistry.ts +144 -0
- package/src/slash-commands/custom/index.ts +7 -0
- package/src/slash-commands/debate.ts +254 -0
- package/src/slash-commands/gmail.ts +105 -0
- package/src/slash-commands/index.ts +388 -0
- package/src/slash-commands/mcpCommand.ts +205 -0
- package/src/slash-commands/types.ts +201 -0
- package/src/store/index.ts +76 -0
- package/src/store/selectors.ts +246 -0
- package/src/store/slices/appSlice.ts +205 -0
- package/src/store/slices/commandSlice.ts +115 -0
- package/src/store/slices/configSlice.ts +46 -0
- package/src/store/slices/focusSlice.ts +64 -0
- package/src/store/slices/index.ts +9 -0
- package/src/store/slices/sessionSlice.ts +424 -0
- package/src/store/streaming-buffer.ts +425 -0
- package/src/store/test.ts +296 -0
- package/src/store/types.ts +274 -0
- package/src/store/vanilla.ts +186 -0
- package/src/tools/builtin/bash.ts +236 -0
- package/src/tools/builtin/council.ts +105 -0
- package/src/tools/builtin/edit.ts +213 -0
- package/src/tools/builtin/glob.ts +136 -0
- package/src/tools/builtin/grep.ts +263 -0
- package/src/tools/builtin/index.ts +61 -0
- package/src/tools/builtin/memory.ts +66 -0
- package/src/tools/builtin/read.ts +168 -0
- package/src/tools/builtin/skill.ts +97 -0
- package/src/tools/builtin/snapshot.ts +40 -0
- package/src/tools/builtin/task.ts +106 -0
- package/src/tools/builtin/write.ts +134 -0
- package/src/tools/createTool.ts +221 -0
- package/src/tools/execution/ExecutionPipeline.ts +263 -0
- package/src/tools/execution/index.ts +40 -0
- package/src/tools/execution/stages/CacheStage.ts +131 -0
- package/src/tools/execution/stages/ConfirmationStage.ts +203 -0
- package/src/tools/execution/stages/DiscoveryStage.ts +46 -0
- package/src/tools/execution/stages/ExecutionStage.ts +48 -0
- package/src/tools/execution/stages/FormattingStage.ts +44 -0
- package/src/tools/execution/stages/HookStage.ts +72 -0
- package/src/tools/execution/stages/PermissionStage.ts +287 -0
- package/src/tools/execution/stages/PostHookStage.ts +71 -0
- package/src/tools/execution/stages/index.ts +12 -0
- package/src/tools/execution/test.ts +266 -0
- package/src/tools/execution/types.ts +273 -0
- package/src/tools/index.ts +81 -0
- package/src/tools/registry.ts +304 -0
- package/src/tools/schemas.ts +109 -0
- package/src/tools/test.ts +220 -0
- package/src/tools/types.ts +175 -0
- package/src/tools/validation/PermissionChecker.ts +242 -0
- package/src/tools/validation/SensitiveFileDetector.ts +210 -0
- package/src/tools/validation/index.ts +11 -0
- package/src/ui/App.tsx +166 -0
- package/src/ui/components/AegisInterface.tsx +484 -0
- package/src/ui/components/common/ChatSearch.tsx +150 -0
- package/src/ui/components/common/ErrorBoundary.tsx +82 -0
- package/src/ui/components/common/ExitMessage.tsx +120 -0
- package/src/ui/components/common/LoadingIndicator.tsx +49 -0
- package/src/ui/components/common/index.ts +6 -0
- package/src/ui/components/dialog/ConfirmationPrompt.tsx +208 -0
- package/src/ui/components/dialog/InteractiveSelector.tsx +149 -0
- package/src/ui/components/dialog/SetupWizard.tsx +297 -0
- package/src/ui/components/dialog/UpdatePrompt.tsx +155 -0
- package/src/ui/components/dialog/index.ts +8 -0
- package/src/ui/components/index.ts +28 -0
- package/src/ui/components/input/CommandSuggestions.tsx +139 -0
- package/src/ui/components/input/CustomTextInput.tsx +220 -0
- package/src/ui/components/input/InputArea.tsx +361 -0
- package/src/ui/components/input/PromptSuggestions.tsx +66 -0
- package/src/ui/components/input/index.ts +6 -0
- package/src/ui/components/layout/ChatStatusBar.tsx +90 -0
- package/src/ui/components/layout/ContextBar.tsx +79 -0
- package/src/ui/components/layout/MessageArea.tsx +96 -0
- package/src/ui/components/layout/MessageList.tsx +647 -0
- package/src/ui/components/layout/MessageSeparator.tsx +26 -0
- package/src/ui/components/layout/WelcomeMessage.tsx +93 -0
- package/src/ui/components/layout/index.ts +7 -0
- package/src/ui/components/markdown/CodeHighlighter.tsx +292 -0
- package/src/ui/components/markdown/MessageRenderer.tsx +1211 -0
- package/src/ui/components/markdown/index.ts +8 -0
- package/src/ui/components/markdown/parser.ts +336 -0
- package/src/ui/components/markdown/types.ts +66 -0
- package/src/ui/focus/FocusManager.ts +137 -0
- package/src/ui/focus/index.ts +13 -0
- package/src/ui/focus/types.ts +54 -0
- package/src/ui/focus/useFocus.ts +75 -0
- package/src/ui/hooks/index.ts +11 -0
- package/src/ui/hooks/useAgent.ts +284 -0
- package/src/ui/hooks/useCommandHistory.ts +87 -0
- package/src/ui/hooks/useCommandProcessor.ts +443 -0
- package/src/ui/hooks/useConfirmation.ts +99 -0
- package/src/ui/hooks/useCtrlCHandler.ts +100 -0
- package/src/ui/hooks/useInputBuffer.ts +122 -0
- package/src/ui/hooks/useTerminalSize.ts +68 -0
- package/src/ui/hooks/useTerminalWidth.ts +5 -0
- package/src/ui/hooks/useWindowedList.ts +118 -0
- package/src/ui/render-debugger.ts +621 -0
- package/src/ui/test.ts +189 -0
- package/src/ui/themes/ThemeManager.ts +332 -0
- package/src/ui/themes/aegisTheme.ts +87 -0
- package/src/ui/themes/darkTheme.ts +87 -0
- package/src/ui/themes/defaultTheme.ts +85 -0
- package/src/ui/themes/index.ts +10 -0
- package/src/ui/themes/lightTheme.ts +89 -0
- package/src/ui/themes/popularThemes.ts +187 -0
- package/src/ui/themes/types.ts +130 -0
- package/src/utils/clipboard.ts +48 -0
- package/src/utils/debug.ts +43 -0
- package/src/utils/environment.ts +68 -0
- package/src/utils/index.ts +10 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grep 工具 - 内容搜索
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { glob } from 'glob';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { createTool } from '../createTool.js';
|
|
10
|
+
import { ToolKind, ToolErrorType } from '../types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_IGNORE = [
|
|
16
|
+
'**/node_modules/**',
|
|
17
|
+
'**/.git/**',
|
|
18
|
+
'**/dist/**',
|
|
19
|
+
'**/build/**',
|
|
20
|
+
'**/.next/**',
|
|
21
|
+
'**/coverage/**',
|
|
22
|
+
'**/*.min.js',
|
|
23
|
+
'**/*.bundle.js',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
const MAX_RESULTS = 100;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Grep 工具 Schema
|
|
33
|
+
*/
|
|
34
|
+
const GrepSchema = z.object({
|
|
35
|
+
pattern: z.string()
|
|
36
|
+
.min(1, '搜索模式不能为空')
|
|
37
|
+
.describe('The regular expression pattern to search for'),
|
|
38
|
+
|
|
39
|
+
path: z.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe('Directory to search in (defaults to current working directory)'),
|
|
42
|
+
|
|
43
|
+
include: z.string()
|
|
44
|
+
.optional()
|
|
45
|
+
.describe('Glob pattern to filter files (e.g., "*.ts" for TypeScript files)'),
|
|
46
|
+
|
|
47
|
+
case_sensitive: z.boolean()
|
|
48
|
+
.default(true)
|
|
49
|
+
.describe('Whether the search is case sensitive'),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
*/
|
|
55
|
+
interface SearchMatch {
|
|
56
|
+
file: string;
|
|
57
|
+
line: number;
|
|
58
|
+
content: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
*
|
|
63
|
+
*/
|
|
64
|
+
async function searchInFile(
|
|
65
|
+
filePath: string,
|
|
66
|
+
regex: RegExp
|
|
67
|
+
): Promise<SearchMatch[]> {
|
|
68
|
+
try {
|
|
69
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
70
|
+
const lines = content.split('\n');
|
|
71
|
+
const matches: SearchMatch[] = [];
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < lines.length; i++) {
|
|
74
|
+
if (regex.test(lines[i])) {
|
|
75
|
+
matches.push({
|
|
76
|
+
file: filePath,
|
|
77
|
+
line: i + 1,
|
|
78
|
+
content: lines[i].trim(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return matches;
|
|
84
|
+
} catch {
|
|
85
|
+
// 忽略无法读取的文
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Grep 工具
|
|
92
|
+
*/
|
|
93
|
+
export const grepTool = createTool({
|
|
94
|
+
name: 'Grep',
|
|
95
|
+
displayName: 'Content Search',
|
|
96
|
+
kind: ToolKind.ReadOnly,
|
|
97
|
+
schema: GrepSchema,
|
|
98
|
+
|
|
99
|
+
description: {
|
|
100
|
+
short: 'Search file contents using regular expressions',
|
|
101
|
+
long: `A powerful search tool for finding patterns in file contents.
|
|
102
|
+
Supports full regex syntax and can filter files by glob pattern.`,
|
|
103
|
+
usageNotes: [
|
|
104
|
+
'Uses JavaScript regex syntax',
|
|
105
|
+
'Common patterns: "function\\s+\\w+" for function definitions',
|
|
106
|
+
'Use include parameter to filter files: "*.ts" for TypeScript only',
|
|
107
|
+
'Results are limited to 100 matches for performance',
|
|
108
|
+
'Automatically ignores node_modules, .git, dist directories',
|
|
109
|
+
],
|
|
110
|
+
examples: [
|
|
111
|
+
{
|
|
112
|
+
description: 'Search for function definitions',
|
|
113
|
+
params: { pattern: 'function\\s+\\w+' },
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
description: 'Search in TypeScript files only',
|
|
117
|
+
params: { pattern: 'export', include: '*.ts' },
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
description: 'Case-insensitive search',
|
|
121
|
+
params: { pattern: 'todo', case_sensitive: false },
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
category: '搜索',
|
|
127
|
+
tags: ['search', 'grep', 'regex'],
|
|
128
|
+
|
|
129
|
+
async execute(params, context) {
|
|
130
|
+
const { pattern, path: searchPath, include, case_sensitive } = params;
|
|
131
|
+
|
|
132
|
+
// 默认搜索目
|
|
133
|
+
const cwd = searchPath || context?.cwd || process.cwd();
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// 创建正则表达
|
|
137
|
+
// 验证正则表达式是否包含未闭合的字符类或分组
|
|
138
|
+
const unclosedBracket = (pattern.match(/\[/g) || []).length !== (pattern.match(/\]/g) || []).length;
|
|
139
|
+
const unclosedParen = (pattern.match(/\(/g) || []).length !== (pattern.match(/\)/g) || []).length;
|
|
140
|
+
|
|
141
|
+
if (unclosedBracket || unclosedParen) {
|
|
142
|
+
return {
|
|
143
|
+
success: false,
|
|
144
|
+
llmContent: `Invalid regex pattern: ${pattern} — unclosed bracket or parenthesis`,
|
|
145
|
+
displayContent: `error: invalid regex — unclosed bracket or parenthesis`,
|
|
146
|
+
error: {
|
|
147
|
+
type: ToolErrorType.VALIDATION_ERROR,
|
|
148
|
+
message: 'Invalid regex pattern — unclosed bracket or parenthesis',
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let regex: RegExp;
|
|
154
|
+
try {
|
|
155
|
+
regex = new RegExp(pattern, case_sensitive ? 'g' : 'gi');
|
|
156
|
+
} catch (e) {
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
llmContent: `Invalid regex pattern: ${pattern}`,
|
|
160
|
+
displayContent: `error: invalid regex: ${pattern}`,
|
|
161
|
+
error: {
|
|
162
|
+
type: ToolErrorType.VALIDATION_ERROR,
|
|
163
|
+
message: 'Invalid regex pattern',
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 获取要搜索的文件列
|
|
169
|
+
const filePattern = include
|
|
170
|
+
? (include.startsWith('**/') ? include : `**/${include}`)
|
|
171
|
+
: '**/*';
|
|
172
|
+
|
|
173
|
+
const files = await glob(filePattern, {
|
|
174
|
+
cwd,
|
|
175
|
+
ignore: DEFAULT_IGNORE,
|
|
176
|
+
nodir: true,
|
|
177
|
+
absolute: true,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// 过滤二进制文件(简单判
|
|
181
|
+
const textExtensions = [
|
|
182
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
183
|
+
'.json', '.md', '.txt', '.yaml', '.yml',
|
|
184
|
+
'.html', '.css', '.scss', '.less',
|
|
185
|
+
'.py', '.rb', '.go', '.rs', '.java',
|
|
186
|
+
'.c', '.cpp', '.h', '.hpp',
|
|
187
|
+
'.sh', '.bash', '.zsh',
|
|
188
|
+
'.xml', '.svg',
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
const textFiles = files.filter(f =>
|
|
192
|
+
textExtensions.some(ext => f.endsWith(ext))
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// 搜索所有文
|
|
196
|
+
const allMatches: SearchMatch[] = [];
|
|
197
|
+
|
|
198
|
+
for (const file of textFiles) {
|
|
199
|
+
if (allMatches.length >= MAX_RESULTS) break;
|
|
200
|
+
|
|
201
|
+
const matches = await searchInFile(file, regex);
|
|
202
|
+
allMatches.push(...matches);
|
|
203
|
+
|
|
204
|
+
if (allMatches.length >= MAX_RESULTS) {
|
|
205
|
+
allMatches.length = MAX_RESULTS;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (allMatches.length === 0) {
|
|
211
|
+
return {
|
|
212
|
+
success: true,
|
|
213
|
+
llmContent: 'No matches found.',
|
|
214
|
+
displayContent: `no matches: ${pattern}`,
|
|
215
|
+
metadata: {
|
|
216
|
+
pattern,
|
|
217
|
+
cwd,
|
|
218
|
+
files_searched: textFiles.length,
|
|
219
|
+
matches: 0,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 格式化输
|
|
225
|
+
const formattedMatches = allMatches
|
|
226
|
+
.map(m => {
|
|
227
|
+
const relPath = path.relative(cwd, m.file);
|
|
228
|
+
return `${relPath}:${m.line}: ${m.content}`;
|
|
229
|
+
})
|
|
230
|
+
.join('\n');
|
|
231
|
+
|
|
232
|
+
const truncated = allMatches.length >= MAX_RESULTS;
|
|
233
|
+
let summary = `${allMatches.length} match${allMatches.length === 1 ? '' : 'es'}`;
|
|
234
|
+
if (truncated) {
|
|
235
|
+
summary += ` (truncated)`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
success: true,
|
|
240
|
+
llmContent: formattedMatches,
|
|
241
|
+
displayContent: summary,
|
|
242
|
+
metadata: {
|
|
243
|
+
pattern,
|
|
244
|
+
cwd,
|
|
245
|
+
files_searched: textFiles.length,
|
|
246
|
+
matches: allMatches.length,
|
|
247
|
+
truncated,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
} catch (error) {
|
|
251
|
+
const errorMessage = error instanceof Error ? error.message : 'unknown error';
|
|
252
|
+
return {
|
|
253
|
+
success: false,
|
|
254
|
+
llmContent: `Grep search failed: ${errorMessage}`,
|
|
255
|
+
displayContent: `error: ${errorMessage}`,
|
|
256
|
+
error: {
|
|
257
|
+
type: ToolErrorType.EXECUTION_ERROR,
|
|
258
|
+
message: errorMessage,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// 文件工
|
|
6
|
+
export { readTool } from './read.js';
|
|
7
|
+
export { editTool } from './edit.js';
|
|
8
|
+
export { writeTool } from './write.js';
|
|
9
|
+
|
|
10
|
+
// 搜索工
|
|
11
|
+
export { grepTool } from './grep.js';
|
|
12
|
+
export { globTool } from './glob.js';
|
|
13
|
+
|
|
14
|
+
// Shell 工
|
|
15
|
+
export { bashTool } from './bash.js';
|
|
16
|
+
|
|
17
|
+
// Skills 工
|
|
18
|
+
export { skillTool } from './skill.js';
|
|
19
|
+
|
|
20
|
+
// Memory 工
|
|
21
|
+
export { memoryTool } from './memory.js';
|
|
22
|
+
|
|
23
|
+
// Multi-agent orchestration 工
|
|
24
|
+
export { taskTool } from './task.js';
|
|
25
|
+
export { councilTool } from './council.js';
|
|
26
|
+
|
|
27
|
+
import { readTool } from './read.js';
|
|
28
|
+
import { editTool } from './edit.js';
|
|
29
|
+
import { writeTool } from './write.js';
|
|
30
|
+
import { grepTool } from './grep.js';
|
|
31
|
+
import { globTool } from './glob.js';
|
|
32
|
+
import { bashTool } from './bash.js';
|
|
33
|
+
import { skillTool } from './skill.js';
|
|
34
|
+
import { memoryTool } from './memory.js';
|
|
35
|
+
import { taskTool } from './task.js';
|
|
36
|
+
import { councilTool } from './council.js';
|
|
37
|
+
import type { Tool } from '../types.js';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
42
|
+
export function getBuiltinTools(): Tool[] {
|
|
43
|
+
return [
|
|
44
|
+
// 文件工
|
|
45
|
+
readTool,
|
|
46
|
+
editTool,
|
|
47
|
+
writeTool,
|
|
48
|
+
// 搜索工
|
|
49
|
+
grepTool,
|
|
50
|
+
globTool,
|
|
51
|
+
// Shell 工
|
|
52
|
+
bashTool,
|
|
53
|
+
// Skills 工
|
|
54
|
+
skillTool,
|
|
55
|
+
// Memory 工
|
|
56
|
+
memoryTool,
|
|
57
|
+
// Multi-agent orchestration 工
|
|
58
|
+
taskTool,
|
|
59
|
+
councilTool,
|
|
60
|
+
];
|
|
61
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory 工具 - 按需检索共享记
|
|
3
|
+
*
|
|
4
|
+
* Shared memory is intentionally NOT injected into every turn's system
|
|
5
|
+
* prompt by default — auto-injecting semantically-similar past memories
|
|
6
|
+
* caused models to recall and repeat their own past mistakes (e.g. a stuck
|
|
7
|
+
* session's hallucinated text got stored, then recalled as "context" in
|
|
8
|
+
* later sessions, reinforcing itself). Memory is opt-in: the model calls
|
|
9
|
+
* this tool only when it decides recalling past conversations would help.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { createTool } from '../createTool.js';
|
|
14
|
+
import { ToolKind } from '../types.js';
|
|
15
|
+
import { sharedMemory } from '../../memory/SharedMemory.js';
|
|
16
|
+
|
|
17
|
+
const MemorySchema = z.object({
|
|
18
|
+
query: z.string()
|
|
19
|
+
.min(1, 'query 不能为空')
|
|
20
|
+
.describe('What to recall — a topic, decision, or past conversation to search for'),
|
|
21
|
+
maxEntries: z.number().int().min(1).max(10).optional()
|
|
22
|
+
.describe('Maximum number of memory entries to return (default 4)'),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const memoryTool = createTool({
|
|
26
|
+
name: 'Memory',
|
|
27
|
+
displayName: 'Recall Memory',
|
|
28
|
+
kind: ToolKind.ReadOnly,
|
|
29
|
+
schema: MemorySchema,
|
|
30
|
+
|
|
31
|
+
description: {
|
|
32
|
+
short: 'Recall relevant context from past conversations and sessions',
|
|
33
|
+
long: `Searches shared long-term memory for past conversations, decisions, and summaries
|
|
34
|
+
relevant to a query. This is opt-in — call it only when recalling prior context would
|
|
35
|
+
genuinely help (e.g. "what did we decide about X last time", "have we hit this error before").`,
|
|
36
|
+
usageNotes: [
|
|
37
|
+
'Do NOT call this by default at the start of every task — only when past context is actually useful',
|
|
38
|
+
'Treat results as recollections that may be incomplete, outdated, or about a different situation — verify against the current codebase/state before relying on them',
|
|
39
|
+
'Never treat a past assistant message recalled here as proof that something is currently blocked, broken, or unavailable — confirm directly instead',
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
category: '记忆',
|
|
44
|
+
tags: ['memory', 'recall', 'context'],
|
|
45
|
+
|
|
46
|
+
execute: async (params, context) => {
|
|
47
|
+
const { query, maxEntries } = params;
|
|
48
|
+
const result = await sharedMemory.buildContext(query, maxEntries || 4, context?.sessionId);
|
|
49
|
+
|
|
50
|
+
if (!result) {
|
|
51
|
+
return {
|
|
52
|
+
success: true,
|
|
53
|
+
llmContent: 'No relevant memory found for this query.',
|
|
54
|
+
displayContent: 'No relevant memory found',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
success: true,
|
|
60
|
+
llmContent: result,
|
|
61
|
+
displayContent: `Recalled memory for: ${query}`,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export default memoryTool;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read 工具 - 文件读取
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { createTool } from '../createTool.js';
|
|
9
|
+
import { ToolKind, ToolErrorType } from '../types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_LINE_LIMIT = 2000;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Read 工具 Schema
|
|
18
|
+
*/
|
|
19
|
+
const ReadSchema = z.object({
|
|
20
|
+
file_path: z.string()
|
|
21
|
+
.min(1, '文件路径不能为空')
|
|
22
|
+
.describe('The absolute path to the file to read'),
|
|
23
|
+
|
|
24
|
+
offset: z.number()
|
|
25
|
+
.int()
|
|
26
|
+
.min(0)
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('The line number to start reading from (0-based)'),
|
|
29
|
+
|
|
30
|
+
limit: z.number()
|
|
31
|
+
.int()
|
|
32
|
+
.min(1)
|
|
33
|
+
.max(10000)
|
|
34
|
+
.optional()
|
|
35
|
+
.describe('The number of lines to read'),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Read 工具
|
|
40
|
+
*/
|
|
41
|
+
export const readTool = createTool({
|
|
42
|
+
name: 'Read',
|
|
43
|
+
displayName: 'File Read',
|
|
44
|
+
kind: ToolKind.ReadOnly,
|
|
45
|
+
schema: ReadSchema,
|
|
46
|
+
|
|
47
|
+
description: {
|
|
48
|
+
short: 'Reads a file from the local filesystem',
|
|
49
|
+
long: `Reads a file from the local filesystem. You can access any file directly by using this tool.
|
|
50
|
+
If the User provides a path to a file assume that path is valid.`,
|
|
51
|
+
usageNotes: [
|
|
52
|
+
'The file_path parameter must be an absolute path, not a relative path',
|
|
53
|
+
`By default, it reads up to ${DEFAULT_LINE_LIMIT} lines starting from the beginning`,
|
|
54
|
+
'You can optionally specify a line offset and limit for long files',
|
|
55
|
+
'Lines in the output are numbered starting at 1',
|
|
56
|
+
'You can call multiple Read tools in parallel to read multiple files at once',
|
|
57
|
+
],
|
|
58
|
+
examples: [
|
|
59
|
+
{
|
|
60
|
+
description: 'Read entire file',
|
|
61
|
+
params: { file_path: '/path/to/file.ts' },
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
description: 'Read with offset and limit',
|
|
65
|
+
params: { file_path: '/path/to/file.ts', offset: 100, limit: 50 },
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
important: [
|
|
69
|
+
'file_path must be an absolute path',
|
|
70
|
+
'Prefer reading the whole file by not providing offset/limit',
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
category: '文件操作',
|
|
75
|
+
tags: ['file', 'read', 'io'],
|
|
76
|
+
|
|
77
|
+
async execute(params, context) {
|
|
78
|
+
const { file_path, offset = 0, limit } = params;
|
|
79
|
+
const effectiveLimit = limit ?? DEFAULT_LINE_LIMIT;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// 检查文件是否存
|
|
83
|
+
try {
|
|
84
|
+
await fs.access(file_path);
|
|
85
|
+
} catch {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
llmContent: `File not found: ${file_path}`,
|
|
89
|
+
displayContent: `error: file not found: ${file_path}`,
|
|
90
|
+
error: {
|
|
91
|
+
type: ToolErrorType.EXECUTION_ERROR,
|
|
92
|
+
message: 'File not found',
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 获取文件信
|
|
98
|
+
const stat = await fs.stat(file_path);
|
|
99
|
+
|
|
100
|
+
// 检查是否为目
|
|
101
|
+
if (stat.isDirectory()) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
llmContent: `Path is a directory, not a file: ${file_path}`,
|
|
105
|
+
displayContent: `error: ${file_path} is a directory`,
|
|
106
|
+
error: {
|
|
107
|
+
type: ToolErrorType.VALIDATION_ERROR,
|
|
108
|
+
message: 'Path is a directory',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 读取文件内
|
|
114
|
+
const content = await fs.readFile(file_path, 'utf-8');
|
|
115
|
+
const lines = content.split('\n');
|
|
116
|
+
const totalLines = lines.length;
|
|
117
|
+
|
|
118
|
+
// 应用 offset
|
|
119
|
+
const selectedLines = lines.slice(offset, offset + effectiveLimit);
|
|
120
|
+
|
|
121
|
+
// 格式化输出(带行
|
|
122
|
+
const formattedContent = selectedLines
|
|
123
|
+
.map((line, i) => {
|
|
124
|
+
const lineNum = (offset + i + 1).toString().padStart(6, ' ');
|
|
125
|
+
return `${lineNum}|${line}`;
|
|
126
|
+
})
|
|
127
|
+
.join('\n');
|
|
128
|
+
|
|
129
|
+
// 计算是否有更多内
|
|
130
|
+
const hasMore = offset + effectiveLimit < totalLines;
|
|
131
|
+
const fileName = path.basename(file_path);
|
|
132
|
+
|
|
133
|
+
let summary = fileName;
|
|
134
|
+
if (offset > 0 || limit) {
|
|
135
|
+
summary += ` lines ${offset + 1}–${Math.min(offset + effectiveLimit, totalLines)} of ${totalLines}`;
|
|
136
|
+
} else {
|
|
137
|
+
summary += ` ${totalLines} lines`;
|
|
138
|
+
}
|
|
139
|
+
if (hasMore) {
|
|
140
|
+
summary += ` ···`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
llmContent: formattedContent,
|
|
146
|
+
displayContent: summary,
|
|
147
|
+
metadata: {
|
|
148
|
+
file_path,
|
|
149
|
+
total_lines: totalLines,
|
|
150
|
+
lines_read: selectedLines.length,
|
|
151
|
+
offset,
|
|
152
|
+
has_more: hasMore,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
const errorMessage = error instanceof Error ? error.message : 'unknown error';
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
llmContent: `Failed to read file: ${errorMessage}`,
|
|
160
|
+
displayContent: `error: ${errorMessage}`,
|
|
161
|
+
error: {
|
|
162
|
+
type: ToolErrorType.EXECUTION_ERROR,
|
|
163
|
+
message: errorMessage,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill 工具
|
|
3
|
+
*
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { createTool } from '../createTool.js';
|
|
10
|
+
import { ToolKind } from '../types.js';
|
|
11
|
+
import { getSkillRegistry } from '../../skills/index.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Skill 工具参数 Schema
|
|
15
|
+
*/
|
|
16
|
+
const SkillSchema = z.object({
|
|
17
|
+
skill: z.string().describe('The name of the skill to load'),
|
|
18
|
+
args: z.string().optional().describe('Optional arguments to pass to the skill'),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Skill 工具
|
|
23
|
+
*
|
|
24
|
+
*
|
|
25
|
+
*
|
|
26
|
+
*/
|
|
27
|
+
export const skillTool = createTool({
|
|
28
|
+
name: 'Skill',
|
|
29
|
+
displayName: 'Load Skill',
|
|
30
|
+
kind: ToolKind.ReadOnly,
|
|
31
|
+
schema: SkillSchema,
|
|
32
|
+
|
|
33
|
+
description: {
|
|
34
|
+
short: 'Load a skill to get specialized instructions',
|
|
35
|
+
long: `Load a skill to get specialized instructions for a task.
|
|
36
|
+
|
|
37
|
+
Use this tool when:
|
|
38
|
+
- You need specific guidance for a task that matches an available skill
|
|
39
|
+
- The user explicitly requests to use a skill
|
|
40
|
+
- You want to follow best practices defined in a skill
|
|
41
|
+
|
|
42
|
+
The skill's instructions will be returned and you should follow them.`,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
execute: async ({ skill, args }) => {
|
|
46
|
+
const registry = getSkillRegistry();
|
|
47
|
+
|
|
48
|
+
// 检查 Skill 是否存
|
|
49
|
+
if (!registry.hasSkill(skill)) {
|
|
50
|
+
const allSkills = registry.getAllSkills();
|
|
51
|
+
const availableNames = allSkills.map(s => s.name).join(', ');
|
|
52
|
+
const errorMsg = `Skill "${skill}" not found. Available skills: ${availableNames || 'none'}`;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
llmContent: errorMsg,
|
|
57
|
+
displayContent: errorMsg,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 加载完整内
|
|
62
|
+
const content = await registry.loadContent(skill);
|
|
63
|
+
|
|
64
|
+
if (!content) {
|
|
65
|
+
const errorMsg = `Failed to load skill "${skill}"`;
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
llmContent: errorMsg,
|
|
69
|
+
displayContent: errorMsg,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 构建返回内
|
|
74
|
+
let result = `## Skill: ${content.metadata.name}\n\n`;
|
|
75
|
+
|
|
76
|
+
// 如果有工具限制,提
|
|
77
|
+
if (content.metadata.allowedTools && content.metadata.allowedTools.length > 0) {
|
|
78
|
+
result += `**Allowed tools for this skill:** ${content.metadata.allowedTools.join(', ')}\n\n`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 添加指令内
|
|
82
|
+
result += content.instructions;
|
|
83
|
+
|
|
84
|
+
// 如果有参数,添加到末
|
|
85
|
+
if (args) {
|
|
86
|
+
result += `\n\n## Arguments\n\n${args}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
llmContent: result,
|
|
92
|
+
displayContent: `✓ Loaded skill: ${content.metadata.name}`,
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export default skillTool;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
const SNAPSHOTS_DIR = path.join(os.homedir(), '.aegiscode', 'snapshots');
|
|
6
|
+
const MAX_SNAPSHOTS = 50;
|
|
7
|
+
|
|
8
|
+
export async function createSnapshot(filePath: string): Promise<string | null> {
|
|
9
|
+
try {
|
|
10
|
+
await fs.mkdir(SNAPSHOTS_DIR, { recursive: true });
|
|
11
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
12
|
+
const basename = path.basename(filePath);
|
|
13
|
+
const snapshotPath = path.join(SNAPSHOTS_DIR, `${timestamp}_${basename}.bak`);
|
|
14
|
+
await fs.copyFile(filePath, snapshotPath);
|
|
15
|
+
await pruneSnapshots();
|
|
16
|
+
return snapshotPath;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function pruneSnapshots(): Promise<void> {
|
|
23
|
+
try {
|
|
24
|
+
const entries = await fs.readdir(SNAPSHOTS_DIR);
|
|
25
|
+
if (entries.length <= MAX_SNAPSHOTS) return;
|
|
26
|
+
const files = await Promise.all(
|
|
27
|
+
entries.map(async (name) => {
|
|
28
|
+
const p = path.join(SNAPSHOTS_DIR, name);
|
|
29
|
+
const stat = await fs.stat(p);
|
|
30
|
+
return { path: p, mtime: stat.mtime.getTime() };
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
files.sort((a, b) => a.mtime - b.mtime);
|
|
34
|
+
await Promise.all(
|
|
35
|
+
files.slice(0, files.length - MAX_SNAPSHOTS).map(f => fs.unlink(f.path))
|
|
36
|
+
);
|
|
37
|
+
} catch {
|
|
38
|
+
// ignore prune errors
|
|
39
|
+
}
|
|
40
|
+
}
|