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 }