The weaver-plugins crate provides the plugin orchestration layer that enables
weaverd to delegate specialist tasks to external tools running in sandboxed
processes.
Plugin categories
Plugins are categorized as either sensors or actuators:
- Sensors provide data to the intelligence engine (e.g.
jedifor Python static analysis). They produce structured JSON output. - Actuators perform actions on the codebase (e.g.
ropefor Python refactoring,srgnfor structural rewriting). They produce unified diffs.
Plugin manifest
Each plugin is described by a manifest containing:
| Field | Description |
|---|---|
name |
Unique plugin identifier (e.g. rope). |
version |
Plugin version string. |
kind |
sensor or actuator. |
languages |
List of supported languages (case-insensitive). |
executable |
Absolute path to the plugin binary. |
args |
Default arguments passed to the executable (optional). |
timeout_secs |
Maximum execution time in seconds (default: 30). |
IPC protocol
Plugins communicate with the broker via a single-line JSONL exchange over standard I/O:
- The broker writes one JSONL request line to the plugin's stdin and closes stdin.
- The plugin writes one JSONL response line to stdout and exits.
- Plugin stderr is captured for diagnostic logging but is not part of the protocol.
File content is passed in-band as part of the request body, so sandboxed plugins do not need filesystem access.
Plugin registry
The daemon maintains a PluginRegistry that stores validated plugin manifests
keyed by name. Plugins can be looked up by name, kind, language, or a
combination thereof (e.g. "find all actuator plugins for Python").
For the current actuator rollout, weaverd registers:
rope- kind:
actuator - language:
python - capabilities:
["rename-symbol"] - executable:
/usr/bin/weaver-plugin-rope(orWEAVER_ROPE_PLUGIN_PATH) - timeout:
30s rust-analyzer- kind:
actuator - language:
rust - capabilities:
["rename-symbol"] - executable:
/usr/bin/weaver-plugin-rust-analyzer(orWEAVER_RUST_ANALYZER_PLUGIN_PATH) - timeout:
60s
Plugin capabilities
Actuator plugins declare the capabilities they support in their manifest. The daemon uses these declarations to route operations to the correct plugin based on both the requested capability and the target language.
Capability identifiers
The following capability identifiers are defined:
Table: Code transformation capabilities.
| Identifier | Description |
|---|---|
rename-symbol |
Rename a symbol across a codebase. |
extricate-symbol |
Move a symbol to a different module or file. |
extract-method |
Extract a code region into a new function or method. |
replace-body |
Replace the body of a function or method. |
extract-predicate |
Extract a conditional expression into a predicate. |
The rename-symbol capability contract
The rename-symbol capability is the first fully specified contract. Plugins
that declare this capability must accept requests containing three required
fields in the arguments map. The built-in rope and rust-analyzer rename
plugins are validated against the same shared contract fixtures. Request and
response checks therefore stay aligned across Python and Rust rename flows.
Table: Required fields for rename-symbol requests.
| Field | Type | Description |
|---|---|---|
uri |
string | File URI of the symbol to rename. |
position |
string | Internal position value used by the daemon-to-plugin request. The current built-in plugins receive a UTF-8 byte offset converted from CLI --position. |
new_name |
string | The new name for the symbol (must be non-empty). |
Successful responses must contain a Diff output with a unified diff patch. If
a plugin reports success with any other output shape, Weaver refuses the
response, exits with status 1, and makes no filesystem changes. Failed
responses may include diagnostics with an optional reason_code field.
Contract versioning
Each capability contract carries a version (major.minor). Contracts with the
same major version are considered compatible. The current rename-symbol
contract version is 1.0.
Refusal reason codes
When a plugin cannot perform a requested operation, it returns a failure
response with diagnostics. Each diagnostic may include a reason_code for
programmatic matching:
Table: Refusal reason codes for plugin diagnostics.
| Reason code | Meaning |
|---|---|
symbol_not_found |
The target symbol could not be located. |
macro_generated |
The symbol is generated by a macro. |
ambiguous_references |
Multiple candidate symbols match the position. |
unsupported_language |
The plugin does not support the target language. |
incomplete_payload |
Required fields are missing from the request. |
name_conflict |
The new name conflicts with an existing symbol. |
operation_not_supported |
The plugin does not support the requested operation. |
Reason codes are stable identifiers intended for automation. They appear in the
JSON diagnostic payload alongside the human-readable message field.
Manifest capability declarations
Actuator plugins declare capabilities in their manifest:
name = "rope"
version = "1.0.0"
kind = "actuator"
languages = ["python"]
executable = "/usr/bin/weaver-plugin-rope"
capabilities = ["rename-symbol"]
Sensor plugins must not declare any capabilities. The registry validates this constraint during registration and rejects manifests that violate it.
The daemon uses capability declarations to select plugins. For example, when
routing a rename-symbol request for Python, the daemon queries the registry
for actuator plugins that declare rename-symbol and support the python
language. For act refactor, operators must still pass --provider
explicitly, using rope for Python rename flows or rust-analyzer for Rust
rename flows. The daemon refuses deterministically for unsupported languages,
unknown providers, and explicit provider/language mismatches.
Safety harness integration
Actuator plugin output (unified diffs) flows through the same Double-Lock
safety harness used by act apply-patch. Changes are validated by both the
syntactic (Tree-sitter) and semantic (LSP) locks before any filesystem write is
committed. If verification fails, the filesystem is left untouched and a
structured error is returned to the caller.