原始内容
Batonlink
Hand off work between AI coding agents with a single URL.
Batonlink lets an agent distill the current session into a baton — a work-context package another agent can pick up by fetching one URL. You say "publish" to the agent you are working with; it writes a short brief, selects the relevant slice of the conversation, uploads it, and hands you back a link. Paste that link to a collaborator and their agent continues where you left off.
A baton has two layers:
- Brief — the agent's distillation: a short title plus a markdown body with the situation, key context, decisions made, and recommended next steps.
- Transcript — the verbatim conversation, attached automatically so the receiver has the raw record, not just the summary.
The receiving side needs nothing installed: a baton is a URL, readable with a
plain curl. This repository is the publishing side — the extensions and
plugins you install into your agent harness.
Supported harnesses
| Harness | Package | Install surface |
|---|---|---|
| pi | @batonlink/pi |
pi extension |
| Claude Code | @batonlink/claude-code |
plugin marketplace (MCP server) |
| OpenAI Codex | @batonlink/codex |
plugin marketplace (MCP server) |
| opencode | @batonlink/opencode |
in-process plugin |
Install
pi
pi install npm:@batonlink/pi
In a session: /batonlink to publish the current discussion. The first
publish opens a browser approval page to pair — no separate login step.
Claude Code
In a session:
/plugin marketplace add Sukitly/batonlink
/plugin install batonlink@batonlink
Then /batonlink:publish (or just ask the agent to publish). The first publish
opens a browser approval page to pair — no separate login step.
Codex
codex plugin marketplace add Sukitly/batonlink
codex plugin add batonlink@batonlink
Then invoke the publish skill (or ask the agent to publish). First use pairs
in the browser automatically.
opencode
Add the plugin to your opencode.json (global ~/.config/opencode/ or a
project-level file) and restart opencode:
{
"plugin": ["@batonlink/opencode"]
}
Then /batonlink to publish. First use pairs in the browser automatically.
Authentication
Publishing needs a Batonlink API key, resolved in this order:
BATONLINK_API_KEYenvironment variable (useful for CI or headless setups).- A credential file written by the login / pairing flow at
${XDG_CONFIG_HOME:-~/.config}/batonlink/credentials.json(mode0600), shared across every harness — log in once, publish everywhere.
If no key is present, the first publish pairs by opening a browser approval
page — the same flow on every harness, with no separate login step. On a
headless machine, set BATONLINK_API_KEY instead.
Environment variables
| Variable | Required | Purpose |
|---|---|---|
BATONLINK_API_KEY |
one of the two auth paths | Bearer token for publishing; overrides the credential file. |
BATONLINK_BASE_URL |
no | Override the API endpoint (defaults to the production endpoint). |
XDG_CONFIG_HOME |
no | Override the credential file base directory (absolute path only). |
CODEX_HOME |
no | Codex only: non-default session directory location. |
Packages
| Package | Role |
|---|---|
@batonlink/core |
Harness-agnostic SDK: wire-contract types, range selection, transcript filtering, chain rebuilding, publish transport, and the device-flow login state machine. No runtime dependencies. |
@batonlink/node |
Node runtime glue: XDG credential store, browser opener, login orchestration, and the shared tool-flow logic. |
@batonlink/mcp |
Harness-agnostic MCP server skeleton: the shared four-tool surface with lazy pairing. |
@batonlink/pi |
pi extension. |
@batonlink/codex |
Codex MCP server plus plugin artifacts. |
@batonlink/claude-code |
Claude Code MCP server (with a hook-based session rendezvous) plus plugin artifacts. |
@batonlink/opencode |
opencode in-process plugin. |
Development
Requires Bun.
bun install # install workspace dependencies
bun run build # compile all packages (topological order)
bun run lint # typecheck + Biome
bun run test # run the test suites
bun run ci # full gate: build + lint + generated-skill check + test + smoke
Releasing
Releases are lockstep: all seven packages plus the two plugin manifests
share one version and ship together. Internal dependencies stay pinned as
workspace:*; bun pm pack rewrites them to the concrete version, so a bump
never has to touch a dependency range.
One command opens a release PR (requires the gh
CLI):
bun run release patch # or minor / major / an explicit X.Y.Z
It bumps every version site on a release/vX.Y.Z branch and opens a PR.
Merging that PR runs .github/workflows/release.yml,
which packs each package with bun (rewriting workspace:*), publishes the
tarballs with npm in topological order, and tags the version.
Publishing uses npm trusted publishing (OIDC): no long-lived token, no CI secrets, and a provenance attestation is added automatically. This needs a trusted publisher configured per package, which can only be set up on a package that already exists — so the first release is published manually from a workstation instead:
bun run release:dry # preview: pack + npm publish --dry-run
bun run release:publish # publish all seven packages (uses your local ~/.npmrc)
git tag -a v0.1.0 -m "Release v0.1.0" && git push origin v0.1.0
Use a granular or automation token in ~/.npmrc (not interactive 2FA — the CI
gate runs first and a one-time password would expire). The first release has no
provenance, which is expected before trusted publishers exist. Afterwards,
configure a trusted publisher for each of the seven packages on npmjs.com
(owner Sukitly, repo batonlink, workflow release.yml); every release
after that runs in CI with pure OIDC and automatic provenance.
bun run release:dry # preview the publish locally, uploads nothing
bun run release:check # verify the lockstep invariant (also part of `bun run ci`)