---
name: journal-plugins
description: Load it when a plugin is installed, upgraded, configured or answers. A plugin is a repository installed into the journal. It reacts to what happens in the journal and can run programs of its own.
keywords: plugins, plugin
commands: plugins
---

# Plugins

A plugin is a repository installed into the journal. It reacts to what happens in the journal and can run programs of its own.

Install a plugin with journal plugin install <url>, upgrade it with journal plugin upgrade <n>, and change a setting with journal plugin configure <n> <key> --value <value>. Install shows every command before it runs any, because a plugin runs as you. Its .journal-plugin/plugin.json says what it listens to, what it runs, which pages it shows and which events it raises.

A setting {"key": "api_key", "type": "secret", "env": "STRIPE_KEY"} asks for one of the user's secrets: the user picks it for the plugin under Settings, Plugins, and only then do the plugin's services get its value, in that variable. An agent never sets one.

Some events can be cancelled before they happen, such as agent.dispatching, raised when a subagent is about to be dispatched. A plugin cancels one through "cancels": {"agent.dispatching": "<command>"} in its manifest: the command reads the event as JSON and answers {"cancel": "<reason>"} to stop it, and the reason is what the agent is told.

"fits": {"languages": ["PHP", "Python"], "files": ["composer.json", "*.csproj"]} names the projects a plugin is for. Plugins listed in plugins.json in the journal's own repository are read from their repositories once a day, and the ones that fit the project and are not installed are suggested at a session start, with a Yes, I want this button that installs the plugin at once.

Its "refuse" command is asked about every write, and every read too with "reads": true. A process started for each tool call is slow, so "refuse_socket": "<service>" names one of its services that answers instead: the service listens on the Unix socket at $JOURNAL_PLUGIN_SOCKET, reads one JSON line and writes its answer, and the command runs only when nothing listens there.

"load": {"<event>": ["<skill>", ...]} names the skills the agent must load when one of the plugin's own events, a journal event or a hook.<event> happens: the agent's tool calls wait until they are loaded, as for the journal's own skills. A skill the plugin ships can carry "keywords: <word>, <word>" in its SKILL.md front matter, and the agent is asked to load it when the user names one of those words.

When one of its servers gives up, you are told once; journal services list|start|stop|restart|log <plugin>.<service> inspects them. The servers a plugin declares are kept up while the session runs and stop with it; an upgrade removes each one and starts it again from the new code. A service may declare "when": "<command>": the journal runs it first, in the project's folder (the plugin's own folder is $JOURNAL_PLUGIN_DIR), and only an exit of 0 starts the service; otherwise it stays unstarted as not needed here, with the command's words as the reason, and is asked again ten minutes later. A service keeps what it writes in $JOURNAL_PLUGIN_DATA, which outlives upgrades, never in the plugin's own folder or the project. A plugin writes back by calling the journal itself, or by appending journal commands to the file at $JOURNAL_QUEUE, one per line, which the host drains a few at a time. Answering an event, that file belongs to the event's environment and its commands run there; a line that names --env is refused. A service answering later writes to the queue its payload names (plugin.queue), so its lines run in the environment the event came from. journal plugin raise <plugin> <event> "<brief>" in that file raises one of the events its manifest declares, with the same card and activity item as an answer that raises it. An event is declared as "events": {"<name>": {"title": "...", "tone": "...", "card": {"label", "icon", "color", "collapsed"}}}: the card shows it in the chat, and "collapsed": true makes its item in the activity list start folded to its title, opening on a click. A raise can name one of its dashboard pages, --open <dashboard>/<page> or "open" in an answer's raise, and clicking its card in the chat opens that page in a side panel. A raise can also carry a key of the plugin's own, --key <key> or "key" in an answer's raise, and journal plugin settle <plugin> <key> [--how "<words>"] (also a line in $JOURNAL_QUEUE) settles every card with that key: it keeps its text but turns green with the words beside it (fixed by default), and a group of cards counts them, such as "5 x Sin found, 3 fixed". The plugin decides when, for example when its next check no longer finds the problem; the journal does not check the cards itself.

A plugin shows its output as a dashboard: "dashboards": [{"name": "<id>", "title": "<Title>"}] in its manifest, and a JSON file it writes to $JOURNAL_PLUGIN_DATA/dashboards/<id>.json whenever its output changes. The viewer lists each dashboard as a button on the Plugins page and opens it in a large panel, drawn from the file: {"title": "...", "start": "<page id>", "pages": {"<page id>": {"title": "...", "view": <node>}}}. A node is {"type": ..., props, "children": [nodes]}: stack (gap), row (gap, wrap), grid (columns, gap) and card (title, note, open) hold children; heading (text, level), divider, stat (label, value, note, tone, open), bars (title, unit, items of label, value, note, tone, open), table (columns, rows of cells, tone, open), list (items of label, note, badge, tone, open), text (body, with the chat's formatting), fact (label, body: a small heading with its text below, for explanations such as what it is and how to fix it), badge (text, tone), code (text, language) and file (path, line, label) draw. tone is note, good, warn, danger or muted; open names another page of the same dashboard, and the panel keeps a trail back. A file that does not fit is shown with the place that is wrong, such as pages.overview.view.children[1].

Its lines and guards are for the main agent only; none reach a subagent.
