Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b700f58
feat(types): pluggable agent-type registry
lucianlamp Jun 18, 2026
314879e
feat(types): drop the aliases= auto-redirect; explicit type selection…
lucianlamp Jun 18, 2026
23f1b8f
refactor(scripts): move the codex subsystem into scripts/codex/
fujibee Jun 21, 2026
fa31c76
refactor(scripts): move init-db to internal/, dispatch to windows/, d…
fujibee Jun 21, 2026
907c370
test(helper): follow init-db move + chmod scripts/codex in setup_test…
fujibee Jun 21, 2026
6a94dda
refactor(delivery): extract hook JSON primitives into lib/hooks-json.sh
fujibee Jun 21, 2026
a2fa650
refactor(delivery): per-type delivery as a Template Method plug
fujibee Jun 21, 2026
801259a
test(storage): follow init-db move to scripts/internal/
fujibee Jun 21, 2026
ccf738d
refactor(check-inbox): drive Stop-hook status output from manifest st…
fujibee Jun 21, 2026
850aae9
Merge remote-tracking branch 'origin/main' into restructure-agent-types
fujibee Jun 21, 2026
1c2d7db
refactor(session-start): extract codex bridge handoff into a type plug
fujibee Jun 21, 2026
c14d1f3
fix(test): follow init-db move to internal/ in the Windows PowerShell…
fujibee Jun 21, 2026
57316c4
refactor(types): wire SKILL templates to type-dir manifests
fujibee Jun 21, 2026
73a3d3b
test(windows): stage types/ in the PowerShell smoke so type checks re…
fujibee Jun 21, 2026
aff73c2
refactor(delivery): move enable/disable side effects into type plugs
fujibee Jun 21, 2026
8072f08
refactor(delivery): status as a Template Method plug
fujibee Jun 21, 2026
ec5982e
refactor(codex): fold the codex runtime into types/codex/
fujibee Jun 21, 2026
8d3dfe0
Merge branch 'cleanup/delivery-status' into restructure-agent-types
fujibee Jun 21, 2026
bb3add4
refactor(delivery): data-drive Windows hook wrapping via manifest
fujibee Jun 21, 2026
fb3e681
Merge remote-tracking branch 'origin/main' into restructure-agent-types
fujibee Jun 21, 2026
de510eb
refactor(delivery): consolidate mode support into delivery_modes mani…
fujibee Jun 21, 2026
b8b3da7
docs(readme): lead Quick Start with npx, the zero-clone install path
fujibee Jun 21, 2026
33d4069
refactor(types): relocate types/ under scripts/drivers/types/
fujibee Jun 21, 2026
c0e5adb
feat(registry): axis-generic driver discovery + external-plugin opt-in
fujibee Jun 21, 2026
1b3757d
docs(agent-types): refresh manifest table + paths for the 1.1.0 layout
fujibee Jun 21, 2026
7b9a59e
docs(plugins): add docs/plugins.md + README section + plugins/ drop-i…
fujibee Jun 21, 2026
44cefa9
feat(hermes): add Hermes Agent as a beta agent type
fujibee Jun 22, 2026
ea6dea8
fix(install): re-point an existing Codex monitor shim on --update
fujibee Jun 22, 2026
a17a727
docs(install): list hermes in the --agent-type help (co1 nit)
fujibee Jun 22, 2026
73a53ae
docs(readme): add supported-agents logo strip
fujibee Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ Cross-agent messaging for CLI AI agents. No daemon, no network, no complexity.

You stop being the copy-paste courier between your agents. Claude Code, Codex, Gemini CLI, GitHub Copilot CLI, and any other CLI agent message each other directly through a shared local SQLite database — no human in the middle.

<p align="center">
<img src="docs/logos/supported-agents.png" width="780"
alt="Supported agents: Claude Code, Codex, Gemini, GitHub Copilot, Antigravity, OpenCode, Hermes">
</p>

**What it isn't:**

- Not MCP. No MCP server, no extra runtime — just `bash` + `sqlite3`.
Expand All @@ -36,11 +41,8 @@ In real use it looks like this — Claude Code asking Codex for a code review an
**Requires:** `bash` and `sqlite3`. macOS ships both. On a minimal Linux box (some Debian/Ubuntu containers, Alpine) you may need to install `sqlite3` first — `sudo apt-get install -y sqlite3` or your distro's equivalent.

```bash
# 1. Install (one-liner)
bash <(curl -fsSL https://raw.githubusercontent.com/fujibee/agmsg/main/setup.sh)

# Or clone first if you want to inspect the code
git clone https://github.com/fujibee/agmsg.git && cd agmsg && ./install.sh
# 1. Install — npx is the fastest path, no clone needed
npx agmsg

# 2. Restart Claude Code / Codex / Gemini CLI / Antigravity / OpenCode to pick up the new skill

Expand All @@ -54,7 +56,7 @@ git clone https://github.com/fujibee/agmsg.git && cd agmsg && ./install.sh

That's it. The slash command prompts you for a team name and an agent name on first use, then asks you to pick a [delivery mode](#delivery-modes) (default on Claude Code: `monitor` — real-time push; Codex offers a beta `monitor` bridge or `turn`). After that, you talk to your agent naturally — see [First run](#first-run) below.

Prefer a different install method? See [Install](#install) below for `npm` / `npx` and the Claude Code plugin marketplace paths.
Prefer to inspect the code first, track the latest `main`, or pick a custom command name? See [Install](#install) below for the `setup.sh` one-liner, `git clone`, and the Claude Code plugin marketplace paths.

## How it works

Expand Down Expand Up @@ -272,7 +274,7 @@ Codex supports `mode monitor` as a **beta** app-server bridge, plus `mode turn`

> ⚠️ **The monitor beta changes how Codex starts — opt in only if you understand it.** Codex has no Monitor tool, so `mode monitor` installs a shim at `~/.agents/bin/codex` and asks you to put `~/.agents/bin` **first on your PATH**, so `codex` then resolves to the shim instead of the real binary. In monitor-mode projects the shim routes interactive launches through a bridge that turns incoming agmsg messages into turns on the current Codex thread; `codex exec` and non-monitor projects pass straight through to the real Codex. It depends on experimental Codex app-server behavior and has known rough edges (orphans on TUI close — #149; one identity per project — #150).

If the shim can't be installed, launch with `~/.agents/skills/<cmd>/scripts/codex-monitor.sh`. Codex sandboxing must allow writes to the skill's `db/`, `teams/`, and `run/` dirs — `install.sh` configures those `writable_roots` when `~/.codex/config.toml` exists. Setup, PATH notes, and internals: [docs/codex-monitor-beta.md](docs/codex-monitor-beta.md).
If the shim can't be installed, launch with `~/.agents/skills/<cmd>/scripts/drivers/types/codex/codex-monitor.sh`. Codex sandboxing must allow writes to the skill's `db/`, `teams/`, and `run/` dirs — `install.sh` configures those `writable_roots` when `~/.codex/config.toml` exists. Setup, PATH notes, and internals: [docs/codex-monitor-beta.md](docs/codex-monitor-beta.md).

### GitHub Copilot CLI

Expand Down Expand Up @@ -309,8 +311,6 @@ See [docs/opencode.md](docs/opencode.md) for full setup instructions.

`send.sh` takes exactly four positional arguments: `<team> <from> <to> "<message>"`. Quote the message so the shell sees it as one argument; an unquoted message with spaces will be misparsed.

`hook.sh on|off` still works as a legacy alias for `delivery.sh set turn|off` but prints a deprecation notice.

## FAQ / Design notes

**Is this MCP? Do I need an MCP server?**
Expand Down Expand Up @@ -380,6 +380,7 @@ Auto-detects installed skill directories and cleans up: skill files, slash comma
| Variable | Default | Purpose |
|---|---|---|
| `AGMSG_STORAGE_PATH` | `<skill>/db` | Directory holding the SQLite message store (`messages.db`). Override to relocate the store — handy for tests, sandboxes, or running isolated instances. |
| `AGMSG_PLUGIN_DIRS` | (unset) | `:`-separated extra directories to search for external drivers, in addition to `<skill>/plugins`. Each holds `<axis>/<name>/` subdirs. Drivers found here are still ignored until opted into with `agmsg plugin trust`. See [docs/plugins.md](docs/plugins.md). |

The message store path resolves as **`AGMSG_STORAGE_PATH` (env) > built-in default**. (A config-file layer is planned to slot in between the two as part of the storage-driver work; the intended order is env > config > default.) The override is scoped to the SQLite store only — team configs under `teams/` are unaffected.

Expand Down Expand Up @@ -480,8 +481,10 @@ bats tests/ # requires bats-core: brew install bats-core
├── SKILL.md # Skill definition (read by CC & Codex)
├── agents/
│ └── openai.yaml # Codex metadata
├── scripts/ # Bash scripts
├── templates/ # Command templates per tool
├── scripts/ # Bash scripts (the type-agnostic engine)
│ ├── lib/ # Sourced helper libraries
│ └── drivers/types/<name>/ # Built-in agent-type drivers (manifest + runtime)
├── plugins/<axis>/<name>/ # External drivers you opt into (agmsg plugin trust)
├── db/messages.db # SQLite WAL-mode message store
└── teams/ # Team configs (self-contained)
└── <team>/
Expand All @@ -495,6 +498,22 @@ bats tests/ # requires bats-core: brew install bats-core
- **No daemon**: Direct filesystem access
- **No network**: Everything local

## Plugins

agmsg's pluggable units are **drivers** grouped by axis (`types` for agent
runtimes; `storage` and `delivery` to follow). Built-ins ship under
`scripts/drivers/`; you can drop your own under `<skill>/plugins/<axis>/<name>/`
(or point `AGMSG_PLUGIN_DIRS` at a directory) to extend agmsg without forking.

Because a driver is shell code that runs with your privileges, **external drivers
are never loaded until you opt in** — an unexpected drop-in is ignored (with a
warning) until you run `agmsg plugin trust <axis>/<name>`. List what's discovered
and its trust state with `agmsg plugin list`.

Full discovery order, the trust model, and authoring guidance:
[docs/plugins.md](docs/plugins.md) (design rationale in
[ADR 0002](docs/adr/0002-driver-discovery-and-plugin-opt-in.md)).

## Community

- **Product Hunt**: #5 Product of the Day, [2026-06-09 launch](https://www.producthunt.com/products/agmsg) — 219 upvotes, 39 comments
Expand Down
3 changes: 1 addition & 2 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ Do NOT manually edit config files. Always use join.sh.
# this session held on <agent_id> so peers can pick them up immediately.
~/.agents/skills/agmsg/scripts/reset.sh "$(pwd)" <type> [agent_id] [session_id]

# Set delivery mode for this project. Replaces the legacy hook.sh on/off,
# which is kept as a deprecated alias only.
# Set delivery mode for this project.
# monitor — real-time push via SessionStart + Monitor tool (claude-code only)
# turn — Stop-hook pulls at the end of each assistant turn
# both — monitor primary, turn as fallback
Expand Down
95 changes: 95 additions & 0 deletions docs/adr/0002-driver-discovery-and-plugin-opt-in.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ADR 0002: Driver discovery, external plugin location, and opt-in trust

**Status:** accepted
**Date:** 2026-06-21
**Deciders:** @fujibee

## Context

[ADR 0001](0001-storage-driver-pluginization.md) established the 3-axis driver
model (storage, agent, delivery) and deliberately **deferred the plugin loader**
— the machinery that discovers drivers shipped outside agmsg core — until a
concrete external driver was wanted. The 1.1.0 restructure brings the agent-type
("types") axis fully into the driver layout, and that work needs a discovery
story now: where bundled drivers live, where external ones live, and how an
external driver — which is shell code run with the user's privileges — is allowed
to load without becoming a drive-by code-execution vector. This ADR makes those
decisions for all axes; it supersedes ADR 0001's deferred-loader note and its
tentative `~/.agents/agmsg/plugins/` path.

## Decision

**Bundled drivers live in-tree at `scripts/drivers/<axis>/<name>`** (the types
axis uses a directory with a `type.conf` manifest; file-based axes may use
`<name>.sh`). The agent-type tree moved from the repo root to
`scripts/drivers/types/` accordingly.

**External drivers are discovered from, in priority order:** `scripts/drivers`
(built-in), then `<install_dir>/plugins`, then each `:`-separated entry of
`$AGMSG_PLUGIN_DIRS`. Each base holds axis subdirs (`<base>/<axis>/<name>`).
Among *eligible* candidates, **later bases override earlier ones**, so an opted-in
plugin can shadow a built-in.

**External drivers are never loaded unless explicitly opted into.** A built-in
(`scripts/drivers`) is always trusted; anything under `plugins/` or
`$AGMSG_PLUGIN_DIRS` is ignored — with a clear stderr warning — until the user
runs `agmsg plugin trust <axis>/<name>`. Trust is **path-pinned**: the allowlist
records `<axis>/<name>` → absolute path, and a trusted name resolved at a
different path is not honored (a directory swap under a trusted name does not
silently activate new code). The allowlist is a TSV at `<install_dir>/db/
trusted-plugins` (preserved across `--update`, like `config.yaml`).

The opt-in CLI is `agmsg plugin list | trust <ref> | untrust <ref>`, where
`<ref>` is `<axis>/<name>` or a bare `<name>` that must be unambiguous across
axes. `driver-registry.sh` provides the axis-generic bases + trust policy;
`type-registry.sh` is the types-axis facade built on it.

`plugin.json` metadata and `min_core_version` gating (ADR 0001 §5) remain
deferred — bundled and opted-in drop-in drivers are enough for now.

## Alternatives considered

- **Auto-load any driver found on the search path (no opt-in).** Rejected: a
malicious or accidental drop-in under `plugins/` would execute with the user's
privileges on the next agmsg invocation. The whole point of a discovery path is
undermined if discovery == execution.
- **Opt-in only when a plugin *overrides a built-in*; auto-load brand-new
types.** Rejected: a brand-new external type is still arbitrary shell code. "I
didn't put this here" is exactly the attack to defend against, override or not.
- **Trust by name only (not path).** Rejected: trusting `types/foo` once would
then honor any future `types/foo`, so swapping the directory contents (or
shadowing via a higher-priority base) silently activates unreviewed code.
- **External plugins at `~/.agents/agmsg/plugins/` (ADR 0001's tentative path).**
Rejected in favor of `<install_dir>/plugins`: it sits beside the install the
plugin extends, the `cp -R` installer never deletes it (survives `--update`),
and per-command-name installs get their own plugin set. `~/.agents/agmsg/`
remains the runtime/config dir (run/, config.yaml).
- **Store the allowlist in `config.yaml`.** Rejected: driver identities contain
`/` (`types/codex`), which the YAML key parser does not handle; a flat TSV is
simpler to append/grep/remove and sidesteps the escaping.
- **In-tree-wins (built-ins reserved, no override).** Rejected: it blocks the
legitimate "customize a built-in type locally" use case. Later-wins among
*trusted* candidates allows it without weakening the trust boundary.

## Consequences

- Positive: dropping a directory under `plugins/` (or pointing `AGMSG_PLUGIN_DIRS`
at one) extends agmsg with no fork; the opt-in step keeps that from being an
execution vector. The same machinery serves the storage and delivery axes.
- Positive: built-ins always work with zero config; the trust prompt only appears
when an external driver is actually present.
- Negative: a new user-facing concept (`agmsg plugin trust`) and a small amount
of registry complexity (per-axis trust gating on every resolution).
- Negative: path-pinned trust means moving a trusted plugin's directory requires
re-trusting it. This is intentional friction.
- Neutral: `AGMSG_TYPES_ROOT` (a test-only in-tree override) is removed; the
registry always resolves its root from the lib's own location. `AGMSG_HOME` /
`~/.config/agmsg/types` external discovery is replaced by the scheme above.

## References

- Supersedes the deferred-loader note and `~/.agents/agmsg/plugins/` path in
[ADR 0001](0001-storage-driver-pluginization.md)
- Specification: [`docs/spec/driver-interface.md`](../spec/driver-interface.md)
- Implements: `scripts/lib/driver-registry.sh`, `scripts/lib/type-registry.sh`,
`scripts/plugin.sh`
117 changes: 117 additions & 0 deletions docs/agent-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Agent types

agmsg supports several agent runtimes — claude-code, codex, gemini, antigravity,
copilot, opencode — and each is described by a small **manifest** so that the rest
of agmsg (detection, the join whitelist, spawn, and delivery routing) discovers it
from data instead of hardcoded `case` arms.

Adding a type is a **manifest + a command template** (plus an optional
`_delivery.sh` plug for bespoke delivery), not an edit across `whoami.sh`,
`join.sh`, `spawn.sh`, and `delivery.sh`.

## The manifest

Each type has `scripts/drivers/types/<name>/type.conf` — read-only `key=value`
**data**. agmsg reads it with a small per-key reader; it is **never `source`d**, so
a manifest cannot execute code. Multi-value keys are whitespace-separated.

| key | required | meaning |
|---|---|---|
| `name` | yes | the type name (matches the directory) |
| `template` | yes | the `/agmsg` command template filename, relative to the type dir (e.g. `template.md`); becomes `SKILL.md` |
| `detect` | — | env-var names whose presence selects this type. `explicit` = never auto-detected from the environment |
| `detect_proc` | — | parent-process-name glob patterns that select this type (e.g. `codex codex-*`) |
| `cli` | spawnable types | the launch binary |
| `spawnable` | — | `yes` if `spawn.sh` can launch this type |
| `spawn` | — | a `.mjs` node-launcher (beside the manifest) `spawn.sh` runs via Node; also marks the type spawnable |
| `hooks_file` | yes | project-relative delivery hooks file (e.g. `.codex/hooks.json`) |
| `monitor` | — | `yes` if the type exposes a native Monitor tool; `spawn` skips the readiness wait when `no` |
| `delivery_modes` | — | space-separated delivery modes the type's CLI accepts (e.g. `monitor turn off`); `delivery.sh`'s gate rejects anything else. Defaults to `monitor turn both off` when omitted |
| `stop_output` | — | output protocol for the Stop/turn inbox check — `json` (codex, copilot) vs. plain text (default) |
| `hook_windows_wrap` | — | `yes` if JSON hook entries also need a Windows-native `commandWindows` variant (codex) |

> The reader does not fail-fast: an omitted key reads as the empty string, so
> "required" above means "needed for the type to actually work", not "validated at
> load time".

### Detection

`whoami.sh` auto-detects the running type when none is passed:

1. **Environment** — the manifests' `detect=` env vars, evaluated in sorted type
order, which preserves the precedence claude-code < codex < gemini (a runtime's
own session vars beat the `GEMINI_*` family that users also set for the SDK).
`detect=explicit` types are never selected here.
2. **Process tree** — walking up from the current process, the first type whose
`detect_proc=` glob matches the ancestor's name wins.
3. Falls back to `claude-code`.

> Precedence note: env detection iterates types in **sorted name order**, so when
> two types' `detect=` vars are both present the alphabetically-earlier type wins.
> Keep `detect=` vars runtime-exclusive (a runtime's own session var, not a shared
> SDK var) so ties don't arise; this is why the `GEMINI_*` family — which users
> set without the CLI — sits behind the claude-code/codex session vars.

### Delivery

`hooks_file=` is the per-project file delivery hooks are written into, and
`delivery_modes=` declares which modes the type accepts (the central gate in
`delivery.sh` rejects the rest). The hook *format* lives in a **Template Method**:
`delivery.sh` defines the default behavior (JSON event-hooks) and a type's optional
`scripts/drivers/types/<name>/_delivery.sh` plug overrides any of
`agmsg_delivery_apply` / `on_enable` / `on_disable` / `status`. Rule-file types
(gemini, antigravity, …) delegate to the shared `rulefile_apply`; codex's plug adds
its bridge/shim lifecycle. No per-type `case` arms remain in `delivery.sh`.

### Node-launcher types (external add-ons)

A type whose manifest sets a `spawn=` key to a `.mjs` file is launched by
`spawn.sh` **via Node** rather than through a `cli=` binary. agmsg core invokes the
launcher with only four **universal** flags:

```
node <type-dir>/<spawn>.mjs --name <name> --team <team> --project <path> --initial-input <text>
```

All type-specific configuration (which binary, which model, which transport, env
vars) is the launcher's **own default / environment** — agmsg core never names any
add-on. This is what lets a node-launcher type ship entirely outside the agmsg tree
as an external plugin (under `<install_dir>/plugins/types/<name>/` or a dir on
`$AGMSG_PLUGIN_DIRS`) with no built-in edits. External types must be opted into
with `agmsg plugin trust types/<name>` — see
[ADR 0002](adr/0002-driver-discovery-and-plugin-opt-in.md).

## Adding a type

1. Create `scripts/drivers/types/<name>/type.conf` with at least `name`,
`template`, and `hooks_file` (add `detect`/`detect_proc` for auto-detection,
`cli` + `spawnable=yes` if `spawn.sh` should launch it, and `delivery_modes` to
restrict the modes the type accepts).
2. Add the command template beside the manifest as `template.md` (the path the
`template=` key names, relative to the type dir).
3. If the type needs a delivery behavior that doesn't exist yet, add a
`_delivery.sh` plug in the type dir overriding `agmsg_delivery_apply` /
`on_enable` / `on_disable` / `status`. Reusing an existing format (default JSON
hooks, or `rulefile_apply`) needs no code.

That's it — `whoami.sh`, `join.sh`, and `spawn.sh` pick the type up from the
registry with no further edits.

## Worked example

The six built-in manifests under `scripts/drivers/types/` are the reference. For
instance `scripts/drivers/types/codex/type.conf`:

```
name=codex
template=template.md
cli=codex
spawnable=yes
detect=CODEX_SANDBOX CODEX_THREAD_ID
detect_proc=codex codex-*
hooks_file=.codex/hooks.json
monitor=no
stop_output=json
hook_windows_wrap=yes
delivery_modes=monitor turn off
```
10 changes: 5 additions & 5 deletions docs/codex-monitor-beta.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ it untouched. You can either move that command aside and run `mode monitor`
again, or launch monitor sessions explicitly:

```bash
~/.agents/skills/agmsg/scripts/codex-monitor.sh
~/.agents/skills/agmsg/scripts/drivers/types/codex/codex-monitor.sh
```

For custom command names, replace `agmsg` with the installed skill name:

```bash
~/.agents/skills/<cmd>/scripts/codex-monitor.sh
~/.agents/skills/<cmd>/scripts/drivers/types/codex/codex-monitor.sh
```

## What The Shim Does
Expand Down Expand Up @@ -259,6 +259,6 @@ For an unattended worker, layer these on top of the gate:
## Related Details

- [Delivery modes](../README.md#delivery-modes)
- [Codex bridge implementation](../scripts/codex-bridge.js)
- [Monitor launcher](../scripts/codex-monitor.sh)
- [Codex shim](../scripts/codex-shim.sh)
- [Codex bridge implementation](../scripts/drivers/types/codex/codex-bridge.js)
- [Monitor launcher](../scripts/drivers/types/codex/codex-monitor.sh)
- [Codex shim](../scripts/drivers/types/codex/codex-shim.sh)
Loading
Loading