Plugins
Install, manage, and write local JavaScript plugins that add abg commands or clean up page
Markdown.
Install a plugin
Section titled “Install a plugin”-
Install from a GitHub repository, a git URL, or a local directory.
--yesis required because plugin code is arbitrary JavaScript:Terminal window abg plugin install user/repo --yesabg plugin install https://github.com/user/repo.git --yesabg plugin install git@github.com:user/private-plugin.git --yesabg plugin install ./my-plugin --name my-plugin --yes -
Check that the Gateway loaded it:
Terminal window abg plugin list --loaded -
Run one of its commands. Plugin commands are
abgsubcommands named after the plugin:Terminal window abg my-plugin greet --name "Ada"
Repository installs use your local git command. Private repositories work with your existing SSH
keys, git credential helper, or GitHub CLI-backed git authentication. ABG does not ask for or store
GitHub tokens, and it rejects HTTPS URLs with embedded credentials.
Manage plugins
Section titled “Manage plugins”abg plugin listabg plugin updateabg plugin update my-pluginabg plugin disable my-pluginabg plugin enable my-pluginabg plugin reload my-pluginabg plugin uninstall my-pluginupdaterunsgit pull --ff-onlyfor git-backed user plugins.disablekeeps the plugin directory and stops loading its commands and transforms. The state is stored inplugin-state.jsonunder the active ABG user directory, not in an app database.reloadreloads a plugin in the running Gateway. Existing tab shares stay in place.
Write a command plugin
Section titled “Write a command plugin”-
Create a directory with two files:
hello-plugin/index.jsplugin.jsonindex.jsis required.plugin.jsonis optional, but listings and--helpuse it. -
Describe the plugin in
plugin.json:{"name": "hello-plugin","version": "0.1.0","description": "Minimal ABG command plugin.","commands": [{"name": "greet","description": "Return a greeting.","args": [{ "name": "name", "type": "string", "required": false, "default": "ABG" }]}]} -
Register the command in
index.js:abg.registerCommand("greet", async function (args) {const name = args.name || "ABG";return { ok: true, message: "Hello, " + name };}); -
Install it and run the command:
Terminal window abg plugin install ./hello-plugin --name hello-plugin --yesabg plugin reloadabg hello-plugin greet --name "Ada"{"message": "Hello, Ada","ok": true} -
After each edit, run
abg plugin reload hello-plugin. If a reload fails, the previous version stays active.
Commands accept arguments in several forms:
abg hello-plugin greet --name "Ada"abg hello-plugin greet --tab t1abg hello-plugin greet --json '{"name":"Ada"}'printf '{"name":"Ada"}' | abg hello-plugin greet --stdin--tab, --tab-id, --match-url, --match-title, and --first bind the command to a shared tab
and are not passed to the plugin. Command results are JSON. Handler failures return a JSON error with
error, message, plugin, and command. The audit log records the plugin name, command name,
argument keys, and argument byte length, but not argument values.
Drive the shared tab
Section titled “Drive the shared tab”Command handlers can operate the bound tab with context.tab.<action>(options). These calls go
through the same Gateway path as the CLI, so sharing, approvals, and audit logging still apply.
abg.registerCommand("clear-and-paste", async function (args, context) { if (context.tabId == null) { return { ok: false, error: "no_tab_context" }; } await context.tab.clear({ selector: args.selector }); await context.tab.paste({ selector: args.selector, value: args.value }); return { ok: true };});Available methods mirror CLI commands: paste, clear, fill, click, key, read, describe,
wait, screenshot, and navigate. Do not shell out from JavaScript to reach the browser another
way.
If a command runs without an explicit tab and plugin.json declares domains, ABG binds it to the
one shared tab whose URL matches. No match returns no_matching_tab. More than one match returns
ambiguous_tab with candidates.
Add domain-specific Markdown
Section titled “Add domain-specific Markdown”Transforms are synchronous string-to-string functions:
abg.log("loaded " + abg.plugin.name);
abg.registerTransform("gmail-clean-markdown", function (input) { return String(input).trim();});Declare the domains and transform in plugin.json:
{ "name": "gmail-plugin", "domains": ["https://mail.google.com/*"], "transforms": ["gmail-clean-markdown"]}When a shared tab URL matches domains, abg read --format markdown tries the plugin’s transform
first, then falls back to the generic html-to-markdown transform. The transform name must contain
markdown.
Reference
Section titled “Reference”Where plugins load from
Section titled “Where plugins load from”ABG searches plugins in this order:
- Bundled plugins in
Agent Browser Gateway.app/Contents/Resources/plugins/. - User-installed plugins under
~/.abg/plugins/. - Local development plugins in the repository checkout, when you run from the checkout.
Development Gateway runs on port 8766 use ~/.abg-dev/plugins/ instead of ~/.abg/plugins/, so
production and development plugin state stay separate.
Bundled plugins
Section titled “Bundled plugins”markdown-plugin: generic HTML to Markdown.notion-plugin: Markdown fornotion.soandnotion.sitepages.gmail-plugin,slack-plugin,linear-plugin: per-domain Markdown examples.slack-pluginalso addscatch-up,pending, andopen-channelcommands.redaction-plugin: opt-in masking withabg read --format markdown --redact.workflow-plugin: command examples that usecontext.tab.*.info-plugin: a loader smoke test.
The full authoring guide is
docs/PLUGINS.md.