1
+---
2
+name: a0-manage-plugin
3
+description: Manage Agent Zero plugins lifecycle: browse the marketplace, scan for security, install from Git/ZIP/marketplace, update, uninstall, enable, disable, debug, and troubleshoot. Use when asked to install, update, uninstall, remove, scan, find, search, enable, disable, debug, or troubleshoot a plugin.
4
+version: 1.2.0
5
+tags: ["plugins", "install", "uninstall", "update", "scan", "security", "debug", "troubleshoot", "marketplace", "manage"]
6
+trigger_patterns:
7
+ - "install plugin"
8
+ - "uninstall plugin"
9
+ - "remove plugin"
10
+ - "delete plugin"
11
+ - "update plugin"
12
+ - "scan plugin"
13
+ - "debug plugin"
14
+ - "troubleshoot plugin"
15
+ - "browse plugins"
16
+ - "search plugins"
17
+ - "plugin not working"
18
+ - "enable plugin"
19
+ - "disable plugin"
20
+ - "plugin marketplace"
21
+ - "list plugins"
22
+---
23
+
24
+# Agent Zero Plugin Management
25
+
26
+## Action Routing
27
+
28
+Identify what the user needs and jump to the relevant section:
29
+
30
+| User need | Section |
31
+|---|---|
32
+| Find / search / browse available plugins | [Browse Marketplace](#browse-marketplace) |
33
+| Scan a plugin for security issues | [Security Scan](#security-scan) |
34
+| Install a plugin | [Install a Plugin](#install-a-plugin) |
35
+| Update an installed plugin | [Update a Plugin](#update-a-plugin) |
36
+| Uninstall / remove a plugin | [Uninstall a Plugin](#uninstall-a-plugin) |
37
+| Enable or disable a plugin | [Activation](#activation) |
38
+| Plugin not loading / crashing / missing | Read `/a0/skills/a0-debug-plugin/SKILL.md` |
39
+| Explain how plugin discovery works | Read `/a0/skills/a0-debug-plugin/SKILL.md` |
40
+
41
+---
42
+
43
+## Browse Marketplace
44
+
45
+Fetch the current community index:
46
+
47
+```python
48
+import urllib.request, json
49
+
50
+url = "https://github.com/agent0ai/a0-plugins/releases/download/generated-index/index.json"
51
+with urllib.request.urlopen(url, timeout=30) as resp:
52
+ index = json.loads(resp.read())
53
+
54
+plugins = index.get("plugins", {})
55
+```
56
+
57
+Each entry in `plugins` is keyed by plugin name with fields: `title`, `description`, `github`, `tags`, `thumbnail` (URL if available).
58
+
59
+**To search**: filter by keyword in `title`, `description`, or `tags`.
60
+
61
+**To list installed plugins** locally:
62
+```bash
63
+ls /a0/usr/plugins/
64
+```
65
+
66
+**Alternatively via UI**: Open the Plugins dialog in Agent Zero and switch to the **Browse** tab (or click **Install** in the toolbar to open the marketplace).
67
+
68
+---
69
+
70
+## Security Scan
71
+
72
+The `_plugin_scan` plugin provides an LLM-driven security scanner that clones the repository, reads all files, and produces a structured markdown report covering 6 checks: structure match, static code review, agent manipulation detection, remote communication, secrets access, and obfuscation.
73
+
74
+### Pre-install scan protocol (follow this for every install)
75
+
76
+**Always offer to scan before installing.** If the user hasn't explicitly declined, say:
77
+
78
+> "Before installing, I strongly recommend running a security scan on this plugin. Third-party plugins execute code inside your Agent Zero environment. Should I scan it first? The scan typically takes 2-4 minutes."
79
+
80
+If the user declines, acknowledge but warn once:
81
+
82
+> "Understood, skipping the scan. Note that installing unscanned third-party code carries security risks. Proceeding with installation."
83
+
84
+Then proceed to install. **Do not ask again** after the user declines.
85
+
86
+### Running a scan
87
+
88
+```python
89
+# (after authentication setup - see Install section)
90
+resp = s.post(
91
+ f"{BASE}/api/plugins/_plugin_scan/plugin_scan_run",
92
+ json={
93
+ "git_url": "https://github.com/<user>/<plugin-repo>",
94
+ "checks": ["structure", "codeReview", "agentManipulation", "remoteComms", "secrets", "obfuscation"],
95
+ },
96
+ headers={"X-CSRF-Token": token, "Origin": ORIGIN},
97
+ timeout=600, # set generously - the scan clones the repo, reads all files, runs 6 LLM checks
98
+)
99
+data = resp.json()
100
+```
101
+
102
+Always pass the full `checks` list explicitly - omitting it has been observed to cause intermittent failures. To run a subset, remove unwanted keys from the list.
103
+
104
+### Interpreting the result
105
+
106
+```python
107
+report = data["report"] # full markdown security report
108
+```
109
+
110
+Present the full `report` to the user. Read the Summary section to determine the overall verdict (**Safe** / **Caution** / **Dangerous**) and act accordingly:
111
+
112
+| Verdict | Action |
113
+|---|---|
114
+| **Safe** | Offer to proceed with installation |
115
+| **Caution** | Show findings, warn that issues were found, ask for explicit confirmation: "Some warnings were found. Do you still want to install?" |
116
+| **Dangerous** | Show findings, **strongly advise against installing**: "The scanner flagged serious security issues. I strongly recommend NOT installing this plugin. Do you want to proceed anyway?" Only install if user explicitly confirms. |
117
+
118
+If the scan times out or errors (500), inform the user and ask whether to proceed without a scan.
119
+
120
+---
121
+
122
+## Install a Plugin
123
+
124
+> **Always use the HTTP API or UI.** Never import Agent Zero framework modules directly from `code_execution_tool` - the agent runs in a separate Python runtime (`/opt/venv`) that does not have the framework's dependencies. All programmatic installs must go through HTTP.
125
+
126
+### How installed state works
127
+
128
+The marketplace marks a plugin as **Installed** by cross-referencing marketplace keys against `usr/plugins/` directory names at request time. To appear installed:
129
+- The plugin directory must exist at `usr/plugins/<name>/` with a valid `plugin.yaml`
130
+- The framework plugin cache must be cleared (the API handles this automatically)
131
+- Re-fetching the marketplace index will then show it as installed
132
+
133
+### API authentication (required for all HTTP calls)
134
+
135
+The Agent Zero API uses CSRF protection. The `Origin` header is **always required** - without it the CSRF endpoint returns `ok: false` even when login is disabled.
136
+
137
+**Step 1: Set the base URL.** Agent Zero listens on port 80 inside Docker (the standard deployment):
138
+
139
+```python
140
+import requests
141
+
142
+BASE = "http://localhost" # port 80 inside Docker
143
+# If running outside Docker (dev mode), check: os.environ.get("WEB_UI_PORT", "5000")
144
+```
145
+
146
+**Step 2: Bootstrap the session and get the CSRF token:**
147
+
148
+```python
149
+s = requests.Session()
150
+ORIGIN = BASE # Origin must match a localhost pattern
151
+
152
+r = s.get(f"{BASE}/api/csrf_token", headers={"Origin": ORIGIN}, timeout=10)
153
+data = r.json()
154
+
155
+if not data.get("ok"):
156
+ raise RuntimeError(f"CSRF bootstrap failed: {data.get('error')}")
157
+
158
+token = data["token"]
159
+runtime_id = data["runtime_id"]
160
+
161
+# Set the CSRF cookie (required alongside the header)
162
+s.cookies.set(f"csrf_token_{runtime_id}", token)
163
+```
164
+
165
+Reuse `s`, `BASE`, `ORIGIN`, and `token` for all subsequent API calls. Always include `headers={"X-CSRF-Token": token, "Origin": ORIGIN}` on every request.
166
+
167
+### Method 1: From a Git URL (via HTTP API) - preferred for programmatic use
168
+
169
+```python
170
+# (after authentication setup above)
171
+resp = s.post(
172
+ f"{BASE}/api/plugins/_plugin_installer/plugin_install",
173
+ json={
174
+ "action": "install_git",
175
+ "git_url": "https://github.com/<user>/<plugin-repo>",
176
+ # "git_token": "<token>", # optional, for private repos
177
+ # "plugin_name": "override" # optional, override directory name
178
+ },
179
+ headers={"X-CSRF-Token": token, "Origin": ORIGIN},
180
+ timeout=120,
181
+)
182
+print(resp.json())
183
+```
184
+
185
+This runs the full pipeline in the framework runtime: clone → validate → place in `usr/plugins/` → run `install` hook → clear plugin cache → notify frontend. The marketplace will show the plugin as installed on the next index fetch.
186
+
187
+### Method 2: From the Marketplace (UI) - preferred for interactive use
188
+
189
+1. Open the Plugins dialog
190
+2. Go to the **Browse** tab (or click **Install**)
191
+3. Find the plugin, click it, click **Install**
192
+
193
+The UI handles everything including marking the plugin as installed in the marketplace view.
194
+
195
+### Method 3: From a ZIP file (via HTTP API)
196
+
197
+```python
198
+# (after authentication setup above)
199
+with open("plugin.zip", "rb") as f:
200
+ resp = s.post(
201
+ f"{BASE}/api/plugins/_plugin_installer/plugin_install",
202
+ data={"action": "install_zip"},
203
+ files={"plugin_file": f},
204
+ headers={"X-CSRF-Token": token, "Origin": ORIGIN},
205
+ )
206
+print(resp.json())
207
+```
208
+
209
+Or via UI: Plugins dialog -> Install -> ZIP tab -> upload file.
210
+
211
+### Manual install (last resort only)
212
+
213
+Only use this if the HTTP API is genuinely unavailable (not because of import errors - those mean you must use the HTTP API instead).
214
+
215
+```bash
216
+git clone https://github.com/<user>/<repo> /a0/usr/plugins/<plugin_name>
217
+```
218
+
219
+After cloning, the plugin is on disk but the framework doesn't know about it. Clear the cache and notify the frontend by calling the toggle API (off then on) which triggers `after_plugin_change()` internally:
220
+
221
+```python
222
+# (after authentication setup above)
223
+for state in [False, True]:
224
+ s.post(
225
+ f"{BASE}/api/plugins",
226
+ json={"action": "toggle_plugin", "plugin_name": "<plugin_name>", "enabled": state},
227
+ headers={"X-CSRF-Token": token, "Origin": ORIGIN},
228
+ )
229
+```
230
+
231
+Or simply restart Agent Zero - on startup it re-scans `usr/plugins/` fresh.
232
+
233
+---
234
+
235
+## Update a Plugin
236
+
237
+> A dedicated update endpoint is being added to the framework. Until it lands, use the flow below.
238
+
239
+### Checking for updates
240
+
241
+**Do not compare version strings.** Contributors often forget to bump the version, so a matching version does not mean the plugin is current. Check for new commits instead:
242
+
243
+```bash
244
+# Is the plugin a git repo?
245
+git -C /a0/usr/plugins/<name> rev-parse --is-inside-work-tree 2>/dev/null
246
+
247
+# Compare local HEAD with remote HEAD (no fetch required)
248
+LOCAL=$(git -C /a0/usr/plugins/<name> rev-parse HEAD)
249
+REMOTE=$(git -C /a0/usr/plugins/<name> ls-remote origin HEAD | awk '{print $1}')
250
+echo "Local: $LOCAL"
251
+echo "Remote: $REMOTE"
252
+[ "$LOCAL" = "$REMOTE" ] && echo "Up to date" || echo "Update available"
253
+```
254
+
255
+If they differ, new commits exist on the remote - report this to the user as "update available" regardless of whether the version field changed.
256
+
257
+### Applying the update
258
+
259
+If installed via Git:
260
+
261
+```bash
262
+cd /a0/usr/plugins/<name>
263
+git pull origin main
264
+```
265
+
266
+Then refresh the framework cache via the toggle API (see [API authentication](#api-authentication-required-for-all-http-calls) for session setup):
267
+
268
+```python
269
+# (after authentication setup)
270
+for state in [False, True]:
271
+ s.post(
272
+ f"{BASE}/api/plugins",
273
+ json={"action": "toggle_plugin", "plugin_name": "<name>", "enabled": state},
274
+ headers={"X-CSRF-Token": token, "Origin": ORIGIN},
275
+ )
276
+```
277
+
278
+If not a git repo: uninstall via the API (see [Uninstall a Plugin](#uninstall-a-plugin)), then reinstall via the Git method above.
279
+
280
+---
281
+
282
+## Uninstall a Plugin
283
+
284
+> **Safety rules - read before proceeding**:
285
+> - **Core plugins** (in `plugins/`, not `usr/plugins/`) cannot be uninstalled via the API - the framework blocks it. Disable them instead (see [Activation](#activation)).
286
+> - **Always ask for explicit user confirmation** before uninstalling: "Are you sure you want to uninstall `<name>`? This will delete all plugin files and cannot be undone."
287
+> - Uninstalling does NOT delete plugin config files stored in `usr/agents/` or project scopes.
288
+
289
+### Standard uninstall (via API)
290
+
291
+Uses the framework's `uninstall_plugin` which calls the plugin's `uninstall` hook (if defined) before deleting. Requires an authenticated session (see [API authentication](#api-authentication-required-for-all-http-calls) in the Install section):
292
+
293
+```python
294
+# (after authentication setup from the Install section)
295
+resp = s.post(
296
+ f"{BASE}/api/plugins",
297
+ json={
298
+ "action": "delete_plugin",
299
+ "plugin_name": "<name>",
300
+ },
301
+ headers={"X-CSRF-Token": token, "Origin": ORIGIN},
302
+)
303
+print(resp.json())
304
+```
305
+
306
+This is the preferred method. The framework will:
307
+1. Call `uninstall()` from `hooks.py` (if present) - runs cleanup
308
+2. Delete the `usr/plugins/<name>/` directory
309
+3. Notify the frontend to refresh the plugin list
310
+
311
+**Via UI**: Plugins dialog -> find the plugin -> click the delete (trash) icon -> confirm.
312
+
313
+### Fallback: direct folder removal
314
+
315
+Use this only if the standard uninstall fails (e.g., broken `uninstall` hook that crashes or hangs):
316
+
317
+```bash
318
+# Confirm the plugin is a custom one (usr/plugins/) - NEVER delete from plugins/
319
+ls /a0/usr/plugins/<name>/
320
+
321
+# Remove it
322
+rm -rf /a0/usr/plugins/<name>/
323
+```
324
+
325
+After manual removal, refresh the plugin list via the UI or restart Agent Zero.
326
+
327
+---
328
+
329
+## Activation
330
+
331
+Plugins are enabled/disabled via toggle files:
332
+- `.toggle-1` = explicitly ON
333
+- `.toggle-0` = explicitly OFF
334
+- No file = default (enabled for most plugins)
335
+
336
+**Enable a plugin**:
337
+```bash
338
+rm -f /a0/usr/plugins/<name>/.toggle-0
339
+touch /a0/usr/plugins/<name>/.toggle-1
340
+```
341
+
342
+**Disable a plugin**:
343
+```bash
344
+rm -f /a0/usr/plugins/<name>/.toggle-1
345
+touch /a0/usr/plugins/<name>/.toggle-0
346
+```
347
+
348
+Via UI: Plugins dialog -> find the plugin -> use the toggle switch.
349
+
350
+Plugins with `always_enabled: true` in `plugin.yaml` cannot be toggled (framework core plugins only).
351
+
352
+**Scoped toggles** (when `per_project_config` or `per_agent_config` is true): use the "Switch" modal in the UI, or place toggle files in the appropriate scoped path:
353
+- Project scope: `project/.a0proj/plugins/<name>/.toggle-1`
354
+- Agent profile scope: `usr/agents/<profile>/plugins/<name>/.toggle-1`
355
+
356
+---
357
+
358
+## References
359
+
360
+- Plugin architecture: `/a0/docs/agents/AGENTS.plugins.md`
361
+- Debug a broken plugin: read `/a0/skills/a0-debug-plugin/SKILL.md`
362
+- Create a new plugin: read `/a0/skills/a0-create-plugin/SKILL.md`
363
+- Review a plugin: read `/a0/skills/a0-review-plugin/SKILL.md`