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,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
插件安装辅助模块
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
本模块提供插件安装和卸载功能。
|
|
6
|
+
|
|
7
|
+
主要功能:
|
|
8
|
+
- 从路径安装插件到用户插件目录
|
|
9
|
+
- 卸载用户插件
|
|
10
|
+
|
|
11
|
+
使用示例:
|
|
12
|
+
>>> from illusion.plugins.installer import install_plugin_from_path, uninstall_plugin
|
|
13
|
+
>>> install_plugin_from_path("/path/to/plugin")
|
|
14
|
+
>>> uninstall_plugin("my_plugin")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import shutil
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from illusion.plugins.loader import get_user_plugins_dir
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def install_plugin_from_path(source: str | Path) -> Path:
|
|
26
|
+
"""安装插件目录到用户插件目录
|
|
27
|
+
|
|
28
|
+
将源插件目录复制到用户插件目录。
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
source: 插件源目录路径
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Path: 安装后的插件目录路径
|
|
35
|
+
"""
|
|
36
|
+
src = Path(source).resolve()
|
|
37
|
+
dest = get_user_plugins_dir() / src.name
|
|
38
|
+
if dest.exists():
|
|
39
|
+
shutil.rmtree(dest)
|
|
40
|
+
shutil.copytree(src, dest)
|
|
41
|
+
return dest
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def uninstall_plugin(name: str) -> bool:
|
|
45
|
+
"""卸载用户插件
|
|
46
|
+
|
|
47
|
+
根据目录名称删除用户插件。
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
name: 插件名称(目录名)
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
bool: 是否成功卸载
|
|
54
|
+
"""
|
|
55
|
+
path = get_user_plugins_dir() / name
|
|
56
|
+
if not path.exists():
|
|
57
|
+
return False
|
|
58
|
+
shutil.rmtree(path)
|
|
59
|
+
return True
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""
|
|
2
|
+
插件发现和加载模块
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
本模块实现插件的发现和加载功能。
|
|
6
|
+
|
|
7
|
+
主要功能:
|
|
8
|
+
- 发现用户和项目插件目录
|
|
9
|
+
- 加载插件清单和配置
|
|
10
|
+
- 解析插件技能、命令、钩子和 MCP 服务器
|
|
11
|
+
|
|
12
|
+
使用示例:
|
|
13
|
+
>>> from illusion.plugins.loader import load_plugins, get_user_plugins_dir
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from illusion.config.paths import get_config_dir
|
|
23
|
+
from illusion.plugins.schemas import PluginManifest
|
|
24
|
+
from illusion.plugins.types import LoadedPlugin
|
|
25
|
+
from illusion.skills.loader import _parse_skill_markdown
|
|
26
|
+
from illusion.skills.types import SkillDefinition
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_user_plugins_dir() -> Path:
|
|
32
|
+
"""获取用户插件目录
|
|
33
|
+
|
|
34
|
+
返回用户级别的插件目录,如果不存在则创建。
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Path: 用户插件目录路径
|
|
38
|
+
"""
|
|
39
|
+
path = get_config_dir() / "plugins"
|
|
40
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
return path
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_project_plugins_dir(cwd: str | Path) -> Path:
|
|
45
|
+
"""获取项目插件目录
|
|
46
|
+
|
|
47
|
+
返回项目级别的插件目录。
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
cwd: 工作目录
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Path: 项目插件目录路径
|
|
54
|
+
"""
|
|
55
|
+
path = Path(cwd).resolve() / ".illusion" / "plugins"
|
|
56
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
return path
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _find_manifest(plugin_dir: Path) -> Path | None:
|
|
61
|
+
"""查找插件清单文件
|
|
62
|
+
|
|
63
|
+
在标准位置或 .claude-plugin/ 目录下查找 plugin.json。
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
plugin_dir: 插件目录
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Path | None: 找到的清单文件路径,不存在则返回 None
|
|
70
|
+
"""
|
|
71
|
+
for candidate in [
|
|
72
|
+
plugin_dir / "plugin.json",
|
|
73
|
+
plugin_dir / ".claude-plugin" / "plugin.json",
|
|
74
|
+
]:
|
|
75
|
+
if candidate.exists():
|
|
76
|
+
return candidate
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def discover_plugin_paths(cwd: str | Path) -> list[Path]:
|
|
81
|
+
"""发现插件目录
|
|
82
|
+
|
|
83
|
+
从用户和项目位置查找所有插件目录。
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
cwd: 工作目录
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
list[Path]: 插件目录路径列表
|
|
90
|
+
"""
|
|
91
|
+
roots = [get_user_plugins_dir(), get_project_plugins_dir(cwd)]
|
|
92
|
+
paths: list[Path] = []
|
|
93
|
+
for root in roots:
|
|
94
|
+
if not root.exists():
|
|
95
|
+
continue
|
|
96
|
+
for path in sorted(root.iterdir()):
|
|
97
|
+
if path.is_dir() and _find_manifest(path) is not None:
|
|
98
|
+
paths.append(path)
|
|
99
|
+
return paths
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def load_plugins(settings, cwd: str | Path) -> list[LoadedPlugin]:
|
|
103
|
+
"""从磁盘加载所有插件
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
settings: 设置对象
|
|
107
|
+
cwd: 工作目录
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
list[LoadedPlugin]: 已加载的插件列表
|
|
111
|
+
"""
|
|
112
|
+
plugins: list[LoadedPlugin] = []
|
|
113
|
+
for path in discover_plugin_paths(cwd):
|
|
114
|
+
plugin = load_plugin(path, settings.enabled_plugins)
|
|
115
|
+
if plugin is not None:
|
|
116
|
+
plugins.append(plugin)
|
|
117
|
+
return plugins
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def load_plugin(path: Path, enabled_plugins: dict[str, bool]) -> LoadedPlugin | None:
|
|
121
|
+
"""加载单个插件目录
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
path: 插件目录路径
|
|
125
|
+
enabled_plugins: 启用的插件配置
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
LoadedPlugin | None: 已加载的插件,未找到清单则返回 None
|
|
129
|
+
"""
|
|
130
|
+
manifest_path = _find_manifest(path)
|
|
131
|
+
if manifest_path is None:
|
|
132
|
+
return None
|
|
133
|
+
try:
|
|
134
|
+
manifest = PluginManifest.model_validate_json(manifest_path.read_text(encoding="utf-8"))
|
|
135
|
+
except Exception as exc:
|
|
136
|
+
logger.debug("Failed to load plugin manifest from %s: %s", manifest_path, exc)
|
|
137
|
+
return None
|
|
138
|
+
enabled = enabled_plugins.get(manifest.name, manifest.enabled_by_default)
|
|
139
|
+
|
|
140
|
+
# 从多个位置发现技能
|
|
141
|
+
skills = _load_plugin_skills(path / manifest.skills_dir)
|
|
142
|
+
|
|
143
|
+
# 从 plugin commands/ 目录发现命令
|
|
144
|
+
commands_dir = path / "commands"
|
|
145
|
+
if commands_dir.exists():
|
|
146
|
+
skills.extend(_load_plugin_skills(commands_dir))
|
|
147
|
+
|
|
148
|
+
# 从 plugin agents/ 目录发现智能体
|
|
149
|
+
agents_dir = path / "agents"
|
|
150
|
+
if agents_dir.exists():
|
|
151
|
+
skills.extend(_load_plugin_skills(agents_dir))
|
|
152
|
+
|
|
153
|
+
# 从 hooks/ 目录或根 hooks.json 发现钩子
|
|
154
|
+
hooks = _load_plugin_hooks(path / manifest.hooks_file)
|
|
155
|
+
hooks_dir_file = path / "hooks" / "hooks.json"
|
|
156
|
+
if not hooks and hooks_dir_file.exists():
|
|
157
|
+
hooks = _load_plugin_hooks_structured(hooks_dir_file, path)
|
|
158
|
+
|
|
159
|
+
mcp = _load_plugin_mcp(path / manifest.mcp_file)
|
|
160
|
+
mcp_json = path / ".mcp.json"
|
|
161
|
+
if not mcp and mcp_json.exists():
|
|
162
|
+
mcp = _load_plugin_mcp(mcp_json)
|
|
163
|
+
|
|
164
|
+
return LoadedPlugin(
|
|
165
|
+
manifest=manifest,
|
|
166
|
+
path=path,
|
|
167
|
+
enabled=enabled,
|
|
168
|
+
skills=skills,
|
|
169
|
+
hooks=hooks,
|
|
170
|
+
mcp_servers=mcp,
|
|
171
|
+
commands=[s for s in skills if s.source == "plugin"],
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _load_plugin_skills(path: Path) -> list[SkillDefinition]:
|
|
176
|
+
"""从目录中的 markdown 和 yaml 文件加载技能定义
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
path: 包含 .md/.yaml/.yml 技能文件的目录
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
list[SkillDefinition]: 解析后的技能定义列表,如果路径不存在则返回空列表
|
|
183
|
+
"""
|
|
184
|
+
if not path.exists():
|
|
185
|
+
return []
|
|
186
|
+
from illusion.skills.loader import _load_yaml_skill
|
|
187
|
+
|
|
188
|
+
skills: list[SkillDefinition] = []
|
|
189
|
+
for skill_path in sorted(path.iterdir()):
|
|
190
|
+
if not skill_path.is_file():
|
|
191
|
+
continue
|
|
192
|
+
if skill_path.suffix in (".yaml", ".yml"):
|
|
193
|
+
skill = _load_yaml_skill(skill_path, source="plugin")
|
|
194
|
+
if skill:
|
|
195
|
+
skills.append(skill)
|
|
196
|
+
elif skill_path.suffix == ".md":
|
|
197
|
+
content = skill_path.read_text(encoding="utf-8")
|
|
198
|
+
name, description = _parse_skill_markdown(skill_path.stem, content)
|
|
199
|
+
skills.append(
|
|
200
|
+
SkillDefinition(
|
|
201
|
+
name=name,
|
|
202
|
+
description=description,
|
|
203
|
+
content=content,
|
|
204
|
+
source="plugin",
|
|
205
|
+
path=str(skill_path),
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
return skills
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _load_plugin_hooks(path: Path) -> dict[str, list]:
|
|
212
|
+
"""从平面 hooks.json 文件加载钩子
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
path: hooks JSON 文件路径
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
dict[str, list]: 事件名称到钩子定义对象列表的字典
|
|
219
|
+
"""
|
|
220
|
+
if not path.exists():
|
|
221
|
+
return {}
|
|
222
|
+
from illusion.hooks.schemas import (
|
|
223
|
+
AgentHookDefinition,
|
|
224
|
+
CommandHookDefinition,
|
|
225
|
+
HttpHookDefinition,
|
|
226
|
+
PromptHookDefinition,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
230
|
+
parsed: dict[str, list] = {}
|
|
231
|
+
for event, hooks in raw.items():
|
|
232
|
+
parsed[event] = []
|
|
233
|
+
for hook in hooks:
|
|
234
|
+
hook_type = hook.get("type")
|
|
235
|
+
if hook_type == "command":
|
|
236
|
+
parsed[event].append(CommandHookDefinition.model_validate(hook))
|
|
237
|
+
elif hook_type == "prompt":
|
|
238
|
+
parsed[event].append(PromptHookDefinition.model_validate(hook))
|
|
239
|
+
elif hook_type == "http":
|
|
240
|
+
parsed[event].append(HttpHookDefinition.model_validate(hook))
|
|
241
|
+
elif hook_type == "agent":
|
|
242
|
+
parsed[event].append(AgentHookDefinition.model_validate(hook))
|
|
243
|
+
return parsed
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _load_plugin_hooks_structured(path: Path, plugin_root: Path) -> dict[str, list]:
|
|
247
|
+
"""从结构化 hooks.json 格式加载钩子
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
path: hooks.json 文件路径
|
|
251
|
+
plugin_root: 插件根目录
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
dict[str, list]: 解析后的钩子字典
|
|
255
|
+
"""
|
|
256
|
+
if not path.exists():
|
|
257
|
+
return {}
|
|
258
|
+
try:
|
|
259
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
260
|
+
except (json.JSONDecodeError, OSError):
|
|
261
|
+
return {}
|
|
262
|
+
hooks_data = raw.get("hooks", raw)
|
|
263
|
+
if not isinstance(hooks_data, dict):
|
|
264
|
+
return {}
|
|
265
|
+
parsed: dict[str, list] = {}
|
|
266
|
+
for event, entries in hooks_data.items():
|
|
267
|
+
if not isinstance(entries, list):
|
|
268
|
+
continue
|
|
269
|
+
parsed[event] = []
|
|
270
|
+
for entry in entries:
|
|
271
|
+
hook_list = entry.get("hooks", [])
|
|
272
|
+
matcher = entry.get("matcher", "")
|
|
273
|
+
for hook in hook_list:
|
|
274
|
+
# 将 ${CLAUDE_PLUGIN_ROOT} 替换为实际路径
|
|
275
|
+
cmd = hook.get("command", "")
|
|
276
|
+
cmd = cmd.replace("${CLAUDE_PLUGIN_ROOT}", str(plugin_root))
|
|
277
|
+
parsed[event].append({
|
|
278
|
+
"type": hook.get("type", "command"),
|
|
279
|
+
"command": cmd,
|
|
280
|
+
"matcher": matcher,
|
|
281
|
+
"timeout": hook.get("timeout"),
|
|
282
|
+
})
|
|
283
|
+
return parsed
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _load_plugin_mcp(path: Path) -> dict[str, object]:
|
|
287
|
+
"""从 JSON 文件加载 MCP 服务器配置
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
path: MCP 配置文件路径(例如 .mcp.json)
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
dict[str, object]: 服务器名称到配置对象的字典
|
|
294
|
+
"""
|
|
295
|
+
if not path.exists():
|
|
296
|
+
return {}
|
|
297
|
+
from illusion.mcp.types import McpJsonConfig
|
|
298
|
+
|
|
299
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
300
|
+
parsed = McpJsonConfig.model_validate(raw)
|
|
301
|
+
return parsed.mcpServers
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
插件清单模式模块
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
本模块定义插件清单的数据模型。
|
|
6
|
+
|
|
7
|
+
主要组件:
|
|
8
|
+
- PluginManifest: 插件清单
|
|
9
|
+
|
|
10
|
+
使用示例:
|
|
11
|
+
>>> from illusion.plugins.schemas import PluginManifest
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PluginManifest(BaseModel):
|
|
20
|
+
"""插件清单
|
|
21
|
+
|
|
22
|
+
定义插件的元数据,存储在 plugin.json 或 .claude-plugin/plugin.json 文件中。
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
name: 插件名称
|
|
26
|
+
version: 插件版本
|
|
27
|
+
description: 插件描述
|
|
28
|
+
enabled_by_default: 默认是否启用
|
|
29
|
+
skills_dir: 技能目录名称
|
|
30
|
+
hooks_file: 钩子文件名
|
|
31
|
+
mcp_file: MCP 配置文件名
|
|
32
|
+
author: 作者信息
|
|
33
|
+
commands: 命令配置
|
|
34
|
+
agents: 智能体配置
|
|
35
|
+
skills: 技能配置
|
|
36
|
+
hooks: 钩子配置
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
name: str # 插件名称
|
|
40
|
+
version: str = "0.0.0" # 插件版本
|
|
41
|
+
description: str = "" # 插件描述
|
|
42
|
+
enabled_by_default: bool = True # 默认是否启用
|
|
43
|
+
skills_dir: str = "skills" # 技能目录名称
|
|
44
|
+
hooks_file: str = "hooks.json" # 钩子文件名
|
|
45
|
+
mcp_file: str = "mcp.json" # MCP 配置文件名
|
|
46
|
+
# 扩展字段:可选的 author, commands, agents 等
|
|
47
|
+
author: dict | None = None # 作者信息
|
|
48
|
+
commands: str | list | dict | None = None # 命令配置
|
|
49
|
+
agents: str | list | None = None # 智能体配置
|
|
50
|
+
skills: str | list | None = None # 技能配置
|
|
51
|
+
hooks: str | dict | list | None = None # 钩子配置
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
插件运行时类型模块
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
本模块定义插件运行时使用的类型。
|
|
6
|
+
|
|
7
|
+
主要组件:
|
|
8
|
+
- LoadedPlugin: 已加载的插件
|
|
9
|
+
|
|
10
|
+
使用示例:
|
|
11
|
+
>>> from illusion.plugins.types import LoadedPlugin
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from illusion.mcp.types import McpServerConfig
|
|
20
|
+
from illusion.plugins.schemas import PluginManifest
|
|
21
|
+
from illusion.skills.types import SkillDefinition
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class LoadedPlugin:
|
|
26
|
+
"""已加载的插件及其贡献的内容
|
|
27
|
+
|
|
28
|
+
表示一个已加载的插件,包含清单、路径、启用状态、技能、钩子等。
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
manifest: 插件清单
|
|
32
|
+
path: 插件目录路径
|
|
33
|
+
enabled: 是否启用
|
|
34
|
+
skills: 技能定义列表
|
|
35
|
+
hooks: 钩子字典
|
|
36
|
+
mcp_servers: MCP 服务器配置字典
|
|
37
|
+
commands: 命令技能列表
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
manifest: PluginManifest # 插件清单
|
|
41
|
+
path: Path # 插件目录路径
|
|
42
|
+
enabled: bool # 是否启用
|
|
43
|
+
skills: list[SkillDefinition] = field(default_factory=list) # 技能列表
|
|
44
|
+
hooks: dict[str, list] = field(default_factory=dict) # 钩子字典
|
|
45
|
+
mcp_servers: dict[str, McpServerConfig] = field(default_factory=dict) # MCP 服务器配置
|
|
46
|
+
commands: list[SkillDefinition] = field(default_factory=list) # 命令列表
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def name(self) -> str:
|
|
50
|
+
"""获取插件名称"""
|
|
51
|
+
return self.manifest.name
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def description(self) -> str:
|
|
55
|
+
"""获取插件描述"""
|
|
56
|
+
return self.manifest.description
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
提示词模块
|
|
3
|
+
==========
|
|
4
|
+
|
|
5
|
+
本模块提供 IllusionCode 系统提示词构建功能。
|
|
6
|
+
|
|
7
|
+
主要组件:
|
|
8
|
+
- build_system_prompt: 构建系统提示词
|
|
9
|
+
- build_runtime_system_prompt: 构建运行时系统提示词
|
|
10
|
+
- discover_claude_md_files: 发现 Claude.md 文件
|
|
11
|
+
- load_claude_md_prompt: 加载 Claude.md 提示词
|
|
12
|
+
- get_environment_info: 获取环境信息
|
|
13
|
+
|
|
14
|
+
使用示例:
|
|
15
|
+
>>> from illusion.prompts import build_system_prompt, get_environment_info
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from illusion.prompts.claudemd import discover_claude_md_files, load_claude_md_prompt
|
|
19
|
+
from illusion.prompts.context import build_runtime_system_prompt
|
|
20
|
+
from illusion.prompts.system_prompt import build_system_prompt
|
|
21
|
+
from illusion.prompts.environment import get_environment_info
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"build_runtime_system_prompt",
|
|
25
|
+
"build_system_prompt",
|
|
26
|
+
"discover_claude_md_files",
|
|
27
|
+
"get_environment_info",
|
|
28
|
+
"load_claude_md_prompt",
|
|
29
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLAUDE.md 发现和加载模块
|
|
3
|
+
========================
|
|
4
|
+
|
|
5
|
+
本模块实现 CLAUDE.md 指令文件的发现和加载功能。
|
|
6
|
+
|
|
7
|
+
主要功能:
|
|
8
|
+
- 在当前目录中查找 CLAUDE.md 文件
|
|
9
|
+
- 发现 .claude/rules 目录下的规则文件
|
|
10
|
+
- 将多个指令文件加载为一个提示词章节
|
|
11
|
+
|
|
12
|
+
使用示例:
|
|
13
|
+
>>> from illusion.prompts.claudemd import discover_claude_md_files, load_claude_md_prompt
|
|
14
|
+
>>> files = discover_claude_md_files("/path/to/project")
|
|
15
|
+
>>> prompt = load_claude_md_prompt("/path/to/project")
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def discover_claude_md_files(cwd: str | Path) -> list[Path]:
|
|
24
|
+
"""发现相关的 CLAUDE.md 指令文件(只在 cwd 中查找)
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
cwd: 工作目录
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
list[Path]: 找到的指令文件路径列表
|
|
31
|
+
"""
|
|
32
|
+
current = Path(cwd).resolve()
|
|
33
|
+
results: list[Path] = []
|
|
34
|
+
seen: set[Path] = set()
|
|
35
|
+
|
|
36
|
+
for candidate in (
|
|
37
|
+
current / "CLAUDE.md",
|
|
38
|
+
current / ".claude" / "CLAUDE.md",
|
|
39
|
+
):
|
|
40
|
+
if candidate.exists() and candidate not in seen:
|
|
41
|
+
results.append(candidate)
|
|
42
|
+
seen.add(candidate)
|
|
43
|
+
|
|
44
|
+
rules_dir = current / ".claude" / "rules"
|
|
45
|
+
if rules_dir.is_dir():
|
|
46
|
+
for rule in sorted(rules_dir.glob("*.md")):
|
|
47
|
+
if rule not in seen:
|
|
48
|
+
results.append(rule)
|
|
49
|
+
seen.add(rule)
|
|
50
|
+
|
|
51
|
+
return results
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_claude_md_prompt(cwd: str | Path, *, max_chars_per_file: int = 12000) -> str | None:
|
|
55
|
+
"""将发现的指令文件加载为一个提示词章节
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
cwd: 工作目录
|
|
59
|
+
max_chars_per_file: 每个文件的最大字符数
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
str | None: 格式化的提示词章节,如果没有文件则返回 None
|
|
63
|
+
"""
|
|
64
|
+
files = discover_claude_md_files(cwd)
|
|
65
|
+
if not files:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
lines = ["# Project Instructions"]
|
|
69
|
+
for path in files:
|
|
70
|
+
content = path.read_text(encoding="utf-8", errors="replace")
|
|
71
|
+
if len(content) > max_chars_per_file:
|
|
72
|
+
content = content[:max_chars_per_file] + "\n...[truncated]..."
|
|
73
|
+
lines.extend(["", f"## {path}", "```md", content.strip(), "```"])
|
|
74
|
+
return "\n".join(lines)
|