refactor: normalize plugin paths, add README viewer to plugin info modal, and update plugin hub filters
- Normalize plugin paths using files.normalize_a0_path in get_enhanced_plugins_list - Add collapsible README section to plugin info modal with loading/error states - Implement loadPluginReadme function to fetch and render plugin documentation - Update plugin hub pagination from 20 to 24 items per page - Change popular filter threshold from >0 to >=3 stars - Add comprehensive README styling with support
frdel committed
Mar 22, 2026 at 08:05 UTC
fd3b5253c761a17917513441a94402a880baec79
16 files changed
+718
-5
helpers/plugins.py
+1
-1
@@ -269,7 +269,7 @@ def get_enhanced_plugins_list(
269
results.append(
270
PluginListItem(
271
name=d.name,
272
- path=str(d),
272
+ path=files.normalize_a0_path(str(d)),
273
display_name=meta.title or d.name,
274
description=meta.description,
275
version=meta.version,
plugins/_chat_branching/README.md
new
+32
@@ -0,0 +1,32 @@
1
+# Chat Branching
2
+
3
+Create a new chat from any existing point in a conversation.
4
+
5
+## What It Does
6
+
7
+This plugin adds an API endpoint that clones an existing chat context, trims its log history up to a selected message, gives the new chat a `(branch)` suffix, persists it immediately, and refreshes connected tabs so the new branch appears in the UI.
8
+
9
+## Main Behavior
10
+
11
+- **Clone context**
12
+ - Serializes the current chat context and deserializes it into a brand-new context with a new ID.
13
+- **Trim history**
14
+ - Keeps log entries only up to and including the selected `log_no`.
15
+ - Includes a fallback for cases where reloaded logs use sequential array indexes.
16
+- **Persist immediately**
17
+ - Saves the newly created branched chat to temporary chat storage.
18
+- **Refresh UI state**
19
+ - Marks state dirty for all tabs after the branch is created.
20
+
21
+## Entry Points
22
+
23
+- **API**
24
+ - `api/branch_chat.py` implements the branching operation.
25
+- **Extensions**
26
+ - `extensions/` contains integration glue for making the feature available in the app.
27
+
28
+## Plugin Metadata
29
+
30
+- **Name**: `_chat_branching`
31
+- **Title**: `Chat Branching`
32
+- **Description**: Branch a chat from any message, creating a new chat with history up to that point.
plugins/_code_execution/README.md
new
+52
@@ -0,0 +1,52 @@
1
+# Code Execution
2
+
3
+Run terminal commands and execute Python or Node.js code through Agent Zero using persistent shell sessions.
4
+
5
+## What It Does
6
+
7
+This plugin provides the code execution tool used by agents for development tasks. It supports:
8
+
9
+- **Terminal commands** in interactive shell sessions
10
+- **Python execution** through `ipython -c`
11
+- **Node.js execution** through `node /exe/node_eval.js`
12
+- **Persistent sessions** keyed by session number
13
+- **Session reset and output retrieval**
14
+- **Local or SSH-backed execution** depending on plugin configuration
15
+
16
+## Main Behavior
17
+
18
+- **Persistent shells**
19
+ - Maintains per-session shell state in agent data so subsequent calls can reuse the same terminal session.
20
+- **Multiple runtimes**
21
+ - Dispatches requests based on `runtime`: `terminal`, `python`, `nodejs`, `output`, or `reset`.
22
+- **Remote execution support**
23
+ - Can open SSH interactive sessions instead of local shells when configured.
24
+- **Streaming output**
25
+ - Continuously reads shell output, updates the current log item, and detects progress while commands are running.
26
+- **Safety around running sessions**
27
+ - Tracks whether a shell is currently busy and can prevent overlapping commands unless explicitly allowed.
28
+
29
+## Key Files
30
+
31
+- **Tool**
32
+ - `tools/code_execution_tool.py` contains runtime dispatch, session lifecycle, and streaming output logic.
33
+- **Helpers**
34
+ - `helpers/shell_local.py` provides the local interactive shell implementation.
35
+ - `helpers/shell_ssh.py` provides the SSH-backed interactive shell implementation.
36
+- **Configuration**
37
+ - `default_config.yaml` defines execution, prompt, and timeout settings.
38
+- **Prompts**
39
+ - `prompts/` contains the response templates shown to the agent.
40
+
41
+## Configuration Scope
42
+
43
+- **Settings section**: `agent`
44
+- **Per-project config**: `true`
45
+- **Per-agent config**: `true`
46
+- **Always enabled**: `false`
47
+
48
+## Plugin Metadata
49
+
50
+- **Name**: `_code_execution`
51
+- **Title**: `Code Execution`
52
+- **Description**: Code execution tool supporting terminal, Python, and Node.js runtimes via local TTY or SSH.
plugins/_email_integration/README.md
new
+52
@@ -0,0 +1,52 @@
1
+# Email Integration
2
+
3
+Communicate with Agent Zero through email inboxes and send replies back over SMTP.
4
+
5
+## What It Does
6
+
7
+This plugin polls configured mailboxes, downloads incoming messages and attachments, decides whether a message should continue an existing chat or start a new one, and sends replies back by email.
8
+
9
+It supports both:
10
+
11
+- **IMAP inbox polling**
12
+- **Exchange inbox polling**
13
+- **SMTP replies**
14
+
15
+## Main Behavior
16
+
17
+- **Mailbox polling**
18
+ - Tracks per-handler mailbox state in `usr/email/state.json`.
19
+ - Uses UID tracking for IMAP accounts so only new mail is processed after initialization.
20
+- **Attachment handling**
21
+ - Downloads attachments into `usr/email/attachments`.
22
+- **Dispatcher workflow**
23
+ - Reuses or creates a background `Email Dispatcher` context.
24
+ - Uses model prompts to decide whether an email belongs to an existing chat or should open a new one.
25
+- **Thread routing**
26
+ - Can continue an existing chat by thread ID found in the email subject.
27
+ - Falls back to model-based dispatch if no direct thread match is available.
28
+- **Notifications and persistence**
29
+ - Saves chats after routing and emits notifications about new or continued conversations.
30
+
31
+## Key Files
32
+
33
+- **Core orchestration**
34
+ - `helpers/handler.py` manages polling, state persistence, dispatching, and reply flow.
35
+- **Mail helpers**
36
+ - `helpers/imap_client.py` handles IMAP and Exchange fetching.
37
+ - `helpers/smtp_client.py` handles outbound replies.
38
+ - `helpers/dispatcher.py` formats chat summaries and parses dispatcher decisions.
39
+- **API and extensions**
40
+ - `api/` and `extensions/` connect the polling loop and UI-facing operations into the main app.
41
+
42
+## Configuration Scope
43
+
44
+- **Settings section**: `external`
45
+- **Per-project config**: `false`
46
+- **Per-agent config**: `false`
47
+
48
+## Plugin Metadata
49
+
50
+- **Name**: `_email_integration`
51
+- **Title**: `Email Integration`
52
+- **Description**: Communicate with Agent Zero via email. Supports IMAP/Exchange inbox polling with SMTP replies.
plugins/_error_retry/README.md
new
+39
@@ -0,0 +1,39 @@
1
+# Error Retry
2
+
3
+Retry once after an unexpected critical exception so the agent has a chance to recover instead of failing immediately.
4
+
5
+## What It Does
6
+
7
+This plugin hooks into the agent lifecycle and automatically retries the current loop once when an unhandled critical exception occurs.
8
+
9
+It does **not** retry exceptions that are already treated as controlled agent flow, such as:
10
+
11
+- `HandledException`
12
+- `RepairableException`
13
+
14
+## Main Behavior
15
+
16
+- **Counter reset per monologue**
17
+ - Clears the retry counter at the start of a new monologue.
18
+- **Critical exception retry**
19
+ - On an unexpected exception, logs a warning, waits briefly, injects an agent-facing critical error message into history, and suppresses the original exception once.
20
+- **Single retry only**
21
+ - Uses an internal counter so the retry happens at most one time per monologue.
22
+
23
+## Key Files
24
+
25
+- `extensions/python/agent_Agent_monologue_start/_10_reset_critical_exception_counter.py`
26
+- `extensions/python/agent_Agent_handle_exception_end/_80_retry_critical_exception.py`
27
+
28
+## Configuration Scope
29
+
30
+- **Settings section**: `agent`
31
+- **Per-project config**: `true`
32
+- **Per-agent config**: `true`
33
+- **Always enabled**: `false`
34
+
35
+## Plugin Metadata
36
+
37
+- **Name**: `_error_retry`
38
+- **Title**: `Error Retry`
39
+- **Description**: Retry on critical exceptions before failing.
plugins/_infection_check/README.md
+29
-2
@@ -1,8 +1,12 @@
1
# Infection Check
2
3
-Safety middleware that scans agent outputs for prompt injection, credential leaks, and malicious behavior before allowing tool execution.
3
+Safety middleware that analyzes agent output for prompt injection and suspicious external influence before allowing tool execution.
4
5
-## How it Works
5
+## What It Does
6
+
7
+This plugin collects streamed reasoning and response text, analyzes that content with a configurable audit model, and blocks tool execution until the safety check either passes, requests clarification, or terminates the agent.
8
+
9
+## How It Works
10
11
1. **Collection** — During streaming, the plugin collects the agent's reasoning and response text via `reasoning_stream_chunk` and `response_stream_chunk` extensions.
12
2. **Analysis** — A security audit model analyzes the collected text against the configurable prompt.
@@ -49,6 +53,17 @@ When the check results in `<terminate/>` (directly or after exhausting clarifica
53
| History Size | `10` | Recent messages included as context |
54
| Prompt | *(built-in)* | Fully customizable security audit system prompt |
55
56
+## Key Files
57
+
58
+- **Checker logic**
59
+ - `helpers/checker.py` implements stream collection, background analysis, gating, clarification, and termination.
60
+- **Extensions**
61
+ - `extensions/python/reasoning_stream_chunk/_50_infection_collect.py`
62
+ - `extensions/python/response_stream_chunk/_50_infection_collect.py`
63
+ - `extensions/python/response_stream/_50_infection_analyze.py`
64
+ - `extensions/python/response_stream_end/_50_infection_analyze.py`
65
+ - `extensions/python/tool_execute_before/_50_infection_check.py`
66
+
67
## Extension Points Used
68
69
| Extension Point | File | Purpose |
@@ -58,3 +73,15 @@ When the check results in `<terminate/>` (directly or after exhausting clarifica
73
| `response_stream` | `_50_infection_analyze.py` | Detect thoughts complete → start background analysis |
74
| `response_stream_end` | `_50_infection_analyze.py` | Start analysis (complete mode / fallback) |
75
| `tool_execute_before` | `_50_infection_check.py` | Await check result → gate tool execution |
76
+
77
+## Configuration Scope
78
+
79
+- **Settings section**: `agent`
80
+- **Per-project config**: `true`
81
+- **Per-agent config**: `true`
82
+
83
+## Plugin Metadata
84
+
85
+- **Name**: `_infection_check`
86
+- **Title**: `Infection Check`
87
+- **Description**: Safety check for prompt injection from external sources.
plugins/_memory/README.md
new
+51
@@ -0,0 +1,51 @@
1
+# Memory
2
+
3
+Provide persistent vector-based memory and knowledge retrieval for Agent Zero.
4
+
5
+## What It Does
6
+
7
+This plugin stores memories and knowledge embeddings in a FAISS-backed vector database, exposes tools for saving and recalling memories, and provides APIs and UI support for browsing, importing, updating, and deleting memory entries.
8
+
9
+## Main Behavior
10
+
11
+- **Persistent vector store**
12
+ - Creates and loads FAISS indexes per memory subdirectory.
13
+ - Stores embedding metadata so the index can be rebuilt if the embedding model changes.
14
+- **Knowledge preloading**
15
+ - Loads configured knowledge directories into memory when a database is initialized.
16
+- **Memory tools**
17
+ - Includes tools for saving, loading, deleting, forgetting, and behavior adjustment workflows.
18
+- **Dashboard APIs**
19
+ - Exposes search, delete, bulk delete, update, and subdirectory listing endpoints for the memory dashboard.
20
+- **Scoped storage**
21
+ - Supports different memory subdirectories so memory can be separated by context or agent scope.
22
+
23
+## Key Files
24
+
25
+- **Core memory engine**
26
+ - `helpers/memory.py` implements FAISS storage, index loading, embedding configuration, and knowledge preload.
27
+- **Knowledge import**
28
+ - `helpers/knowledge_import.py` imports external knowledge into memory storage.
29
+- **Consolidation**
30
+ - `helpers/memory_consolidation.py` contains memory consolidation logic.
31
+- **Tools**
32
+ - `tools/memory_save.py`
33
+ - `tools/memory_load.py`
34
+ - `tools/memory_delete.py`
35
+ - `tools/memory_forget.py`
36
+ - `tools/behaviour_adjustment.py`
37
+- **API**
38
+ - `api/memory_dashboard.py` powers the memory management dashboard.
39
+ - `api/import_knowledge.py` and `api/knowledge_reindex.py` handle knowledge import and reindexing.
40
+
41
+## Configuration Scope
42
+
43
+- **Settings section**: `agent`
44
+- **Per-project config**: `true`
45
+- **Per-agent config**: `true`
46
+
47
+## Plugin Metadata
48
+
49
+- **Name**: `_memory`
50
+- **Title**: `Memory`
51
+- **Description**: Provides persistent memory capabilities to Agent Zero agents.
plugins/_model_config/README.md
new
+53
@@ -0,0 +1,53 @@
1
+# Model Configuration
2
+
3
+Manage which models Agent Zero uses for chat, utility, and embeddings, with support for scoped overrides and reusable presets.
4
+
5
+## What It Does
6
+
7
+This plugin centralizes model selection and model-related settings for the application. It provides helpers and APIs for:
8
+
9
+- selecting chat, utility, and embedding models
10
+- reading and saving model presets
11
+- checking for missing API keys
12
+- allowing optional per-chat model overrides
13
+- resolving config at global, project, agent, and chat scope
14
+
15
+## Main Behavior
16
+
17
+- **Scoped configuration**
18
+ - Reads plugin config through the standard plugin config system with project and agent overrides.
19
+- **Preset management**
20
+ - Loads presets from a user file when present and falls back to bundled defaults.
21
+- **Per-chat override**
22
+ - Allows a chat context to store a temporary override or preset reference in context data.
23
+- **Model object construction**
24
+ - Builds `ModelConfig` objects and the runtime chat, browser, utility, and embedding wrappers used elsewhere in the app.
25
+- **API key validation**
26
+ - Reports configured providers that still require API keys.
27
+
28
+## Key Files
29
+
30
+- **Core helper**
31
+ - `helpers/model_config.py` resolves config, presets, overrides, and runtime model objects.
32
+- **APIs**
33
+ - `api/model_config_get.py`
34
+ - `api/model_config_set.py`
35
+ - `api/model_override.py`
36
+ - `api/model_presets.py`
37
+ - `api/model_search.py`
38
+ - `api/api_keys.py`
39
+- **Hooks**
40
+ - `hooks.py` exposes plugin-level integration hooks.
41
+
42
+## Configuration Scope
43
+
44
+- **Settings section**: `agent`
45
+- **Per-project config**: `true`
46
+- **Per-agent config**: `true`
47
+- **Always enabled**: `true`
48
+
49
+## Plugin Metadata
50
+
51
+- **Name**: `_model_config`
52
+- **Title**: `Model Configuration`
53
+- **Description**: Manages LLM model selection and configuration for chat, utility, and embedding models. Supports per-project and per-agent overrides with optional per-chat model switching.
plugins/_plugin_installer/README.md
new
+44
@@ -0,0 +1,44 @@
1
+# Plugin Installer
2
+
3
+Install and update Agent Zero plugins from ZIP uploads, Git repositories, or a community index.
4
+
5
+## What It Does
6
+
7
+This plugin provides the built-in installation workflow for third-party plugins. It validates plugin manifests, prevents naming conflicts, installs plugins into `usr/plugins/`, optionally updates Git-based plugins, and exposes a UI for browsing and installing community plugins.
8
+
9
+## Main Behavior
10
+
11
+- **ZIP install**
12
+ - Accepts an uploaded archive, extracts it safely, locates `plugin.yaml`, validates metadata, and moves the plugin into `usr/plugins/`.
13
+- **Git install**
14
+ - Clones a repository to a temporary directory, validates the plugin, then installs it into `usr/plugins/`.
15
+- **Plugin update**
16
+ - Updates already installed Git-backed custom plugins and re-runs installation hooks.
17
+- **Safety checks**
18
+ - Rejects archives with unsafe paths.
19
+ - Rejects missing or invalid `plugin.yaml` files.
20
+ - Rejects plugin name conflicts.
21
+- **Install hooks and refresh**
22
+ - Runs the plugin install hook when present and calls `after_plugin_change(...)` so the app refreshes plugin state.
23
+- **Community browsing UI**
24
+ - The web UI store handles browsing index entries, showing readme content, prompting about third-party plugin risk, and launching install/update actions.
25
+
26
+## Key Files
27
+
28
+- **API**
29
+ - `api/plugin_install.py` dispatches install, update, and index fetch actions.
30
+- **Installer logic**
31
+ - `helpers/install.py` contains archive extraction, Git install, update, validation, and hook execution.
32
+- **Frontend**
33
+ - `webui/pluginInstallStore.js` manages the installer modal state and community index interactions.
34
+
35
+## Configuration Scope
36
+
37
+- **Settings sections**: none
38
+- **Always enabled**: `true`
39
+
40
+## Plugin Metadata
41
+
42
+- **Name**: `_plugin_installer`
43
+- **Title**: `Plugin Installer`
44
+- **Description**: Install plugins from ZIP files, Git repositories, or the community index.
plugins/_plugin_installer/webui/pluginInstallStore.js
+2
-2
@@ -11,7 +11,7 @@ import { store as pluginExecuteStore } from "/components/plugins/list/plugin-exe
11
import { store as pluginSettingsStore } from "/components/plugins/plugin-settings-store.js";
12
13
const PLUGIN_API = "plugins/_plugin_installer/plugin_install";
14
-const PER_PAGE = 20;
14
+const PER_PAGE = 24;
15
16
const SECURITY_WARNING = {
17
title: "Security Warning",
@@ -143,7 +143,7 @@ const model = {
143
if (!filterKey || filterKey === "all") return true;
144
if (filterKey === "installed") return !!plugin?.installed;
145
if (filterKey === "update") return !!plugin?.has_update;
146
- if (filterKey === "popular") return (plugin?.stars || 0) > 0;
146
+ if (filterKey === "popular") return (plugin?.stars || 0) >= 3;
147
if (filterKey.startsWith("tag:")) {
148
return this._pluginPrimaryTag(plugin) === filterKey.slice(4);
149
}
plugins/_plugin_scan/README.md
new
+40
@@ -0,0 +1,40 @@
1
+# Plugin Scanner
2
+
3
+Run an LLM-guided security review of third-party Agent Zero plugins from a Git repository.
4
+
5
+## What It Does
6
+
7
+This plugin builds a structured scanning prompt from a selectable checklist, runs that prompt in a temporary agent context, and returns a markdown report describing the plugin's security posture.
8
+
9
+## Main Behavior
10
+
11
+- **Prompt-driven scan**
12
+ - Loads scan checks and a markdown prompt template from the plugin's `webui/` assets.
13
+- **Temporary scan context**
14
+ - Creates a temporary chat context, sends the generated prompt as a user message, waits for the model result, and then removes the chat.
15
+- **Selectable checks**
16
+ - Supports scanning all checks by default or only the subset selected by the caller.
17
+- **UI integration**
18
+ - Includes API endpoints and web UI files for queueing, starting, and running scans.
19
+
20
+## Key Files
21
+
22
+- **Scan runner**
23
+ - `api/plugin_scan_run.py` performs a synchronous end-to-end scan and returns the report.
24
+- **Prompt builder**
25
+ - `helpers/prompt.py` loads check definitions and renders the final scan prompt.
26
+- **Additional APIs**
27
+ - `api/plugin_scan_queue.py`
28
+ - `api/plugin_scan_start.py`
29
+
30
+## Configuration Scope
31
+
32
+- **Settings sections**: none
33
+- **Per-project config**: `false`
34
+- **Per-agent config**: `false`
35
+
36
+## Plugin Metadata
37
+
38
+- **Name**: `_plugin_scan`
39
+- **Title**: `Plugin Scanner`
40
+- **Description**: Security scanner for third-party A0 plugins.
plugins/_plugin_validator/README.md
new
+41
@@ -0,0 +1,41 @@
1
+# Plugin Validator
2
+
3
+Validate Agent Zero plugins against structural, manifest, convention, and security expectations.
4
+
5
+## What It Does
6
+
7
+This plugin generates a structured validation prompt for either a local plugin or an external source, runs the review in a temporary agent context, and returns a markdown report that checks whether a plugin follows Agent Zero plugin conventions.
8
+
9
+## Main Behavior
10
+
11
+- **Source-aware validation**
12
+ - Supports validating a local plugin by name or a plugin fetched from a Git repository.
13
+- **Checklist-based review**
14
+ - Loads validation criteria, status icons, and guidance text from plugin assets.
15
+- **Temporary validation context**
16
+ - Creates a temporary agent context, runs the generated prompt, and cleans up the context and temporary chat afterward.
17
+- **Operational guidance in prompt**
18
+ - Embeds source-specific handling instructions into the prompt, including cleanup rules for temporary validation directories.
19
+
20
+## Key Files
21
+
22
+- **Validation runner**
23
+ - `api/plugin_validator_run.py` performs a synchronous validation and returns the report.
24
+- **Prompt builder**
25
+ - `helpers/prompt.py` builds the validation prompt with source instructions, selected checks, and scoring guidance.
26
+- **Additional APIs**
27
+ - `api/plugin_validator_prepare_zip.py`
28
+ - `api/plugin_validator_queue.py`
29
+ - `api/plugin_validator_start.py`
30
+
31
+## Configuration Scope
32
+
33
+- **Settings sections**: none
34
+- **Per-project config**: `false`
35
+- **Per-agent config**: `false`
36
+
37
+## Plugin Metadata
38
+
39
+- **Name**: `_plugin_validator`
40
+- **Title**: `Plugin Validator`
41
+- **Description**: Validate Agent Zero plugins against manifest, structure, code pattern, and security conventions.
plugins/_promptinclude/README.md
new
+40
@@ -0,0 +1,40 @@
1
+# Prompt Include
2
+
3
+Automatically inject persistent behavioral rules and preferences into the system prompt from project files.
4
+
5
+## What It Does
6
+
7
+This plugin scans a workspace for `*.promptinclude.md` files, applies gitignore-aware filtering and token budgets, and makes the collected content available for prompt injection.
8
+
9
+## Main Behavior
10
+
11
+- **Workspace scanning**
12
+ - Recursively searches for files matching `*.promptinclude.md`.
13
+- **Ignore support**
14
+ - Respects ignore patterns derived from gitignore-style content.
15
+- **Budgeted inclusion**
16
+ - Applies per-file and total token limits.
17
+ - Crops oversized files when they partially fit within the remaining token budget.
18
+- **Structured scan result**
19
+ - Returns included file content together with path, token count, status, and skipped count.
20
+
21
+## Key Files
22
+
23
+- **Scanner**
24
+ - `helpers/scanner.py` implements file discovery, ignore handling, token budgeting, and trimming.
25
+- **Configuration**
26
+ - `default_config.yaml` contains prompt-include scanning defaults.
27
+- **Prompts and UI**
28
+ - `prompts/` and `webui/` provide integration with the broader app.
29
+
30
+## Configuration Scope
31
+
32
+- **Settings section**: `agent`
33
+- **Per-project config**: `true`
34
+- **Per-agent config**: `true`
35
+
36
+## Plugin Metadata
37
+
38
+- **Name**: `_promptinclude`
39
+- **Title**: `Prompt Include`
40
+- **Description**: Persistent behavioral rules and preferences auto-injected into system prompt.
plugins/_text_editor/README.md
new
+44
@@ -0,0 +1,44 @@
1
+# Text Editor
2
+
3
+Provide an LLM-friendly file editing tool for reading, writing, and patching text files.
4
+
5
+## What It Does
6
+
7
+This plugin exposes a native text editing tool that agents can use to inspect files, write complete contents, and apply validated patch operations while tracking file freshness between reads and edits.
8
+
9
+## Main Behavior
10
+
11
+- **Read**
12
+ - Reads whole files or line ranges with token-aware limits.
13
+ - Records file metadata so later patch operations can detect stale edits.
14
+- **Write**
15
+ - Writes full file contents and then re-reads the resulting file for confirmation.
16
+- **Patch**
17
+ - Validates edit structures before applying them.
18
+ - Rejects edits if the file changed since it was last observed.
19
+ - Reads back the affected patch region after applying changes.
20
+- **Extension hooks**
21
+ - Exposes before and after extension points for read, write, and patch operations.
22
+
23
+## Key Files
24
+
25
+- **Tool**
26
+ - `tools/text_editor.py` implements method dispatch, stale-file checks, patching flow, and prompt responses.
27
+- **Helpers**
28
+ - `helpers/file_ops.py` provides file info, read/write helpers, edit validation, and patch application.
29
+- **Configuration**
30
+ - `default_config.yaml` defines read limits and token budgets.
31
+- **Prompts**
32
+ - `prompts/` contains the agent-facing success and error messages.
33
+
34
+## Configuration Scope
35
+
36
+- **Settings section**: `agent`
37
+- **Per-project config**: `true`
38
+- **Per-agent config**: `true`
39
+
40
+## Plugin Metadata
41
+
42
+- **Name**: `_text_editor`
43
+- **Title**: `Text Editor`
44
+- **Description**: Native tool to read, write, and patch text files in an LLM-friendly way.
webui/components/plugins/list/pluginListStore.js
+32
@@ -1,5 +1,7 @@
1
import { createStore } from "/js/AlpineStore.js";
2
import * as api from "/js/api.js";
3
+import { marked } from "/vendor/marked/marked.esm.js";
4
+import { addBlankTargetsToLinks } from "/js/messages.js";
5
import { store as pluginSettingsStore } from "/components/plugins/plugin-settings-store.js";
6
import { store as pluginToggleStore } from "/components/plugins/toggle/plugin-toggle-store.js";
7
import { store as pluginExecuteStore } from "/components/plugins/list/plugin-execute-store.js";
@@ -17,6 +19,9 @@ const model = {
19
plugins: [],
20
selectedPlugin: null,
21
activeTab: "custom",
22
+ readmeContent: "",
23
+ readmeLoading: false,
24
+ readmeError: "",
25
26
async init() {
27
this.loading = false;
@@ -151,9 +156,36 @@ const model = {
156
}
157
},
158
159
+ async loadPluginReadme(plugin) {
160
+ this.readmeLoading = true;
161
+ this.readmeContent = "";
162
+ this.readmeError = "";
163
+ try {
164
+ const response = await api.callJsonApi("plugins", {
165
+ action: "get_doc",
166
+ plugin_name: plugin.name,
167
+ doc: "readme",
168
+ });
169
+ if (response?.error) throw new Error(response.error);
170
+ const html = marked.parse(response.content || "", { breaks: true });
171
+ this.readmeContent = addBlankTargetsToLinks(html);
172
+ } catch (e) {
173
+ const error = e instanceof Error ? e : new Error(String(e));
174
+ this.readmeError = error.message || "Failed to load README";
175
+ } finally {
176
+ this.readmeLoading = false;
177
+ }
178
+ },
179
+
180
openPluginInfo(plugin) {
181
if (!plugin) return;
182
this.selectedPlugin = plugin;
183
+ this.readmeContent = "";
184
+ this.readmeLoading = false;
185
+ this.readmeError = "";
186
+ if (plugin.has_readme) {
187
+ void this.loadPluginReadme(plugin);
188
+ }
189
window.openModal?.("components/plugins/plugin-info.html");
190
},
191
webui/components/plugins/plugin-info.html
+166
@@ -73,6 +73,32 @@
73
<div class="plugin-info-value"
74
x-text="$store.pluginListStore.selectedPlugin.has_config_screen ? 'Available' : 'Not available'"></div>
75
</div>
76
+
77
+ <div class="plugin-info-readme-section"
78
+ x-show="$store.pluginListStore.selectedPlugin.has_readme || $store.pluginListStore.readmeLoading || $store.pluginListStore.readmeError || $store.pluginListStore.readmeContent">
79
+ <button type="button"
80
+ class="plugin-info-readme-toggle"
81
+ data-bs-toggle="collapse"
82
+ data-bs-target="#plugin-info-readme-collapse"
83
+ aria-expanded="false"
84
+ aria-controls="plugin-info-readme-collapse">
85
+ <span>README</span>
86
+ <span class="material-symbols-outlined plugin-info-readme-toggle-icon">expand_more</span>
87
+ </button>
88
+ <div class="collapse" id="plugin-info-readme-collapse">
89
+ <div class="plugin-info-readme-body">
90
+ <div x-show="$store.pluginListStore.readmeLoading" class="plugin-info-loading-text">
91
+ Loading readme...
92
+ </div>
93
+ <div x-show="$store.pluginListStore.readmeError && !$store.pluginListStore.readmeLoading"
94
+ class="plugin-info-readme-error"
95
+ x-text="$store.pluginListStore.readmeError"></div>
96
+ <div x-show="$store.pluginListStore.readmeContent && !$store.pluginListStore.readmeLoading"
97
+ class="plugin-info-readme-content"
98
+ x-html="$store.pluginListStore.readmeContent"></div>
99
+ </div>
100
+ </div>
101
+ </div>
102
</div>
103
</template>
104
@@ -170,6 +196,146 @@
196
gap: 0.5rem 0.75rem;
197
}
198
199
+ .plugin-info-readme-section {
200
+ margin-top: 1.5rem;
201
+ padding-top: 1rem;
202
+ border-top: 1px solid var(--color-border);
203
+ }
204
+
205
+ .plugin-info-readme-toggle {
206
+ width: 100%;
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: space-between;
210
+ gap: 0.75rem;
211
+ padding: 0;
212
+ border: 0;
213
+ background: transparent;
214
+ color: var(--color-text-primary);
215
+ font-size: 0.95rem;
216
+ font-weight: 600;
217
+ text-align: left;
218
+ cursor: pointer;
219
+ }
220
+
221
+ .plugin-info-readme-toggle-icon {
222
+ transition: transform 0.2s ease;
223
+ }
224
+
225
+ .plugin-info-readme-toggle[aria-expanded="true"] .plugin-info-readme-toggle-icon {
226
+ transform: rotate(180deg);
227
+ }
228
+
229
+ .plugin-info-readme-body {
230
+ padding-top: 0.85rem;
231
+ }
232
+
233
+ .plugin-info-loading-text,
234
+ .plugin-info-readme-error {
235
+ color: var(--color-text-secondary);
236
+ }
237
+
238
+ .plugin-info-readme-content {
239
+ font-size: 0.95rem;
240
+ line-height: 1.6;
241
+ color: var(--color-text-primary);
242
+ overflow-x: auto;
243
+ scrollbar-width: none;
244
+ -ms-overflow-style: none;
245
+ }
246
+
247
+ .plugin-info-readme-content::-webkit-scrollbar {
248
+ display: none;
249
+ }
250
+
251
+ .plugin-info-readme-content h1,
252
+ .plugin-info-readme-content h2,
253
+ .plugin-info-readme-content h3,
254
+ .plugin-info-readme-content h4 {
255
+ margin-top: 1rem;
256
+ margin-bottom: 0.5rem;
257
+ color: var(--color-text-primary);
258
+ }
259
+
260
+ .plugin-info-readme-content h1 { font-size: 1.5rem; }
261
+ .plugin-info-readme-content h2 { font-size: 1.25rem; }
262
+ .plugin-info-readme-content h3 { font-size: 1.1rem; }
263
+ .plugin-info-readme-content h4 { font-size: 1rem; }
264
+
265
+ .plugin-info-readme-content p {
266
+ margin-bottom: 0.75rem;
267
+ }
268
+
269
+ .plugin-info-readme-content code {
270
+ background: var(--color-panel);
271
+ padding: 0.15rem 0.35rem;
272
+ border-radius: 4px;
273
+ font-family: var(--font-family-mono);
274
+ font-size: 0.85em;
275
+ }
276
+
277
+ .plugin-info-readme-content pre {
278
+ background: var(--color-panel);
279
+ padding: 1rem;
280
+ border-radius: 8px;
281
+ overflow-x: auto;
282
+ margin-bottom: 1rem;
283
+ }
284
+
285
+ .plugin-info-readme-content pre code {
286
+ background: none;
287
+ padding: 0;
288
+ }
289
+
290
+ .plugin-info-readme-content ul,
291
+ .plugin-info-readme-content ol {
292
+ margin-bottom: 0.75rem;
293
+ padding-left: 1.5rem;
294
+ }
295
+
296
+ .plugin-info-readme-content li {
297
+ margin-bottom: 0.25rem;
298
+ }
299
+
300
+ .plugin-info-readme-content a {
301
+ color: var(--color-highlight);
302
+ text-decoration: none;
303
+ }
304
+
305
+ .plugin-info-readme-content a:hover {
306
+ text-decoration: underline;
307
+ }
308
+
309
+ .plugin-info-readme-content img {
310
+ max-width: 100%;
311
+ border-radius: 8px;
312
+ margin: 0.5rem 0;
313
+ }
314
+
315
+ .plugin-info-readme-content blockquote {
316
+ border-left: 3px solid var(--color-border);
317
+ padding-left: 1rem;
318
+ margin-left: 0;
319
+ color: var(--color-text-secondary);
320
+ }
321
+
322
+ .plugin-info-readme-content table {
323
+ width: 100%;
324
+ border-collapse: collapse;
325
+ margin-bottom: 1rem;
326
+ }
327
+
328
+ .plugin-info-readme-content th,
329
+ .plugin-info-readme-content td {
330
+ border: 1px solid var(--color-border);
331
+ padding: 0.5rem;
332
+ text-align: left;
333
+ }
334
+
335
+ .plugin-info-readme-content th {
336
+ background: var(--color-panel);
337
+ }
338
+
339
.plugin-info-grid-pair {
340
display: contents;
341
}