|
15 | 15 |
|
16 | 16 | var WORKDIR = Directory.GetCurrentDirectory(); |
17 | 17 |
|
| 18 | +var SKILLDIR = Path.Combine(WORKDIR, "skills"); |
| 19 | + |
18 | 20 | var AGENTS = new Dictionary<string, JsonObject>() |
19 | 21 | { |
20 | 22 | ["explore"] = new JsonObject |
|
45 | 47 | ["plan"] = ("📋", "yellow") |
46 | 48 | }; |
47 | 49 |
|
48 | | -var SYSTEM = $""" |
49 | | - "You are a coding agent operating INSIDE the user's repository at {WORKDIR}.\n" |
50 | | - "Follow this loop strictly: plan briefly → use TOOLS to act directly on files/shell → report concise results.\n" |
51 | | - "Rules:\n" |
52 | | - "- Prefer taking actions with tools (read/write/edit/bash) over long prose.\n" |
53 | | - "- Keep outputs terse. Use bullet lists / checklists when summarizing.\n" |
54 | | - "- Use Task tool for subtasks that need focused exploration or implementation.\n" |
55 | | - "- Never invent file paths. Ask via reads or list directories first if unsure.\n" |
56 | | - "- For edits, apply the smallest change that satisfies the request.\n" |
57 | | - "- For bash, avoid destructive or privileged commands; stay inside the workspace.\n" |
58 | | - "- Use the Todo tool to maintain multi-step plans when needed.\n" |
59 | | - "- After finishing, summarize what changed and how to run or test." |
60 | | -
|
61 | | - "Task:\n" |
62 | | - {GetAgentDescription()} |
63 | | -"""; |
64 | | - |
65 | 50 | var INITIAL_REMINDER = $""" |
66 | 51 | '<reminder source="system" topic="todos">' |
67 | 52 | "System message: complex work should be tracked with the Todo tool. " |
|
95 | 80 |
|
96 | 81 | var mcp = new McpManager(WORKDIR); |
97 | 82 |
|
| 83 | +var skills = new SkillsManager(SKILLDIR); |
| 84 | + |
| 85 | +var SYSTEM = $""" |
| 86 | + "You are a coding agent operating INSIDE the user's repository at {WORKDIR}.\n" |
| 87 | + "Follow this loop strictly: plan briefly → use TOOLS to act directly on files/shell → report concise results.\n" |
| 88 | + "Rules:\n" |
| 89 | + "- Prefer taking actions with tools (read/write/edit/bash) over long prose.\n" |
| 90 | + "- Keep outputs terse. Use bullet lists / checklists when summarizing.\n" |
| 91 | + "- Use Skill tool IMMEDIATELY when a task matches a skill description.\n" |
| 92 | + "- Use Task tool for subtasks that need focused exploration or implementation.\n" |
| 93 | + "- Never invent file paths. Ask via reads or list directories first if unsure.\n" |
| 94 | + "- For edits, apply the smallest change that satisfies the request.\n" |
| 95 | + "- For bash, avoid destructive or privileged commands; stay inside the workspace.\n" |
| 96 | + "- Use the Todo tool to maintain multi-step plans when needed.\n" |
| 97 | + "- After finishing, summarize what changed and how to run or test." |
| 98 | +
|
| 99 | + "Task:\n" |
| 100 | + {GetAgentDescription()} |
| 101 | +
|
| 102 | + "Skills available (invoke with Skill tool when task matches)::\n" |
| 103 | + {skills.GetDescription()} |
| 104 | +"""; |
| 105 | + |
98 | 106 | var tools = await mcp.Regist( |
99 | 107 | (RunTodoUpdate, "TodoWriter", null), |
100 | 108 | (RunToTask, "Task", $$""" |
101 | | - {"name": "Task", |
| 109 | + { |
| 110 | + "name": "Task", |
102 | 111 | "description": "Spawn a subagent for a focused subtask. Subagents run in ISOLATED context - they don't see parent's history. Use this to keep the main conversation clean. \n Agent types: \n {{GetAgentDescription()}} \n Example uses:\n - Task(explore): \"Find all files using the auth module.\"\n - Task(plan): \"Design a migration strategy for the database\"\n - Task(code): \"Implement the user registration form\"\n ", |
103 | 112 | "arguments": { |
104 | 113 | "type": "object", |
|
110 | 119 | "required": ["description", "prompt", "agent_type"], |
111 | 120 | } |
112 | 121 | } |
| 122 | + """), |
| 123 | + (RunToSkill, "Skill", $$""" |
| 124 | + { |
| 125 | + "name": "Skill", |
| 126 | + "description": "Load a skill to gain specialized knowledge for a task. Available skills: \n {{skills.GetDescription()}} \n When to use:\n - IMMEDIATELY when user task matches a skill description.\n - Before attempting domain-specific work. (PDF, MCP, etc.)\n The skill content will be injected into the conversation, giving you detailed instructions and access to resources.", |
| 127 | + "arguments": { |
| 128 | + "type": "object", |
| 129 | + "properties": { |
| 130 | + "skill": { "type": "string", "description": "Name of the skill to load." }, |
| 131 | + }, |
| 132 | + "required": ["skill"], |
| 133 | + } |
| 134 | + } |
113 | 135 | """)); |
114 | 136 |
|
115 | 137 | var agent = new OpenAIClient( |
@@ -413,6 +435,26 @@ You are a {agentType} subagent operating INSIDE the user's repository at {WORKDI |
413 | 435 | return "(subagent returned no text)"; |
414 | 436 | } |
415 | 437 |
|
| 438 | +string RunToSkill(string skillName) |
| 439 | +{ |
| 440 | + var content = skills.GetSkillContent(skillName); |
| 441 | + |
| 442 | + if (String.IsNullOrWhiteSpace(content)) |
| 443 | + { |
| 444 | + var available = String.Join(',', skills.GetSkills()) ?? "none"; |
| 445 | + |
| 446 | + return $"Error: Unknown skill '{skillName}'. Available: {available}"; |
| 447 | + } |
| 448 | + |
| 449 | + return $""" |
| 450 | + <skill-loaded name="{skillName}"> |
| 451 | + {content} |
| 452 | + </skill-loaded> |
| 453 | +
|
| 454 | + Follow the instructions in the skill above to complete the user's task. |
| 455 | + """; |
| 456 | +} |
| 457 | + |
416 | 458 | string GetAgentDescription() |
417 | 459 | { |
418 | 460 | return String.Join("\n", AGENTS.Select(x => $"- {x.Key}: {x.Value["description"]}")); |
@@ -598,7 +640,6 @@ string EscapeMarkup(string text) |
598 | 640 | .Replace("]", "]]"); |
599 | 641 | } |
600 | 642 |
|
601 | | - |
602 | 643 | void ShowToolSpinner(string toolName) |
603 | 644 | { |
604 | 645 | AnsiConsole.Markup($"[yellow]>[/] [dim]{EscapeMarkup(toolName)} executing...[/] "); |
|
0 commit comments