@xagent-ai/cli 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +1 -1
  2. package/README_CN.md +1 -1
  3. package/dist/agents.js +164 -164
  4. package/dist/agents.js.map +1 -1
  5. package/dist/ai-client.d.ts +4 -6
  6. package/dist/ai-client.d.ts.map +1 -1
  7. package/dist/ai-client.js +137 -115
  8. package/dist/ai-client.js.map +1 -1
  9. package/dist/auth.js +4 -4
  10. package/dist/auth.js.map +1 -1
  11. package/dist/cli.js +184 -1
  12. package/dist/cli.js.map +1 -1
  13. package/dist/config.js +3 -3
  14. package/dist/config.js.map +1 -1
  15. package/dist/context-compressor.d.ts.map +1 -1
  16. package/dist/context-compressor.js +65 -81
  17. package/dist/context-compressor.js.map +1 -1
  18. package/dist/conversation.d.ts +1 -1
  19. package/dist/conversation.d.ts.map +1 -1
  20. package/dist/conversation.js +5 -31
  21. package/dist/conversation.js.map +1 -1
  22. package/dist/memory.d.ts +5 -1
  23. package/dist/memory.d.ts.map +1 -1
  24. package/dist/memory.js +77 -37
  25. package/dist/memory.js.map +1 -1
  26. package/dist/remote-ai-client.d.ts +1 -8
  27. package/dist/remote-ai-client.d.ts.map +1 -1
  28. package/dist/remote-ai-client.js +55 -65
  29. package/dist/remote-ai-client.js.map +1 -1
  30. package/dist/retry.d.ts +35 -0
  31. package/dist/retry.d.ts.map +1 -0
  32. package/dist/retry.js +166 -0
  33. package/dist/retry.js.map +1 -0
  34. package/dist/session.d.ts +0 -5
  35. package/dist/session.d.ts.map +1 -1
  36. package/dist/session.js +243 -312
  37. package/dist/session.js.map +1 -1
  38. package/dist/slash-commands.d.ts +1 -0
  39. package/dist/slash-commands.d.ts.map +1 -1
  40. package/dist/slash-commands.js +91 -9
  41. package/dist/slash-commands.js.map +1 -1
  42. package/dist/smart-approval.d.ts.map +1 -1
  43. package/dist/smart-approval.js +18 -17
  44. package/dist/smart-approval.js.map +1 -1
  45. package/dist/system-prompt-generator.d.ts.map +1 -1
  46. package/dist/system-prompt-generator.js +149 -139
  47. package/dist/system-prompt-generator.js.map +1 -1
  48. package/dist/theme.d.ts +48 -0
  49. package/dist/theme.d.ts.map +1 -1
  50. package/dist/theme.js +254 -0
  51. package/dist/theme.js.map +1 -1
  52. package/dist/tools/edit-diff.d.ts +32 -0
  53. package/dist/tools/edit-diff.d.ts.map +1 -0
  54. package/dist/tools/edit-diff.js +185 -0
  55. package/dist/tools/edit-diff.js.map +1 -0
  56. package/dist/tools/edit.d.ts +11 -0
  57. package/dist/tools/edit.d.ts.map +1 -0
  58. package/dist/tools/edit.js +129 -0
  59. package/dist/tools/edit.js.map +1 -0
  60. package/dist/tools.d.ts +19 -5
  61. package/dist/tools.d.ts.map +1 -1
  62. package/dist/tools.js +979 -631
  63. package/dist/tools.js.map +1 -1
  64. package/dist/types.d.ts +6 -31
  65. package/dist/types.d.ts.map +1 -1
  66. package/package.json +3 -2
  67. package/src/agents.ts +504 -504
  68. package/src/ai-client.ts +1559 -1458
  69. package/src/auth.ts +4 -4
  70. package/src/cli.ts +195 -1
  71. package/src/config.ts +3 -3
  72. package/src/memory.ts +55 -14
  73. package/src/remote-ai-client.ts +663 -683
  74. package/src/retry.ts +217 -0
  75. package/src/session.ts +1736 -1840
  76. package/src/slash-commands.ts +98 -9
  77. package/src/smart-approval.ts +626 -625
  78. package/src/system-prompt-generator.ts +853 -843
  79. package/src/theme.ts +284 -0
  80. package/src/tools.ts +390 -70
@@ -1,843 +1,853 @@
1
- import { ToolRegistry } from './tools.js';
2
- import { ExecutionMode, AgentConfig } from './types.js';
3
- import { getAgentManager } from './agents.js';
4
- import { getSkillInvoker, SkillInfo } from './skill-invoker.js';
5
- import { MCPManager } from './mcp.js';
6
-
7
- export interface ToolParameter {
8
- type: string;
9
- description: string;
10
- required?: boolean;
11
- enum?: string[];
12
- default?: any;
13
- }
14
-
15
- export interface ToolSchema {
16
- name: string;
17
- description: string;
18
- parameters: Record<string, ToolParameter>;
19
- usage: string;
20
- examples: string[];
21
- bestPractices: string[];
22
- }
23
-
24
- export class SystemPromptGenerator {
25
- private toolRegistry: ToolRegistry;
26
- private executionMode: ExecutionMode;
27
- private agentConfig?: AgentConfig;
28
- private mcpManager?: MCPManager;
29
-
30
- constructor(toolRegistry: ToolRegistry, executionMode: ExecutionMode, agentConfig?: AgentConfig, mcpManager?: MCPManager) {
31
- this.toolRegistry = toolRegistry;
32
- this.executionMode = executionMode;
33
- this.agentConfig = agentConfig;
34
- this.mcpManager = mcpManager;
35
- }
36
-
37
- async generateEnhancedSystemPrompt(baseSystemPrompt: string): Promise<string> {
38
- let localTools = this.toolRegistry.getAll().filter(
39
- tool => tool.allowedModes.includes(this.executionMode)
40
- );
41
-
42
- if (this.agentConfig) {
43
- const agentManager = getAgentManager();
44
- const allowedToolNames = agentManager.getAvailableToolsForAgent(this.agentConfig, this.executionMode);
45
- localTools = localTools.filter(tool => allowedToolNames.includes(tool.name));
46
- }
47
-
48
- // Get available local tools (includes MCP wrapper tools registered via registerMCPTools)
49
- let allAvailableTools = localTools;
50
-
51
- let enhancedPrompt = baseSystemPrompt;
52
-
53
- // Only add tool-related content if tools are available
54
- if (allAvailableTools.length > 0) {
55
- // Pre-load skills for dynamic generation
56
- const skillInvoker = getSkillInvoker();
57
- await skillInvoker.initialize();
58
- const availableSkills = await skillInvoker.listAvailableSkills();
59
-
60
- const toolSchemas = this.getToolSchemas(allAvailableTools, availableSkills);
61
- const toolUsageGuide = this.generateToolUsageGuide(toolSchemas);
62
- const decisionMakingGuide = this.generateDecisionMakingGuide(localTools, availableSkills);
63
- const executionStrategy = this.generateExecutionStrategy();
64
-
65
-
66
- enhancedPrompt += `
67
-
68
- ${toolUsageGuide}
69
-
70
- ${decisionMakingGuide}
71
-
72
- ${executionStrategy}
73
-
74
-
75
-
76
- ## Important Notes
77
- - Always verify tool results before proceeding to next steps
78
- - If a tool fails, analyze the error and try alternative approaches
79
- - Use tools efficiently - avoid redundant calls
80
- - When in doubt, ask the user for clarification
81
- - Maintain context across tool calls to build a coherent solution`;
82
- } else {
83
- // No tools available - explicitly tell the AI not to use tools
84
- enhancedPrompt += `
85
-
86
- ## IMPORTANT: READ-ONLY MODE
87
-
88
- You are in DEFAULT mode (read-only mode). You CANNOT use any tools or functions.
89
-
90
- STRICT PROHIBITIONS:
91
- - DO NOT attempt to call any functions, tools, or commands
92
- - DO NOT output any tool call syntax, such as:
93
- - <function_calls>...</function_calls>
94
- - ToolName(params)
95
- - Function call format
96
- - Any similar syntax
97
- - DO NOT simulate tool calls or pretend to use tools
98
- - DO NOT output code that would execute tools
99
-
100
- REQUIRED BEHAVIOR:
101
- - Respond ONLY with plain text
102
- - Answer questions based on your knowledge
103
- - If you need to read files or perform actions, ask the user to switch modes
104
- - Tell the user they can use "/mode yolo" or "/mode accept_edits" to enable tools
105
-
106
- Remember: You are in a conversational mode, not a tool-execution mode. Just talk to the user!`;
107
- }
108
-
109
- return enhancedPrompt;
110
- }
111
-
112
- private getToolSchemas(tools: any[], skills: SkillInfo[] = []): ToolSchema[] {
113
- return tools.map(tool => this.createToolSchema(tool, skills));
114
- }
115
-
116
- //
117
- // createToolSchema
118
- //
119
- // This method defines custom schemas for each tool, which OVERRIDES the description
120
- // property defined in tools.ts (e.g., ReadTool.description, WriteTool.description).
121
- //
122
- // The tool.description from tools.ts is read but NOT directly used - instead, we use
123
- // the schemas defined here. This allows for:
124
- // 1. Dynamic content (like skills list in InvokeSkill)
125
- // 2. Customized descriptions for better LLM understanding
126
- // 3. Consistent formatting across all tools
127
- //
128
- // Only tools NOT in the schemas object will fall back to using tool.description.
129
- //
130
- private createToolSchema(tool: any, skills: SkillInfo[] = []): ToolSchema {
131
- const schemas: Record<string, ToolSchema> = {
132
- Read: {
133
- name: 'Read',
134
- description: 'Read the contents of a file from the filesystem',
135
- parameters: {
136
- filePath: {
137
- type: 'string',
138
- description: 'Path to the file to read (relative or absolute)',
139
- required: true
140
- },
141
- offset: {
142
- type: 'number',
143
- description: 'Starting line number (0-indexed)',
144
- required: false,
145
- default: 0
146
- },
147
- limit: {
148
- type: 'number',
149
- description: 'Maximum number of lines to read',
150
- required: false
151
- }
152
- },
153
- usage: 'Use this tool when you need to examine file contents, understand code structure, or read configuration files',
154
- examples: [
155
- 'Read a specific file: Read(filePath="package.json")',
156
- 'Read with line range: Read(filePath="src/index.ts", offset=100, limit=50)'
157
- ],
158
- bestPractices: [
159
- 'Always check if file exists before reading',
160
- 'Use offset and limit for large files to avoid excessive output',
161
- 'Read configuration files first to understand project structure'
162
- ]
163
- },
164
- Write: {
165
- name: 'Write',
166
- description: 'Write content to a file, creating directories if needed',
167
- parameters: {
168
- filePath: {
169
- type: 'string',
170
- description: 'Path where the file should be written',
171
- required: true
172
- },
173
- content: {
174
- type: 'string',
175
- description: 'Content to write to the file',
176
- required: true
177
- }
178
- },
179
- usage: 'Use this tool to create new files or completely overwrite existing files',
180
- examples: [
181
- 'Create a new file: Write(filePath="src/utils.ts", content="export function helper() {}")',
182
- 'Write configuration: Write(filePath=".env", content="API_KEY=secret")'
183
- ],
184
- bestPractices: [
185
- 'Always read existing file before overwriting',
186
- 'Use proper file extensions',
187
- 'Create necessary directory structures',
188
- 'Include appropriate comments and documentation'
189
- ]
190
- },
191
- Grep: {
192
- name: 'Grep',
193
- description: 'Search for text patterns across multiple files',
194
- parameters: {
195
- pattern: {
196
- type: 'string',
197
- description: 'Text pattern or regex to search for',
198
- required: true
199
- },
200
- path: {
201
- type: 'string',
202
- description: 'Directory path to search in',
203
- required: false,
204
- default: '.'
205
- },
206
- include: {
207
- type: 'string',
208
- description: 'File pattern to include (glob)',
209
- required: false
210
- },
211
- exclude: {
212
- type: 'string',
213
- description: 'File pattern to exclude (glob)',
214
- required: false
215
- },
216
- case_sensitive: {
217
- type: 'boolean',
218
- description: 'Whether search is case-sensitive',
219
- required: false,
220
- default: false
221
- },
222
- fixed_strings: {
223
- type: 'boolean',
224
- description: 'Treat pattern as literal string, not regex',
225
- required: false,
226
- default: false
227
- },
228
- context: {
229
- type: 'number',
230
- description: 'Number of context lines before and after match',
231
- required: false
232
- },
233
- before: {
234
- type: 'number',
235
- description: 'Number of context lines before match',
236
- required: false
237
- },
238
- after: {
239
- type: 'number',
240
- description: 'Number of context lines after match',
241
- required: false
242
- },
243
- no_ignore: {
244
- type: 'boolean',
245
- description: 'Ignore .gitignore patterns',
246
- required: false,
247
- default: false
248
- }
249
- },
250
- usage: 'Search for code patterns, function definitions, or text across the codebase',
251
- examples: [
252
- 'Search for function: Grep(pattern="function.*\\(.*\\)")',
253
- 'Find specific text: Grep(pattern="TODO", case_sensitive=true)',
254
- 'Search in specific files: Grep(pattern="import", include="*.ts")'
255
- ],
256
- bestPractices: [
257
- 'Use fixed_strings for simple text searches',
258
- 'Use context to see surrounding code',
259
- 'Narrow search with include/exclude patterns',
260
- 'Use case_sensitive for exact matches'
261
- ]
262
- },
263
- Bash: {
264
- name: 'Bash',
265
- description: 'Execute shell commands in the terminal',
266
- parameters: {
267
- command: {
268
- type: 'string',
269
- description: 'Shell command to execute',
270
- required: true
271
- },
272
- cwd: {
273
- type: 'string',
274
- description: 'Working directory for command execution',
275
- required: false
276
- },
277
- description: {
278
- type: 'string',
279
- description: 'Human-readable description of what the command does',
280
- required: false
281
- },
282
- timeout: {
283
- type: 'number',
284
- description: 'Timeout in seconds (default: 120)',
285
- required: false,
286
- default: 120
287
- },
288
- run_in_bg: {
289
- type: 'boolean',
290
- description: 'Run command in background',
291
- required: false,
292
- default: false
293
- }
294
- },
295
- usage: 'Execute terminal commands, run tests, install dependencies, or perform system operations',
296
- examples: [
297
- 'Install dependencies: Bash(command="npm install")',
298
- 'Run tests: Bash(command="npm test", description="Run unit tests")',
299
- 'Build project: Bash(command="npm run build")'
300
- ],
301
- bestPractices: [
302
- 'Always provide clear descriptions',
303
- 'Use appropriate timeout values',
304
- 'Check command exit codes',
305
- 'Handle errors gracefully',
306
- 'Use run_in_bg for long-running processes'
307
- ]
308
- },
309
- ListDirectory: {
310
- name: 'ListDirectory',
311
- description: 'List files and directories in a given path',
312
- parameters: {
313
- path: {
314
- type: 'string',
315
- description: 'Directory path to list',
316
- required: true
317
- },
318
- recursive: {
319
- type: 'boolean',
320
- description: 'List recursively',
321
- required: false,
322
- default: false
323
- }
324
- },
325
- usage: 'Explore directory structure, find files, or understand project layout',
326
- examples: [
327
- 'List current directory: ListDirectory(path=".")',
328
- 'List recursively: ListDirectory(path="src", recursive=true)'
329
- ],
330
- bestPractices: [
331
- 'Use recursive for deep exploration',
332
- 'Combine with Read to examine files',
333
- 'Check directory existence first'
334
- ]
335
- },
336
- SearchCodebase: {
337
- name: 'SearchCodebase',
338
- description: 'Semantic search through the codebase using embeddings',
339
- parameters: {
340
- query: {
341
- type: 'string',
342
- description: 'Natural language query describing what to search for',
343
- required: true
344
- },
345
- target_directories: {
346
- type: 'array',
347
- description: 'Specific directories to search in',
348
- required: false
349
- }
350
- },
351
- usage: 'Find code by meaning rather than exact text matches',
352
- examples: [
353
- 'Find authentication logic: SearchCodebase(query="how do we check authentication headers?")',
354
- 'Find error handling: SearchCodebase(query="where do we do error handling in the file watcher?")'
355
- ],
356
- bestPractices: [
357
- 'Use natural language queries',
358
- 'Be specific about what you are looking for',
359
- 'Combine with Read to examine found code'
360
- ]
361
- },
362
- DeleteFile: {
363
- name: 'DeleteFile',
364
- description: 'Delete a file from the filesystem',
365
- parameters: {
366
- filePath: {
367
- type: 'string',
368
- description: 'Path to the file to delete',
369
- required: true
370
- }
371
- },
372
- usage: 'Remove files that are no longer needed',
373
- examples: [
374
- 'Delete file: DeleteFile(filePath="old-file.txt")'
375
- ],
376
- bestPractices: [
377
- 'Verify file is not needed before deletion',
378
- 'Use with caution - deletion is permanent',
379
- 'Consider backing up important files'
380
- ]
381
- },
382
- CreateDirectory: {
383
- name: 'CreateDirectory',
384
- description: 'Create a directory and any necessary parent directories',
385
- parameters: {
386
- path: {
387
- type: 'string',
388
- description: 'Directory path to create',
389
- required: true
390
- }
391
- },
392
- usage: 'Create directory structures for organizing files',
393
- examples: [
394
- 'Create directory: CreateDirectory(path="src/components")'
395
- ],
396
- bestPractices: [
397
- 'Use descriptive directory names',
398
- 'Follow project conventions',
399
- 'Create necessary parent directories automatically'
400
- ]
401
- },
402
- Replace: {
403
- name: 'Replace',
404
- description: 'Replace text in a file using search and replace',
405
- parameters: {
406
- filePath: {
407
- type: 'string',
408
- description: 'Path to the file to modify',
409
- required: true
410
- },
411
- old_str: {
412
- type: 'string',
413
- description: 'Text to search for',
414
- required: true
415
- },
416
- new_str: {
417
- type: 'string',
418
- description: 'Text to replace with',
419
- required: true
420
- }
421
- },
422
- usage: 'Make targeted edits to files without rewriting entire content',
423
- examples: [
424
- 'Replace text: Replace(filePath="config.json", old_str="old value", new_str="new value")'
425
- ],
426
- bestPractices: [
427
- 'Use unique old_str to avoid multiple replacements',
428
- 'Read file first to verify content',
429
- 'Use Write for large changes'
430
- ]
431
- },
432
- WebSearch: {
433
- name: 'WebSearch',
434
- description: 'Search the web for information',
435
- parameters: {
436
- query: {
437
- type: 'string',
438
- description: 'Search query',
439
- required: true
440
- },
441
- num: {
442
- type: 'number',
443
- description: 'Number of results to return',
444
- required: false,
445
- default: 5
446
- }
447
- },
448
- usage: 'Find information online, research topics, or get current data',
449
- examples: [
450
- 'Search web: WebSearch(query="latest Node.js version")',
451
- 'Multiple results: WebSearch(query="React best practices", num=10)'
452
- ],
453
- bestPractices: [
454
- 'Use specific search queries',
455
- 'Limit results for focused answers',
456
- 'Verify information from multiple sources'
457
- ]
458
- },
459
- WebFetch: {
460
- name: 'WebFetch',
461
- description: 'Fetch and process URL content',
462
- parameters: {
463
- prompt: {
464
- type: 'string',
465
- description: 'Prompt containing URL to fetch',
466
- required: true
467
- }
468
- },
469
- usage: 'Retrieve content from web pages, APIs, or online resources',
470
- examples: [
471
- 'Fetch URL: WebFetch(prompt="https://api.example.com/data")'
472
- ],
473
- bestPractices: [
474
- 'Handle network errors gracefully',
475
- 'Validate URL format',
476
- 'Consider rate limiting'
477
- ]
478
- },
479
- TodoWrite: {
480
- name: 'TodoWrite',
481
- description: 'Create and manage structured task lists',
482
- parameters: {
483
- todos: {
484
- type: 'array',
485
- description: 'Array of todo items with id, task, status, and priority',
486
- required: true
487
- }
488
- },
489
- usage: 'Plan and track complex multi-step tasks',
490
- examples: [
491
- 'Create todos: TodoWrite(todos=[{"id":"1","task":"Install dependencies","status":"pending","priority":"high"}])'
492
- ],
493
- bestPractices: [
494
- 'Use descriptive task names',
495
- 'Set appropriate priorities',
496
- 'Update status as tasks progress',
497
- 'Break down complex tasks into smaller steps'
498
- ]
499
- },
500
- Task: {
501
- name: 'Task',
502
- description: 'Launch a specialized agent to handle specific tasks',
503
- parameters: {
504
- description: {
505
- type: 'string',
506
- description: 'Brief description of the task',
507
- required: true
508
- },
509
- query: {
510
- type: 'string',
511
- description: 'Detailed task instructions',
512
- required: true
513
- },
514
- subagent_type: {
515
- type: 'string',
516
- description: 'Type of agent to use (plan-agent, explore-agent, frontend-tester, code-reviewer, frontend-developer, backend-developer)',
517
- required: true
518
- },
519
- response_language: {
520
- type: 'string',
521
- description: 'Language for the response',
522
- required: false
523
- }
524
- },
525
- usage: 'Delegate specialized tasks to expert agents',
526
- examples: [
527
- 'Plan task: Task(description="Create implementation plan", query="Create a detailed plan for implementing user authentication system", subagent_type="plan-agent")',
528
- 'Explore codebase: Task(description="Explore auth module", query="Find and analyze all authentication-related code in the codebase", subagent_type="explore-agent")',
529
- 'Run tests: Task(description="Create component tests", query="Write unit tests for the Button component including edge cases", subagent_type="frontend-tester")'
530
- ],
531
- bestPractices: [
532
- 'Choose appropriate agent type for the task',
533
- 'Provide clear task descriptions',
534
- 'Specify desired response language if needed',
535
- 'Review agent results carefully'
536
- ]
537
- },
538
- InvokeSkill: (() => {
539
- // Build skills list string from the provided skills, including description
540
- const skillsList = skills && skills.length > 0
541
- ? skills.map(skill => `- ${skill.name}: ${skill.description}`).join('\n')
542
- : 'No skills available';
543
-
544
- return {
545
- name: 'InvokeSkill',
546
- description: 'Invoke a specialized skill to get execution guidance for complex tasks. The skill will analyze your request and provide a step-by-step execution plan - you must follow these steps to complete the task.\n\n**Workflow**: Invoke skill → Receive guidance with nextSteps → Execute each step using appropriate tools → Complete the task.\n\n**Available Skills**:\n' + skillsList,
547
- parameters: {
548
- skillId: {
549
- type: 'string',
550
- description: 'The skill identifier (e.g., "docx", "pdf", "pptx", "frontend-design")',
551
- required: true
552
- },
553
- taskDescription: {
554
- type: 'string',
555
- description: 'Detailed description of what to accomplish',
556
- required: true
557
- }
558
- },
559
- usage: 'Use when facing complex domain-specific tasks. The skill provides guidance - you must execute the steps yourself.',
560
- examples: skills && skills.length > 0
561
- ? skills.slice(0, 3).map(skill =>
562
- `InvokeSkill(skillId="${skill.id}", taskDescription="...")`
563
- )
564
- : [],
565
- bestPractices: [
566
- 'Invoke skill to get step-by-step execution guidance',
567
- 'Follow the nextSteps provided by the skill in order',
568
- 'Use appropriate tools (Read/Write/Run) to execute each step'
569
- ]
570
- };
571
- })()
572
- };
573
-
574
- // Fallback for unknown tools (including MCP tools)
575
- // Get the tool name - for MCP tools it's in function.name, for local tools it's in name
576
- const actualToolName = tool.function?.name || tool.name;
577
- // Get the description - for MCP tools it's in function.description, for local tools it's in description
578
- const actualDescription = tool.function?.description || tool.description;
579
-
580
- if (schemas[actualToolName]) {
581
- return schemas[actualToolName];
582
- }
583
-
584
- // Convert MCP inputSchema to parameters format
585
- const parameters: Record<string, ToolParameter> = {};
586
- if (tool.inputSchema?.properties) {
587
- for (const [paramName, paramDef] of Object.entries<any>(tool.inputSchema.properties)) {
588
- parameters[paramName] = {
589
- type: paramDef.type || 'any',
590
- description: paramDef.description || '',
591
- required: tool.inputSchema.required?.includes(paramName) || false,
592
- enum: paramDef.enum
593
- };
594
- }
595
- }
596
-
597
- return {
598
- name: actualToolName,
599
- description: actualDescription,
600
- parameters,
601
- usage: `Use ${actualToolName} for related tasks`,
602
- examples: [],
603
- bestPractices: []
604
- };
605
- }
606
-
607
- private generateToolUsageGuide(toolSchemas: ToolSchema[], skillInstructions: string = ''): string {
608
- let guide = '## Available Tools\n\n';
609
- guide += 'You have access to the following tools. Use them to accomplish user requests:\n\n';
610
-
611
- // Separate tools: regular tools first, then InvokeSkill (to be followed by Available Skills), then MCP tools
612
- const invokeSkillSchema = toolSchemas.find(s => s.name === 'InvokeSkill');
613
- // MCP tools are registered with _mcp{N} suffix to avoid name conflicts
614
- const mcpSchemas = toolSchemas.filter(s => s.name !== 'InvokeSkill' && /_mcp\d*$/.test(s.name));
615
- const regularSchemas = toolSchemas.filter(s => s.name !== 'InvokeSkill' && !mcpSchemas.includes(s));
616
-
617
- // Generate guide for regular tools
618
- regularSchemas.forEach((schema, index) => {
619
- guide += this.formatToolSchema(schema);
620
- if (index < regularSchemas.length - 1 || invokeSkillSchema || mcpSchemas.length > 0) {
621
- guide += '\n---\n\n';
622
- }
623
- });
624
-
625
- // Generate guide for InvokeSkill (put it before Available Skills)
626
- if (invokeSkillSchema) {
627
- guide += this.formatToolSchema(invokeSkillSchema);
628
- // Add Available Skills immediately after InvokeSkill (no separator)
629
- if (skillInstructions) {
630
- guide += `\n${skillInstructions}\n`;
631
- }
632
- // Add separator after Available Skills if there are MCP tools
633
- if (mcpSchemas.length > 0) {
634
- guide += '\n---\n\n';
635
- }
636
- }
637
-
638
- // Generate guide for MCP tools
639
- mcpSchemas.forEach((schema, index) => {
640
- guide += this.formatToolSchema(schema);
641
- if (index < mcpSchemas.length - 1) {
642
- guide += '\n---\n\n';
643
- }
644
- });
645
-
646
- return guide;
647
- }
648
-
649
- private formatToolSchema(schema: ToolSchema): string {
650
- let output = `### ${schema.name}\n\n`;
651
- output += `**Description**: ${schema.description}\n\n`;
652
- output += `**Usage**: ${schema.usage}\n\n`;
653
- output += `**Parameters**:\n`;
654
-
655
- Object.entries(schema.parameters).forEach(([paramName, param]) => {
656
- const required = param.required ? ' (required)' : ' (optional)';
657
- const defaultValue = param.default !== undefined ? ` (default: ${param.default})` : '';
658
- output += `- \`${paramName}\`${required}${defaultValue}: ${param.description}\n`;
659
- });
660
-
661
- if (schema.examples.length > 0) {
662
- output += `\n**Examples**:\n`;
663
- schema.examples.forEach(example => {
664
- output += `- ${example}\n`;
665
- });
666
- }
667
-
668
- if (schema.bestPractices.length > 0) {
669
- output += `\n**Best Practices**:\n`;
670
- schema.bestPractices.forEach(practice => {
671
- output += `- ${practice}\n`;
672
- });
673
- }
674
-
675
- return output;
676
- }
677
-
678
- private generateDecisionMakingGuide(availableTools: any[], skills: SkillInfo[] = []): string {
679
- // Tool name to short description mapping
680
- const toolDescriptions: Record<string, string> = {
681
- 'Read': 'When you need to understand existing code, configuration, or documentation',
682
- 'Write': 'When creating new files or completely replacing existing content',
683
- 'Grep': 'When searching for specific patterns, function names, or text across files',
684
- 'Bash': 'When running tests, installing dependencies, building projects, or executing terminal commands',
685
- 'SearchCodebase': 'When finding code by meaning rather than exact text matches',
686
- 'ListDirectory': 'When exploring project structure or finding files',
687
- 'Replace': 'When making targeted edits without rewriting entire files',
688
- 'web_search': 'When you need current information from the internet',
689
- 'web_fetch': 'When retrieving content from specific URLs',
690
- 'todo_write': 'When planning and tracking complex multi-step tasks',
691
- 'task': 'When delegating specialized work to expert agents',
692
- 'DeleteFile': 'When you need to remove a file from the filesystem',
693
- 'CreateDirectory': 'When you need to create a new directory or folder structure',
694
- 'ReadBashOutput': 'When you need to read the output of a background task',
695
- 'ask_user_question': 'When you need to ask the user for clarification or decisions',
696
- 'save_memory': 'When you need to remember important information for future sessions',
697
- 'exit_plan_mode': 'When you have completed planning and are ready to execute',
698
- 'xml_escape': 'When you need to escape special characters in XML/HTML files',
699
- 'image_read': 'When you need to analyze or read image files',
700
- 'InvokeSkill': 'When you need to use specialized skills for domain tasks (see InvokeSkill tool description for available skills)'
701
- };
702
-
703
- // Generate based on available tools "When to Use Tools" 部分
704
- let toolsSection = '### When to Use Tools\n';
705
- if (availableTools.length > 0) {
706
- for (const tool of availableTools) {
707
- const description = toolDescriptions[tool.name] || `When you need to use ${tool.name}`;
708
- toolsSection += `- **${tool.name}**: ${description}\n`;
709
- }
710
- } else {
711
- toolsSection += '- (No tools available in current mode)\n';
712
- }
713
-
714
- return `## Decision Making Guide
715
-
716
- ${toolsSection}
717
-
718
- ### CRITICAL: IMMEDIATE TOOL EXECUTION
719
- **YOU MUST CALL TOOLS IMMEDIATELY when needed - DO NOT say "let me..." or "I will..." first!**
720
-
721
- When a user asks you to:
722
- - Read files → IMMEDIATELY call Read tool with the file path
723
- - List directory IMMEDIATELY call ListDirectory tool
724
- - Search code → IMMEDIATELY call Grep or SearchCodebase tool
725
- - Run commands → IMMEDIATELY call Bash tool
726
- - Introduce the codebase → IMMEDIATELY call ListDirectory and Read tools
727
- - Analyze the project → IMMEDIATELY call ListDirectory and Read tools
728
- - Explain the code IMMEDIATELY call Read tools
729
- - Any action requiring tools → IMMEDIATELY make the tool call
730
-
731
- **ABSOLUTELY WRONG**:
732
- - "Let me explore the codebase structure first" (then do nothing)
733
- - "I will read the file for you" (then do nothing)
734
- - "Let me help you explore this codebase" (then do nothing)
735
- - "Let me understand this first" (then do nothing)
736
-
737
- **ABSOLUTELY CORRECT**:
738
- - Call ListDirectory(path=".") immediately to explore the structure
739
- - Call Read(filePath="path/to/file") immediately to read the file
740
- - Call ListDirectory(path=".") immediately when asked to introduce the codebase
741
-
742
- **ZERO DELAY POLICY**:
743
- - Do NOT add any conversational filler before tool calls
744
- - Do NOT say "I'm going to" or "Let me" - just CALL THE TOOL
745
- - Your response should START with the tool call, not with a statement about what you'll do
746
-
747
- ### Tool Selection Strategy
748
- 1. **Analyze the user's request** - Understand what they want to accomplish
749
- 2. **Identify the core task** - Determine the primary action needed
750
- 3. **Choose the most appropriate tool** - Select the tool that best matches the task
751
- 4. **Consider dependencies** - Some tasks require multiple tools in sequence
752
- 5. **Plan the execution order** - Determine the logical sequence of tool calls
753
- 6. **EXECUTE IMMEDIATELY** - Make the tool call right away without delay
754
-
755
- ### Common Patterns
756
- - **Code exploration**: ListDirectory → Read → Grep/SearchCodebase
757
- - **Feature implementation**: Read (existing code) → Write (new files) → Bash (test)
758
- - **Bug fixing**: Grep/SearchCodebase (find issue) Read (understand) Replace/Write (fix) → Bash (verify)
759
- - **Project setup**: WebSearch (research) Write (create files) Bash (install/build)
760
- - **Documentation**: Read (code) Write (docs)
761
-
762
- ### Error Handling
763
- - If a tool fails, analyze the error message
764
- - Try alternative approaches or parameters
765
- - Ask the user for clarification if needed
766
- - Report errors clearly with context`;
767
- }
768
-
769
- private generateExecutionStrategy(): string {
770
- return `## Execution Strategy
771
-
772
- ### Step-by-Step Approach
773
- 1. **Understand the goal**: Clarify what the user wants to achieve
774
- 2. **Plan the approach**: Break down complex tasks into smaller steps
775
- 3. **Execute systematically**: Use tools in the right order
776
- 4. **Verify results**: Check each step before proceeding
777
- 5. **Report progress**: Keep the user informed of your actions
778
-
779
- ### Efficiency Tips
780
- - **Batch similar operations**: Group related tool calls together
781
- - **Avoid redundant calls**: Don't read the same file multiple times
782
- - **Use context wisely**: Leverage information from previous tool results
783
- - **Be precise**: Use specific parameters to get exactly what you need
784
-
785
- ### Quality Assurance
786
- - Always verify tool outputs match expectations
787
- - Test code changes before declaring success
788
- - Check for edge cases and error conditions
789
- - Ensure changes don't break existing functionality
790
-
791
- ### Communication
792
- - Explain your reasoning before taking action
793
- - Provide clear summaries of what you've done
794
- - Highlight any assumptions you've made
795
- - Ask for confirmation on destructive operations`;
796
- }
797
-
798
- getToolDefinitions(): any[] {
799
- const tools = this.toolRegistry.getAll().filter(
800
- tool => tool.allowedModes.includes(this.executionMode)
801
- );
802
-
803
- const localTools = tools.map(tool => {
804
- const schema = this.createToolSchema(tool);
805
- const properties: Record<string, any> = {};
806
- const required: string[] = [];
807
-
808
- Object.entries(schema.parameters).forEach(([paramName, param]) => {
809
- properties[paramName] = {
810
- type: param.type,
811
- description: param.description
812
- };
813
-
814
- if (param.enum) {
815
- properties[paramName].enum = param.enum;
816
- }
817
-
818
- if (param.default !== undefined) {
819
- properties[paramName].default = param.default;
820
- }
821
-
822
- if (param.required) {
823
- required.push(paramName);
824
- }
825
- });
826
-
827
- return {
828
- type: 'function',
829
- function: {
830
- name: tool.name,
831
- description: schema.description + '. ' + schema.usage,
832
- parameters: {
833
- type: 'object',
834
- properties,
835
- required
836
- }
837
- }
838
- };
839
- });
840
-
841
- return localTools;
842
- }
843
- }
1
+ import { ToolRegistry } from './tools.js';
2
+ import { ExecutionMode, AgentConfig } from './types.js';
3
+ import { getAgentManager } from './agents.js';
4
+ import { getSkillInvoker, SkillInfo } from './skill-invoker.js';
5
+ import { MCPManager } from './mcp.js';
6
+
7
+ export interface ToolParameter {
8
+ type: string;
9
+ description: string;
10
+ required?: boolean;
11
+ enum?: string[];
12
+ default?: any;
13
+ }
14
+
15
+ export interface ToolSchema {
16
+ name: string;
17
+ description: string;
18
+ parameters: Record<string, ToolParameter>;
19
+ usage: string;
20
+ examples: string[];
21
+ bestPractices: string[];
22
+ }
23
+
24
+ export class SystemPromptGenerator {
25
+ private toolRegistry: ToolRegistry;
26
+ private executionMode: ExecutionMode;
27
+ private agentConfig?: AgentConfig;
28
+ private mcpManager?: MCPManager;
29
+
30
+ constructor(toolRegistry: ToolRegistry, executionMode: ExecutionMode, agentConfig?: AgentConfig, mcpManager?: MCPManager) {
31
+ this.toolRegistry = toolRegistry;
32
+ this.executionMode = executionMode;
33
+ this.agentConfig = agentConfig;
34
+ this.mcpManager = mcpManager;
35
+ }
36
+
37
+ async generateEnhancedSystemPrompt(baseSystemPrompt: string): Promise<string> {
38
+ let localTools = this.toolRegistry.getAll().filter(
39
+ tool => tool.allowedModes.includes(this.executionMode)
40
+ );
41
+
42
+ if (this.agentConfig) {
43
+ const agentManager = getAgentManager();
44
+ const allowedToolNames = agentManager.getAvailableToolsForAgent(this.agentConfig, this.executionMode);
45
+ localTools = localTools.filter(tool => allowedToolNames.includes(tool.name));
46
+ }
47
+
48
+ // Get available local tools (includes MCP wrapper tools registered via registerMCPTools)
49
+ let allAvailableTools = localTools;
50
+
51
+ let enhancedPrompt = baseSystemPrompt;
52
+
53
+ // Only add tool-related content if tools are available
54
+ if (allAvailableTools.length > 0) {
55
+ // Pre-load skills for dynamic generation
56
+ const skillInvoker = getSkillInvoker();
57
+ await skillInvoker.initialize();
58
+ const availableSkills = await skillInvoker.listAvailableSkills();
59
+
60
+ const toolSchemas = this.getToolSchemas(allAvailableTools, availableSkills);
61
+ const toolUsageGuide = this.generateToolUsageGuide(toolSchemas);
62
+ const decisionMakingGuide = this.generateDecisionMakingGuide(localTools, availableSkills);
63
+ const executionStrategy = this.generateExecutionStrategy();
64
+
65
+
66
+ enhancedPrompt += `
67
+
68
+ ${toolUsageGuide}
69
+
70
+ ${decisionMakingGuide}
71
+
72
+ ${executionStrategy}
73
+
74
+
75
+
76
+ ## Important Notes
77
+ - Always verify tool results before proceeding to next steps
78
+ - If a tool fails, analyze the error and try alternative approaches
79
+ - Use tools efficiently - avoid redundant calls
80
+ - When in doubt, ask the user for clarification
81
+ - Maintain context across tool calls to build a coherent solution`;
82
+ } else {
83
+ // No tools available - explicitly tell the AI not to use tools
84
+ enhancedPrompt += `
85
+
86
+ ## IMPORTANT: READ-ONLY MODE
87
+
88
+ You are in DEFAULT mode (read-only mode). You CANNOT use any tools or functions.
89
+
90
+ STRICT PROHIBITIONS:
91
+ - DO NOT attempt to call any functions, tools, or commands
92
+ - DO NOT output any tool call syntax, such as:
93
+ - <function_calls>...</function_calls>
94
+ - ToolName(params)
95
+ - Function call format
96
+ - Any similar syntax
97
+ - DO NOT simulate tool calls or pretend to use tools
98
+ - DO NOT output code that would execute tools
99
+
100
+ REQUIRED BEHAVIOR:
101
+ - Respond ONLY with plain text
102
+ - Answer questions based on your knowledge
103
+ - If you need to read files or perform actions, ask the user to switch modes
104
+ - Tell the user they can use "/mode yolo" or "/mode accept_edits" to enable tools
105
+
106
+ Remember: You are in a conversational mode, not a tool-execution mode. Just talk to the user!`;
107
+ }
108
+
109
+ return enhancedPrompt;
110
+ }
111
+
112
+ private getToolSchemas(tools: any[], skills: SkillInfo[] = []): ToolSchema[] {
113
+ return tools.map(tool => this.createToolSchema(tool, skills));
114
+ }
115
+
116
+ //
117
+ // createToolSchema
118
+ //
119
+ // This method defines custom schemas for each tool, which OVERRIDES the description
120
+ // property defined in tools.ts (e.g., ReadTool.description, WriteTool.description).
121
+ //
122
+ // The tool.description from tools.ts is read but NOT directly used - instead, we use
123
+ // the schemas defined here. This allows for:
124
+ // 1. Dynamic content (like skills list in InvokeSkill)
125
+ // 2. Customized descriptions for better LLM understanding
126
+ // 3. Consistent formatting across all tools
127
+ //
128
+ // Only tools NOT in the schemas object will fall back to using tool.description.
129
+ //
130
+ private createToolSchema(tool: any, skills: SkillInfo[] = []): ToolSchema {
131
+ const schemas: Record<string, ToolSchema> = {
132
+ Read: {
133
+ name: 'Read',
134
+ description: 'Read the contents of a file from the filesystem',
135
+ parameters: {
136
+ filePath: {
137
+ type: 'string',
138
+ description: 'Path to the file to read (relative or absolute)',
139
+ required: true
140
+ },
141
+ offset: {
142
+ type: 'number',
143
+ description: 'Starting line number (0-indexed)',
144
+ required: false,
145
+ default: 0
146
+ },
147
+ limit: {
148
+ type: 'number',
149
+ description: 'Maximum number of lines to read',
150
+ required: false
151
+ }
152
+ },
153
+ usage: 'Use this tool when you need to examine file contents, understand code structure, or read configuration files',
154
+ examples: [
155
+ 'Read a specific file: Read(filePath="package.json")',
156
+ 'Read with line range: Read(filePath="src/index.ts", offset=100, limit=50)'
157
+ ],
158
+ bestPractices: [
159
+ 'Always check if file exists before reading',
160
+ 'Use offset and limit for large files to avoid excessive output',
161
+ 'Read configuration files first to understand project structure'
162
+ ]
163
+ },
164
+ Write: {
165
+ name: 'Write',
166
+ description: 'Write content to a file, creating directories if needed',
167
+ parameters: {
168
+ filePath: {
169
+ type: 'string',
170
+ description: 'Path where the file should be written',
171
+ required: true
172
+ },
173
+ content: {
174
+ type: 'string',
175
+ description: 'Content to write to the file',
176
+ required: true
177
+ }
178
+ },
179
+ usage: 'Use this tool to create new files or completely overwrite existing files',
180
+ examples: [
181
+ 'Create a new file: Write(filePath="src/utils.ts", content="export function helper() {}")',
182
+ 'Write configuration: Write(filePath=".env", content="API_KEY=secret")'
183
+ ],
184
+ bestPractices: [
185
+ 'Always read existing file before overwriting',
186
+ 'Use proper file extensions',
187
+ 'Create necessary directory structures',
188
+ 'Include appropriate comments and documentation'
189
+ ]
190
+ },
191
+ Grep: {
192
+ name: 'Grep',
193
+ description: 'Search for text patterns across multiple files',
194
+ parameters: {
195
+ pattern: {
196
+ type: 'string',
197
+ description: 'Text pattern or regex to search for',
198
+ required: true
199
+ },
200
+ path: {
201
+ type: 'string',
202
+ description: 'Directory path to search in',
203
+ required: false,
204
+ default: '.'
205
+ },
206
+ include: {
207
+ type: 'string',
208
+ description: 'File pattern to include (glob)',
209
+ required: false
210
+ },
211
+ exclude: {
212
+ type: 'string',
213
+ description: 'File pattern to exclude (glob)',
214
+ required: false
215
+ },
216
+ case_sensitive: {
217
+ type: 'boolean',
218
+ description: 'Whether search is case-sensitive',
219
+ required: false,
220
+ default: false
221
+ },
222
+ fixed_strings: {
223
+ type: 'boolean',
224
+ description: 'Treat pattern as literal string, not regex',
225
+ required: false,
226
+ default: false
227
+ },
228
+ context: {
229
+ type: 'number',
230
+ description: 'Number of context lines before and after match',
231
+ required: false
232
+ },
233
+ before: {
234
+ type: 'number',
235
+ description: 'Number of context lines before match',
236
+ required: false
237
+ },
238
+ after: {
239
+ type: 'number',
240
+ description: 'Number of context lines after match',
241
+ required: false
242
+ },
243
+ no_ignore: {
244
+ type: 'boolean',
245
+ description: 'Ignore .gitignore patterns',
246
+ required: false,
247
+ default: false
248
+ }
249
+ },
250
+ usage: 'Search for code patterns, function definitions, or text across the codebase',
251
+ examples: [
252
+ 'Search for function: Grep(pattern="function.*\\(.*\\)")',
253
+ 'Find specific text: Grep(pattern="TODO", case_sensitive=true)',
254
+ 'Search in specific files: Grep(pattern="import", include="*.ts")'
255
+ ],
256
+ bestPractices: [
257
+ 'Use fixed_strings for simple text searches',
258
+ 'Use context to see surrounding code',
259
+ 'Narrow search with include/exclude patterns',
260
+ 'Use case_sensitive for exact matches'
261
+ ]
262
+ },
263
+ Bash: {
264
+ name: 'Bash',
265
+ description: 'Execute shell commands in the terminal',
266
+ parameters: {
267
+ command: {
268
+ type: 'string',
269
+ description: 'Shell command to execute',
270
+ required: true
271
+ },
272
+ cwd: {
273
+ type: 'string',
274
+ description: 'Working directory for command execution',
275
+ required: false
276
+ },
277
+ description: {
278
+ type: 'string',
279
+ description: 'Human-readable description of what the command does',
280
+ required: false
281
+ },
282
+ timeout: {
283
+ type: 'number',
284
+ description: 'Timeout in seconds (default: 120)',
285
+ required: false,
286
+ default: 120
287
+ },
288
+ run_in_bg: {
289
+ type: 'boolean',
290
+ description: 'Run command in background',
291
+ required: false,
292
+ default: false
293
+ }
294
+ },
295
+ usage: 'Execute terminal commands, run tests, install dependencies, or perform system operations',
296
+ examples: [
297
+ 'Install dependencies: Bash(command="npm install")',
298
+ 'Run tests: Bash(command="npm test", description="Run unit tests")',
299
+ 'Build project: Bash(command="npm run build")'
300
+ ],
301
+ bestPractices: [
302
+ 'Always provide clear descriptions',
303
+ 'Use appropriate timeout values',
304
+ 'Check command exit codes',
305
+ 'Handle errors gracefully',
306
+ 'Use run_in_bg for long-running processes'
307
+ ]
308
+ },
309
+ ListDirectory: {
310
+ name: 'ListDirectory',
311
+ description: 'List files and directories in a given path',
312
+ parameters: {
313
+ path: {
314
+ type: 'string',
315
+ description: 'Directory path to list',
316
+ required: true
317
+ },
318
+ recursive: {
319
+ type: 'boolean',
320
+ description: 'List recursively',
321
+ required: false,
322
+ default: false
323
+ }
324
+ },
325
+ usage: 'Explore directory structure, find files, or understand project layout',
326
+ examples: [
327
+ 'List current directory: ListDirectory(path=".")',
328
+ 'List recursively: ListDirectory(path="src", recursive=true)'
329
+ ],
330
+ bestPractices: [
331
+ 'Use recursive for deep exploration',
332
+ 'Combine with Read to examine files',
333
+ 'Check directory existence first'
334
+ ]
335
+ },
336
+ SearchFiles: {
337
+ name: 'SearchFiles',
338
+ description: 'Search for files matching a glob pattern by name or extension',
339
+ parameters: {
340
+ pattern: {
341
+ type: 'string',
342
+ description: 'Glob pattern to match files (e.g., "**/*.ts")',
343
+ required: true
344
+ },
345
+ path: {
346
+ type: 'string',
347
+ description: 'Directory to search in',
348
+ required: false
349
+ },
350
+ limit: {
351
+ type: 'number',
352
+ description: 'Maximum number of results to return',
353
+ required: false
354
+ }
355
+ },
356
+ usage: 'Find files by name or extension pattern',
357
+ examples: [
358
+ 'Find all TypeScript files: SearchFiles(pattern="**/*.ts")',
359
+ 'Find config files: SearchFiles(pattern="**/config.*")'
360
+ ],
361
+ bestPractices: [
362
+ 'Use **/*.ts for recursive search',
363
+ 'Combine with Read to examine found files'
364
+ ]
365
+ },
366
+ DeleteFile: {
367
+ name: 'DeleteFile',
368
+ description: 'Delete a file from the filesystem',
369
+ parameters: {
370
+ filePath: {
371
+ type: 'string',
372
+ description: 'Path to the file to delete',
373
+ required: true
374
+ }
375
+ },
376
+ usage: 'Remove files that are no longer needed',
377
+ examples: [
378
+ 'Delete file: DeleteFile(filePath="old-file.txt")'
379
+ ],
380
+ bestPractices: [
381
+ 'Verify file is not needed before deletion',
382
+ 'Use with caution - deletion is permanent',
383
+ 'Consider backing up important files'
384
+ ]
385
+ },
386
+ CreateDirectory: {
387
+ name: 'CreateDirectory',
388
+ description: 'Create a directory and any necessary parent directories',
389
+ parameters: {
390
+ path: {
391
+ type: 'string',
392
+ description: 'Directory path to create',
393
+ required: true
394
+ }
395
+ },
396
+ usage: 'Create directory structures for organizing files',
397
+ examples: [
398
+ 'Create directory: CreateDirectory(path="src/components")'
399
+ ],
400
+ bestPractices: [
401
+ 'Use descriptive directory names',
402
+ 'Follow project conventions',
403
+ 'Create necessary parent directories automatically'
404
+ ]
405
+ },
406
+ edit: {
407
+ name: 'Edit',
408
+ description: 'Edit a file by replacing exact text',
409
+ parameters: {
410
+ file_path: {
411
+ type: 'string',
412
+ description: 'Path to the file to modify',
413
+ required: true
414
+ },
415
+ instruction: {
416
+ type: 'string',
417
+ description: 'Description of what to change',
418
+ required: true
419
+ },
420
+ old_string: {
421
+ type: 'string',
422
+ description: 'The exact text to find and replace',
423
+ required: true
424
+ },
425
+ new_string: {
426
+ type: 'string',
427
+ description: 'Text to replace with',
428
+ required: true
429
+ }
430
+ },
431
+ usage: 'Make targeted edits to files without rewriting entire content',
432
+ examples: [
433
+ 'Edit text: Edit(file_path="config.json", instruction="Update API endpoint", old_string="old value", new_string="new value")'
434
+ ],
435
+ bestPractices: [
436
+ 'Use unique old_string to avoid multiple replacements',
437
+ 'Read file first to verify content',
438
+ 'Use Write for large changes'
439
+ ]
440
+ },
441
+ WebSearch: {
442
+ name: 'WebSearch',
443
+ description: 'Search the web for information',
444
+ parameters: {
445
+ query: {
446
+ type: 'string',
447
+ description: 'Search query',
448
+ required: true
449
+ },
450
+ num: {
451
+ type: 'number',
452
+ description: 'Number of results to return',
453
+ required: false,
454
+ default: 5
455
+ }
456
+ },
457
+ usage: 'Find information online, research topics, or get current data',
458
+ examples: [
459
+ 'Search web: WebSearch(query="latest Node.js version")',
460
+ 'Multiple results: WebSearch(query="React best practices", num=10)'
461
+ ],
462
+ bestPractices: [
463
+ 'Use specific search queries',
464
+ 'Limit results for focused answers',
465
+ 'Verify information from multiple sources'
466
+ ]
467
+ },
468
+ WebFetch: {
469
+ name: 'WebFetch',
470
+ description: 'Fetch and process URL content',
471
+ parameters: {
472
+ prompt: {
473
+ type: 'string',
474
+ description: 'Prompt containing URL to fetch',
475
+ required: true
476
+ }
477
+ },
478
+ usage: 'Retrieve content from web pages, APIs, or online resources',
479
+ examples: [
480
+ 'Fetch URL: WebFetch(prompt="https://api.example.com/data")'
481
+ ],
482
+ bestPractices: [
483
+ 'Handle network errors gracefully',
484
+ 'Validate URL format',
485
+ 'Consider rate limiting'
486
+ ]
487
+ },
488
+ TodoWrite: {
489
+ name: 'TodoWrite',
490
+ description: 'Create and manage structured task lists',
491
+ parameters: {
492
+ todos: {
493
+ type: 'array',
494
+ description: 'Array of todo items with id, task, status, and priority',
495
+ required: true
496
+ }
497
+ },
498
+ usage: 'Plan and track complex multi-step tasks',
499
+ examples: [
500
+ 'Create todos: TodoWrite(todos=[{"id":"1","task":"Install dependencies","status":"pending","priority":"high"}])'
501
+ ],
502
+ bestPractices: [
503
+ 'Use descriptive task names',
504
+ 'Set appropriate priorities',
505
+ 'Update status as tasks progress',
506
+ 'Break down complex tasks into smaller steps'
507
+ ]
508
+ },
509
+ Task: {
510
+ name: 'Task',
511
+ description: 'Launch a specialized agent to handle specific tasks',
512
+ parameters: {
513
+ description: {
514
+ type: 'string',
515
+ description: 'Brief description of the task',
516
+ required: true
517
+ },
518
+ query: {
519
+ type: 'string',
520
+ description: 'Detailed task instructions',
521
+ required: true
522
+ },
523
+ subagent_type: {
524
+ type: 'string',
525
+ description: 'Type of agent to use (plan-agent, explore-agent, frontend-tester, code-reviewer, frontend-developer, backend-developer)',
526
+ required: true
527
+ },
528
+ response_language: {
529
+ type: 'string',
530
+ description: 'Language for the response',
531
+ required: false
532
+ }
533
+ },
534
+ usage: 'Delegate specialized tasks to expert agents',
535
+ examples: [
536
+ 'Plan task: Task(description="Create implementation plan", query="Create a detailed plan for implementing user authentication system", subagent_type="plan-agent")',
537
+ 'Explore codebase: Task(description="Explore auth module", query="Find and analyze all authentication-related code in the codebase", subagent_type="explore-agent")',
538
+ 'Run tests: Task(description="Create component tests", query="Write unit tests for the Button component including edge cases", subagent_type="frontend-tester")'
539
+ ],
540
+ bestPractices: [
541
+ 'Choose appropriate agent type for the task',
542
+ 'Provide clear task descriptions',
543
+ 'Specify desired response language if needed',
544
+ 'Review agent results carefully'
545
+ ]
546
+ },
547
+ InvokeSkill: (() => {
548
+ // Build skills list string from the provided skills, including description
549
+ const skillsList = skills && skills.length > 0
550
+ ? skills.map(skill => `- ${skill.name}: ${skill.description}`).join('\n')
551
+ : 'No skills available';
552
+
553
+ return {
554
+ name: 'InvokeSkill',
555
+ description: 'Invoke a specialized skill to get execution guidance for complex tasks. The skill will analyze your request and provide a step-by-step execution plan - you must follow these steps to complete the task.\n\n**Workflow**: Invoke skill → Receive guidance with nextSteps → Execute each step using appropriate tools → Complete the task.\n\n**Available Skills**:\n' + skillsList,
556
+ parameters: {
557
+ skillId: {
558
+ type: 'string',
559
+ description: 'The skill identifier (e.g., "docx", "pdf", "pptx", "frontend-design")',
560
+ required: true
561
+ },
562
+ taskDescription: {
563
+ type: 'string',
564
+ description: 'Detailed description of what to accomplish',
565
+ required: true
566
+ }
567
+ },
568
+ usage: 'Use when facing complex domain-specific tasks. The skill provides guidance - you must execute the steps yourself.',
569
+ examples: skills && skills.length > 0
570
+ ? skills.slice(0, 3).map(skill =>
571
+ `InvokeSkill(skillId="${skill.id}", taskDescription="...")`
572
+ )
573
+ : [],
574
+ bestPractices: [
575
+ 'Invoke skill to get step-by-step execution guidance',
576
+ 'Follow the nextSteps provided by the skill in order',
577
+ 'Use appropriate tools (Read/Write/Run) to execute each step'
578
+ ]
579
+ };
580
+ })()
581
+ };
582
+
583
+ // Fallback for unknown tools (including MCP tools)
584
+ // Get the tool name - for MCP tools it's in function.name, for local tools it's in name
585
+ const actualToolName = tool.function?.name || tool.name;
586
+ // Get the description - for MCP tools it's in function.description, for local tools it's in description
587
+ const actualDescription = tool.function?.description || tool.description;
588
+
589
+ if (schemas[actualToolName]) {
590
+ return schemas[actualToolName];
591
+ }
592
+
593
+ // Convert MCP inputSchema to parameters format
594
+ const parameters: Record<string, ToolParameter> = {};
595
+ if (tool.inputSchema?.properties) {
596
+ for (const [paramName, paramDef] of Object.entries<any>(tool.inputSchema.properties)) {
597
+ parameters[paramName] = {
598
+ type: paramDef.type || 'any',
599
+ description: paramDef.description || '',
600
+ required: tool.inputSchema.required?.includes(paramName) || false,
601
+ enum: paramDef.enum
602
+ };
603
+ }
604
+ }
605
+
606
+ return {
607
+ name: actualToolName,
608
+ description: actualDescription,
609
+ parameters,
610
+ usage: `Use ${actualToolName} for related tasks`,
611
+ examples: [],
612
+ bestPractices: []
613
+ };
614
+ }
615
+
616
+ private generateToolUsageGuide(toolSchemas: ToolSchema[], skillInstructions: string = ''): string {
617
+ let guide = '## Available Tools\n\n';
618
+ guide += 'You have access to the following tools. Use them to accomplish user requests:\n\n';
619
+
620
+ // Separate tools: regular tools first, then InvokeSkill (to be followed by Available Skills), then MCP tools
621
+ const invokeSkillSchema = toolSchemas.find(s => s.name === 'InvokeSkill');
622
+ // MCP tools are registered with _mcp{N} suffix to avoid name conflicts
623
+ const mcpSchemas = toolSchemas.filter(s => s.name !== 'InvokeSkill' && /_mcp\d*$/.test(s.name));
624
+ const regularSchemas = toolSchemas.filter(s => s.name !== 'InvokeSkill' && !mcpSchemas.includes(s));
625
+
626
+ // Generate guide for regular tools
627
+ regularSchemas.forEach((schema, index) => {
628
+ guide += this.formatToolSchema(schema);
629
+ if (index < regularSchemas.length - 1 || invokeSkillSchema || mcpSchemas.length > 0) {
630
+ guide += '\n---\n\n';
631
+ }
632
+ });
633
+
634
+ // Generate guide for InvokeSkill (put it before Available Skills)
635
+ if (invokeSkillSchema) {
636
+ guide += this.formatToolSchema(invokeSkillSchema);
637
+ // Add Available Skills immediately after InvokeSkill (no separator)
638
+ if (skillInstructions) {
639
+ guide += `\n${skillInstructions}\n`;
640
+ }
641
+ // Add separator after Available Skills if there are MCP tools
642
+ if (mcpSchemas.length > 0) {
643
+ guide += '\n---\n\n';
644
+ }
645
+ }
646
+
647
+ // Generate guide for MCP tools
648
+ mcpSchemas.forEach((schema, index) => {
649
+ guide += this.formatToolSchema(schema);
650
+ if (index < mcpSchemas.length - 1) {
651
+ guide += '\n---\n\n';
652
+ }
653
+ });
654
+
655
+ return guide;
656
+ }
657
+
658
+ private formatToolSchema(schema: ToolSchema): string {
659
+ let output = `### ${schema.name}\n\n`;
660
+ output += `**Description**: ${schema.description}\n\n`;
661
+ output += `**Usage**: ${schema.usage}\n\n`;
662
+ output += `**Parameters**:\n`;
663
+
664
+ Object.entries(schema.parameters).forEach(([paramName, param]) => {
665
+ const required = param.required ? ' (required)' : ' (optional)';
666
+ const defaultValue = param.default !== undefined ? ` (default: ${param.default})` : '';
667
+ output += `- \`${paramName}\`${required}${defaultValue}: ${param.description}\n`;
668
+ });
669
+
670
+ if (schema.examples.length > 0) {
671
+ output += `\n**Examples**:\n`;
672
+ schema.examples.forEach(example => {
673
+ output += `- ${example}\n`;
674
+ });
675
+ }
676
+
677
+ if (schema.bestPractices.length > 0) {
678
+ output += `\n**Best Practices**:\n`;
679
+ schema.bestPractices.forEach(practice => {
680
+ output += `- ${practice}\n`;
681
+ });
682
+ }
683
+
684
+ return output;
685
+ }
686
+
687
+ private generateDecisionMakingGuide(availableTools: any[], skills: SkillInfo[] = []): string {
688
+ // Tool name to short description mapping
689
+ const toolDescriptions: Record<string, string> = {
690
+ 'Read': 'When you need to understand existing code, configuration, or documentation',
691
+ 'Write': 'When creating new files or completely replacing existing content',
692
+ 'Grep': 'When searching for specific patterns, function names, or text across files',
693
+ 'Bash': 'When running tests, installing dependencies, building projects, or executing terminal commands',
694
+ 'SearchFiles': 'When finding files by name or extension pattern',
695
+ 'ListDirectory': 'When exploring project structure or finding files',
696
+ 'Edit': 'When making targeted edits without rewriting entire files',
697
+ 'web_search': 'When you need current information from the internet',
698
+ 'web_fetch': 'When retrieving content from specific URLs',
699
+ 'todo_write': 'When planning and tracking complex multi-step tasks',
700
+ 'task': 'When delegating specialized work to expert agents',
701
+ 'DeleteFile': 'When you need to remove a file from the filesystem',
702
+ 'CreateDirectory': 'When you need to create a new directory or folder structure',
703
+ 'ReadBashOutput': 'When you need to read the output of a background task',
704
+ 'ask_user_question': 'When you need to ask the user for clarification or decisions',
705
+ 'save_memory': 'When you need to remember important information for future sessions',
706
+ 'exit_plan_mode': 'When you have completed planning and are ready to execute',
707
+ 'xml_escape': 'When you need to escape special characters in XML/HTML files',
708
+ 'image_read': 'When you need to analyze or read image files',
709
+ 'InvokeSkill': 'When you need to use specialized skills for domain tasks (see InvokeSkill tool description for available skills)'
710
+ };
711
+
712
+ // Generate based on available tools "When to Use Tools" 部分
713
+ let toolsSection = '### When to Use Tools\n';
714
+ if (availableTools.length > 0) {
715
+ for (const tool of availableTools) {
716
+ const description = toolDescriptions[tool.name] || `When you need to use ${tool.name}`;
717
+ toolsSection += `- **${tool.name}**: ${description}\n`;
718
+ }
719
+ } else {
720
+ toolsSection += '- (No tools available in current mode)\n';
721
+ }
722
+
723
+ return `## Decision Making Guide
724
+
725
+ ${toolsSection}
726
+
727
+ ### CRITICAL: IMMEDIATE TOOL EXECUTION
728
+ **YOU MUST CALL TOOLS IMMEDIATELY when needed - DO NOT say "let me..." or "I will..." first!**
729
+
730
+ When a user asks you to:
731
+ - Read files → IMMEDIATELY call Read tool with the file path
732
+ - List directory IMMEDIATELY call ListDirectory tool
733
+ - Search files IMMEDIATELY call SearchFiles tool
734
+ - Search code IMMEDIATELY call Grep tool
735
+ - Run commands IMMEDIATELY call Bash tool
736
+ - Introduce the codebase → IMMEDIATELY call ListDirectory and Read tools
737
+ - Analyze the project → IMMEDIATELY call ListDirectory and Read tools
738
+ - Explain the code IMMEDIATELY call Read tools
739
+ - Any action requiring tools IMMEDIATELY make the tool call
740
+
741
+ **ABSOLUTELY WRONG**:
742
+ - "Let me explore the codebase structure first" (then do nothing)
743
+ - "I will read the file for you" (then do nothing)
744
+ - "Let me help you explore this codebase" (then do nothing)
745
+ - "Let me understand this first" (then do nothing)
746
+
747
+ **ABSOLUTELY CORRECT**:
748
+ - Call ListDirectory(path=".") immediately to explore the structure
749
+ - Call Read(filePath="path/to/file") immediately to read the file
750
+ - Call ListDirectory(path=".") immediately when asked to introduce the codebase
751
+
752
+ **ZERO DELAY POLICY**:
753
+ - Do NOT add any conversational filler before tool calls
754
+ - Do NOT say "I'm going to" or "Let me" - just CALL THE TOOL
755
+ - Your response should START with the tool call, not with a statement about what you'll do
756
+
757
+ ### Tool Selection Strategy
758
+ 1. **Analyze the user's request** - Understand what they want to accomplish
759
+ 2. **Identify the core task** - Determine the primary action needed
760
+ 3. **Choose the most appropriate tool** - Select the tool that best matches the task
761
+ 4. **Consider dependencies** - Some tasks require multiple tools in sequence
762
+ 5. **Plan the execution order** - Determine the logical sequence of tool calls
763
+ 6. **EXECUTE IMMEDIATELY** - Make the tool call right away without delay
764
+
765
+ ### Common Patterns
766
+ - **Code exploration**: ListDirectory Read → Grep/SearchFiles
767
+ - **Feature implementation**: Read (existing code) → Write (new files) → Bash (test)
768
+ - **Bug fixing**: Grep (find issue) → Read (understand) → Edit/Write (fix) → Bash (verify)
769
+ - **Project setup**: WebSearch (research) Write (create files) → Bash (install/build)
770
+ - **Documentation**: Read (code) → Write (docs)
771
+
772
+ ### Error Handling
773
+ - If a tool fails, analyze the error message
774
+ - Try alternative approaches or parameters
775
+ - Ask the user for clarification if needed
776
+ - Report errors clearly with context`;
777
+ }
778
+
779
+ private generateExecutionStrategy(): string {
780
+ return `## Execution Strategy
781
+
782
+ ### Step-by-Step Approach
783
+ 1. **Understand the goal**: Clarify what the user wants to achieve
784
+ 2. **Plan the approach**: Break down complex tasks into smaller steps
785
+ 3. **Execute systematically**: Use tools in the right order
786
+ 4. **Verify results**: Check each step before proceeding
787
+ 5. **Report progress**: Keep the user informed of your actions
788
+
789
+ ### Efficiency Tips
790
+ - **Batch similar operations**: Group related tool calls together
791
+ - **Avoid redundant calls**: Don't read the same file multiple times
792
+ - **Use context wisely**: Leverage information from previous tool results
793
+ - **Be precise**: Use specific parameters to get exactly what you need
794
+
795
+ ### Quality Assurance
796
+ - Always verify tool outputs match expectations
797
+ - Test code changes before declaring success
798
+ - Check for edge cases and error conditions
799
+ - Ensure changes don't break existing functionality
800
+
801
+ ### Communication
802
+ - Explain your reasoning before taking action
803
+ - Provide clear summaries of what you've done
804
+ - Highlight any assumptions you've made
805
+ - Ask for confirmation on destructive operations`;
806
+ }
807
+
808
+ getToolDefinitions(): any[] {
809
+ const tools = this.toolRegistry.getAll().filter(
810
+ tool => tool.allowedModes.includes(this.executionMode)
811
+ );
812
+
813
+ const localTools = tools.map(tool => {
814
+ const schema = this.createToolSchema(tool);
815
+ const properties: Record<string, any> = {};
816
+ const required: string[] = [];
817
+
818
+ Object.entries(schema.parameters).forEach(([paramName, param]) => {
819
+ properties[paramName] = {
820
+ type: param.type,
821
+ description: param.description
822
+ };
823
+
824
+ if (param.enum) {
825
+ properties[paramName].enum = param.enum;
826
+ }
827
+
828
+ if (param.default !== undefined) {
829
+ properties[paramName].default = param.default;
830
+ }
831
+
832
+ if (param.required) {
833
+ required.push(paramName);
834
+ }
835
+ });
836
+
837
+ return {
838
+ type: 'function',
839
+ function: {
840
+ name: tool.name,
841
+ description: schema.description + '. ' + schema.usage,
842
+ parameters: {
843
+ type: 'object',
844
+ properties,
845
+ required
846
+ }
847
+ }
848
+ };
849
+ });
850
+
851
+ return localTools;
852
+ }
853
+ }