# plugins

Plugins are opt-in external processes that talk to hrdx over bounded newline-delimited JSON on stdin and stdout. They can add context-menu actions, finder search providers, notifications, footer status, scoped workspace and pane queries, pane operations and input, private storage, and floating or docked text views. Normal startup runs no plugins. Custom harnesses and holder sessions are unchanged.

Plugins are trusted executable code, not sandboxed code. Approving a plugin lets it run with your OS permissions and inherited environment. Capability grants restrict what the plugin can ask hrdx to do, nothing more. Inspect a package before you approve it.

## try the reference plugin

# build the standard-library example from a checkout
$ go build -o examples/plugins/hello/hello.exe ./examples/plugins/hello
 
# approve it with explicit grants, then start hrdx with plugins enabled
$ hrdx plugins approve --package ./examples/plugins/hello --trust \
--grant ui.action.contribute --grant ui.notification --grant ui.provider.contribute \
--set greeting=Hi
$ hrdx --plugins
 
# optional floating panel: reapprove with view grants and enable views
$ hrdx plugins approve --package ./examples/plugins/hello --trust \
--grant ui.action.contribute --grant ui.notification \
--grant ui.view.contribute --grant ui.view.input
$ hrdx --plugins --plugin-views

Right-click a pane or workspace and pick Hello from plugin. Open the finder with ctrl+b /and type two characters to see the example search provider's rows. The process starts lazily on first use, with activation progress in the footer.

## flags and commands

flagpurpose
--pluginsenable the runtime for explicitly approved plugins (default off)
--plugin-viewsallow approved plugin views, requires --plugins (default off)
hrdx plugins ...effect
list, doctorread-only inventory and diagnostics, never executes a package
approve --package PATH --trustbind approval to the package path and content digest, with --grant, --workspace or --instance, and --set key=value
inspect, enable, disable, revoke --id IDshow or change the saved approval record
status, start, stop, restart, reload --id IDlifecycle control of a running instance through the control socket

Approvals live in plugin-approvals/ next to the state file, private plugin data in plugin-data/. Any change to a package's files requires explicit reapproval. A running instance checks approvals every 500 ms and revokes changed, removed, or disabled records. New approvals and changed grants need a restart; a reapproved package at the same path can be reloaded live. ctrl+b P opens lifecycle controls, and the settings window gains a plugins section.

## capabilities

Every operation needs its own named grant, and workspace operations also need a matching --workspace scope or --instance. Grants are checked at request time, not only at startup.

grantallows
ui.action.contributecontext-menu actions declared in the manifest
ui.provider.contributesearch providers merged into the finder
ui.notificationtransient footer notices with info or error severity
ui.status.contributea prioritized footer status entry
ui.view.contribute, ui.view.inputfloating or docked plain-text views, and keyboard/mouse input to them
workspace.read, pane.read_metadatascoped snapshots and subscriptions without screen content
pane.read_screena pane's visible plain-text screen
pane.create, pane.closedurable splits and tabs, or temporary floats owned by the plugin
pane.send_inputbounded, ordered typing into in-scope panes
workspace.create, workspace.closeopen or close workspaces in scope
storage.plugin_privatea quota-limited key-value store per plugin
host.events.subscribecoalesced snapshot.changed events

## manifest

A package is a directory with a plugin.json and an executable entrypoint. Contributions, capability requests, activation markers, and configuration options are declared, not discovered by running code.

{
  "schema": 1,
  "id": "example.hello",
  "version": "1.1.0",
  "entrypoint": "hello.exe",
  "protocol": {"min": 1, "max": 1},
  "requests": ["ui.action.contribute", "ui.notification", "ui.provider.contribute"],
  "activation": {"markers": [".git"]},
  "config": [{"key": "greeting", "type": "string", "default": "Hello"}],
  "contributes": {
    "actions": [{"id": "example.hello.greet", "label": "Hello from plugin", "targets": ["workspace", "pane"]}],
    "providers": [{"id": "example.hello.search", "kind": "search", "label": "Hello search"}]
  }
}

Activation markers hide a plugin's actions and providers in workspaces that do not contain one of the named entries. Configuration values are typed, set at approval time, stored in ordinary state, and delivered to the plugin at startup. They are not a secret store.

## wire protocol

One JSON object per line. The peer sends hello, receives hello_ack with its granted capabilities and configuration, replies ready, then exchanges requests and responses in both directions with connection local ids. Frames are capped at 256 KiB, calls at 32 per direction with a ten second deadline, and a plugin that floods or stalls is disconnected rather than allowed to block the interface.

{"type":"hello","plugin":"example.hello","protocol":1}
{"type":"hello_ack","protocol":1,"instance":"...","grants":["ui.notification"],"config":{"greeting":"Hi"}}
{"type":"ready"}
{"type":"request","id":"7","method":"command.invoke","params":{"action_id":"example.hello.greet","target":"pane"}}
{"type":"response","id":"7","result":{"notification":"Hi from the example plugin"}}
{"type":"request","id":"1","method":"ui.notify","params":{"text":"Done","severity":"info"}}
{"type":"shutdown"}

The complete manifest, method, grant, limit, and lifecycle reference lives in the repository at docs/plugin-platform.md. The platform is experimental and independently versioned; it does not claim compatibility with any other plugin protocol.