illusion-code 0.1.0__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 (214) hide show
  1. illusion/__init__.py +24 -0
  2. illusion/__main__.py +15 -0
  3. illusion/_frontend/dist/index.mjs +39208 -0
  4. illusion/_frontend/package.json +27 -0
  5. illusion/_frontend/src/App.tsx +624 -0
  6. illusion/_frontend/src/components/CommandPicker.tsx +98 -0
  7. illusion/_frontend/src/components/Composer.tsx +55 -0
  8. illusion/_frontend/src/components/ComposerController.tsx +128 -0
  9. illusion/_frontend/src/components/ConversationView.tsx +750 -0
  10. illusion/_frontend/src/components/Footer.tsx +25 -0
  11. illusion/_frontend/src/components/MarkdownContent.tsx +537 -0
  12. illusion/_frontend/src/components/MarkdownTable.tsx +245 -0
  13. illusion/_frontend/src/components/ModalHost.tsx +425 -0
  14. illusion/_frontend/src/components/MultilineTextInput.tsx +250 -0
  15. illusion/_frontend/src/components/PromptInput.tsx +64 -0
  16. illusion/_frontend/src/components/SelectModal.tsx +78 -0
  17. illusion/_frontend/src/components/SidePanel.tsx +175 -0
  18. illusion/_frontend/src/components/Spinner.tsx +77 -0
  19. illusion/_frontend/src/components/StatusBar.tsx +142 -0
  20. illusion/_frontend/src/components/SwarmPanel.tsx +141 -0
  21. illusion/_frontend/src/components/TodoPanel.tsx +126 -0
  22. illusion/_frontend/src/components/ToolCallDisplay.tsx +202 -0
  23. illusion/_frontend/src/components/TranscriptPane.tsx +79 -0
  24. illusion/_frontend/src/components/WelcomeBanner.tsx +37 -0
  25. illusion/_frontend/src/hooks/useBackendSession.ts +468 -0
  26. illusion/_frontend/src/hooks/useTerminalSize.ts +9 -0
  27. illusion/_frontend/src/i18n.ts +78 -0
  28. illusion/_frontend/src/index.tsx +42 -0
  29. illusion/_frontend/src/theme/ThemeContext.tsx +19 -0
  30. illusion/_frontend/src/theme/builtinThemes.ts +89 -0
  31. illusion/_frontend/src/types.ts +110 -0
  32. illusion/_frontend/src/utils/markdown.ts +33 -0
  33. illusion/_frontend/src/utils/thinking.ts +191 -0
  34. illusion/_frontend/tsconfig.json +13 -0
  35. illusion/_web_dist/assets/index-BseIw-ik.css +10 -0
  36. illusion/_web_dist/assets/index-C_0ZWMuW.js +82 -0
  37. illusion/_web_dist/index.html +16 -0
  38. illusion/api/__init__.py +36 -0
  39. illusion/api/client.py +568 -0
  40. illusion/api/codex_client.py +563 -0
  41. illusion/api/compat.py +138 -0
  42. illusion/api/effort.py +128 -0
  43. illusion/api/errors.py +57 -0
  44. illusion/api/openai_client.py +819 -0
  45. illusion/api/provider.py +148 -0
  46. illusion/api/registry.py +479 -0
  47. illusion/api/usage.py +45 -0
  48. illusion/auth/__init__.py +50 -0
  49. illusion/auth/copilot.py +419 -0
  50. illusion/auth/external.py +612 -0
  51. illusion/auth/flows.py +58 -0
  52. illusion/auth/manager.py +214 -0
  53. illusion/auth/storage.py +372 -0
  54. illusion/bridge/__init__.py +38 -0
  55. illusion/bridge/manager.py +190 -0
  56. illusion/bridge/session_runner.py +84 -0
  57. illusion/bridge/types.py +113 -0
  58. illusion/bridge/work_secret.py +131 -0
  59. illusion/cli.py +1228 -0
  60. illusion/commands/__init__.py +32 -0
  61. illusion/commands/registry.py +1934 -0
  62. illusion/config/__init__.py +39 -0
  63. illusion/config/i18n.py +522 -0
  64. illusion/config/paths.py +259 -0
  65. illusion/config/settings.py +564 -0
  66. illusion/coordinator/__init__.py +41 -0
  67. illusion/coordinator/agent_definitions.py +1093 -0
  68. illusion/coordinator/coordinator_mode.py +127 -0
  69. illusion/engine/__init__.py +95 -0
  70. illusion/engine/cost_tracker.py +55 -0
  71. illusion/engine/messages.py +369 -0
  72. illusion/engine/query.py +632 -0
  73. illusion/engine/query_engine.py +343 -0
  74. illusion/engine/stream_events.py +169 -0
  75. illusion/hooks/__init__.py +67 -0
  76. illusion/hooks/events.py +43 -0
  77. illusion/hooks/executor.py +397 -0
  78. illusion/hooks/hot_reload.py +74 -0
  79. illusion/hooks/loader.py +133 -0
  80. illusion/hooks/schemas.py +121 -0
  81. illusion/hooks/types.py +86 -0
  82. illusion/mcp/__init__.py +104 -0
  83. illusion/mcp/client.py +377 -0
  84. illusion/mcp/config.py +140 -0
  85. illusion/mcp/types.py +175 -0
  86. illusion/memory/__init__.py +36 -0
  87. illusion/memory/manager.py +94 -0
  88. illusion/memory/memdir.py +58 -0
  89. illusion/memory/paths.py +57 -0
  90. illusion/memory/scan.py +120 -0
  91. illusion/memory/search.py +83 -0
  92. illusion/memory/types.py +43 -0
  93. illusion/output_styles/__init__.py +15 -0
  94. illusion/output_styles/loader.py +64 -0
  95. illusion/permissions/__init__.py +39 -0
  96. illusion/permissions/checker.py +174 -0
  97. illusion/permissions/modes.py +38 -0
  98. illusion/platforms.py +148 -0
  99. illusion/plugins/__init__.py +71 -0
  100. illusion/plugins/bundled/__init__.py +0 -0
  101. illusion/plugins/installer.py +59 -0
  102. illusion/plugins/loader.py +301 -0
  103. illusion/plugins/schemas.py +51 -0
  104. illusion/plugins/types.py +56 -0
  105. illusion/prompts/__init__.py +29 -0
  106. illusion/prompts/claudemd.py +74 -0
  107. illusion/prompts/context.py +187 -0
  108. illusion/prompts/environment.py +189 -0
  109. illusion/prompts/system_prompt.py +155 -0
  110. illusion/py.typed +0 -0
  111. illusion/sandbox/__init__.py +29 -0
  112. illusion/sandbox/adapter.py +174 -0
  113. illusion/services/__init__.py +59 -0
  114. illusion/services/compact/__init__.py +1015 -0
  115. illusion/services/cron.py +338 -0
  116. illusion/services/cron_scheduler.py +715 -0
  117. illusion/services/file_history.py +258 -0
  118. illusion/services/lsp/__init__.py +455 -0
  119. illusion/services/session_storage.py +237 -0
  120. illusion/services/token_estimation.py +72 -0
  121. illusion/skills/__init__.py +60 -0
  122. illusion/skills/bundled/__init__.py +110 -0
  123. illusion/skills/bundled/content/batch.md +86 -0
  124. illusion/skills/bundled/content/coding-guidelines.md +70 -0
  125. illusion/skills/bundled/content/debug.md +38 -0
  126. illusion/skills/bundled/content/loop.md +82 -0
  127. illusion/skills/bundled/content/remember.md +105 -0
  128. illusion/skills/bundled/content/simplify.md +53 -0
  129. illusion/skills/bundled/content/skillify.md +113 -0
  130. illusion/skills/bundled/content/stuck.md +54 -0
  131. illusion/skills/bundled/content/update-config.md +329 -0
  132. illusion/skills/bundled/content/verify.md +74 -0
  133. illusion/skills/loader.py +219 -0
  134. illusion/skills/registry.py +40 -0
  135. illusion/skills/types.py +24 -0
  136. illusion/state/__init__.py +18 -0
  137. illusion/state/app_state.py +67 -0
  138. illusion/state/store.py +93 -0
  139. illusion/swarm/__init__.py +71 -0
  140. illusion/swarm/agent_executor.py +857 -0
  141. illusion/swarm/in_process.py +259 -0
  142. illusion/swarm/subprocess_backend.py +136 -0
  143. illusion/swarm/team_helpers.py +123 -0
  144. illusion/swarm/types.py +159 -0
  145. illusion/swarm/worktree.py +347 -0
  146. illusion/tasks/__init__.py +33 -0
  147. illusion/tasks/local_agent_task.py +42 -0
  148. illusion/tasks/local_shell_task.py +27 -0
  149. illusion/tasks/manager.py +377 -0
  150. illusion/tasks/stop_task.py +21 -0
  151. illusion/tasks/types.py +88 -0
  152. illusion/tools/__init__.py +126 -0
  153. illusion/tools/agent_tool.py +388 -0
  154. illusion/tools/ask_user_question_tool.py +186 -0
  155. illusion/tools/base.py +149 -0
  156. illusion/tools/bash_tool.py +413 -0
  157. illusion/tools/config_tool.py +90 -0
  158. illusion/tools/cron_tool.py +473 -0
  159. illusion/tools/enter_plan_mode_tool.py +147 -0
  160. illusion/tools/enter_worktree_tool.py +188 -0
  161. illusion/tools/exit_plan_mode_tool.py +69 -0
  162. illusion/tools/exit_worktree_tool.py +225 -0
  163. illusion/tools/file_edit_tool.py +283 -0
  164. illusion/tools/file_read_tool.py +294 -0
  165. illusion/tools/file_write_tool.py +184 -0
  166. illusion/tools/glob_tool.py +165 -0
  167. illusion/tools/grep_tool.py +190 -0
  168. illusion/tools/list_mcp_resources_tool.py +80 -0
  169. illusion/tools/lsp_tool.py +333 -0
  170. illusion/tools/mcp_auth_tool.py +100 -0
  171. illusion/tools/mcp_tool.py +75 -0
  172. illusion/tools/notebook_edit_tool.py +242 -0
  173. illusion/tools/powershell_tool.py +334 -0
  174. illusion/tools/read_mcp_resource_tool.py +63 -0
  175. illusion/tools/repl_tool.py +100 -0
  176. illusion/tools/send_message_tool.py +112 -0
  177. illusion/tools/shell_common.py +187 -0
  178. illusion/tools/skill_tool.py +86 -0
  179. illusion/tools/sleep_tool.py +62 -0
  180. illusion/tools/structured_output_tool.py +58 -0
  181. illusion/tools/task_create_tool.py +98 -0
  182. illusion/tools/task_get_tool.py +94 -0
  183. illusion/tools/task_list_tool.py +94 -0
  184. illusion/tools/task_output_tool.py +55 -0
  185. illusion/tools/task_stop_tool.py +52 -0
  186. illusion/tools/task_update_tool.py +224 -0
  187. illusion/tools/team_create_tool.py +236 -0
  188. illusion/tools/team_delete_tool.py +104 -0
  189. illusion/tools/todo_write_tool.py +198 -0
  190. illusion/tools/tool_search_tool.py +156 -0
  191. illusion/tools/web_fetch_tool.py +264 -0
  192. illusion/tools/web_search_tool.py +186 -0
  193. illusion/ui/__init__.py +23 -0
  194. illusion/ui/app.py +258 -0
  195. illusion/ui/backend_host.py +1180 -0
  196. illusion/ui/input.py +86 -0
  197. illusion/ui/output.py +363 -0
  198. illusion/ui/permission_dialog.py +47 -0
  199. illusion/ui/permission_store.py +99 -0
  200. illusion/ui/protocol.py +384 -0
  201. illusion/ui/react_launcher.py +280 -0
  202. illusion/ui/runtime.py +787 -0
  203. illusion/ui/textual_app.py +603 -0
  204. illusion/ui/web/__init__.py +10 -0
  205. illusion/ui/web/server.py +87 -0
  206. illusion/ui/web/ws_host.py +1197 -0
  207. illusion/utils/__init__.py +0 -0
  208. illusion/utils/ripgrep.py +299 -0
  209. illusion/utils/shell.py +248 -0
  210. illusion_code-0.1.0.dist-info/METADATA +1159 -0
  211. illusion_code-0.1.0.dist-info/RECORD +214 -0
  212. illusion_code-0.1.0.dist-info/WHEEL +4 -0
  213. illusion_code-0.1.0.dist-info/entry_points.txt +2 -0
  214. illusion_code-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1093 @@
1
+ """
2
+ 代理定义加载系统模块
3
+ ==================
4
+
5
+ 本模块提供 IllusionCode 代理定义加载和管理功能。
6
+
7
+ 主要功能:
8
+ - 内置代理定义
9
+ - 从 markdown 文件加载代理定义
10
+ - YAML frontmatter 解析
11
+
12
+ 类说明:
13
+ - AgentDefinition: 完整的代理定义数据模型
14
+
15
+ 常量说明:
16
+ - AGENT_COLORS: 有效的代理颜色名称
17
+ - EFFORT_LEVELS: 有效的 Effort 级别
18
+ - PERMISSION_MODES: 有效的权限模式
19
+ - MEMORY_SCOPES: 有效的记忆范围
20
+ - ISOLATION_MODES: 有效的隔离模式
21
+
22
+ 函数说明:
23
+ - get_builtin_agent_definitions: 获取内置代理定义
24
+ - get_all_agent_definitions: 获取所有代理定义
25
+ - get_agent_definition: 获取指定名称的代理定义
26
+ - load_agents_dir: 从目录加载代理定义
27
+
28
+ 使用示例:
29
+ >>> from illusion.coordinator import get_builtin_agent_definitions
30
+ >>> agents = get_builtin_agent_definitions()
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import logging
36
+ from pathlib import Path
37
+ from typing import Any, Literal
38
+
39
+ import yaml
40
+ from pydantic import BaseModel, Field
41
+
42
+ from illusion.config.paths import get_config_dir
43
+
44
+ logger = logging.getLogger(__name__)
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Constants
48
+ # ---------------------------------------------------------------------------
49
+
50
+ #: 有效的颜色名称 (对应 TS 中的 AgentColorName)
51
+ AGENT_COLORS: frozenset[str] = frozenset(
52
+ {
53
+ "red", # 红色
54
+ "green", # 绿色
55
+ "blue", # 蓝色
56
+ "yellow", # 黄色
57
+ "purple", # 紫色
58
+ "orange", # 橙色
59
+ "cyan", # 青色
60
+ "magenta", # 品红
61
+ "white", # 白色
62
+ "gray", # 灰色
63
+ }
64
+ )
65
+
66
+ #: 有效的 Effort 级别 (对应 TS 中的 EFFORT_LEVELS)
67
+ EFFORT_LEVELS: tuple[str, ...] = ("low", "medium", "high", "xhigh", "max")
68
+
69
+ #: 有效的权限模式 (对应 TS 中的 PERMISSION_MODES)
70
+ PERMISSION_MODES: tuple[str, ...] = (
71
+ "default", # 默认
72
+ "acceptEdits", # 接受编辑
73
+ "bypassPermissions", # 绕过权限
74
+ "plan", # 计划模式
75
+ "dontAsk", # 不询问
76
+ )
77
+
78
+ #: 有效的记忆范围 (对应 TS 中的 AgentMemoryScope)
79
+ MEMORY_SCOPES: tuple[str, ...] = ("user", "project", "local")
80
+
81
+ #: 有效的隔离模式
82
+ ISOLATION_MODES: tuple[str, ...] = ("worktree", "remote")
83
+
84
+
85
+ # ---------------------------------------------------------------------------
86
+ # AgentDefinition model
87
+ # ---------------------------------------------------------------------------
88
+
89
+
90
+ class AgentDefinition(BaseModel):
91
+ """完整的代理定义,包含所有配置字段
92
+
93
+ 字段映射到 TypeScript ``BaseAgentDefinition``:
94
+ - ``name`` → ``agentType``
95
+ - ``description`` → ``whenToUse``
96
+ - ``system_prompt`` → ``getSystemPrompt()`` 返回值
97
+ - ``tools`` → ``tools`` (None 表示所有工具 / ``['*']`` 等效)
98
+ - ``disallowed_tools`` → ``disallowedTools``
99
+ - ``skills`` → ``skills``
100
+ - ``mcp_servers`` → ``mcpServers``
101
+ - ``hooks`` → ``hooks``
102
+ - ``color`` → ``color``
103
+ - ``model`` → ``model``
104
+ - ``effort`` → ``effort``
105
+ - ``permission_mode`` → ``permissionMode``
106
+ - ``max_turns`` → ``maxTurns``
107
+ - ``filename`` → ``filename``
108
+ - ``base_dir`` → ``baseDir``
109
+ - ``critical_system_reminder`` → ``criticalSystemReminder_EXPERIMENTAL``
110
+ - ``required_mcp_servers`` → ``requiredMcpServers``
111
+ - ``background`` → ``background``
112
+ - ``initial_prompt`` → ``initialPrompt``
113
+ - ``memory`` → ``memory``
114
+ - ``isolation`` → ``isolation``
115
+ - ``omit_claude_md`` → ``omitClaudeMd``
116
+ """
117
+
118
+ # --- required ---
119
+ name: str # 代理类型标识
120
+ description: str # 使用时机描述
121
+
122
+ # --- prompt / tools ---
123
+ system_prompt: str | None = None # 系统提示词
124
+ tools: list[str] | None = None # None 表示所有工具允许; ['*'] 等效
125
+ disallowed_tools: list[str] | None = None # 禁止的工具列表
126
+
127
+ # --- model & effort ---
128
+ model: str | None = None # 模型覆盖; None 表示继承默认值
129
+ effort: str | int | None = None # "low" | "medium" | "high" 或正整数
130
+
131
+ # --- permissions ---
132
+ permission_mode: str | None = None # PERMISSION_MODES 之一
133
+
134
+ # --- agent loop control ---
135
+ max_turns: int | None = None # 代理停止前的最大代理轮次数; 必须 > 0
136
+
137
+ # --- skills & mcp ---
138
+ skills: list[str] = Field(default_factory=list) # 技能列表
139
+ mcp_servers: list[Any] | None = None # str 引用或 {name: config} 字典
140
+ required_mcp_servers: list[str] | None = None # 必须存在的服务器名模式
141
+
142
+ # --- hooks ---
143
+ hooks: dict[str, Any] | None = None # 代理启动时注册的作用域 hooks
144
+
145
+ # --- ui ---
146
+ color: str | None = None # AGENT_COLORS 之一
147
+
148
+ # --- lifecycle ---
149
+ background: bool = False # 生成时始终作为后台任务运行
150
+ initial_prompt: str | None = None # 附加到第一个用户回合
151
+ memory: str | None = None # MEMORY_SCOPES 之一
152
+ isolation: str | None = None # ISOLATION_MODES 之一
153
+
154
+ # --- metadata ---
155
+ filename: str | None = None # 不含 .md 扩展名的原始文件名
156
+ base_dir: str | None = None # 加载代理定义的目录
157
+ critical_system_reminder: str | None = None # 短消息,在每个用户回合重新注入
158
+ pending_snapshot_update: dict[str, Any] | None = None # 记忆快照跟踪
159
+ omit_claude_md: bool = False # 跳过此代理的 CLAUDE.md 注入
160
+
161
+ # --- Python-specific ---
162
+ permissions: list[str] = Field(default_factory=list) # 额外的权限规则
163
+ subagent_type: str = "general-purpose" # 线束使用的路由键
164
+ source: Literal["builtin", "user", "plugin"] = "builtin" # 来源
165
+
166
+
167
+ # ---------------------------------------------------------------------------
168
+ # System-prompt constants (translated from TS built-in agent files)
169
+ # ---------------------------------------------------------------------------
170
+
171
+ # 共享代理前缀
172
+ _SHARED_AGENT_PREFIX = (
173
+ "You are an agent for Illusion Code, an AI agent which is like Claude Code's internal agent system. "
174
+ "Given the user's message, you should use the tools available to complete the task. "
175
+ "Complete the task fully — don't gold-plate, but don't leave it half-done."
176
+ )
177
+
178
+ _SHARED_AGENT_GUIDELINES = """Your strengths:
179
+ - Searching for code, configurations, and patterns across large codebases
180
+ - Analyzing multiple files to understand system architecture
181
+ - Investigating complex questions that require exploring many files
182
+ - Performing multi-step research tasks
183
+
184
+ Guidelines:
185
+ - For file searches: search broadly when you don't know where something lives. Use Read when you know the specific file path.
186
+ - For analysis: Start broad and narrow down. Use multiple search strategies if the first doesn't yield results.
187
+ - Be thorough: Check multiple locations, consider different naming conventions, look for related files.
188
+ - NEVER create files unless they're absolutely necessary for achieving your goal. ALWAYS prefer editing an existing file to creating a new one.
189
+ - NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested."""
190
+
191
+ # 通用代理系统提示词
192
+ _GENERAL_PURPOSE_SYSTEM_PROMPT = (
193
+ f"{_SHARED_AGENT_PREFIX} When you complete the task, respond with a concise report covering "
194
+ "what was done and any key findings — the caller will relay this to the user, so it only needs "
195
+ f"the essentials.\n\n{_SHARED_AGENT_GUIDELINES}"
196
+ )
197
+
198
+ # 探索代理系统提示词
199
+ _EXPLORE_SYSTEM_PROMPT = """You are a file search specialist for Illusion Code. You excel at thoroughly navigating and exploring codebases.
200
+
201
+ Your strengths:
202
+ - Rapidly finding files using glob patterns
203
+ - Searching code and text with powerful regex patterns
204
+ - Reading and analyzing file contents
205
+
206
+ Guidelines:
207
+ - Use Glob for broad file pattern matching
208
+ - Use Grep for searching file contents with regex
209
+ - Use Read when you know the specific file path you need to read
210
+ - Use Bash for shell operations when needed
211
+ - Adapt your search approach based on the thoroughness level specified by the caller
212
+ - Communicate your final report directly as a regular message
213
+
214
+ NOTE: You are meant to be a fast agent that returns output as quickly as possible. In order to achieve this you must:
215
+ - Make efficient use of the tools that you have at your disposal: be smart about how you search for files and implementations
216
+ - Wherever possible you should try to spawn multiple parallel tool calls for grepping and reading files
217
+
218
+ Complete the user's search request efficiently and report your findings clearly."""
219
+
220
+ # 计划代理系统提示词
221
+ _PLAN_SYSTEM_PROMPT = """You are a software architect and planning specialist for Illusion Code. Your role is to explore the codebase and design implementation plans.
222
+
223
+ You will be provided with a set of requirements and optionally a perspective on how to approach the design process.
224
+
225
+ ## Your Process
226
+
227
+ 1. **Understand Requirements**: Focus on the requirements provided and apply your assigned perspective throughout the design process.
228
+
229
+ 2. **Explore Thoroughly**:
230
+ - Read any files provided to you in the initial prompt
231
+ - Find existing patterns and conventions using Glob, Grep, and Read
232
+ - Understand the current architecture
233
+ - Identify similar features as reference
234
+ - Trace through relevant code paths
235
+ - Use Bash ONLY for read-only operations (ls, git status, git log, git diff, find, cat, head, tail)
236
+ - NEVER use Bash for: mkdir, touch, rm, cp, mv, git add, git commit, npm install, pip install, or any file creation/modification
237
+
238
+ 3. **Design Solution**:
239
+ - Create implementation approach based on your assigned perspective
240
+ - Consider trade-offs and architectural decisions
241
+ - Follow existing patterns where appropriate
242
+
243
+ 4. **Detail the Plan**:
244
+ - Provide step-by-step implementation strategy
245
+ - Identify dependencies and sequencing
246
+ - Anticipate potential challenges
247
+
248
+ ## Required Output
249
+
250
+ End your response with:
251
+
252
+ ### Critical Files for Implementation
253
+ List 3-5 files most critical for implementing this plan:
254
+ - path/to/file1.py
255
+ - path/to/file2.py
256
+ - path/to/file3.py
257
+
258
+ REMEMBER: You can ONLY explore and plan. You CANNOT and MUST NOT write, edit, or modify any files. You do NOT have access to file editing tools."""
259
+
260
+ # 验证代理系统提示词
261
+ _VERIFICATION_SYSTEM_PROMPT = """You are a verification specialist. Your job is not to confirm the implementation works — it's to try to break it.
262
+
263
+ You have two documented failure patterns. First, verification avoidance: when faced with a check, you find reasons not to run it — you read code, narrate what you would test, write "PASS," and move on. Second, being seduced by the first 80%: you see a polished UI or a passing test suite and feel inclined to pass it, not noticing half the buttons do nothing, the state vanishes on refresh, or the backend crashes on bad input. The first 80% is the easy part. Your entire value is in finding the last 20%. The caller may spot-check your commands by re-running them — if a PASS step has no command output, or output that doesn't match re-execution, your report gets rejected.
264
+
265
+ === CRITICAL: DO NOT MODIFY THE PROJECT ===
266
+ You are STRICTLY PROHIBITED from:
267
+ - Creating, modifying, or deleting any files IN THE PROJECT DIRECTORY
268
+ - Installing dependencies or packages
269
+ - Running git write operations (add, commit, push)
270
+
271
+ You MAY write ephemeral test scripts to a temp directory (/tmp or $TMPDIR) via Bash redirection when inline commands aren't sufficient — e.g., a multi-step race harness or a Playwright test. Clean up after yourself.
272
+
273
+ Check your ACTUAL available tools rather than assuming from this prompt. You may have browser automation (mcp__claude-in-chrome__*, mcp__playwright__*), WebFetch, or other MCP tools depending on the session — do not skip capabilities you didn't think to check for.
274
+
275
+ === WHAT YOU RECEIVE ===
276
+ You will receive: the original task description, files changed, approach taken, and optionally a plan file path.
277
+
278
+ === VERIFICATION STRATEGY ===
279
+ Adapt your strategy based on what was changed:
280
+
281
+ **Frontend changes**: Start dev server → check your tools for browser automation (mcp__claude-in-chrome__*, mcp__playwright__*) and USE them to navigate, screenshot, click, and read console — do NOT say "needs a real browser" without attempting → curl a sample of page subresources since HTML can serve 200 while everything it references fails → run frontend tests
282
+ **Backend/API changes**: Start server → curl/fetch endpoints → verify response shapes against expected values (not just status codes) → test error handling → check edge cases
283
+ **CLI/script changes**: Run with representative inputs → verify stdout/stderr/exit codes → test edge inputs (empty, malformed, boundary) → verify --help / usage output is accurate
284
+ **Infrastructure/config changes**: Validate syntax → dry-run where possible (terraform plan, kubectl apply --dry-run=server, docker build, nginx -t) → check env vars / secrets are actually referenced, not just defined
285
+ **Library/package changes**: Build → full test suite → import the library from a fresh context and exercise the public API as a consumer would → verify exported types match README/docs examples
286
+ **Bug fixes**: Reproduce the original bug → verify fix → run regression tests → check related functionality for side effects
287
+ **Mobile (iOS/Android)**: Clean build → install on simulator/emulator → dump accessibility/UI tree (idb ui describe-all / uiautomator dump), find elements by label, tap by tree coords, re-dump to verify; screenshots secondary → kill and relaunch to test persistence → check crash logs (logcat / device console)
288
+ **Data/ML pipeline**: Run with sample input → verify output shape/schema/types → test empty input, single row, NaN/null handling → check for silent data loss (row counts in vs out)
289
+ **Database migrations**: Run migration up → verify schema matches intent → run migration down (reversibility) → test against existing data, not just empty DB
290
+ **Refactoring (no behavior change)**: Existing test suite MUST pass unchanged → diff the public API surface (no new/removed exports) → spot-check observable behavior is identical (same inputs → same outputs)
291
+ **Other change types**: The pattern is always the same — (a) figure out how to exercise this change directly (run/call/invoke/deploy it), (b) check outputs against expectations, (c) try to break it with inputs/conditions the implementer didn't test. The strategies above are worked examples for common cases.
292
+
293
+ === REQUIRED STEPS (universal baseline) ===
294
+ 1. Read the project's ILLUSION.md / CLAUDE.md / README for build/test commands and conventions. Check package.json / Makefile / pyproject.toml for script names. If the implementer pointed you to a plan or spec file, read it — that's the success criteria.
295
+ 2. Run the build (if applicable). A broken build is an automatic FAIL.
296
+ 3. Run the project's test suite (if it has one). Failing tests are an automatic FAIL.
297
+ 4. Run linters/type-checkers if configured (eslint, tsc, mypy, etc.).
298
+ 5. Check for regressions in related code.
299
+
300
+ Then apply the type-specific strategy above. Match rigor to stakes: a one-off script doesn't need race-condition probes; production payments code needs everything.
301
+
302
+ Test suite results are context, not evidence. Run the suite, note pass/fail, then move on to your real verification. The implementer is an LLM too — its tests may be heavy on mocks, circular assertions, or happy-path coverage that proves nothing about whether the system actually works end-to-end.
303
+
304
+ === RECOGNIZE YOUR OWN RATIONALIZATIONS ===
305
+ You will feel the urge to skip checks. These are the exact excuses you reach for — recognize them and do the opposite:
306
+ - "The code looks correct based on my reading" — reading is not verification. Run it.
307
+ - "The implementer's tests already pass" — the implementer is an LLM. Verify independently.
308
+ - "This is probably fine" — probably is not verified. Run it.
309
+ - "Let me start the server and check the code" — no. Start the server and hit the endpoint.
310
+ - "I don't have a browser" — did you actually check for mcp__claude-in-chrome__* / mcp__playwright__*? If present, use them. If an MCP tool fails, troubleshoot (server running? selector right?). The fallback exists so you don't invent your own "can't do this" story.
311
+ - "This would take too long" — not your call.
312
+ If you catch yourself writing an explanation instead of a command, stop. Run the command.
313
+
314
+ === ADVERSARIAL PROBES (adapt to the change type) ===
315
+ Functional tests confirm the happy path. Also try to break it:
316
+ - **Concurrency** (servers/APIs): parallel requests to create-if-not-exists paths — duplicate sessions? lost writes?
317
+ - **Boundary values**: 0, -1, empty string, very long strings, unicode, MAX_INT
318
+ - **Idempotency**: same mutating request twice — duplicate created? error? correct no-op?
319
+ - **Orphan operations**: delete/reference IDs that don't exist
320
+ These are seeds, not a checklist — pick the ones that fit what you're verifying.
321
+
322
+ === BEFORE ISSUING PASS ===
323
+ Your report must include at least one adversarial probe you ran (concurrency, boundary, idempotency, orphan op, or similar) and its result — even if the result was "handled correctly." If all your checks are "returns 200" or "test suite passes," you have confirmed the happy path, not verified correctness. Go back and try to break something.
324
+
325
+ === BEFORE ISSUING FAIL ===
326
+ You found something that looks broken. Before reporting FAIL, check you haven't missed why it's actually fine:
327
+ - **Already handled**: is there defensive code elsewhere (validation upstream, error recovery downstream) that prevents this?
328
+ - **Intentional**: does ILLUSION.md / CLAUDE.md / comments / commit message explain this as deliberate?
329
+ - **Not actionable**: is this a real limitation but unfixable without breaking an external contract (stable API, protocol spec, backwards compat)? If so, note it as an observation, not a FAIL — a "bug" that can't be fixed isn't actionable.
330
+ Don't use these as excuses to wave away real issues — but don't FAIL on intentional behavior either.
331
+
332
+ === OUTPUT FORMAT (REQUIRED) ===
333
+ Every check MUST follow this structure. A check without a Command run block is not a PASS — it's a skip.
334
+
335
+ ```
336
+ ### Check: [what you're verifying]
337
+ **Command run:**
338
+ [exact command you executed]
339
+ **Output observed:**
340
+ [actual terminal output — copy-paste, not paraphrased. Truncate if very long but keep the relevant part.]
341
+ **Result: PASS** (or FAIL — with Expected vs Actual)
342
+ ```
343
+
344
+ Good:
345
+ ```
346
+ ### Check: POST /api/register validation
347
+ **Command run:**
348
+ curl -s -X POST http://localhost:3000/api/register -H 'Content-Type: application/json' -d '{"email":"test@example.com","password":"short"}'
349
+ **Output observed:**
350
+ {"error":"Password must be at least 8 characters","code":"VALIDATION_ERROR"}
351
+ **Result: PASS**
352
+ ```
353
+
354
+ Bad (rejected):
355
+ ```
356
+ ### Check: POST /api/register validation
357
+ **Result: PASS**
358
+ Evidence: Reviewed the route handler in routes/auth.py. The logic correctly validates
359
+ email format and password length before DB insert.
360
+ ```
361
+ (No command run. Reading code is not verification.)
362
+
363
+ End with exactly this line (parsed by caller):
364
+
365
+ VERDICT: PASS
366
+ or
367
+ VERDICT: FAIL
368
+ or
369
+ VERDICT: PARTIAL
370
+
371
+ PARTIAL is for environmental limitations only (no test framework, tool unavailable, server can't start) — not for "I'm unsure whether this is a bug." If you can run the check, you must decide PASS or FAIL.
372
+
373
+ Use the literal string `VERDICT: ` followed by exactly one of `PASS`, `FAIL`, `PARTIAL`. No markdown bold, no punctuation, no variation.
374
+ - **FAIL**: include what failed, exact error output, reproduction steps.
375
+ - **PARTIAL**: what was verified, what could not and why (missing tool/env), what the implementer should know."""
376
+
377
+ # 验证关键提醒
378
+ _VERIFICATION_CRITICAL_REMINDER = (
379
+ "CRITICAL: This is a VERIFICATION-ONLY task. You CANNOT edit, write, or create files "
380
+ "IN THE PROJECT DIRECTORY (tmp is allowed for ephemeral test scripts). "
381
+ "You MUST end with VERDICT: PASS, VERDICT: FAIL, or VERDICT: PARTIAL."
382
+ )
383
+
384
+ # 工作代理系统提示词
385
+ _WORKER_SYSTEM_PROMPT = (
386
+ "You are an implementation-focused worker agent. Execute the assigned task precisely "
387
+ "and efficiently. Write clean, well-structured code that follows the conventions already "
388
+ "present in the codebase. When finished, run relevant tests and typecheck, then commit "
389
+ "your changes and report the commit hash."
390
+ )
391
+
392
+ # 状态行设置代理系统提示词
393
+ _STATUSLINE_SYSTEM_PROMPT = """You are a status line setup agent for Illusion Code. Your job is to create or update the statusLine command in the user's Illusion Code settings.
394
+
395
+ When asked to convert the user's shell PS1 configuration, follow these steps:
396
+ 1. Read the user's shell configuration files in this order of preference:
397
+ - ~/.zshrc
398
+ - ~/.bashrc
399
+ - ~/.bash_profile
400
+ - ~/.profile
401
+
402
+ 2. Extract the PS1 value using this regex pattern: /(?:^|\\n)\\s*(?:export\\s+)?PS1\\s*=\\s*["']([^"']+)["']/m
403
+
404
+ 3. Convert PS1 escape sequences to shell commands:
405
+ - \\u → $(whoami)
406
+ - \\h → $(hostname -s)
407
+ - \\H → $(hostname)
408
+ - \\w → $(pwd)
409
+ - \\W → $(basename "$(pwd)")
410
+ - \\$ → $
411
+ - \\n → \\n
412
+ - \\t → $(date +%H:%M:%S)
413
+ - \\d → $(date "+%a %b %d")
414
+ - \\@ → $(date +%I:%M%p)
415
+ - \\# → #
416
+ - \\! → !
417
+
418
+ 4. When using ANSI color codes, be sure to use `printf`. Do not remove colors. Note that the status line will be printed in a terminal using dimmed colors.
419
+
420
+ 5. If the imported PS1 would have trailing "$" or ">" characters in the output, you MUST remove them.
421
+
422
+ 6. If no PS1 is found and user did not provide other instructions, ask for further instructions.
423
+
424
+ How to use the statusLine command:
425
+ 1. The statusLine command will receive the following JSON input via stdin:
426
+ {
427
+ "session_id": "string",
428
+ "session_name": "string",
429
+ "transcript_path": "string",
430
+ "cwd": "string",
431
+ "model": {
432
+ "id": "string",
433
+ "display_name": "string"
434
+ },
435
+ "workspace": {
436
+ "current_dir": "string",
437
+ "project_dir": "string",
438
+ "added_dirs": ["string"]
439
+ },
440
+ "version": "string",
441
+ "output_style": {
442
+ "name": "string"
443
+ },
444
+ "context_window": {
445
+ "total_input_tokens": 0,
446
+ "total_output_tokens": 0,
447
+ "context_window_size": 0,
448
+ "current_usage": {
449
+ "input_tokens": 0,
450
+ "output_tokens": 0,
451
+ "cache_creation_input_tokens": 0,
452
+ "cache_read_input_tokens": 0
453
+ },
454
+ "used_percentage": null,
455
+ "remaining_percentage": null
456
+ },
457
+ "rate_limits": {
458
+ "five_hour": {
459
+ "used_percentage": 0,
460
+ "resets_at": "ISO timestamp"
461
+ },
462
+ "seven_day": {
463
+ "used_percentage": 0,
464
+ "resets_at": "ISO timestamp"
465
+ }
466
+ },
467
+ "vim": {
468
+ "mode": "INSERT|NORMAL"
469
+ },
470
+ "agent": {
471
+ "name": "string",
472
+ "type": "string"
473
+ },
474
+ "worktree": {
475
+ "name": "string",
476
+ "path": "string",
477
+ "branch": "string",
478
+ "original_cwd": "string",
479
+ "original_branch": "string"
480
+ }
481
+ }
482
+
483
+ Examples using jq:
484
+ - Model name: `cat | jq -r '.model.display_name'`
485
+ - Context usage: `cat | jq -r '.context_window.used_percentage // "N/A"'`
486
+ - Rate limits: `cat | jq -r '.rate_limits.five_hour.used_percentage // "N/A"'`
487
+
488
+ 2. For longer commands, you can save a new file in the user's ~/.illusion directory, e.g.:
489
+ - ~/.illusion/statusline-command.sh and reference that file in the settings.
490
+
491
+ 3. Update the user's ~/.illusion/settings.json with:
492
+ {
493
+ "statusLine": {
494
+ "type": "command",
495
+ "command": "your_command_here"
496
+ }
497
+ }
498
+
499
+ 4. If ~/.illusion/settings.json is a symlink, update the target file instead.
500
+
501
+ Guidelines:
502
+ - Preserve existing settings when updating
503
+ - Return a summary of what was configured, including the name of the script file if used
504
+ - If the script includes git commands, they should skip optional locks
505
+ - IMPORTANT: At the end of your response, inform the parent agent that this "statusline-setup" agent must be used for further status line changes.
506
+ Also ensure that the user is informed that they can ask Illusion Code to continue to make changes to the status line.
507
+ """
508
+
509
+ # Illusion Code指南代理系统提示词
510
+ _CLAUDE_CODE_GUIDE_SYSTEM_PROMPT = """You are the Illusion Code guide agent. Your primary responsibility is helping users understand and use Illusion Code, the Claude Agent SDK, and the Claude API (formerly the Anthropic API) effectively.
511
+
512
+ **Your expertise spans three domains:**
513
+
514
+ 1. **Claude Code** (the CLI tool): Installation, configuration, hooks, skills, MCP servers, keyboard shortcuts, IDE integrations, settings, and workflows.
515
+
516
+ 2. **Claude Agent SDK**: A framework for building custom AI agents based on Claude Code technology. Available for Node.js/TypeScript and Python.
517
+
518
+ 3. **Claude API**: The Claude API (formerly known as the Anthropic API) for direct model interaction, tool use, and integrations.
519
+
520
+ **Documentation sources:**
521
+
522
+ - **Claude Code docs** (https://code.claude.com/docs/en/claude_code_docs_map.md): Fetch this for questions about the Claude Code CLI tool, including:
523
+ - Installation, setup, and getting started
524
+ - Hooks (pre/post command execution)
525
+ - Custom skills
526
+ - MCP server configuration
527
+ - IDE integrations (VS Code, JetBrains)
528
+ - Settings files and configuration
529
+ - Keyboard shortcuts and hotkeys
530
+ - Subagents and plugins
531
+ - Sandboxing and security
532
+
533
+ - **Claude Agent SDK docs** (https://platform.claude.com/llms.txt): Fetch this for questions about:
534
+ - SDK overview and getting started (Python and TypeScript)
535
+ - Agent configuration and custom tools
536
+ - Session management and permissions
537
+ - MCP integration in agents
538
+ - Hosting and deployment
539
+
540
+ - **Claude API docs** (https://platform.claude.com/llms.txt): Fetch this for questions about:
541
+ - Messages API and streaming
542
+ - Tool use (function calling)
543
+ - Vision, PDF support, and citations
544
+ - Extended thinking and structured outputs
545
+ - Cost tracking and billing
546
+ - Cloud provider integrations (Bedrock, Vertex AI)
547
+
548
+ **Approach:**
549
+ 1. Determine which domain the user's question falls into
550
+ 2. Use WebFetch to fetch the appropriate docs map
551
+ 3. Identify the most relevant documentation URLs from the map
552
+ 4. Fetch the specific documentation pages
553
+ 5. Provide clear, actionable guidance based on official documentation
554
+ 6. Use WebSearch if docs don't cover the topic
555
+ 7. Reference local project files (ILLUSION.md, CLAUDE.md, .illusion/ directory) when relevant using Read, Glob, and Grep
556
+
557
+ **Guidelines:**
558
+ - Always prioritize official documentation over assumptions
559
+ - Keep responses concise and actionable
560
+ - Include specific examples or code snippets when helpful
561
+ - Reference exact documentation URLs in your responses
562
+ - Help users discover features by proactively suggesting related commands, shortcuts, or capabilities
563
+ - When you cannot find an answer or the feature doesn't exist, direct the user to report the issue
564
+
565
+ Complete the user's request by providing accurate, documentation-based guidance."""
566
+
567
+
568
+ # ---------------------------------------------------------------------------
569
+ # Built-in agent definitions
570
+ # ---------------------------------------------------------------------------
571
+
572
+ # 内置代理定义列表
573
+ _BUILTIN_AGENTS: list[AgentDefinition] = [
574
+ AgentDefinition(
575
+ name="general-purpose", # 通用代理
576
+ description=(
577
+ "General-purpose agent for researching complex questions, searching for code, "
578
+ "and executing multi-step tasks. When you are searching for a keyword or file "
579
+ "and are not confident that you will find the right match in the first few tries "
580
+ "use this agent to perform the search for you."
581
+ ),
582
+ tools=["*"], # 所有工具
583
+ system_prompt=_GENERAL_PURPOSE_SYSTEM_PROMPT, # 系统提示词
584
+ subagent_type="general-purpose", # 代理类型
585
+ source="builtin", # 来源
586
+ base_dir="built-in", # 基础目录
587
+ ),
588
+ AgentDefinition(
589
+ name="statusline-setup", # 状态行设置
590
+ description="Use this agent to configure the user's Illusion Code status line setting.", # 使用说明
591
+ tools=["Read", "Edit"], # 允许的工具
592
+ system_prompt=_STATUSLINE_SYSTEM_PROMPT, # 系统提示词
593
+ color="orange", # 颜色
594
+ subagent_type="statusline-setup", # 代理类型
595
+ source="builtin", # 来源
596
+ base_dir="built-in", # 基础目录
597
+ ),
598
+ AgentDefinition(
599
+ name="illusion-guide", # Illusion Code指南
600
+ description=(
601
+ 'Use this agent when the user asks questions ("Can Illusion...", "Does Illusion...", '
602
+ '"How do I...") about: (1) Illusion Code (the CLI tool) - features, hooks, slash '
603
+ "commands, MCP servers, settings, IDE integrations, keyboard shortcuts; "
604
+ "(2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic "
605
+ "API) - API usage, tool use, Anthropic SDK usage. **IMPORTANT:** Before spawning a "
606
+ "new agent, check if there is already a running or recently completed illusion-guide "
607
+ "agent that you can continue via SendMessage." # 使用说明
608
+ ),
609
+ tools=["Glob", "Grep", "Read", "WebFetch", "WebSearch"], # 允许的工具
610
+ system_prompt=_CLAUDE_CODE_GUIDE_SYSTEM_PROMPT, # 系统提示词
611
+ permission_mode="dontAsk", # 权限模式
612
+ subagent_type="illusion-guide", # 代理类型
613
+ source="builtin", # 来源
614
+ base_dir="built-in", # 基础目录
615
+ ),
616
+ AgentDefinition(
617
+ name="Explore", # 探索代理
618
+ description=(
619
+ "Fast agent specialized for exploring codebases. Use this when you need to "
620
+ "quickly find files by patterns (eg. \"src/components/**/*.tsx\"), search code "
621
+ "for keywords (eg. \"API endpoints\"), or answer questions about the codebase "
622
+ "(eg. \"how do API endpoints work?\"). When calling this agent, specify the "
623
+ "desired thoroughness level: \"quick\" for basic searches, \"medium\" for "
624
+ "moderate exploration, or \"very thorough\" for comprehensive analysis across "
625
+ "multiple locations and naming conventions." # 使用说明
626
+ ),
627
+ disallowed_tools=["agent", "exit_plan_mode", "file_edit", "file_write", "notebook_edit"], # 禁止的工具
628
+ system_prompt=_EXPLORE_SYSTEM_PROMPT, # 系统提示词
629
+ omit_claude_md=True, # 跳过CLAUDE.md
630
+ subagent_type="Explore", # 代理类型
631
+ source="builtin", # 来源
632
+ base_dir="built-in", # 基础目录
633
+ ),
634
+ AgentDefinition(
635
+ name="Plan", # 计划代理
636
+ description=(
637
+ "Software architect agent for designing implementation plans. Use this when you "
638
+ "need to plan the implementation strategy for a task. Returns step-by-step plans, "
639
+ "identifies critical files, and considers architectural trade-offs." # 使用说明
640
+ ),
641
+ disallowed_tools=["agent", "exit_plan_mode", "file_edit", "file_write", "notebook_edit"], # 禁止的工具
642
+ system_prompt=_PLAN_SYSTEM_PROMPT, # 系统提示词
643
+ model="inherit", # 模型
644
+ omit_claude_md=True, # 跳过CLAUDE.md
645
+ subagent_type="Plan", # 代理类型
646
+ source="builtin", # 来源
647
+ base_dir="built-in", # 基础目录
648
+ ),
649
+ AgentDefinition(
650
+ name="worker", # 工作代理
651
+ description=(
652
+ "Implementation-focused worker agent. Use this for concrete coding tasks: "
653
+ "writing features, fixing bugs, refactoring code, and running tests." # 使用说明
654
+ ),
655
+ tools=None, # 所有工具
656
+ system_prompt=_WORKER_SYSTEM_PROMPT, # 系统提示词
657
+ subagent_type="worker", # 代理类型
658
+ source="builtin", # 来源
659
+ base_dir="built-in", # 基础目录
660
+ ),
661
+ AgentDefinition(
662
+ name="verification", # 验证代理
663
+ description=(
664
+ "Use this agent to verify that implementation work is correct before reporting "
665
+ "completion. Invoke after non-trivial tasks (3+ file edits, backend/API changes, "
666
+ "infrastructure changes). Pass the ORIGINAL user task description, list of files "
667
+ "changed, and approach taken. The agent runs builds, tests, linters, and checks "
668
+ "to produce a PASS/FAIL/PARTIAL verdict with evidence." # 使用说明
669
+ ),
670
+ disallowed_tools=["agent", "exit_plan_mode", "file_edit", "file_write", "notebook_edit"], # 禁止的工具
671
+ system_prompt=_VERIFICATION_SYSTEM_PROMPT, # 系统提示词
672
+ critical_system_reminder=_VERIFICATION_CRITICAL_REMINDER, # 关键提醒
673
+ color="red", # 颜色
674
+ background=True, # 后台运行
675
+ model="inherit", # 模型
676
+ subagent_type="verification", # 代理类型
677
+ source="builtin", # 来源
678
+ base_dir="built-in", # 基础目录
679
+ ),
680
+ ]
681
+
682
+
683
+ def get_builtin_agent_definitions() -> list[AgentDefinition]:
684
+ """获取内置代理定义列表
685
+
686
+ Returns:
687
+ list[AgentDefinition]: 内置代理定义列表
688
+ """
689
+ return list(_BUILTIN_AGENTS)
690
+
691
+
692
+ # ---------------------------------------------------------------------------
693
+ # Markdown / YAML-frontmatter loader
694
+ # ---------------------------------------------------------------------------
695
+
696
+
697
+ def _parse_agent_frontmatter(content: str) -> tuple[dict[str, Any], str]:
698
+ """从markdown文件解析YAML frontmatter
699
+
700
+ 返回 (frontmatter_dict, body) 元组。使用 ``yaml.safe_load`` 进行
701
+ 正确的YAML解析 (支持 hooks, mcpServers 等嵌套结构)。
702
+
703
+ Args:
704
+ content: 文件完整内容
705
+
706
+ Returns:
707
+ tuple[dict[str, Any], str]: (frontmatter字典, body文本)
708
+ """
709
+ frontmatter: dict[str, Any] = {}
710
+ body = content
711
+
712
+ lines = content.splitlines() # 分割行
713
+ if not lines or lines[0].strip() != "---": # 无frontmatter
714
+ return frontmatter, body
715
+
716
+ end_index: int | None = None # 结束索引
717
+ for i, line in enumerate(lines[1:], start=1): # 遍历内容行
718
+ if line.strip() == "---": # 找到结束标记
719
+ end_index = i
720
+ break
721
+
722
+ if end_index is None: # 未找到结束标记
723
+ return frontmatter, body
724
+
725
+ fm_text = "\n".join(lines[1:end_index]) # frontmatter文本
726
+ try:
727
+ parsed = yaml.safe_load(fm_text) # 解析YAML
728
+ if isinstance(parsed, dict): # 是字典
729
+ frontmatter = parsed
730
+ except yaml.YAMLError: # 解析失败
731
+ # 回退到简单的 key:value 解析
732
+ for fm_line in lines[1:end_index]:
733
+ if ":" in fm_line:
734
+ key, _, value = fm_line.partition(":") # 分割
735
+ frontmatter[key.strip()] = value.strip().strip("'\"") # 添加
736
+
737
+ # Body是 --- 之后的所有内容
738
+ body = "\n".join(lines[end_index + 1 :]).strip() # 合并
739
+ return frontmatter, body
740
+
741
+
742
+ def _parse_str_list(raw: Any) -> list[str] | None:
743
+ """将逗号分隔的字符串或列表解析为字符串列表
744
+
745
+ Args:
746
+ raw: 原始值
747
+
748
+ Returns:
749
+ list[str] | None: 解析后的列表
750
+ """
751
+ if raw is None: # 空值
752
+ return None
753
+ if isinstance(raw, list): # 列表
754
+ return [str(item).strip() for item in raw if str(item).strip()]
755
+ if isinstance(raw, str): # 字符串
756
+ items = [t.strip() for t in raw.split(",") if t.strip()] # 分割
757
+ return items if items else None
758
+ return None
759
+
760
+
761
+ def _parse_positive_int(raw: Any) -> int | None:
762
+ """从frontmatter解析正整数,无效时返回None
763
+
764
+ Args:
765
+ raw: 原始值
766
+
767
+ Returns:
768
+ int | None: 解析后的整数
769
+ """
770
+ if raw is None: # 空值
771
+ return None
772
+ try:
773
+ val = int(raw) # 转换为整数
774
+ return val if val > 0 else None # 正数
775
+ except (TypeError, ValueError): # 转换失败
776
+ return None
777
+
778
+
779
+ def load_agents_dir(directory: Path) -> list[AgentDefinition]:
780
+ """从目录中的 .md 文件加载代理定义
781
+
782
+ 每个文件应包含YAML frontmatter,至少有 ``name`` 和
783
+ ``description`` 字段。markdown body 成为 ``system_prompt``。
784
+
785
+ 支持的 frontmatter 字段 (全部可选,除非注明):
786
+
787
+ 必需:
788
+ * ``name`` — 代理类型标识
789
+ * ``description`` — 显示给生成代理的使用时机描述
790
+
791
+ 可选:
792
+ * ``tools`` — 逗号分隔或YAML列表的工具名
793
+ * ``disallowedTools`` / ``disallowed_tools`` — 逗号分隔或列表的禁止工具
794
+ * ``model`` — 模型覆盖 (如 "default", "inherit")
795
+ * ``effort`` — "low", "medium", "high", 或正整数
796
+ * ``permissionMode`` / ``permission_mode`` — PERMISSION_MODES 之一
797
+ * ``maxTurns`` / ``max_turns`` — 正整数回合限制
798
+ * ``skills`` — 逗号分隔或技能名列表
799
+ * ``mcpServers`` / ``mcp_servers`` — MCP服务器引用或内联配置的列表
800
+ * ``hooks`` — YAML字典的作用域hooks
801
+ * ``color`` — AGENT_COLORS 之一
802
+ * ``background`` — true/false; 作为后台任务运行
803
+ * ``initialPrompt`` / ``initial_prompt` - 附加到第一个用户回合的字符串
804
+ * ``memory`` — MEMORY_SCOPES 之一
805
+ * ``isolation`` — ISOLATION_MODES 之一
806
+ * ``omitClaudeMd`` / ``omit_claude_md`` — true/false; 跳过CLAUDE.md注入
807
+ * ``criticalSystemReminder`` / ``critical_system_reminder`` — 重新注入的消息
808
+ * ``requiredMcpServers`` / ``required_mcp_servers`` — 必需服务器模式列表
809
+ * ``permissions`` — 逗号分隔的额外权限规则
810
+ * ``subagent_type`` — 路由键 (Python特定,默认为name)
811
+
812
+ Args:
813
+ directory: 代理定义目录
814
+
815
+ Returns:
816
+ list[AgentDefinition]: 加载的代理定义列表
817
+ """
818
+ agents: list[AgentDefinition] = [] # 代理列表
819
+
820
+ if not directory.is_dir(): # 非目录
821
+ return agents
822
+
823
+ for path in sorted(directory.glob("*.md")): # 遍历md文件
824
+ try:
825
+ content = path.read_text(encoding="utf-8") # 读取内容
826
+ frontmatter, body = _parse_agent_frontmatter(content) # 解析frontmatter
827
+
828
+ name = str(frontmatter.get("name", "")).strip() or path.stem # 名称
829
+ description = str(frontmatter.get("description", "")).strip() # 描述
830
+ if not description: # 无描述
831
+ description = f"Agent: {name}" # 默认描述
832
+
833
+ # 从YAML反转义 literal \n
834
+ description = description.replace("\\n", "\n")
835
+
836
+ # --- tools ---
837
+ tools = _parse_str_list(frontmatter.get("tools")) # 工具
838
+
839
+ # --- disallowed tools ---
840
+ disallowed_raw = frontmatter.get(
841
+ "disallowedTools", frontmatter.get("disallowed_tools") # 尝试两个键名
842
+ )
843
+ disallowed_tools = _parse_str_list(disallowed_raw) # 解析
844
+
845
+ # --- model ---
846
+ model_raw = frontmatter.get("model") # 模型
847
+ model: str | None = None
848
+ if isinstance(model_raw, str) and model_raw.strip(): # 字符串
849
+ trimmed = model_raw.strip()
850
+ model = "inherit" if trimmed.lower() == "inherit" else trimmed # inherit转换
851
+
852
+ # --- effort ---
853
+ effort_raw = frontmatter.get("effort") # effort
854
+ effort: str | int | None = None
855
+ if effort_raw is not None: # 有值
856
+ if isinstance(effort_raw, int): # 整数
857
+ effort = effort_raw if effort_raw > 0 else None # 正数
858
+ elif isinstance(effort_raw, str) and effort_raw in EFFORT_LEVELS: # 有效字符串
859
+ effort = effort_raw
860
+ else:
861
+ logger.debug("Agent %s: invalid effort %r", name, effort_raw) # 无效
862
+
863
+ # --- permissionMode ---
864
+ perm_raw = frontmatter.get("permissionMode", frontmatter.get("permission_mode")) # 尝试两个键名
865
+ permission_mode: str | None = None
866
+ if isinstance(perm_raw, str) and perm_raw in PERMISSION_MODES: # 有效值
867
+ permission_mode = perm_raw
868
+ elif perm_raw is not None: # 有值但无效
869
+ logger.debug("Agent %s: invalid permissionMode %r", name, perm_raw)
870
+
871
+ # --- maxTurns ---
872
+ max_turns_raw = frontmatter.get("maxTurns", frontmatter.get("max_turns")) # 尝试两个键名
873
+ max_turns = _parse_positive_int(max_turns_raw) # 解析
874
+ if max_turns_raw is not None and max_turns is None: # 有值但解析失败
875
+ logger.debug("Agent %s: invalid maxTurns %r", name, max_turns_raw)
876
+
877
+ # --- skills ---
878
+ skills_raw = frontmatter.get("skills") # 技能
879
+ skills = _parse_str_list(skills_raw) or [] # 解析
880
+
881
+ # --- mcpServers ---
882
+ mcp_raw = frontmatter.get("mcpServers", frontmatter.get("mcp_servers")) # MCP服务器
883
+ mcp_servers: list[Any] | None = None
884
+ if isinstance(mcp_raw, list): # 列表
885
+ mcp_servers = mcp_raw if mcp_raw else None # 空列表转None
886
+
887
+ # --- hooks ---
888
+ hooks_raw = frontmatter.get("hooks") # hooks
889
+ hooks: dict[str, Any] | None = None
890
+ if isinstance(hooks_raw, dict): # 字典
891
+ hooks = hooks_raw
892
+
893
+ # --- color ---
894
+ color_raw = frontmatter.get("color") # 颜色
895
+ color: str | None = None
896
+ if isinstance(color_raw, str) and color_raw in AGENT_COLORS: # 有效值
897
+ color = color_raw
898
+
899
+ # --- background ---
900
+ bg_raw = frontmatter.get("background") # 后台
901
+ background = bg_raw is True or bg_raw == "true" # 布尔转换
902
+
903
+ # --- initialPrompt ---
904
+ ip_raw = frontmatter.get("initialPrompt", frontmatter.get("initial_prompt")) # 初始提示词
905
+ initial_prompt: str | None = None
906
+ if isinstance(ip_raw, str) and ip_raw.strip(): # 非空字符串
907
+ initial_prompt = ip_raw
908
+
909
+ # --- memory ---
910
+ memory_raw = frontmatter.get("memory") # 记忆
911
+ memory: str | None = None
912
+ if isinstance(memory_raw, str) and memory_raw in MEMORY_SCOPES: # 有效值
913
+ memory = memory_raw
914
+ elif memory_raw is not None: # 有值但无效
915
+ logger.debug("Agent %s: invalid memory %r", name, memory_raw)
916
+
917
+ # --- isolation ---
918
+ iso_raw = frontmatter.get("isolation") # 隔离
919
+ isolation: str | None = None
920
+ if isinstance(iso_raw, str) and iso_raw in ISOLATION_MODES: # 有效值
921
+ isolation = iso_raw
922
+ elif iso_raw is not None: # 有值但无效
923
+ logger.debug("Agent %s: invalid isolation %r", name, iso_raw)
924
+
925
+ # --- omitClaudeMd ---
926
+ ocm_raw = frontmatter.get("omitClaudeMd", frontmatter.get("omit_claude_md")) # 跳过CLAUDE.md
927
+ omit_claude_md = ocm_raw is True or ocm_raw == "true" # 布尔转换
928
+
929
+ # --- criticalSystemReminder ---
930
+ csr_raw = frontmatter.get(
931
+ "criticalSystemReminder", frontmatter.get("critical_system_reminder") # 关键提醒
932
+ )
933
+ critical_system_reminder: str | None = None
934
+ if isinstance(csr_raw, str) and csr_raw.strip(): # 非空字符串
935
+ critical_system_reminder = csr_raw
936
+
937
+ # --- requiredMcpServers ---
938
+ rms_raw = frontmatter.get(
939
+ "requiredMcpServers", frontmatter.get("required_mcp_servers") # 必需MCP服务器
940
+ )
941
+ required_mcp_servers = _parse_str_list(rms_raw) # 解析
942
+
943
+ # --- permissions (Python-specific) ---
944
+ permissions: list[str] = [] # 权限
945
+ raw_perms = frontmatter.get("permissions", "") # 权限规则
946
+ if raw_perms: # 有值
947
+ permissions = [p.strip() for p in str(raw_perms).split(",") if p.strip()] # 解析
948
+
949
+ agents.append(
950
+ AgentDefinition(
951
+ name=name, # 名称
952
+ description=description, # 描述
953
+ system_prompt=body or None, # 系统提示词
954
+ tools=tools, # 工具
955
+ disallowed_tools=disallowed_tools, # 禁止工具
956
+ model=model, # 模型
957
+ effort=effort, # effort
958
+ permission_mode=permission_mode, # 权限模式
959
+ max_turns=max_turns, # 最大回合
960
+ skills=skills, # 技能
961
+ mcp_servers=mcp_servers, # MCP服务器
962
+ hooks=hooks, # hooks
963
+ color=color, # 颜色
964
+ background=background, # 后台
965
+ initial_prompt=initial_prompt, # 初始提示词
966
+ memory=memory, # 记忆
967
+ isolation=isolation, # 隔离
968
+ omit_claude_md=omit_claude_md, # 跳过CLAUDE.md
969
+ critical_system_reminder=critical_system_reminder, # 关键提醒
970
+ required_mcp_servers=required_mcp_servers, # 必需MCP服务器
971
+ permissions=permissions, # 权限
972
+ filename=path.stem, # 文件名
973
+ base_dir=str(directory), # 基础目录
974
+ subagent_type=str(frontmatter.get("subagent_type", name)), # 代理类型
975
+ source="user", # 来源
976
+ )
977
+ )
978
+ except Exception: # 解析失败
979
+ logger.debug("Failed to parse agent from %s", path, exc_info=True)
980
+ continue
981
+
982
+ return agents
983
+
984
+
985
+ # ---------------------------------------------------------------------------
986
+ # Public API
987
+ # ---------------------------------------------------------------------------
988
+
989
+
990
+ def _get_user_agents_dir() -> Path:
991
+ """获取用户代理定义目录
992
+
993
+ Returns:
994
+ Path: 用户代理目录路径 (~/.illusion/agents/)
995
+ """
996
+ return get_config_dir() / "agents"
997
+
998
+
999
+ def get_all_agent_definitions() -> list[AgentDefinition]:
1000
+ """获取所有代理定义: 内置 + 用户 + 插件
1001
+
1002
+ 合并顺序 (相同名称后写入者胜出):
1003
+ 1. 内置代理 (最低优先级)
1004
+ 2. 用户代理 (~/.illusion/agents/)
1005
+ 3. 插件代理 (从活动的插件加载)
1006
+
1007
+ 用户定义覆盖同名内置代理; 插件定义覆盖用户定义。
1008
+
1009
+ Returns:
1010
+ list[AgentDefinition]: 所有代理定义列表
1011
+ """
1012
+ agent_map: dict[str, AgentDefinition] = {} # 代理映射
1013
+
1014
+ # 1. 内置代理 (最低优先级)
1015
+ for agent in get_builtin_agent_definitions():
1016
+ agent_map[agent.name] = agent
1017
+
1018
+ # 2. 用户自定义代理
1019
+ user_agents = load_agents_dir(_get_user_agents_dir())
1020
+ for agent in user_agents:
1021
+ agent_map[agent.name] = agent
1022
+
1023
+ # 3. 插件代理 — 延迟加载以避免导入循环
1024
+ try:
1025
+ from illusion.plugins.loader import load_plugins # noqa: PLC0415
1026
+ from illusion.config.settings import load_settings # noqa: PLC0415
1027
+
1028
+ settings = load_settings() # 加载设置
1029
+ import os # noqa: PLC0415
1030
+
1031
+ cwd = os.getcwd() # 当前目录
1032
+ for plugin in load_plugins(settings, cwd): # 加载插件
1033
+ if not plugin.enabled: # 未启用
1034
+ continue
1035
+ for agent_def in getattr(plugin, "agents", []): # 遍历代理定义
1036
+ if isinstance(agent_def, AgentDefinition): # 是代理定义
1037
+ agent_map[agent_def.name] = agent_def # 添加
1038
+ except Exception:
1039
+ pass
1040
+
1041
+ return list(agent_map.values())
1042
+
1043
+
1044
+ def get_agent_definition(name: str) -> AgentDefinition | None:
1045
+ """获取指定名称的代理定义
1046
+
1047
+ Args:
1048
+ name: 代理名称
1049
+
1050
+ Returns:
1051
+ AgentDefinition | None: 代理定义,若不存在返回None
1052
+ """
1053
+ for agent in get_all_agent_definitions(): # 遍历所有代理
1054
+ if agent.name == name: # 匹配
1055
+ return agent
1056
+ return None
1057
+
1058
+
1059
+ def has_required_mcp_servers(agent: AgentDefinition, available_servers: list[str]) -> bool:
1060
+ """检查代理的必需MCP服务器是否全部可用
1061
+
1062
+ ``required_mcp_servers`` 中的每个模式必须匹配 (不区分大小写)
1063
+ ``available_servers`` 中至少一个服务器的子字符串。
1064
+
1065
+ Args:
1066
+ agent: 代理定义
1067
+ available_servers: 可用的MCP服务器列表
1068
+
1069
+ Returns:
1070
+ bool: 所有必需MCP服务器是否可用
1071
+ """
1072
+ if not agent.required_mcp_servers: # 无必需服务器
1073
+ return True
1074
+ return all(
1075
+ any(pattern.lower() in server.lower() for server in available_servers) # 模式匹配
1076
+ for pattern in agent.required_mcp_servers
1077
+ )
1078
+
1079
+
1080
+ def filter_agents_by_mcp_requirements(
1081
+ agents: list[AgentDefinition],
1082
+ available_servers: list[str],
1083
+ ) -> list[AgentDefinition]:
1084
+ """只返回必需MCP服务器可用的代理
1085
+
1086
+ Args:
1087
+ agents: 代理定义列表
1088
+ available_servers: 可用的MCP服务器列表
1089
+
1090
+ Returns:
1091
+ list[AgentDefinition]: 过滤后的代理列表
1092
+ """
1093
+ return [a for a in agents if has_required_mcp_servers(a, available_servers)]