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.
- illusion/__init__.py +24 -0
- illusion/__main__.py +15 -0
- illusion/_frontend/dist/index.mjs +39208 -0
- illusion/_frontend/package.json +27 -0
- illusion/_frontend/src/App.tsx +624 -0
- illusion/_frontend/src/components/CommandPicker.tsx +98 -0
- illusion/_frontend/src/components/Composer.tsx +55 -0
- illusion/_frontend/src/components/ComposerController.tsx +128 -0
- illusion/_frontend/src/components/ConversationView.tsx +750 -0
- illusion/_frontend/src/components/Footer.tsx +25 -0
- illusion/_frontend/src/components/MarkdownContent.tsx +537 -0
- illusion/_frontend/src/components/MarkdownTable.tsx +245 -0
- illusion/_frontend/src/components/ModalHost.tsx +425 -0
- illusion/_frontend/src/components/MultilineTextInput.tsx +250 -0
- illusion/_frontend/src/components/PromptInput.tsx +64 -0
- illusion/_frontend/src/components/SelectModal.tsx +78 -0
- illusion/_frontend/src/components/SidePanel.tsx +175 -0
- illusion/_frontend/src/components/Spinner.tsx +77 -0
- illusion/_frontend/src/components/StatusBar.tsx +142 -0
- illusion/_frontend/src/components/SwarmPanel.tsx +141 -0
- illusion/_frontend/src/components/TodoPanel.tsx +126 -0
- illusion/_frontend/src/components/ToolCallDisplay.tsx +202 -0
- illusion/_frontend/src/components/TranscriptPane.tsx +79 -0
- illusion/_frontend/src/components/WelcomeBanner.tsx +37 -0
- illusion/_frontend/src/hooks/useBackendSession.ts +468 -0
- illusion/_frontend/src/hooks/useTerminalSize.ts +9 -0
- illusion/_frontend/src/i18n.ts +78 -0
- illusion/_frontend/src/index.tsx +42 -0
- illusion/_frontend/src/theme/ThemeContext.tsx +19 -0
- illusion/_frontend/src/theme/builtinThemes.ts +89 -0
- illusion/_frontend/src/types.ts +110 -0
- illusion/_frontend/src/utils/markdown.ts +33 -0
- illusion/_frontend/src/utils/thinking.ts +191 -0
- illusion/_frontend/tsconfig.json +13 -0
- illusion/_web_dist/assets/index-BseIw-ik.css +10 -0
- illusion/_web_dist/assets/index-C_0ZWMuW.js +82 -0
- illusion/_web_dist/index.html +16 -0
- illusion/api/__init__.py +36 -0
- illusion/api/client.py +568 -0
- illusion/api/codex_client.py +563 -0
- illusion/api/compat.py +138 -0
- illusion/api/effort.py +128 -0
- illusion/api/errors.py +57 -0
- illusion/api/openai_client.py +819 -0
- illusion/api/provider.py +148 -0
- illusion/api/registry.py +479 -0
- illusion/api/usage.py +45 -0
- illusion/auth/__init__.py +50 -0
- illusion/auth/copilot.py +419 -0
- illusion/auth/external.py +612 -0
- illusion/auth/flows.py +58 -0
- illusion/auth/manager.py +214 -0
- illusion/auth/storage.py +372 -0
- illusion/bridge/__init__.py +38 -0
- illusion/bridge/manager.py +190 -0
- illusion/bridge/session_runner.py +84 -0
- illusion/bridge/types.py +113 -0
- illusion/bridge/work_secret.py +131 -0
- illusion/cli.py +1228 -0
- illusion/commands/__init__.py +32 -0
- illusion/commands/registry.py +1934 -0
- illusion/config/__init__.py +39 -0
- illusion/config/i18n.py +522 -0
- illusion/config/paths.py +259 -0
- illusion/config/settings.py +564 -0
- illusion/coordinator/__init__.py +41 -0
- illusion/coordinator/agent_definitions.py +1093 -0
- illusion/coordinator/coordinator_mode.py +127 -0
- illusion/engine/__init__.py +95 -0
- illusion/engine/cost_tracker.py +55 -0
- illusion/engine/messages.py +369 -0
- illusion/engine/query.py +632 -0
- illusion/engine/query_engine.py +343 -0
- illusion/engine/stream_events.py +169 -0
- illusion/hooks/__init__.py +67 -0
- illusion/hooks/events.py +43 -0
- illusion/hooks/executor.py +397 -0
- illusion/hooks/hot_reload.py +74 -0
- illusion/hooks/loader.py +133 -0
- illusion/hooks/schemas.py +121 -0
- illusion/hooks/types.py +86 -0
- illusion/mcp/__init__.py +104 -0
- illusion/mcp/client.py +377 -0
- illusion/mcp/config.py +140 -0
- illusion/mcp/types.py +175 -0
- illusion/memory/__init__.py +36 -0
- illusion/memory/manager.py +94 -0
- illusion/memory/memdir.py +58 -0
- illusion/memory/paths.py +57 -0
- illusion/memory/scan.py +120 -0
- illusion/memory/search.py +83 -0
- illusion/memory/types.py +43 -0
- illusion/output_styles/__init__.py +15 -0
- illusion/output_styles/loader.py +64 -0
- illusion/permissions/__init__.py +39 -0
- illusion/permissions/checker.py +174 -0
- illusion/permissions/modes.py +38 -0
- illusion/platforms.py +148 -0
- illusion/plugins/__init__.py +71 -0
- illusion/plugins/bundled/__init__.py +0 -0
- illusion/plugins/installer.py +59 -0
- illusion/plugins/loader.py +301 -0
- illusion/plugins/schemas.py +51 -0
- illusion/plugins/types.py +56 -0
- illusion/prompts/__init__.py +29 -0
- illusion/prompts/claudemd.py +74 -0
- illusion/prompts/context.py +187 -0
- illusion/prompts/environment.py +189 -0
- illusion/prompts/system_prompt.py +155 -0
- illusion/py.typed +0 -0
- illusion/sandbox/__init__.py +29 -0
- illusion/sandbox/adapter.py +174 -0
- illusion/services/__init__.py +59 -0
- illusion/services/compact/__init__.py +1015 -0
- illusion/services/cron.py +338 -0
- illusion/services/cron_scheduler.py +715 -0
- illusion/services/file_history.py +258 -0
- illusion/services/lsp/__init__.py +455 -0
- illusion/services/session_storage.py +237 -0
- illusion/services/token_estimation.py +72 -0
- illusion/skills/__init__.py +60 -0
- illusion/skills/bundled/__init__.py +110 -0
- illusion/skills/bundled/content/batch.md +86 -0
- illusion/skills/bundled/content/coding-guidelines.md +70 -0
- illusion/skills/bundled/content/debug.md +38 -0
- illusion/skills/bundled/content/loop.md +82 -0
- illusion/skills/bundled/content/remember.md +105 -0
- illusion/skills/bundled/content/simplify.md +53 -0
- illusion/skills/bundled/content/skillify.md +113 -0
- illusion/skills/bundled/content/stuck.md +54 -0
- illusion/skills/bundled/content/update-config.md +329 -0
- illusion/skills/bundled/content/verify.md +74 -0
- illusion/skills/loader.py +219 -0
- illusion/skills/registry.py +40 -0
- illusion/skills/types.py +24 -0
- illusion/state/__init__.py +18 -0
- illusion/state/app_state.py +67 -0
- illusion/state/store.py +93 -0
- illusion/swarm/__init__.py +71 -0
- illusion/swarm/agent_executor.py +857 -0
- illusion/swarm/in_process.py +259 -0
- illusion/swarm/subprocess_backend.py +136 -0
- illusion/swarm/team_helpers.py +123 -0
- illusion/swarm/types.py +159 -0
- illusion/swarm/worktree.py +347 -0
- illusion/tasks/__init__.py +33 -0
- illusion/tasks/local_agent_task.py +42 -0
- illusion/tasks/local_shell_task.py +27 -0
- illusion/tasks/manager.py +377 -0
- illusion/tasks/stop_task.py +21 -0
- illusion/tasks/types.py +88 -0
- illusion/tools/__init__.py +126 -0
- illusion/tools/agent_tool.py +388 -0
- illusion/tools/ask_user_question_tool.py +186 -0
- illusion/tools/base.py +149 -0
- illusion/tools/bash_tool.py +413 -0
- illusion/tools/config_tool.py +90 -0
- illusion/tools/cron_tool.py +473 -0
- illusion/tools/enter_plan_mode_tool.py +147 -0
- illusion/tools/enter_worktree_tool.py +188 -0
- illusion/tools/exit_plan_mode_tool.py +69 -0
- illusion/tools/exit_worktree_tool.py +225 -0
- illusion/tools/file_edit_tool.py +283 -0
- illusion/tools/file_read_tool.py +294 -0
- illusion/tools/file_write_tool.py +184 -0
- illusion/tools/glob_tool.py +165 -0
- illusion/tools/grep_tool.py +190 -0
- illusion/tools/list_mcp_resources_tool.py +80 -0
- illusion/tools/lsp_tool.py +333 -0
- illusion/tools/mcp_auth_tool.py +100 -0
- illusion/tools/mcp_tool.py +75 -0
- illusion/tools/notebook_edit_tool.py +242 -0
- illusion/tools/powershell_tool.py +334 -0
- illusion/tools/read_mcp_resource_tool.py +63 -0
- illusion/tools/repl_tool.py +100 -0
- illusion/tools/send_message_tool.py +112 -0
- illusion/tools/shell_common.py +187 -0
- illusion/tools/skill_tool.py +86 -0
- illusion/tools/sleep_tool.py +62 -0
- illusion/tools/structured_output_tool.py +58 -0
- illusion/tools/task_create_tool.py +98 -0
- illusion/tools/task_get_tool.py +94 -0
- illusion/tools/task_list_tool.py +94 -0
- illusion/tools/task_output_tool.py +55 -0
- illusion/tools/task_stop_tool.py +52 -0
- illusion/tools/task_update_tool.py +224 -0
- illusion/tools/team_create_tool.py +236 -0
- illusion/tools/team_delete_tool.py +104 -0
- illusion/tools/todo_write_tool.py +198 -0
- illusion/tools/tool_search_tool.py +156 -0
- illusion/tools/web_fetch_tool.py +264 -0
- illusion/tools/web_search_tool.py +186 -0
- illusion/ui/__init__.py +23 -0
- illusion/ui/app.py +258 -0
- illusion/ui/backend_host.py +1180 -0
- illusion/ui/input.py +86 -0
- illusion/ui/output.py +363 -0
- illusion/ui/permission_dialog.py +47 -0
- illusion/ui/permission_store.py +99 -0
- illusion/ui/protocol.py +384 -0
- illusion/ui/react_launcher.py +280 -0
- illusion/ui/runtime.py +787 -0
- illusion/ui/textual_app.py +603 -0
- illusion/ui/web/__init__.py +10 -0
- illusion/ui/web/server.py +87 -0
- illusion/ui/web/ws_host.py +1197 -0
- illusion/utils/__init__.py +0 -0
- illusion/utils/ripgrep.py +299 -0
- illusion/utils/shell.py +248 -0
- illusion_code-0.1.0.dist-info/METADATA +1159 -0
- illusion_code-0.1.0.dist-info/RECORD +214 -0
- illusion_code-0.1.0.dist-info/WHEEL +4 -0
- illusion_code-0.1.0.dist-info/entry_points.txt +2 -0
- 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)]
|