6.8 KiB
@elizaos/plugin-birdclaw
Birdclaw (https://birdclaw.sh) local-first Twitter/X memory for elizaOS: the agent and the app get a typed window onto the owner's private Twitter archive — archived timeline, mentions, DMs, likes, and bookmarks in a local SQLite database — plus live-sync triggers and AI digests.
Purpose / role
birdclaw is steipete's local-first
Twitter workspace: birdclaw init + archive import + optional xurl/bird
transports populate a single SQLite DB (default ~/.birdclaw/). Its stable
integration surface is the CLI — "stable --json envelopes go to stdout,
progress and warnings to stderr". This plugin wraps that CLI with a service,
gives the agent an owner-gated BIRDCLAW action, serves /api/birdclaw/*
routes, and registers the Birdclaw launcher view.
Everything degrades explicitly when birdclaw is missing: the view renders a
setup screen with install guidance, data routes return
503 { installed: false }, and the action stops validating (the planner never
offers a dead capability).
Enablement
Auto-on when the host actually has birdclaw (see birdclawRequested in
packages/agent/src/runtime/plugin-collector.ts): the birdclaw binary on
PATH, BIRDCLAW_BIN/BIRDCLAW_HOME set, or an existing ~/.birdclaw root.
Never loads on mobile (the CLI cannot exist in a store-build sandbox, so the
tile never appears where it cannot work). Force with config
agents.defaults.birdclaw: true|false or ELIZA_BIRDCLAW=1/0. Also listed in
OPTIONAL_CORE_PLUGINS for admin-panel enablement.
Plugin surface
Service
BirdclawService(serviceType = "BIRDCLAW_SERVICE",src/birdclaw/service.ts) — binary resolution (BIRDCLAW_BIN→birdclawon PATH), a 30s-cached availability probe (--version), and typed methods:status()(db stats + transport),searchTweets(),inbox(),sync(collection),digest(period). Spawns viaexecFileargv arrays (no shell), with an env allowlist (PATH, HOME,BIRDCLAW_HOME, plusOPENAI_API_KEYonly from the dedicatedBIRDCLAW_OPENAI_API_KEYknob) — the agent's own provider keys are never inherited by the CLI.
Action
BIRDCLAW(src/actions/birdclaw.ts) — owner-only umbrella action with op dispatch:search(full-text over home/mentions/authored,--liked,--bookmarked),inbox(ranked mention/DM triage),sync(timeline|mentions|authored|likes|bookmarks),digest(today|24h|yesterday|week; needs birdclaw's OpenAI key),status.validaterequires the service to be registered AND the CLI installed (cached probe — no per-message process storm).
Routes (src/routes/birdclaw-routes.ts, private, rawPath)
GET /api/birdclaw/status— always 200;{ installed: false, message }drives the view's setup screen.GET /api/birdclaw/tweets?resource=&q=&liked=&bookmarked=&limit=GET /api/birdclaw/inbox?kind=&limit=POST /api/birdclaw/sync { collection }POST /api/birdclaw/digest { period }- Errors: 400 invalid params, 502 CLI failure (stderr tail in
error), 503 service/binary missing.
View
birdclaw—BirdclawView, path/birdclaw, bundledist/views/bundle.js, modalities["gui"]. Tabs: Timeline / Mentions / Posted / Likes / Bookmarks / Inbox; per-tab Sync button (shown only when a live transport is installed); needs-reply nudge; chat handoff on "Ask" (per the chat-first design law, free-form search lives in the floating chat via the BIRDCLAW action, not an input in the view).
Layout
src/
index.ts Public API barrel
plugin.ts birdclawPlugin (service + action + routes + view)
types.ts Display DTOs + enum guards (resources, kinds, collections, periods)
birdclaw/
cli.ts execFile seam: BirdclawExec, runBirdclawJson/Text, typed BirdclawCliError
service.ts BirdclawService + pure arg builders + wire parsers
birdclaw.real.test.ts REAL-CLI suite (gated; bun run test:real)
actions/birdclaw.ts BIRDCLAW umbrella action + formatters
routes/birdclaw-routes.ts status/tweets/inbox/sync/digest handlers
components/birdclaw/
BirdclawSpatialView.tsx Presentational (snapshot + onAction, spatial primitives)
BirdclawView.tsx Data wrapper (fetcher seam, tab state machine, sync flow)
birdclaw-view-bundle.ts Vite bundle entry — re-exports BirdclawView
Commands
bun run --cwd plugins/plugin-birdclaw typecheck
bun run --cwd plugins/plugin-birdclaw lint
bun run --cwd plugins/plugin-birdclaw test # offline unit suite (fake exec seam)
bun run --cwd plugins/plugin-birdclaw test:real # REAL birdclaw CLI against a temp BIRDCLAW_HOME
bun run --cwd plugins/plugin-birdclaw build # build:js (tsup) + build:views (vite) + build:types (tsc)
Config / env vars
| Variable | Default | Purpose |
|---|---|---|
BIRDCLAW_BIN |
birdclaw on PATH |
Absolute path to the birdclaw CLI. |
BIRDCLAW_HOME |
CLI's own ~/.birdclaw |
Data root passed to every spawn. |
BIRDCLAW_OPENAI_API_KEY |
— | Forwarded to the CLI as OPENAI_API_KEY for its AI features (digest, inbox scoring). Never falls back to the agent's own key. |
ELIZA_BIRDCLAW |
auto-detect | 1/0 force-enables/disables plugin loading (collector gate). |
Conventions / gotchas
- Exit code is the only success signal. birdclaw writes warnings (e.g.
Node 22's
node:sqliteExperimentalWarning) to stderr on successful runs — never treat stderr output as failure. search tweetsreturns a top-level JSON array;inboxreturns{ items }. The parsers narrow both to flat display DTOs and skip malformed rows (schema churn upstream is expected — "WIP. Real and usable").- The digest envelope is still settling upstream —
digest()accepts a JSON envelope (digest/text/reportfield) or raw markdown. - Timeouts: reads 30s, sync/digest 120s. Sync runs synchronously in the route; the view disables the button while in flight.
- No background poll in the view. The archive only changes via sync/import, so the view reloads after its own syncs instead of polling.
- The view-bundle vite config must keep
codeSplitting: false(shared config does this) — see the #11040 blank-view regression. - See the root
AGENTS.mdfor repo-wide conventions.
⛔ NON-NEGOTIABLE — evidence & real end-to-end tests
The binding standard is AGENTS.md. The unit
suite proves the arg-building/parsing/route/action/view logic against an
injected exec seam; birdclaw.real.test.ts (bun run test:real) drives the
REAL CLI — real spawn, real SQLite, real --json envelopes — against a
throwaway BIRDCLAW_HOME seeded by birdclaw init. UI changes additionally
require the rendered proof set (screenshots, walkthrough, client+server logs)
from AGENTS.md.