minion-code 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

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 (115) hide show
  1. examples/cli_entrypoint.py +60 -0
  2. examples/{agent_with_todos.py → components/agent_with_todos.py} +58 -47
  3. examples/{message_response_children_demo.py → components/message_response_children_demo.py} +61 -55
  4. examples/components/messages_component.py +199 -0
  5. examples/file_freshness_example.py +22 -22
  6. examples/file_watching_example.py +32 -26
  7. examples/interruptible_tui.py +921 -3
  8. examples/repl_tui.py +129 -0
  9. examples/skills/example_usage.py +57 -0
  10. examples/start.py +173 -0
  11. minion_code/__init__.py +1 -1
  12. minion_code/acp_server/__init__.py +34 -0
  13. minion_code/acp_server/agent.py +539 -0
  14. minion_code/acp_server/hooks.py +354 -0
  15. minion_code/acp_server/main.py +194 -0
  16. minion_code/acp_server/permissions.py +142 -0
  17. minion_code/acp_server/test_client.py +104 -0
  18. minion_code/adapters/__init__.py +22 -0
  19. minion_code/adapters/output_adapter.py +207 -0
  20. minion_code/adapters/rich_adapter.py +169 -0
  21. minion_code/adapters/textual_adapter.py +254 -0
  22. minion_code/agents/__init__.py +2 -2
  23. minion_code/agents/code_agent.py +517 -104
  24. minion_code/agents/hooks.py +378 -0
  25. minion_code/cli.py +538 -429
  26. minion_code/cli_simple.py +665 -0
  27. minion_code/commands/__init__.py +136 -29
  28. minion_code/commands/clear_command.py +19 -46
  29. minion_code/commands/help_command.py +33 -49
  30. minion_code/commands/history_command.py +37 -55
  31. minion_code/commands/model_command.py +194 -0
  32. minion_code/commands/quit_command.py +9 -12
  33. minion_code/commands/resume_command.py +181 -0
  34. minion_code/commands/skill_command.py +89 -0
  35. minion_code/commands/status_command.py +48 -73
  36. minion_code/commands/tools_command.py +54 -52
  37. minion_code/commands/version_command.py +34 -69
  38. minion_code/components/ConfirmDialog.py +430 -0
  39. minion_code/components/Message.py +318 -97
  40. minion_code/components/MessageResponse.py +30 -29
  41. minion_code/components/Messages.py +351 -0
  42. minion_code/components/PromptInput.py +499 -245
  43. minion_code/components/__init__.py +24 -17
  44. minion_code/const.py +7 -0
  45. minion_code/screens/REPL.py +1453 -469
  46. minion_code/screens/__init__.py +1 -1
  47. minion_code/services/__init__.py +20 -20
  48. minion_code/services/event_system.py +19 -14
  49. minion_code/services/file_freshness_service.py +223 -170
  50. minion_code/skills/__init__.py +25 -0
  51. minion_code/skills/skill.py +128 -0
  52. minion_code/skills/skill_loader.py +198 -0
  53. minion_code/skills/skill_registry.py +177 -0
  54. minion_code/subagents/__init__.py +31 -0
  55. minion_code/subagents/builtin/__init__.py +30 -0
  56. minion_code/subagents/builtin/claude_code_guide.py +32 -0
  57. minion_code/subagents/builtin/explore.py +36 -0
  58. minion_code/subagents/builtin/general_purpose.py +19 -0
  59. minion_code/subagents/builtin/plan.py +61 -0
  60. minion_code/subagents/subagent.py +116 -0
  61. minion_code/subagents/subagent_loader.py +147 -0
  62. minion_code/subagents/subagent_registry.py +151 -0
  63. minion_code/tools/__init__.py +8 -2
  64. minion_code/tools/bash_tool.py +16 -3
  65. minion_code/tools/file_edit_tool.py +201 -104
  66. minion_code/tools/file_read_tool.py +183 -26
  67. minion_code/tools/file_write_tool.py +17 -3
  68. minion_code/tools/glob_tool.py +23 -2
  69. minion_code/tools/grep_tool.py +229 -21
  70. minion_code/tools/ls_tool.py +28 -3
  71. minion_code/tools/multi_edit_tool.py +89 -84
  72. minion_code/tools/python_interpreter_tool.py +9 -1
  73. minion_code/tools/skill_tool.py +210 -0
  74. minion_code/tools/task_tool.py +287 -0
  75. minion_code/tools/todo_read_tool.py +28 -24
  76. minion_code/tools/todo_write_tool.py +82 -65
  77. minion_code/{types.py → type_defs.py} +15 -2
  78. minion_code/utils/__init__.py +45 -17
  79. minion_code/utils/config.py +610 -0
  80. minion_code/utils/history.py +114 -0
  81. minion_code/utils/logs.py +53 -0
  82. minion_code/utils/mcp_loader.py +153 -55
  83. minion_code/utils/output_truncator.py +233 -0
  84. minion_code/utils/session_storage.py +369 -0
  85. minion_code/utils/todo_file_utils.py +26 -22
  86. minion_code/utils/todo_storage.py +43 -33
  87. minion_code/web/__init__.py +9 -0
  88. minion_code/web/adapters/__init__.py +5 -0
  89. minion_code/web/adapters/web_adapter.py +524 -0
  90. minion_code/web/api/__init__.py +7 -0
  91. minion_code/web/api/chat.py +277 -0
  92. minion_code/web/api/interactions.py +136 -0
  93. minion_code/web/api/sessions.py +135 -0
  94. minion_code/web/server.py +149 -0
  95. minion_code/web/services/__init__.py +5 -0
  96. minion_code/web/services/session_manager.py +420 -0
  97. minion_code-0.1.2.dist-info/METADATA +476 -0
  98. minion_code-0.1.2.dist-info/RECORD +111 -0
  99. {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/WHEEL +1 -1
  100. minion_code-0.1.2.dist-info/entry_points.txt +6 -0
  101. tests/test_adapter.py +67 -0
  102. tests/test_adapter_simple.py +79 -0
  103. tests/test_file_read_tool.py +144 -0
  104. tests/test_readonly_tools.py +0 -2
  105. tests/test_skills.py +441 -0
  106. examples/advance_tui.py +0 -508
  107. examples/rich_example.py +0 -4
  108. examples/simple_file_watching.py +0 -57
  109. examples/simple_tui.py +0 -267
  110. examples/simple_usage.py +0 -69
  111. minion_code-0.1.0.dist-info/METADATA +0 -350
  112. minion_code-0.1.0.dist-info/RECORD +0 -59
  113. minion_code-0.1.0.dist-info/entry_points.txt +0 -4
  114. {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/licenses/LICENSE +0 -0
  115. {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Skills system for MinionCode
5
+
6
+ Skills are folders of instructions, scripts, and resources that are loaded
7
+ dynamically to improve performance on specialized tasks. Each skill has a
8
+ SKILL.md file with YAML frontmatter containing name and description.
9
+
10
+ Skill search paths (in order):
11
+ - .claude/skills (project-level)
12
+ - ~/.claude/skills (user-level)
13
+ - .minion/skills (project-level)
14
+ - ~/.minion/skills (user-level)
15
+ """
16
+
17
+ from .skill import Skill
18
+ from .skill_registry import SkillRegistry
19
+ from .skill_loader import SkillLoader
20
+
21
+ __all__ = [
22
+ "Skill",
23
+ "SkillRegistry",
24
+ "SkillLoader",
25
+ ]
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Skill data class representing a loaded skill.
5
+ """
6
+
7
+ import re
8
+ from dataclasses import dataclass, field
9
+ from pathlib import Path
10
+ from typing import Optional, Dict, Any, List
11
+ import yaml
12
+
13
+
14
+ @dataclass
15
+ class Skill:
16
+ """Represents a loaded skill with its metadata and content."""
17
+
18
+ name: str
19
+ description: str
20
+ content: str # The markdown body (instructions)
21
+ path: Path # Path to the skill directory
22
+
23
+ # Optional metadata from frontmatter
24
+ license: Optional[str] = None
25
+ allowed_tools: List[str] = field(default_factory=list)
26
+ metadata: Dict[str, Any] = field(default_factory=dict)
27
+
28
+ # Source location type
29
+ location: str = "project" # project, user, managed
30
+
31
+ @classmethod
32
+ def from_skill_md(
33
+ cls, skill_md_path: Path, location: str = "project"
34
+ ) -> Optional["Skill"]:
35
+ """
36
+ Parse a SKILL.md file and create a Skill instance.
37
+
38
+ Args:
39
+ skill_md_path: Path to the SKILL.md file
40
+ location: Where the skill was found (project, user, managed)
41
+
42
+ Returns:
43
+ Skill instance or None if parsing fails
44
+ """
45
+ if not skill_md_path.exists():
46
+ return None
47
+
48
+ content = skill_md_path.read_text(encoding="utf-8")
49
+ frontmatter, body = cls._parse_frontmatter(content)
50
+
51
+ if not frontmatter:
52
+ return None
53
+
54
+ name = frontmatter.get("name")
55
+ description = frontmatter.get("description")
56
+
57
+ if not name or not description:
58
+ return None
59
+
60
+ return cls(
61
+ name=name,
62
+ description=description,
63
+ content=body.strip(),
64
+ path=skill_md_path.parent,
65
+ license=frontmatter.get("license"),
66
+ allowed_tools=frontmatter.get("allowed-tools", []) or [],
67
+ metadata=frontmatter.get("metadata", {}) or {},
68
+ location=location,
69
+ )
70
+
71
+ @staticmethod
72
+ def _parse_frontmatter(content: str) -> tuple[Dict[str, Any], str]:
73
+ """
74
+ Parse YAML frontmatter from markdown content.
75
+
76
+ Args:
77
+ content: Raw markdown content with potential YAML frontmatter
78
+
79
+ Returns:
80
+ Tuple of (frontmatter dict, body content)
81
+ """
82
+ # Match YAML frontmatter pattern: starts with ---, ends with ---
83
+ pattern = r"^---\s*\n(.*?)\n---\s*\n(.*)$"
84
+ match = re.match(pattern, content, re.DOTALL)
85
+
86
+ if not match:
87
+ return {}, content
88
+
89
+ yaml_content = match.group(1)
90
+ body = match.group(2)
91
+
92
+ try:
93
+ frontmatter = yaml.safe_load(yaml_content) or {}
94
+ except yaml.YAMLError:
95
+ return {}, content
96
+
97
+ return frontmatter, body
98
+
99
+ def get_prompt(self) -> str:
100
+ """
101
+ Get the full prompt content for this skill.
102
+ Includes the skill location header for resolving relative paths
103
+ to bundled resources (references/, scripts/, assets/).
104
+
105
+ Returns:
106
+ Full prompt string with base directory header
107
+ """
108
+ header = f"""Loading: {self.name}
109
+ Base directory: {self.path}
110
+
111
+ """
112
+ return header + self.content
113
+
114
+ def to_xml(self) -> str:
115
+ """
116
+ Format skill as XML for inclusion in prompts.
117
+
118
+ Returns:
119
+ XML formatted skill entry
120
+ """
121
+ return f"""<skill>
122
+ <name>{self.name}</name>
123
+ <description>{self.description}</description>
124
+ <location>{self.location}</location>
125
+ </skill>"""
126
+
127
+ def __repr__(self) -> str:
128
+ return f"Skill(name={self.name!r}, location={self.location!r})"
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Skill Loader - discovers and loads skills from standard directories.
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+ from typing import List, Optional
10
+ import logging
11
+
12
+ from .skill import Skill
13
+ from .skill_registry import SkillRegistry, get_skill_registry
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class SkillLoader:
19
+ """
20
+ Discovers and loads skills from standard directories.
21
+
22
+ Search paths (in priority order):
23
+ 1. .claude/skills (project-level)
24
+ 2. .minion/skills (project-level)
25
+ 3. ~/.claude/skills (user-level)
26
+ 4. ~/.minion/skills (user-level)
27
+
28
+ Project-level skills override user-level skills with the same name.
29
+ """
30
+
31
+ # Default skill directory names
32
+ SKILL_DIRS = [
33
+ ".claude/skills",
34
+ ".minion/skills",
35
+ ]
36
+
37
+ SKILL_FILE = "SKILL.md"
38
+
39
+ def __init__(self, project_root: Optional[Path] = None):
40
+ """
41
+ Initialize the skill loader.
42
+
43
+ Args:
44
+ project_root: Root directory of the project. Defaults to current directory.
45
+ """
46
+ self.project_root = Path(project_root) if project_root else Path.cwd()
47
+ self.home_dir = Path.home()
48
+
49
+ def get_search_paths(self) -> List[tuple[Path, str]]:
50
+ """
51
+ Get all skill search paths with their location type.
52
+
53
+ Returns:
54
+ List of (path, location_type) tuples
55
+ """
56
+ paths = []
57
+
58
+ # Project-level paths (higher priority)
59
+ for skill_dir in self.SKILL_DIRS:
60
+ project_path = self.project_root / skill_dir
61
+ paths.append((project_path, "project"))
62
+
63
+ # User-level paths (lower priority)
64
+ for skill_dir in self.SKILL_DIRS:
65
+ user_path = self.home_dir / skill_dir
66
+ paths.append((user_path, "user"))
67
+
68
+ return paths
69
+
70
+ def discover_skills(self, skills_dir: Path) -> List[Path]:
71
+ """
72
+ Discover all skill directories within a skills directory.
73
+
74
+ A skill directory must contain a SKILL.md file.
75
+
76
+ Args:
77
+ skills_dir: Directory containing skill subdirectories
78
+
79
+ Returns:
80
+ List of paths to SKILL.md files
81
+ """
82
+ if not skills_dir.exists() or not skills_dir.is_dir():
83
+ return []
84
+
85
+ skill_files = []
86
+
87
+ for item in skills_dir.iterdir():
88
+ if item.is_dir():
89
+ skill_md = item / self.SKILL_FILE
90
+ if skill_md.exists():
91
+ skill_files.append(skill_md)
92
+ else:
93
+ # Check for nested skill directories (e.g., document-skills/pdf)
94
+ for nested_item in item.iterdir():
95
+ if nested_item.is_dir():
96
+ nested_skill_md = nested_item / self.SKILL_FILE
97
+ if nested_skill_md.exists():
98
+ skill_files.append(nested_skill_md)
99
+
100
+ return skill_files
101
+
102
+ def load_skill(self, skill_md_path: Path, location: str) -> Optional[Skill]:
103
+ """
104
+ Load a single skill from its SKILL.md file.
105
+
106
+ Args:
107
+ skill_md_path: Path to the SKILL.md file
108
+ location: Location type (project, user, managed)
109
+
110
+ Returns:
111
+ Skill instance or None if loading fails
112
+ """
113
+ try:
114
+ skill = Skill.from_skill_md(skill_md_path, location)
115
+ if skill:
116
+ logger.debug(f"Loaded skill: {skill.name} from {skill_md_path}")
117
+ else:
118
+ logger.warning(f"Failed to parse skill: {skill_md_path}")
119
+ return skill
120
+ except Exception as e:
121
+ logger.error(f"Error loading skill from {skill_md_path}: {e}")
122
+ return None
123
+
124
+ def load_all(self, registry: Optional[SkillRegistry] = None) -> SkillRegistry:
125
+ """
126
+ Load all skills from all search paths into the registry.
127
+
128
+ Args:
129
+ registry: Optional registry to load into. Creates new if not provided.
130
+
131
+ Returns:
132
+ SkillRegistry containing all loaded skills
133
+ """
134
+ if registry is None:
135
+ registry = get_skill_registry()
136
+
137
+ for search_path, location in self.get_search_paths():
138
+ skill_files = self.discover_skills(search_path)
139
+
140
+ for skill_md_path in skill_files:
141
+ skill = self.load_skill(skill_md_path, location)
142
+ if skill:
143
+ registered = registry.register(skill)
144
+ if registered:
145
+ logger.info(f"Registered skill: {skill.name} ({location})")
146
+ else:
147
+ logger.debug(
148
+ f"Skipped skill {skill.name} - already registered from higher priority location"
149
+ )
150
+
151
+ return registry
152
+
153
+ def reload(self, registry: Optional[SkillRegistry] = None) -> SkillRegistry:
154
+ """
155
+ Reload all skills, clearing the existing registry first.
156
+
157
+ Args:
158
+ registry: Optional registry to reload. Uses global if not provided.
159
+
160
+ Returns:
161
+ SkillRegistry containing all reloaded skills
162
+ """
163
+ if registry is None:
164
+ registry = get_skill_registry()
165
+
166
+ registry.clear()
167
+ return self.load_all(registry)
168
+
169
+
170
+ def load_skills(project_root: Optional[Path] = None) -> SkillRegistry:
171
+ """
172
+ Convenience function to load all skills.
173
+
174
+ Args:
175
+ project_root: Root directory of the project
176
+
177
+ Returns:
178
+ SkillRegistry containing all loaded skills
179
+ """
180
+ loader = SkillLoader(project_root)
181
+ return loader.load_all()
182
+
183
+
184
+ def get_available_skills() -> List[Skill]:
185
+ """
186
+ Get list of all available skills.
187
+
188
+ Loads skills if the registry is empty.
189
+
190
+ Returns:
191
+ List of available skills
192
+ """
193
+ registry = get_skill_registry()
194
+
195
+ if len(registry) == 0:
196
+ load_skills()
197
+
198
+ return registry.list_all()
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Skill Registry - manages loaded skills and provides lookup functionality.
5
+ """
6
+
7
+ from typing import Dict, Optional, List
8
+ from .skill import Skill
9
+
10
+
11
+ class SkillRegistry:
12
+ """
13
+ Registry for managing loaded skills.
14
+
15
+ Skills are organized by name and can be looked up for execution.
16
+ The registry also handles skill deduplication (project skills override user skills).
17
+ """
18
+
19
+ def __init__(self):
20
+ self._skills: Dict[str, Skill] = {}
21
+ self._skills_by_location: Dict[str, List[Skill]] = {
22
+ "project": [],
23
+ "user": [],
24
+ "managed": [],
25
+ }
26
+
27
+ def register(self, skill: Skill) -> bool:
28
+ """
29
+ Register a skill in the registry.
30
+
31
+ Project skills take precedence over user skills.
32
+ If a skill with the same name already exists from a higher priority location,
33
+ the new skill is not registered.
34
+
35
+ Priority order: project > user > managed
36
+
37
+ Args:
38
+ skill: Skill instance to register
39
+
40
+ Returns:
41
+ True if the skill was registered, False if it was skipped
42
+ """
43
+ existing = self._skills.get(skill.name)
44
+
45
+ if existing:
46
+ # Check priority
47
+ priority = {"project": 0, "user": 1, "managed": 2}
48
+ existing_priority = priority.get(existing.location, 99)
49
+ new_priority = priority.get(skill.location, 99)
50
+
51
+ if new_priority >= existing_priority:
52
+ # Skip - existing skill has higher or equal priority
53
+ return False
54
+
55
+ self._skills[skill.name] = skill
56
+ self._skills_by_location[skill.location].append(skill)
57
+ return True
58
+
59
+ def get(self, name: str) -> Optional[Skill]:
60
+ """
61
+ Get a skill by name.
62
+
63
+ Args:
64
+ name: Name of the skill
65
+
66
+ Returns:
67
+ Skill instance or None if not found
68
+ """
69
+ return self._skills.get(name)
70
+
71
+ def exists(self, name: str) -> bool:
72
+ """
73
+ Check if a skill exists in the registry.
74
+
75
+ Args:
76
+ name: Name of the skill
77
+
78
+ Returns:
79
+ True if the skill exists
80
+ """
81
+ return name in self._skills
82
+
83
+ def list_all(self) -> List[Skill]:
84
+ """
85
+ Get all registered skills.
86
+
87
+ Returns:
88
+ List of all skills
89
+ """
90
+ return list(self._skills.values())
91
+
92
+ def list_by_location(self, location: str) -> List[Skill]:
93
+ """
94
+ Get skills by location type.
95
+
96
+ Args:
97
+ location: Location type (project, user, managed)
98
+
99
+ Returns:
100
+ List of skills from that location
101
+ """
102
+ return self._skills_by_location.get(location, [])
103
+
104
+ def clear(self):
105
+ """Clear all registered skills."""
106
+ self._skills.clear()
107
+ for location in self._skills_by_location:
108
+ self._skills_by_location[location].clear()
109
+
110
+ def generate_skills_prompt(self, char_budget: int = 10000) -> str:
111
+ """
112
+ Generate a prompt listing all available skills.
113
+
114
+ Limits output to character budget to manage context window.
115
+
116
+ Args:
117
+ char_budget: Maximum characters for skills list
118
+
119
+ Returns:
120
+ Formatted skills prompt
121
+ """
122
+ skills = self.list_all()
123
+
124
+ if not skills:
125
+ return ""
126
+
127
+ entries = []
128
+ total_chars = 0
129
+
130
+ for skill in skills:
131
+ entry = skill.to_xml()
132
+ if total_chars + len(entry) > char_budget:
133
+ break
134
+ entries.append(entry)
135
+ total_chars += len(entry)
136
+
137
+ if not entries:
138
+ return ""
139
+
140
+ skills_xml = "\n".join(entries)
141
+ return f"""<available_skills>
142
+ {skills_xml}
143
+ </available_skills>"""
144
+
145
+ def __len__(self) -> int:
146
+ return len(self._skills)
147
+
148
+ def __contains__(self, name: str) -> bool:
149
+ return name in self._skills
150
+
151
+ def __iter__(self):
152
+ return iter(self._skills.values())
153
+
154
+
155
+ # Global skill registry instance
156
+ _skill_registry: Optional[SkillRegistry] = None
157
+
158
+
159
+ def get_skill_registry() -> SkillRegistry:
160
+ """
161
+ Get the global skill registry instance.
162
+
163
+ Creates the registry if it doesn't exist.
164
+
165
+ Returns:
166
+ SkillRegistry instance
167
+ """
168
+ global _skill_registry
169
+ if _skill_registry is None:
170
+ _skill_registry = SkillRegistry()
171
+ return _skill_registry
172
+
173
+
174
+ def reset_skill_registry():
175
+ """Reset the global skill registry."""
176
+ global _skill_registry
177
+ _skill_registry = None
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Subagents system for MinionCode
5
+
6
+ Subagents are specialized agent configurations that can be invoked via the Task tool.
7
+ Each subagent has specific tools, system prompts, and use cases.
8
+
9
+ Subagent search paths (in priority order):
10
+ - builtin (code-defined)
11
+ - .claude/subagents or .minion/subagents (project-level, highest priority)
12
+ - ~/.claude/subagents or ~/.minion/subagents (user-level)
13
+ """
14
+
15
+ from .subagent import SubagentConfig
16
+ from .subagent_registry import (
17
+ SubagentRegistry,
18
+ get_subagent_registry,
19
+ reset_subagent_registry,
20
+ )
21
+ from .subagent_loader import SubagentLoader, load_subagents, get_available_subagents
22
+
23
+ __all__ = [
24
+ "SubagentConfig",
25
+ "SubagentRegistry",
26
+ "get_subagent_registry",
27
+ "reset_subagent_registry",
28
+ "SubagentLoader",
29
+ "load_subagents",
30
+ "get_available_subagents",
31
+ ]
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Built-in subagent configurations."""
4
+
5
+ from typing import List
6
+ from ..subagent import SubagentConfig
7
+
8
+ from .general_purpose import get_general_purpose_subagent
9
+ from .explore import get_explore_subagent
10
+ from .plan import get_plan_subagent
11
+ from .claude_code_guide import get_claude_code_guide_subagent
12
+
13
+
14
+ def get_all_builtin_subagents() -> List[SubagentConfig]:
15
+ """Get all built-in subagent configurations."""
16
+ return [
17
+ get_general_purpose_subagent(),
18
+ get_explore_subagent(),
19
+ get_plan_subagent(),
20
+ get_claude_code_guide_subagent(),
21
+ ]
22
+
23
+
24
+ __all__ = [
25
+ "get_all_builtin_subagents",
26
+ "get_general_purpose_subagent",
27
+ "get_explore_subagent",
28
+ "get_plan_subagent",
29
+ "get_claude_code_guide_subagent",
30
+ ]
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Built-in claude-code-guide subagent configuration."""
4
+
5
+ from ..subagent import SubagentConfig
6
+
7
+ GUIDE_SYSTEM_PROMPT = """You are a documentation lookup specialist for Claude Code and related tools.
8
+
9
+ Your role is to help users understand:
10
+ - How to use Claude Code features and commands
11
+ - Best practices for working with AI coding assistants
12
+ - Tool usage patterns and examples
13
+ - Configuration and customization options
14
+
15
+ Use web search and documentation fetching to find accurate, up-to-date information.
16
+ Always cite your sources with URLs when providing information.
17
+
18
+ Focus on practical, actionable guidance with clear examples."""
19
+
20
+
21
+ def get_claude_code_guide_subagent() -> SubagentConfig:
22
+ """Get the claude-code-guide subagent configuration."""
23
+ return SubagentConfig(
24
+ name="claude-code-guide",
25
+ description="Documentation lookup agent for Claude Code features, commands, and best practices",
26
+ when_to_use="When you need to look up Claude Code documentation, understand tool usage, or find best practices for AI-assisted coding",
27
+ tools=["web_fetch", "web_search", "file_read"],
28
+ system_prompt=GUIDE_SYSTEM_PROMPT,
29
+ model_name="inherit",
30
+ location="builtin",
31
+ readonly=True,
32
+ )
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Built-in Explore subagent configuration."""
4
+
5
+ from ..subagent import SubagentConfig
6
+
7
+ EXPLORE_SYSTEM_PROMPT = """You are a fast codebase exploration specialist. Your role is to quickly navigate and understand codebases.
8
+
9
+ Your capabilities:
10
+ - Search for files using glob patterns
11
+ - Search for content using grep/regex patterns
12
+ - Read files to understand code structure
13
+ - Fetch web documentation when needed
14
+ - Search the web for relevant information
15
+
16
+ Guidelines:
17
+ - Be thorough but efficient - use glob and grep to narrow down before reading
18
+ - Make multiple parallel tool calls when searching different patterns
19
+ - Summarize findings clearly with file paths and line numbers
20
+ - Do NOT modify any files - you are read-only
21
+
22
+ Focus on answering the user's question with concrete file locations and code references."""
23
+
24
+
25
+ def get_explore_subagent() -> SubagentConfig:
26
+ """Get the Explore subagent configuration."""
27
+ return SubagentConfig(
28
+ name="Explore",
29
+ description="Fast codebase exploration agent specialized for finding files, searching content, and understanding code structure",
30
+ when_to_use="When you need to quickly explore a codebase, find specific files or patterns, understand code structure, or gather information before making changes",
31
+ tools=["glob", "grep", "file_read", "ls", "web_fetch", "web_search"],
32
+ system_prompt=EXPLORE_SYSTEM_PROMPT,
33
+ model_name="inherit",
34
+ location="builtin",
35
+ readonly=True,
36
+ )
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Built-in general-purpose subagent configuration."""
4
+
5
+ from ..subagent import SubagentConfig
6
+
7
+
8
+ def get_general_purpose_subagent() -> SubagentConfig:
9
+ """Get the general-purpose subagent configuration."""
10
+ return SubagentConfig(
11
+ name="general-purpose",
12
+ description="General-purpose agent for complex tasks requiring full tool access",
13
+ when_to_use="For complex, multi-step tasks that need full tool capabilities including file editing, bash, and code execution",
14
+ tools=["*"],
15
+ system_prompt=None, # Uses default system prompt
16
+ model_name="inherit",
17
+ location="builtin",
18
+ readonly=False,
19
+ )