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,329 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: update-config
|
|
3
|
+
description: Configure Illusion Code via settings.json. Use for permissions, hooks, env vars, MCP servers, and other settings. Examples: "allow npm commands", "set DEBUG=true", "add a hook to format code after writes".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Update Config Skill
|
|
7
|
+
|
|
8
|
+
Modify Illusion Code configuration by updating settings.json files.
|
|
9
|
+
|
|
10
|
+
## When Hooks Are Required
|
|
11
|
+
|
|
12
|
+
If the user wants something to happen automatically in response to an EVENT, they need a **hook** configured in settings.json.
|
|
13
|
+
|
|
14
|
+
**These require hooks:**
|
|
15
|
+
- "After writing files, run prettier" → `post_tool_use` hook with matcher `Write|Edit`
|
|
16
|
+
- "Before running bash commands, validate them" → `pre_tool_use` hook with matcher `Bash`
|
|
17
|
+
- "When session starts, show a greeting" → `session_start` hook
|
|
18
|
+
|
|
19
|
+
**Hook events (only 4):**
|
|
20
|
+
- `session_start` — When session starts
|
|
21
|
+
- `session_end` — When session ends
|
|
22
|
+
- `pre_tool_use` — Before tool execution (can block)
|
|
23
|
+
- `post_tool_use` — After tool execution
|
|
24
|
+
|
|
25
|
+
## CRITICAL: Read Before Write
|
|
26
|
+
|
|
27
|
+
**Always read the existing settings file before making changes.** Merge new settings with existing ones - never replace the entire file.
|
|
28
|
+
|
|
29
|
+
## CRITICAL: Use `ask_user_question` for Ambiguity
|
|
30
|
+
|
|
31
|
+
When the user's request is ambiguous, use `ask_user_question` to clarify:
|
|
32
|
+
- Which settings file to modify (user/project)
|
|
33
|
+
- Whether to add to existing arrays or replace them
|
|
34
|
+
- Specific values when multiple options exist
|
|
35
|
+
|
|
36
|
+
## Settings File Locations
|
|
37
|
+
|
|
38
|
+
| File | Scope | Use For |
|
|
39
|
+
|------|-------|---------|
|
|
40
|
+
| `~/.illusion/settings.json` | Global | Personal preferences for all projects |
|
|
41
|
+
| `.illusion/settings.json` | Project | Team-wide hooks, permissions, plugins |
|
|
42
|
+
|
|
43
|
+
Settings load in order: user → project (later overrides earlier).
|
|
44
|
+
|
|
45
|
+
## Settings Schema Reference
|
|
46
|
+
|
|
47
|
+
### Model Configuration
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"model": "env_1.model_1",
|
|
51
|
+
"max_tokens": 16384,
|
|
52
|
+
"max_turns": 200,
|
|
53
|
+
"context_window": 200000,
|
|
54
|
+
"effort": "medium"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The `model` field format is `env_N.model_N` referencing an environment configuration.
|
|
59
|
+
|
|
60
|
+
### Environment Configuration (env_N)
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"env_1": {
|
|
64
|
+
"api_format": "anthropic",
|
|
65
|
+
"base_url": null,
|
|
66
|
+
"api_key": "",
|
|
67
|
+
"model_1": "claude-sonnet-4-6",
|
|
68
|
+
"model_2": "claude-opus-4-6"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Permissions
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"permission": {
|
|
77
|
+
"mode": "default",
|
|
78
|
+
"allowed_tools": ["Bash(npm:*)", "Edit", "Read"],
|
|
79
|
+
"denied_tools": ["Bash(rm -rf:*)"],
|
|
80
|
+
"denied_commands": ["git push --force"],
|
|
81
|
+
"path_rules": [
|
|
82
|
+
{"pattern": ".env*", "allow": false},
|
|
83
|
+
{"pattern": "*.md", "allow": true}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Permission modes:** `default`, `plan`, `accept_edits`, `dont_ask`
|
|
90
|
+
|
|
91
|
+
**Permission Rule Syntax:**
|
|
92
|
+
- Exact match: `"Bash(npm run test)"`
|
|
93
|
+
- Prefix wildcard: `"Bash(git:*)"` - matches `git status`, `git commit`, etc.
|
|
94
|
+
- Tool only: `"Read"` - allows all Read operations
|
|
95
|
+
|
|
96
|
+
### Hooks
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"hooks": {
|
|
100
|
+
"pre_tool_use": [
|
|
101
|
+
{
|
|
102
|
+
"type": "command",
|
|
103
|
+
"command": "echo 'Tool called'",
|
|
104
|
+
"timeout_seconds": 30,
|
|
105
|
+
"matcher": "Bash",
|
|
106
|
+
"block_on_failure": false
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
"post_tool_use": [
|
|
110
|
+
{
|
|
111
|
+
"type": "command",
|
|
112
|
+
"command": "prettier --write $FILE",
|
|
113
|
+
"timeout_seconds": 30,
|
|
114
|
+
"matcher": "Write|Edit"
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Memory
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"memory": {
|
|
125
|
+
"enabled": true,
|
|
126
|
+
"max_files": 5,
|
|
127
|
+
"max_entrypoint_lines": 200
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### MCP Servers
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"mcp_servers": {
|
|
136
|
+
"server-name": {
|
|
137
|
+
"command": "node",
|
|
138
|
+
"args": ["server.js"],
|
|
139
|
+
"env": {}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Other Settings
|
|
146
|
+
- `ui_language`: Interface language (e.g., "zh-CN", "en-US")
|
|
147
|
+
- `output_style`: Output style name
|
|
148
|
+
- `show_thinking`: Show thinking process (boolean)
|
|
149
|
+
- `fast_mode`: Enable fast mode (boolean)
|
|
150
|
+
- `verbose`: Verbose output (boolean)
|
|
151
|
+
- `passes`: Number of passes (integer)
|
|
152
|
+
- `enabled_plugins`: Plugin enable/disable map
|
|
153
|
+
|
|
154
|
+
## Hook Types
|
|
155
|
+
|
|
156
|
+
### 1. Command Hook
|
|
157
|
+
Runs a shell command:
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"type": "command",
|
|
161
|
+
"command": "prettier --write $FILE",
|
|
162
|
+
"timeout_seconds": 30,
|
|
163
|
+
"matcher": "Write|Edit",
|
|
164
|
+
"block_on_failure": false
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 2. Prompt Hook
|
|
169
|
+
Uses LLM to evaluate a condition:
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"type": "prompt",
|
|
173
|
+
"prompt": "Is this command safe? $ARGUMENTS",
|
|
174
|
+
"model": "claude-sonnet-4-6",
|
|
175
|
+
"timeout_seconds": 30,
|
|
176
|
+
"matcher": "Bash",
|
|
177
|
+
"block_on_failure": true
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 3. HTTP Hook
|
|
182
|
+
Sends event payload to an HTTP endpoint:
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"type": "http",
|
|
186
|
+
"url": "https://example.com/webhook",
|
|
187
|
+
"headers": {"Authorization": "Bearer token"},
|
|
188
|
+
"timeout_seconds": 30,
|
|
189
|
+
"matcher": "Write|Edit",
|
|
190
|
+
"block_on_failure": false
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 4. Agent Hook
|
|
195
|
+
Uses an agent for deep validation:
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"type": "agent",
|
|
199
|
+
"prompt": "Verify this change is safe: $ARGUMENTS",
|
|
200
|
+
"model": "claude-sonnet-4-6",
|
|
201
|
+
"timeout_seconds": 60,
|
|
202
|
+
"matcher": "Write|Edit",
|
|
203
|
+
"block_on_failure": true
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Hook Fields
|
|
208
|
+
|
|
209
|
+
| Field | Type | Default | Description |
|
|
210
|
+
|-------|------|---------|-------------|
|
|
211
|
+
| `type` | string | required | Hook type: `command`, `prompt`, `http`, `agent` |
|
|
212
|
+
| `command` | string | (command) | Shell command to execute |
|
|
213
|
+
| `prompt` | string | (prompt/agent) | Prompt for LLM evaluation |
|
|
214
|
+
| `url` | string | (http) | HTTP endpoint URL |
|
|
215
|
+
| `headers` | object | `{}` | HTTP headers |
|
|
216
|
+
| `model` | string | null | Model override (prompt/agent) |
|
|
217
|
+
| `timeout_seconds` | int | 30/60 | Timeout in seconds |
|
|
218
|
+
| `matcher` | string | null | Tool name pattern to match |
|
|
219
|
+
| `block_on_failure` | bool | varies | Block execution on failure |
|
|
220
|
+
|
|
221
|
+
### Hook Input (stdin JSON)
|
|
222
|
+
Hooks receive JSON on stdin:
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"session_id": "abc123",
|
|
226
|
+
"tool_name": "Write",
|
|
227
|
+
"tool_input": { "file_path": "/path/to/file.txt", "content": "..." },
|
|
228
|
+
"tool_response": { "success": true }
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Hook Output
|
|
233
|
+
|
|
234
|
+
Command hooks can output JSON to control behavior:
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"blocked": true,
|
|
238
|
+
"reason": "Command not allowed",
|
|
239
|
+
"output": "Detailed explanation"
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
- `blocked` — Set to `true` to block the tool execution
|
|
244
|
+
- `reason` — Message shown when blocking
|
|
245
|
+
- `output` — Output text (displayed to user or injected as context)
|
|
246
|
+
|
|
247
|
+
## Common Patterns
|
|
248
|
+
|
|
249
|
+
### Auto-format after writes
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"hooks": {
|
|
253
|
+
"post_tool_use": [{
|
|
254
|
+
"type": "command",
|
|
255
|
+
"command": "jq -r '.tool_input.file_path // .tool_response.filePath' | { read -r f; prettier --write \"$f\"; } 2>/dev/null || true",
|
|
256
|
+
"matcher": "Write|Edit",
|
|
257
|
+
"timeout_seconds": 30
|
|
258
|
+
}]
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Log all bash commands
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"hooks": {
|
|
267
|
+
"pre_tool_use": [{
|
|
268
|
+
"type": "command",
|
|
269
|
+
"command": "jq -r '.tool_input.command' >> ~/.illusion/bash-log.txt",
|
|
270
|
+
"matcher": "Bash"
|
|
271
|
+
}]
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Block dangerous commands
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"hooks": {
|
|
280
|
+
"pre_tool_use": [{
|
|
281
|
+
"type": "command",
|
|
282
|
+
"command": "jq -r '.tool_input.command' | grep -qE 'rm -rf|drop table' && echo '{\"blocked\": true, \"reason\": \"Dangerous command blocked\"}' || true",
|
|
283
|
+
"matcher": "Bash",
|
|
284
|
+
"block_on_failure": false
|
|
285
|
+
}]
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Workflow
|
|
291
|
+
|
|
292
|
+
1. **Clarify intent** — Ask if the request is ambiguous
|
|
293
|
+
2. **Read existing file** — Use Read tool on the target settings file
|
|
294
|
+
3. **Merge carefully** — Preserve existing settings, especially arrays
|
|
295
|
+
4. **Edit file** — Use Edit tool (if file doesn't exist, create it first)
|
|
296
|
+
5. **Validate** — Check JSON syntax
|
|
297
|
+
6. **Confirm** — Tell user what was changed
|
|
298
|
+
|
|
299
|
+
## Merging Arrays (Important!)
|
|
300
|
+
|
|
301
|
+
When adding to permission arrays or hook arrays, **merge with existing**, don't replace:
|
|
302
|
+
|
|
303
|
+
**WRONG** (replaces existing):
|
|
304
|
+
```json
|
|
305
|
+
{ "permission": { "allowed_tools": ["Bash(npm:*)"] } }
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**RIGHT** (preserves existing + adds new):
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"permission": {
|
|
312
|
+
"allowed_tools": [
|
|
313
|
+
"Bash(git:*)",
|
|
314
|
+
"Edit",
|
|
315
|
+
"Bash(npm:*)"
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Troubleshooting
|
|
322
|
+
|
|
323
|
+
If a hook isn't running:
|
|
324
|
+
1. Check the settings file exists and has valid JSON
|
|
325
|
+
2. Verify the event name is correct (lowercase with underscores)
|
|
326
|
+
3. Check the matcher matches the tool name
|
|
327
|
+
4. Check hook type is one of: `command`, `prompt`, `http`, `agent`
|
|
328
|
+
5. Test the command manually
|
|
329
|
+
6. Check `timeout_seconds` isn't too low
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verify
|
|
3
|
+
description: Verify that a code change actually does what it's supposed to by running the app and observing behavior. Use when asked to verify a PR, confirm a fix works, test a change manually, check that a feature works, or validate local changes before pushing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Verify: Manual Feature Verification
|
|
7
|
+
|
|
8
|
+
Verify that a code change actually does what it's supposed to by running the app and observing behavior.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
Use this skill when:
|
|
13
|
+
- Asked to verify a PR or confirm a fix works
|
|
14
|
+
- Testing a change manually before pushing
|
|
15
|
+
- Checking that a feature works as expected
|
|
16
|
+
- Validating local changes
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
### 1. Understand the Change
|
|
21
|
+
|
|
22
|
+
Run `git diff` (or `git diff HEAD` if there are staged changes) to see what changed. If there are no git changes, review the most recently modified files.
|
|
23
|
+
|
|
24
|
+
Identify:
|
|
25
|
+
- What feature/fix was implemented
|
|
26
|
+
- What the expected behavior should be
|
|
27
|
+
- How to trigger/test the behavior
|
|
28
|
+
|
|
29
|
+
### 2. Determine Verification Method
|
|
30
|
+
|
|
31
|
+
Based on the change type, choose the appropriate verification:
|
|
32
|
+
|
|
33
|
+
**UI Changes:**
|
|
34
|
+
- Start the dev server
|
|
35
|
+
- Navigate to the affected page/component
|
|
36
|
+
- Interact with the changed elements
|
|
37
|
+
- Screenshot the result
|
|
38
|
+
|
|
39
|
+
**API Changes:**
|
|
40
|
+
- Start the server
|
|
41
|
+
- Use curl or similar to hit the affected endpoints
|
|
42
|
+
- Verify response codes and data
|
|
43
|
+
|
|
44
|
+
**CLI Changes:**
|
|
45
|
+
- Run the CLI with relevant flags/arguments
|
|
46
|
+
- Check output matches expectations
|
|
47
|
+
|
|
48
|
+
**Logic Changes:**
|
|
49
|
+
- Write a simple test script if needed
|
|
50
|
+
- Run with specific inputs
|
|
51
|
+
- Verify outputs
|
|
52
|
+
|
|
53
|
+
### 3. Execute Verification
|
|
54
|
+
|
|
55
|
+
1. **Setup**: Start any required services (dev server, database, etc.)
|
|
56
|
+
2. **Test the happy path**: The primary use case should work
|
|
57
|
+
3. **Test edge cases**: Boundary conditions, error cases
|
|
58
|
+
4. **Check for regressions**: Existing features still work
|
|
59
|
+
|
|
60
|
+
### 4. Report Results
|
|
61
|
+
|
|
62
|
+
Provide a clear report:
|
|
63
|
+
- What was tested
|
|
64
|
+
- What passed
|
|
65
|
+
- What failed (if anything)
|
|
66
|
+
- Screenshots/logs if relevant
|
|
67
|
+
|
|
68
|
+
## Rules
|
|
69
|
+
|
|
70
|
+
- Actually run the code, don't just read it
|
|
71
|
+
- Test the specific change, not the entire application
|
|
72
|
+
- Report honestly — if something doesn't work, say so
|
|
73
|
+
- Include reproduction steps for any failures
|
|
74
|
+
- If verification requires setup the user hasn't done, ask first
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Skill 加载模块 — 从内置和用户目录加载 Skills
|
|
3
|
+
=========================================
|
|
4
|
+
|
|
5
|
+
本模块提供从内置目录和用户配置目录加载 Skills 的功能。
|
|
6
|
+
|
|
7
|
+
主要功能:
|
|
8
|
+
- 获取用户 skills 目录
|
|
9
|
+
- 加载 skill 注册表
|
|
10
|
+
- 加载用户 skills
|
|
11
|
+
- 解析 skill markdown 文件
|
|
12
|
+
|
|
13
|
+
类说明:
|
|
14
|
+
- get_user_skills_dir: 获取用户 skills 目录
|
|
15
|
+
- load_skill_registry: 加载内置和用户定义的 skills
|
|
16
|
+
- load_user_skills: 从用户配置目录加载 markdown skills
|
|
17
|
+
|
|
18
|
+
使用示例:
|
|
19
|
+
>>> from illusion.skills.loader import get_user_skills_dir, load_skill_registry
|
|
20
|
+
>>> # 获取用户 skills 目录
|
|
21
|
+
>>> skills_dir = get_user_skills_dir()
|
|
22
|
+
>>> # 加载 skill 注册表
|
|
23
|
+
>>> registry = load_skill_registry(cwd="/path/to/project")
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
import yaml
|
|
31
|
+
|
|
32
|
+
from illusion.config.paths import get_config_dir, get_project_config_dir
|
|
33
|
+
from illusion.config.settings import load_settings
|
|
34
|
+
from illusion.skills.bundled import get_bundled_skills
|
|
35
|
+
from illusion.skills.registry import SkillRegistry
|
|
36
|
+
from illusion.skills.types import SkillDefinition
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_user_skills_dir() -> Path:
|
|
40
|
+
"""返回用户 skills 目录。"""
|
|
41
|
+
path = get_config_dir() / "skills"
|
|
42
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
return path
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_project_skills_dir(cwd: str | Path) -> Path:
|
|
47
|
+
"""返回项目级 skills 目录(.illusion/skills/)。"""
|
|
48
|
+
path = get_project_config_dir(cwd) / "skills"
|
|
49
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
return path
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_project_rules_dir(cwd: str | Path) -> Path:
|
|
54
|
+
"""返回项目级 rules 目录(.illusion/rules/)。"""
|
|
55
|
+
path = get_project_config_dir(cwd) / "rules"
|
|
56
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
return path
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def load_skill_registry(cwd: str | Path | None = None) -> SkillRegistry:
|
|
61
|
+
"""加载内置和用户定义的 skills。"""
|
|
62
|
+
registry = SkillRegistry()
|
|
63
|
+
# 注册内置 skills
|
|
64
|
+
for skill in get_bundled_skills():
|
|
65
|
+
registry.register(skill)
|
|
66
|
+
# 注册用户 skills
|
|
67
|
+
for skill in load_user_skills():
|
|
68
|
+
registry.register(skill)
|
|
69
|
+
# 如果提供了工作目录,加载项目级 skills 和插件 skills
|
|
70
|
+
if cwd is not None:
|
|
71
|
+
# 项目级 skills(同名时覆盖全局)
|
|
72
|
+
for skill in load_project_skills(cwd):
|
|
73
|
+
registry.register(skill)
|
|
74
|
+
from illusion.plugins.loader import load_plugins
|
|
75
|
+
|
|
76
|
+
settings = load_settings()
|
|
77
|
+
for plugin in load_plugins(settings, cwd):
|
|
78
|
+
if not plugin.enabled:
|
|
79
|
+
continue
|
|
80
|
+
for skill in plugin.skills:
|
|
81
|
+
registry.register(skill)
|
|
82
|
+
return registry
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def load_user_skills() -> list[SkillDefinition]:
|
|
86
|
+
"""从用户配置目录加载 skills(支持 .md、.yaml、.yml)。"""
|
|
87
|
+
skills: list[SkillDefinition] = []
|
|
88
|
+
for path in sorted(get_user_skills_dir().iterdir()):
|
|
89
|
+
if path.suffix in (".yaml", ".yml"):
|
|
90
|
+
skill = _load_yaml_skill(path, source="user")
|
|
91
|
+
if skill:
|
|
92
|
+
skills.append(skill)
|
|
93
|
+
elif path.suffix == ".md":
|
|
94
|
+
content = path.read_text(encoding="utf-8")
|
|
95
|
+
name, description = _parse_skill_markdown(path.stem, content)
|
|
96
|
+
skills.append(
|
|
97
|
+
SkillDefinition(
|
|
98
|
+
name=name,
|
|
99
|
+
description=description,
|
|
100
|
+
content=content,
|
|
101
|
+
source="user",
|
|
102
|
+
path=str(path),
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
return skills
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def load_project_skills(cwd: str | Path) -> list[SkillDefinition]:
|
|
109
|
+
"""从项目目录加载 skills(支持 .md、.yaml、.yml)。
|
|
110
|
+
|
|
111
|
+
目录结构: <project>/.illusion/skills/<skill_name>/<skill_name>.md 或 .yaml/.yml
|
|
112
|
+
"""
|
|
113
|
+
skills: list[SkillDefinition] = []
|
|
114
|
+
skills_dir = get_project_skills_dir(cwd)
|
|
115
|
+
for sub in sorted(skills_dir.iterdir()):
|
|
116
|
+
if not sub.is_dir():
|
|
117
|
+
continue
|
|
118
|
+
for path in sorted(sub.iterdir()):
|
|
119
|
+
if path.suffix in (".yaml", ".yml"):
|
|
120
|
+
skill = _load_yaml_skill(path, source="project")
|
|
121
|
+
if skill:
|
|
122
|
+
skills.append(skill)
|
|
123
|
+
elif path.suffix == ".md":
|
|
124
|
+
content = path.read_text(encoding="utf-8")
|
|
125
|
+
name, description = _parse_skill_markdown(path.stem, content)
|
|
126
|
+
skills.append(
|
|
127
|
+
SkillDefinition(
|
|
128
|
+
name=name,
|
|
129
|
+
description=description,
|
|
130
|
+
content=content,
|
|
131
|
+
source="project",
|
|
132
|
+
path=str(path),
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
return skills
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _parse_skill_markdown(default_name: str, content: str) -> tuple[str, str]:
|
|
139
|
+
"""解析 skill markdown 文件的名称和描述,支持 YAML frontmatter。"""
|
|
140
|
+
name = default_name
|
|
141
|
+
description = ""
|
|
142
|
+
|
|
143
|
+
lines = content.splitlines()
|
|
144
|
+
|
|
145
|
+
# 先尝试 YAML frontmatter(--- ... ---)
|
|
146
|
+
if lines and lines[0].strip() == "---":
|
|
147
|
+
end_idx = -1
|
|
148
|
+
for i, line in enumerate(lines[1:], 1):
|
|
149
|
+
if line.strip() == "---":
|
|
150
|
+
end_idx = i
|
|
151
|
+
break
|
|
152
|
+
if end_idx > 0:
|
|
153
|
+
fm_text = "\n".join(lines[1:end_idx])
|
|
154
|
+
try:
|
|
155
|
+
data = yaml.safe_load(fm_text)
|
|
156
|
+
if isinstance(data, dict):
|
|
157
|
+
if data.get("name"):
|
|
158
|
+
name = str(data["name"]).strip()
|
|
159
|
+
if data.get("description"):
|
|
160
|
+
description = str(data["description"]).strip()
|
|
161
|
+
except Exception:
|
|
162
|
+
# YAML 解析失败,回退到手动解析
|
|
163
|
+
for fm_line in lines[1:end_idx]:
|
|
164
|
+
fm_stripped = fm_line.strip()
|
|
165
|
+
if fm_stripped.startswith("name:"):
|
|
166
|
+
val = fm_stripped[5:].strip().strip("'\"")
|
|
167
|
+
if val:
|
|
168
|
+
name = val
|
|
169
|
+
elif fm_stripped.startswith("description:"):
|
|
170
|
+
val = fm_stripped[12:].strip().strip("'\"")
|
|
171
|
+
if val:
|
|
172
|
+
description = val
|
|
173
|
+
|
|
174
|
+
# 回退:从标题和第一段提取
|
|
175
|
+
if not description:
|
|
176
|
+
for line in lines:
|
|
177
|
+
stripped = line.strip()
|
|
178
|
+
if stripped.startswith("# "):
|
|
179
|
+
if not name or name == default_name:
|
|
180
|
+
name = stripped[2:].strip() or default_name
|
|
181
|
+
continue
|
|
182
|
+
if stripped and not stripped.startswith("---") and not stripped.startswith("#"):
|
|
183
|
+
description = stripped[:200]
|
|
184
|
+
break
|
|
185
|
+
|
|
186
|
+
if not description:
|
|
187
|
+
description = f"Skill: {name}"
|
|
188
|
+
return name, description
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _load_yaml_skill(path: Path, source: str) -> SkillDefinition | None:
|
|
192
|
+
"""从 YAML 文件加载 skill 定义。
|
|
193
|
+
|
|
194
|
+
YAML skill 文件格式:
|
|
195
|
+
name: my-skill
|
|
196
|
+
description: "A skill defined in YAML"
|
|
197
|
+
content: |
|
|
198
|
+
# Instructions here
|
|
199
|
+
...
|
|
200
|
+
"""
|
|
201
|
+
try:
|
|
202
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
203
|
+
except Exception:
|
|
204
|
+
return None
|
|
205
|
+
if not isinstance(data, dict):
|
|
206
|
+
return None
|
|
207
|
+
name = data.get("name", path.stem)
|
|
208
|
+
description = data.get("description", "")
|
|
209
|
+
content = data.get("content", "")
|
|
210
|
+
if not content:
|
|
211
|
+
# 如果没有 content 字段,将整个 yaml 序列化作为 content
|
|
212
|
+
content = yaml.dump(data, allow_unicode=True, default_flow_style=False)
|
|
213
|
+
return SkillDefinition(
|
|
214
|
+
name=name,
|
|
215
|
+
description=description or f"Skill: {name}",
|
|
216
|
+
content=content,
|
|
217
|
+
source=source,
|
|
218
|
+
path=str(path),
|
|
219
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Skill 注册表模块
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
本模块提供 Skill 注册表功能,按名称存储已加载的 skills。
|
|
6
|
+
|
|
7
|
+
类说明:
|
|
8
|
+
- SkillRegistry: 按名称存储已加载的 skills
|
|
9
|
+
|
|
10
|
+
使用示例:
|
|
11
|
+
>>> from illusion.skills import SkillRegistry
|
|
12
|
+
>>> registry = SkillRegistry()
|
|
13
|
+
>>> # 注册 skill
|
|
14
|
+
>>> registry.register(skill_definition)
|
|
15
|
+
>>> # 获取 skill
|
|
16
|
+
>>> skill = registry.get("my_skill")
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from illusion.skills.types import SkillDefinition
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SkillRegistry:
|
|
25
|
+
"""按名称存储已加载的 skills。"""
|
|
26
|
+
|
|
27
|
+
def __init__(self) -> None:
|
|
28
|
+
self._skills: dict[str, SkillDefinition] = {}
|
|
29
|
+
|
|
30
|
+
def register(self, skill: SkillDefinition) -> None:
|
|
31
|
+
"""注册一个 skill。"""
|
|
32
|
+
self._skills[skill.name] = skill
|
|
33
|
+
|
|
34
|
+
def get(self, name: str) -> SkillDefinition | None:
|
|
35
|
+
"""按名称返回 skill。"""
|
|
36
|
+
return self._skills.get(name)
|
|
37
|
+
|
|
38
|
+
def list_skills(self) -> list[SkillDefinition]:
|
|
39
|
+
"""返回所有 skills,按名称排序。"""
|
|
40
|
+
return sorted(self._skills.values(), key=lambda skill: skill.name)
|
illusion/skills/types.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Skill 数据模型模块
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
本模块定义 Skill 相关的数据模型。
|
|
6
|
+
|
|
7
|
+
类说明:
|
|
8
|
+
- SkillDefinition: 已加载的 Skill 数据类
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class SkillDefinition:
|
|
18
|
+
"""已加载的 Skill。"""
|
|
19
|
+
|
|
20
|
+
name: str
|
|
21
|
+
description: str
|
|
22
|
+
content: str
|
|
23
|
+
source: str
|
|
24
|
+
path: str | None = None
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
状态管理模块
|
|
3
|
+
============
|
|
4
|
+
|
|
5
|
+
本模块提供 IllusionCode 应用状态管理功能。
|
|
6
|
+
|
|
7
|
+
主要组件:
|
|
8
|
+
- AppState: 应用状态
|
|
9
|
+
- AppStateStore: 应用状态存储
|
|
10
|
+
|
|
11
|
+
使用示例:
|
|
12
|
+
>>> from illusion.state import AppState, AppStateStore
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from illusion.state.app_state import AppState
|
|
16
|
+
from illusion.state.store import AppStateStore
|
|
17
|
+
|
|
18
|
+
__all__ = ["AppState", "AppStateStore"]
|