aegiscode 3.1.7 → 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 +555 -556
- 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,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Council tool — lets the running agent convene a multi-perspective
|
|
3
|
+
* deliberation mid-conversation, without the user typing /research.
|
|
4
|
+
* Mirrors the /research slash command (builtinCommands.ts) exactly:
|
|
5
|
+
* fixed 4-member council, read-only tools, no confirmation needed.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { createTool } from '../createTool.js';
|
|
10
|
+
import { ToolKind } from '../types.js';
|
|
11
|
+
import { CouncilAgent } from '../../agent/orchestrator/CouncilAgent.js';
|
|
12
|
+
import { requireModelConfig, buildSourceContext } from '../../agent/orchestrator/utils.js';
|
|
13
|
+
|
|
14
|
+
const CouncilSchema = z.object({
|
|
15
|
+
question: z.string().describe('The question or decision to deliberate on'),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const councilTool = createTool({
|
|
19
|
+
name: 'Council',
|
|
20
|
+
displayName: 'Convene Council',
|
|
21
|
+
kind: ToolKind.ReadOnly,
|
|
22
|
+
schema: CouncilSchema,
|
|
23
|
+
|
|
24
|
+
description: {
|
|
25
|
+
short: 'Convene a 4-perspective council to deliberate on a question',
|
|
26
|
+
long: `Spawns a research council of read-only sub-agents with different perspectives —
|
|
27
|
+
Analyst (data/empirical), Architect (systems/design), Ethicist (safety/fairness),
|
|
28
|
+
Pragmatist (implementation reality) — who each vote and reason independently,
|
|
29
|
+
then returns their views plus a synthesis.
|
|
30
|
+
|
|
31
|
+
Use this for genuinely contested questions where multiple angles matter
|
|
32
|
+
(architecture tradeoffs, risk calls, "should we do X" decisions) — not for
|
|
33
|
+
straightforward lookups.`,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
execute: async ({ question }, context) => {
|
|
37
|
+
let modelConfig;
|
|
38
|
+
try {
|
|
39
|
+
modelConfig = requireModelConfig();
|
|
40
|
+
} catch (e) {
|
|
41
|
+
const msg = (e as Error).message;
|
|
42
|
+
return { success: false, llmContent: msg, displayContent: msg };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const cwd = context?.cwd || process.cwd();
|
|
46
|
+
const sourceCtx = buildSourceContext(cwd);
|
|
47
|
+
const baseModelCfg = { model: modelConfig.model, baseURL: modelConfig.baseURL || undefined, apiKey: modelConfig.apiKey };
|
|
48
|
+
const researchTools = ['Read', 'Grep', 'Glob'];
|
|
49
|
+
const researchNote = `\n\nWorkspace context (project metadata + file tree + structure summaries + git changes):\n${sourceCtx}\n\nRead files with Read tool, search with Grep, browse with Glob.\nAlways state VOTE: approve, reject, or abstain and REASONING: with clear justification.`;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const council = new CouncilAgent('council', modelConfig, {
|
|
53
|
+
rule: 'majority',
|
|
54
|
+
maxTokensPerAgent: 800,
|
|
55
|
+
enableIteration: false,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
council.addMember('analyst', 'Data Analyst',
|
|
59
|
+
`You are a Data Analyst on a research council. You reason from data, statistics, and empirical evidence.\nYou value measurable outcomes and quantitative reasoning.${researchNote}`,
|
|
60
|
+
1, baseModelCfg, researchTools);
|
|
61
|
+
|
|
62
|
+
council.addMember('architect', 'Systems Architect',
|
|
63
|
+
`You are a Systems Architect on a research council. You evaluate designs, tradeoffs, and architectural decisions.\nYou focus on scalability, maintainability, and system coherence.${researchNote}`,
|
|
64
|
+
1, baseModelCfg, researchTools);
|
|
65
|
+
|
|
66
|
+
council.addMember('ethicist', 'Ethics & Safety Officer',
|
|
67
|
+
`You are an Ethics & Safety Officer on a research council. You evaluate safety, fairness, privacy, and societal impact.\nYou raise concerns others might miss and advocate for responsible practices.${researchNote}`,
|
|
68
|
+
1, baseModelCfg, researchTools);
|
|
69
|
+
|
|
70
|
+
council.addMember('pragmatist', 'Pragmatic Engineer',
|
|
71
|
+
`You are a Pragmatic Engineer on a research council. You evaluate practicality, implementation effort, and real-world constraints.\nYou balance idealism with what actually works in production.${researchNote}`,
|
|
72
|
+
1, baseModelCfg, researchTools);
|
|
73
|
+
|
|
74
|
+
const result = await council.deliberate(question, context?.sessionId);
|
|
75
|
+
|
|
76
|
+
const lines: string[] = [];
|
|
77
|
+
for (const v of result.voteResults) {
|
|
78
|
+
lines.push(`### ${v.role}`);
|
|
79
|
+
lines.push(`VOTE: ${v.vote}`);
|
|
80
|
+
lines.push(v.reasoning);
|
|
81
|
+
lines.push('');
|
|
82
|
+
}
|
|
83
|
+
lines.push('### Synthesis');
|
|
84
|
+
lines.push('');
|
|
85
|
+
const summaryLines = result.summary.split('\n');
|
|
86
|
+
const verdictIdx = summaryLines.findIndex(l => l.includes('APPROVED') || l.includes('REJECTED'));
|
|
87
|
+
const synthesis = verdictIdx > -1
|
|
88
|
+
? summaryLines.slice(verdictIdx + 1).join('\n').trim()
|
|
89
|
+
: result.summary;
|
|
90
|
+
lines.push(synthesis || `${result.voteResults.length} perspectives gathered.`);
|
|
91
|
+
|
|
92
|
+
const formatted = lines.join('\n').trim();
|
|
93
|
+
return {
|
|
94
|
+
success: true,
|
|
95
|
+
llmContent: formatted,
|
|
96
|
+
displayContent: `✓ Council deliberated (${result.voteResults.length} perspectives, ${result.rounds} round${result.rounds === 1 ? '' : 's'})`,
|
|
97
|
+
};
|
|
98
|
+
} catch (e) {
|
|
99
|
+
const msg = `Council deliberation failed: ${(e as Error).message}`;
|
|
100
|
+
return { success: false, llmContent: msg, displayContent: msg };
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export default councilTool;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit 工具
|
|
3
|
+
*
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { createTool } from '../createTool.js';
|
|
11
|
+
import { ToolKind, ToolErrorType } from '../types.js';
|
|
12
|
+
import { createSnapshot } from './snapshot.js';
|
|
13
|
+
|
|
14
|
+
// ========== Schema 定
|
|
15
|
+
|
|
16
|
+
const EditSchema = z.object({
|
|
17
|
+
file_path: z.string()
|
|
18
|
+
.min(1, '文件路径不能为空')
|
|
19
|
+
.describe('The absolute path to the file to modify'),
|
|
20
|
+
old_string: z.string()
|
|
21
|
+
.describe('The text to replace (must be unique in the file unless replace_all is true)'),
|
|
22
|
+
new_string: z.string()
|
|
23
|
+
.describe('The text to replace it with'),
|
|
24
|
+
replace_all: z.boolean()
|
|
25
|
+
.default(false)
|
|
26
|
+
.describe('If true, replace all occurrences of old_string'),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// ========== Edit 工
|
|
30
|
+
|
|
31
|
+
export const editTool = createTool({
|
|
32
|
+
name: 'Edit',
|
|
33
|
+
displayName: 'File Edit',
|
|
34
|
+
kind: ToolKind.Write,
|
|
35
|
+
schema: EditSchema,
|
|
36
|
+
|
|
37
|
+
description: {
|
|
38
|
+
short: 'Performs exact string replacements in files',
|
|
39
|
+
long: 'Edits files by replacing specified text with new text. Requires the old_string to be unique unless replace_all is true.',
|
|
40
|
+
usageNotes: [
|
|
41
|
+
'You MUST use the Read tool first before editing a file',
|
|
42
|
+
'The edit will FAIL if old_string is not unique in the file',
|
|
43
|
+
'Use replace_all=true for renaming variables across the file',
|
|
44
|
+
'Preserve exact indentation (tabs/spaces) as it appears',
|
|
45
|
+
'If you want to create a new file, use the Write tool instead',
|
|
46
|
+
],
|
|
47
|
+
examples: [
|
|
48
|
+
{
|
|
49
|
+
description: 'Replace a function name',
|
|
50
|
+
params: {
|
|
51
|
+
file_path: '/path/to/file.ts',
|
|
52
|
+
old_string: 'function oldName(',
|
|
53
|
+
new_string: 'function newName(',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
description: 'Replace all occurrences of a variable',
|
|
58
|
+
params: {
|
|
59
|
+
file_path: '/path/to/file.ts',
|
|
60
|
+
old_string: 'oldVar',
|
|
61
|
+
new_string: 'newVar',
|
|
62
|
+
replace_all: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
important: [
|
|
67
|
+
'NEVER guess file contents - always Read first',
|
|
68
|
+
'If old_string is not found, the edit will fail',
|
|
69
|
+
'If multiple matches found without replace_all, provide more context',
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
category: '文件操作',
|
|
74
|
+
tags: ['file', 'io', 'write', 'edit'],
|
|
75
|
+
|
|
76
|
+
// 提取签名内容(用于权限规
|
|
77
|
+
extractSignatureContent: (params: unknown) => {
|
|
78
|
+
const p = params as { file_path: string };
|
|
79
|
+
return p.file_path;
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
// 抽象权限规
|
|
83
|
+
abstractPermissionRule: (params: unknown) => {
|
|
84
|
+
const p = params as { file_path: string };
|
|
85
|
+
const dir = path.dirname(p.file_path);
|
|
86
|
+
return `Edit:${dir}/*`;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async execute(params, context) {
|
|
90
|
+
const { file_path, old_string, new_string, replace_all } = params;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
// 1. 检查文件是否存
|
|
94
|
+
try {
|
|
95
|
+
const stat = await fs.stat(file_path);
|
|
96
|
+
if (stat.isDirectory()) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
llmContent: `Error: ${file_path} is a directory, not a file`,
|
|
100
|
+
displayContent: `error: ${file_path} is a directory`,
|
|
101
|
+
error: {
|
|
102
|
+
type: ToolErrorType.VALIDATION_ERROR,
|
|
103
|
+
message: 'Path is a directory',
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
llmContent: `Error: File not found: ${file_path}`,
|
|
111
|
+
displayContent: `error: file not found: ${file_path}`,
|
|
112
|
+
error: {
|
|
113
|
+
type: ToolErrorType.VALIDATION_ERROR,
|
|
114
|
+
message: 'File not found',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 2. 读取文件内
|
|
120
|
+
const content = await fs.readFile(file_path, 'utf8');
|
|
121
|
+
|
|
122
|
+
// 3. old_string 和 new_string 相同检
|
|
123
|
+
if (old_string === new_string) {
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
llmContent: 'Error: old_string and new_string are identical',
|
|
127
|
+
displayContent: 'error: old_string and new_string are identical',
|
|
128
|
+
error: {
|
|
129
|
+
type: ToolErrorType.VALIDATION_ERROR,
|
|
130
|
+
message: 'old_string and new_string are identical',
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 4. 检查 old_string 是否存
|
|
136
|
+
const matchCount = content.split(old_string).length - 1;
|
|
137
|
+
|
|
138
|
+
if (matchCount === 0) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
llmContent: `Error: old_string not found in file. Make sure you have read the file first and the content is up to date.`,
|
|
142
|
+
displayContent: `error: old_string not found in file`,
|
|
143
|
+
error: {
|
|
144
|
+
type: ToolErrorType.VALIDATION_ERROR,
|
|
145
|
+
message: 'old_string not found in file',
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 5. 多重匹配检查(非 replace_all 模
|
|
151
|
+
if (matchCount > 1 && !replace_all) {
|
|
152
|
+
return {
|
|
153
|
+
success: false,
|
|
154
|
+
llmContent: `Error: Multiple matches (${matchCount}) found for old_string. Either:
|
|
155
|
+
1. Provide more context in old_string to make it unique
|
|
156
|
+
2. Set replace_all=true to replace all occurrences`,
|
|
157
|
+
displayContent: `error: ${matchCount} matches found — use replace_all or add more context`,
|
|
158
|
+
error: {
|
|
159
|
+
type: ToolErrorType.VALIDATION_ERROR,
|
|
160
|
+
message: `Multiple matches (${matchCount}) found`,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 6. 快照原始文件(支持 undo)
|
|
166
|
+
const snapshotPath = await createSnapshot(file_path);
|
|
167
|
+
|
|
168
|
+
// 7. 执行替
|
|
169
|
+
const newContent = replace_all
|
|
170
|
+
? content.replaceAll(old_string, new_string)
|
|
171
|
+
: content.replace(old_string, new_string);
|
|
172
|
+
|
|
173
|
+
// 8. 写入文
|
|
174
|
+
await fs.writeFile(file_path, newContent, 'utf8');
|
|
175
|
+
|
|
176
|
+
// 9. 计算替换数
|
|
177
|
+
const replacements = replace_all ? matchCount : 1;
|
|
178
|
+
|
|
179
|
+
// Render as a unified-style diff: old_string lines removed, new_string lines
|
|
180
|
+
// added. The renderer (MessageRenderer's ToolUseBlock) detects +/- prefixed
|
|
181
|
+
// lines and colors them, deriving the "+N -M lines" tally from the counts.
|
|
182
|
+
const diffLines = [
|
|
183
|
+
...old_string.split('\n').map(l => `-${l}`),
|
|
184
|
+
...new_string.split('\n').map(l => `+${l}`),
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
success: true,
|
|
189
|
+
llmContent: `Successfully edited ${file_path} (${replacements} replacement${replacements > 1 ? 's' : ''})`,
|
|
190
|
+
displayContent: diffLines.join('\n'),
|
|
191
|
+
metadata: {
|
|
192
|
+
file_path,
|
|
193
|
+
replacements,
|
|
194
|
+
snapshot: snapshotPath,
|
|
195
|
+
old_string_preview: old_string.length > 50
|
|
196
|
+
? old_string.substring(0, 50) + '...'
|
|
197
|
+
: old_string,
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
} catch (error) {
|
|
201
|
+
const errorMessage = error instanceof Error ? error.message : 'unknown error';
|
|
202
|
+
return {
|
|
203
|
+
success: false,
|
|
204
|
+
llmContent: `Error editing file: ${errorMessage}`,
|
|
205
|
+
displayContent: `error: ${errorMessage}`,
|
|
206
|
+
error: {
|
|
207
|
+
type: ToolErrorType.EXECUTION_ERROR,
|
|
208
|
+
message: errorMessage,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glob 工具 - 文件搜索
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { glob } from 'glob';
|
|
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_IGNORE = [
|
|
15
|
+
'**/node_modules/**',
|
|
16
|
+
'**/.git/**',
|
|
17
|
+
'**/dist/**',
|
|
18
|
+
'**/build/**',
|
|
19
|
+
'**/.next/**',
|
|
20
|
+
'**/coverage/**',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Glob 工具 Schema
|
|
25
|
+
*/
|
|
26
|
+
const GlobSchema = z.object({
|
|
27
|
+
pattern: z.string()
|
|
28
|
+
.min(1, '模式不能为空')
|
|
29
|
+
.describe('The glob pattern to match files against'),
|
|
30
|
+
|
|
31
|
+
path: z.string()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe('Directory to search in (defaults to current working directory)'),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Glob 工具
|
|
38
|
+
*/
|
|
39
|
+
export const globTool = createTool({
|
|
40
|
+
name: 'Glob',
|
|
41
|
+
displayName: 'File Search',
|
|
42
|
+
kind: ToolKind.ReadOnly,
|
|
43
|
+
schema: GlobSchema,
|
|
44
|
+
|
|
45
|
+
description: {
|
|
46
|
+
short: 'Find files matching a glob pattern',
|
|
47
|
+
long: `Search for files matching a glob pattern. Returns matching file paths sorted by modification time.
|
|
48
|
+
Use this tool when you need to find files by name patterns.`,
|
|
49
|
+
usageNotes: [
|
|
50
|
+
'Patterns not starting with "**/" are automatically prepended with "**/" to enable recursive searching',
|
|
51
|
+
'Common patterns: "*.ts" for TypeScript files, "*.test.ts" for test files',
|
|
52
|
+
'Results are sorted by modification time (newest first)',
|
|
53
|
+
'Automatically ignores node_modules, .git, dist, build directories',
|
|
54
|
+
],
|
|
55
|
+
examples: [
|
|
56
|
+
{
|
|
57
|
+
description: 'Find all TypeScript files',
|
|
58
|
+
params: { pattern: '*.ts' },
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
description: 'Find all test files',
|
|
62
|
+
params: { pattern: '**/*.test.ts' },
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
description: 'Find files in specific directory',
|
|
66
|
+
params: { pattern: '*.json', path: '/project/config' },
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
category: '搜索',
|
|
72
|
+
tags: ['search', 'file', 'glob'],
|
|
73
|
+
|
|
74
|
+
async execute(params, context) {
|
|
75
|
+
let { pattern, path: searchPath } = params;
|
|
76
|
+
|
|
77
|
+
// 默认搜索目
|
|
78
|
+
const cwd = searchPath || context?.cwd || process.cwd();
|
|
79
|
+
|
|
80
|
+
// 自动添
|
|
81
|
+
if (!pattern.startsWith('**/') && !pattern.startsWith('/')) {
|
|
82
|
+
pattern = `**/${pattern}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// 执行 glob 搜
|
|
87
|
+
const files = await glob(pattern, {
|
|
88
|
+
cwd,
|
|
89
|
+
ignore: DEFAULT_IGNORE,
|
|
90
|
+
nodir: true,
|
|
91
|
+
absolute: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (files.length === 0) {
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
llmContent: 'No files found matching the pattern.',
|
|
98
|
+
displayContent: `no files: ${pattern}`,
|
|
99
|
+
metadata: {
|
|
100
|
+
pattern,
|
|
101
|
+
cwd,
|
|
102
|
+
count: 0,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 格式化输
|
|
108
|
+
const formattedFiles = files
|
|
109
|
+
.map(f => path.relative(cwd, f))
|
|
110
|
+
.join('\n');
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
success: true,
|
|
114
|
+
llmContent: formattedFiles,
|
|
115
|
+
displayContent: `${files.length} file${files.length === 1 ? '' : 's'}`,
|
|
116
|
+
metadata: {
|
|
117
|
+
pattern,
|
|
118
|
+
cwd,
|
|
119
|
+
count: files.length,
|
|
120
|
+
files: files.slice(0, 100), // 只存储前 100
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
} catch (error) {
|
|
124
|
+
const errorMessage = error instanceof Error ? error.message : '未知错误';
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
llmContent: `Glob search failed: ${errorMessage}`,
|
|
128
|
+
displayContent: `error: ${errorMessage}`,
|
|
129
|
+
error: {
|
|
130
|
+
type: ToolErrorType.EXECUTION_ERROR,
|
|
131
|
+
message: errorMessage,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
});
|