Skip to content

feat(plugin-birdclaw): birdclaw.sh local-first Twitter/X memory — service + agent action + launcher view (+ view package-dir resolution fix) - #11385

Merged
lalalune merged 2 commits into
developfrom
feat/plugin-birdclaw
Jul 2, 2026
Merged

feat(plugin-birdclaw): birdclaw.sh local-first Twitter/X memory — service + agent action + launcher view (+ view package-dir resolution fix)#11385
lalalune merged 2 commits into
developfrom
feat/plugin-birdclaw

Conversation

@NubsCarson

Copy link
Copy Markdown
Member

Closes #11305

What

birdclaw (steipete/birdclaw) — a local-first Twitter/X workspace that keeps your timeline, mentions, DMs, likes, and bookmarks in a private SQLite DB — integrated as a first-class plugin: plugins/plugin-birdclaw.

  • BIRDCLAW_SERVICE — typed wrapper over the CLI's stable --json envelopes. execFile argv (no shell), spawn-env allowlist (PATH/HOME/BIRDCLAW_HOME + a dedicated BIRDCLAW_OPENAI_API_KEY knob only — the agent's own provider keys never reach the CLI), 30s-cached availability probe, typed BirdclawCliError (not-installed/timeout/failed/bad-json), parsers that skip malformed rows (upstream declares schema churn).
  • BIRDCLAW action (OWNER role-gated) — search | inbox | sync | digest | status over the local archive. validate() requires the binary via the cached probe, so the planner never sees a dead action.
  • Private /api/birdclaw/* routesstatus (always 200; installed:false + guidance drives the view's setup screen), tweets, inbox, sync, digest. Error contract: 400 invalid params / 502 CLI failure with the stderr tail / 503 not installed.
  • Birdclaw launcher view (/birdclaw, GUI+XR+TUI from one spatial component) — Timeline / Mentions / Posted / Likes / Bookmarks / Inbox tabs, transport-aware Sync button, needs-reply nudge, chat handoff on "Ask" (chat-first law: free-form search lives in the floating chat via the action). No poll loop — the archive only changes on sync/import, so the view reloads after its own syncs.
  • Enablement — auto-on only when the host actually has birdclaw (binary on PATH, BIRDCLAW_BIN/BIRDCLAW_HOME, or ~/.birdclaw); ELIZA_BIRDCLAW=1/0 + config birdclaw: true|false override; never on mobile (the CLI can't exist in a store sandbox, so the tile never appears where the archive can't load).

Bug found & fixed along the way

registerPluginViews resolved a plugin's package dir by probing the bare short name first. A plugin short name can collide with an unrelated npm package — concretely, plugin birdclaw vs the birdclaw CLI on npm — and under Bun the bare-name resolve hit that package's install cache, registering the view against a directory that isn't the plugin at all (bundle 404). views-registry.ts now probes the canonical @elizaos/plugin-<name> first (bare name stays as the community-plugin fallback), covered by views-registry.package-resolution.test.ts. This protects every short-named view plugin, not just this one.

Evidence (.github/issue-evidence/11305-birdclaw-*)

Live e2e (packages/app/scripts/birdclaw-live-e2e.mjs) against the real dev stack — real agent, real birdclaw CLI (v0.8.5, Node), real SQLite archive — at desktop (1440×900) and phone (390×844) viewports: 14/14 PASS. Screenshots (launcher tile, timeline with real archive rows + like counts + liked/bookmarked marks, mentions with the needs-reply nudge + accent dots, inbox) reviewed by hand and committed.

Live route drives (curl against the running agent):

  • GET /api/birdclaw/statusinstalled:true, version 0.8.5, counts {home:4, mentions:2, dms:4, needsReply:2} + transport state
  • GET /api/birdclaw/tweets / inbox → real flattened rows from the archive
  • POST /api/birdclaw/sync with no live transport → 502 with the CLI's real diagnostic; bad params → 400s
  • GET /api/views → birdclaw entry with correct pluginDir (after the resolver fix), GET /api/views/birdclaw/bundle.js → 200 (9.9 kB single chunk), hero → 200

Boot log: [BirdclawService] started (bin=…/birdclaw, installed=true, version=0.8.5) via the collector auto-detect path.

  • Real-LLM trajectory: N/A — no funded live model in this dev environment; the action surface is covered by 11 unit tests over the real handler + the real-CLI service lane underneath it, and the routes/view are proven live above.
  • Video walkthrough: N/A — superseded by the scripted 14-check browser e2e + per-state screenshots (loading/setup/error/empty/ready all unit-asserted; setup/ready/mentions/inbox captured live).

Tests & gates

  • Plugin: 69 unit tests (CLI seam, parsers, service, routes, action, both view layers incl. jsdom interaction) + gated real-CLI lane (test:real, 5/5: real spawn → birdclaw init-seeded SQLite → real envelopes → parsers; graceful not-installed).
  • Agent: 7 collector-gate tests (auto-detect / env force / config force / mobile-never) + 3 resolver tests; existing collector + views suites green (25/25 views-routes).
  • Full turbo build: 174/174 (the plugin's earlier @elizaos/agent dep created an agent↔plugin cycle — removed; the plugin depends only on core+ui).
  • View-bundle size gate: PASS (3.27 kB gzip ≤ 3.8 kB budget; total PASS). Registry drift gate: clean. Plugin typecheck/lint/format:check: clean. Type-safety ratchet: byte-identical failure state to develop HEAD on this host (pre-existing, none of it in this diff).

— [cloud-frontdoor]

…vice + action + routes + launcher view

Integrates birdclaw (https://birdclaw.sh, steipete/birdclaw) as a first-class
plugin: the owner's local Twitter/X archive (timeline, mentions, DMs, likes,
bookmarks in a private SQLite DB) becomes a launcher app and an agent
capability.

- BIRDCLAW_SERVICE: typed wrapper over the CLI's stable --json envelopes.
  execFile argv (no shell), env allowlist (PATH/HOME/BIRDCLAW_HOME + dedicated
  BIRDCLAW_OPENAI_API_KEY only), 30s-cached availability probe, typed
  BirdclawCliError (not-installed / timeout / failed / bad-json).
- BIRDCLAW action (owner roleGate): search | inbox | sync | digest | status.
  validate() requires the binary, so the planner never offers a dead action.
- Private /api/birdclaw/* routes: status (200 + installed:false drives the
  view's setup screen), tweets, inbox, sync, digest; 400/502/503 error
  contract with the CLI stderr tail.
- Birdclaw view (GUI+XR+TUI from one spatial component): Timeline / Mentions /
  Posted / Likes / Bookmarks / Inbox tabs, transport-aware Sync button,
  needs-reply nudge, chat handoff on Ask; setup screen when not installed.
- Enablement: auto-on only when the host has birdclaw (binary on PATH,
  BIRDCLAW_BIN/BIRDCLAW_HOME, or ~/.birdclaw); ELIZA_BIRDCLAW=1/0 + config
  birdclaw:true|false override; never on mobile (CLI can't exist in a store
  sandbox). Wired via plugin-collector gate (+7 tests), static registration,
  optional-plugin ladder, OPTIONAL_CORE_PLUGINS, zod + shared config types.
- Tests: 69 unit tests against an injected exec seam (CLI runner, parsers,
  service, routes, action, both view layers) + a gated real-CLI lane
  (test:real) that drives the actual birdclaw binary against a throwaway
  BIRDCLAW_HOME seeded by birdclaw init.
- View-bundle budget entry (measured 3.27 kB gzip, budget 3.8 kB); gate PASS.

Closes #11305
…in view package resolution + birdclaw live e2e evidence

The view registry resolved a plugin's package dir by probing the BARE short
name first. A plugin short name can collide with an unrelated published npm
package — concretely, plugin "birdclaw" vs the birdclaw CLI on npm — and
under Bun a bare-name resolve can hit that package's install cache, serving
the view bundle (404) and hero from a directory that isn't the plugin at all.
Probe the canonical @elizaos/plugin-<name> candidate first; bare name stays
as the community-plugin fallback. Covered by
views-registry.package-resolution.test.ts (candidate ordering + end-to-end
short-name registration resolving to plugins/plugin-birdclaw).

Also:
- plugin-birdclaw: drop the unused @elizaos/agent dep (it created a turbo
  build cycle agent -> plugin -> agent), import the client from the narrow
  host-external @elizaos/ui/api subpath, add format scripts, and pin
  react-router-dom/@date-fns store aliases for the jsdom suite.
- packages/app/scripts/birdclaw-live-e2e.mjs: one-shot live e2e driving the
  real dev stack (registry -> bundle -> launcher tile -> /birdclaw view ->
  tabs -> archive rows) at desktop + phone viewports; 14/14 PASS.
- .github/issue-evidence/11305-birdclaw-*: launcher/timeline/mentions/inbox
  screenshots (both viewports) + e2e summary against the real birdclaw CLI
  archive.

Refs #11305

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1c4c50bb-5634-4338-9be3-26a639b14d20

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/plugin-birdclaw

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lalalune

lalalune commented Jul 2, 2026

Copy link
Copy Markdown
Member

QA (subagent-verified): conventional plugin shape; no credential surface (external CLI owns auth under ~/.birdclaw, spawn env is a minimal allowlist excluding provider keys, argv-array execFile, owner-gated action, no public routes); the views-registry candidate reorder fixes a real bare-name collision with the npm CLI cache. 69/69 plugin + 10/10 agent-side, tsc clean, workspace-only lockfile delta. Merging.

@lalalune
lalalune merged commit 85c8f86 into develop Jul 2, 2026
40 of 66 checks passed
@lalalune
lalalune deleted the feat/plugin-birdclaw branch July 2, 2026 09:35
@claude

claude Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error —— View job


I'll analyze this and get back to you.

lalalune pushed a commit that referenced this pull request Jul 3, 2026
…r gate, 5 coverage-ratchet drifts, deterministic #11030 deadlock guard

packages/ui no-backdrop-blur-gate (#9141 battery): #11829's new
NotificationCenter reintroduced backdrop-blur-2xl/backdrop-saturate-150 on the
sheet/panel shell. Drop the GPU backdrop filter and carry readability with a
near-opaque base (bg-neutral-950/[0.87]) instead; refresh the committed
home-screen e2e artifacts (sheet + panel verified readable over the live
launcher in headless chromium, 10 screenshots + walkthrough video).

packages/app coverage/ratchet gates (5 reds, all real drift, no baselines
loosened):
- core-view-action-surface + view-switching matrices: track the automations
  core view (added by b07f3d9 without a surface owner) and the background
  settings subsection (#10994 registered it in SETTINGS_SECTION_META only) —
  AutomationsFeed + BackgroundSettings* are real agent-surface owners.
- route-coverage: plugin-birdclaw (#11385) declares a production collapsed
  gui/xr/tui view that was never added to the manifest ratchet. Wire it fully:
  manifest list, XR ratchet, runtime-plugin boot classification,
  gui+tui visual-matrix cases, tracked visual-review rows, manager-visible
  tile case, HMR lockstep probe, GUI interaction owners (its real
  plugin.test.ts + BirdclawView.test.tsx), and the ui-smoke stub
  (view registration + honest zero-key /api/birdclaw/status "not installed"
  state). Both birdclaw Playwright visual cases pass live (real BirdclawView
  bundle renders the setup card; audit JSON + screenshots reviewed).
- ui-smoke-coverage: scenario-pr.yml still hand-named the 10 pre-#11442
  voice-workbench spec filenames; point the slice at the renamed specs.
- view-interaction-coverage: the launcher owner declaration still claimed
  edit-mode/drag-to-reorder coverage that #11523's read-only launcher removed;
  declare what run-launcher-e2e.mjs actually proves now (no-edit long-press,
  tap-launch telemetry).

ios-local-agent-transport #11030 deadlock guard (both ui + app-core copies,
~1-in-4 flaky under a loaded suite): the 10s wall-clock Promise.race lost to a
CPU-starved event loop. Replace it with a deterministic detector — promise
assimilation invoking the hostile proxy's fabricated `then` proves the raw
Capacitor proxy crossed an await (the exact #11030 regression) and rejects
instantly with the descriptive error. Verified: reintroducing the raw-proxy
bug fails the test immediately with the #11030 message; fixed transport never
touches `then` (asserted).

Suites: packages/ui 544 files / 5532 pass; packages/app 37 files / 316 pass;
app-core transport suite 30 pass; typecheck green (ui, app, app-core); biome
clean on touched files.
lalalune pushed a commit that referenced this pull request Jul 3, 2026
…r gate, 5 coverage-ratchet drifts, deterministic #11030 deadlock guard (#11898)

packages/ui no-backdrop-blur-gate (#9141 battery): #11829's new
NotificationCenter reintroduced backdrop-blur-2xl/backdrop-saturate-150 on the
sheet/panel shell. Drop the GPU backdrop filter and carry readability with a
near-opaque base (bg-neutral-950/[0.87]) instead; refresh the committed
home-screen e2e artifacts (sheet + panel verified readable over the live
launcher in headless chromium, 10 screenshots + walkthrough video).

packages/app coverage/ratchet gates (5 reds, all real drift, no baselines
loosened):
- core-view-action-surface + view-switching matrices: track the automations
  core view (added by b07f3d9 without a surface owner) and the background
  settings subsection (#10994 registered it in SETTINGS_SECTION_META only) —
  AutomationsFeed + BackgroundSettings* are real agent-surface owners.
- route-coverage: plugin-birdclaw (#11385) declares a production collapsed
  gui/xr/tui view that was never added to the manifest ratchet. Wire it fully:
  manifest list, XR ratchet, runtime-plugin boot classification,
  gui+tui visual-matrix cases, tracked visual-review rows, manager-visible
  tile case, HMR lockstep probe, GUI interaction owners (its real
  plugin.test.ts + BirdclawView.test.tsx), and the ui-smoke stub
  (view registration + honest zero-key /api/birdclaw/status "not installed"
  state). Both birdclaw Playwright visual cases pass live (real BirdclawView
  bundle renders the setup card; audit JSON + screenshots reviewed).
- ui-smoke-coverage: scenario-pr.yml still hand-named the 10 pre-#11442
  voice-workbench spec filenames; point the slice at the renamed specs.
- view-interaction-coverage: the launcher owner declaration still claimed
  edit-mode/drag-to-reorder coverage that #11523's read-only launcher removed;
  declare what run-launcher-e2e.mjs actually proves now (no-edit long-press,
  tap-launch telemetry).

ios-local-agent-transport #11030 deadlock guard (both ui + app-core copies,
~1-in-4 flaky under a loaded suite): the 10s wall-clock Promise.race lost to a
CPU-starved event loop. Replace it with a deterministic detector — promise
assimilation invoking the hostile proxy's fabricated `then` proves the raw
Capacitor proxy crossed an await (the exact #11030 regression) and rejects
instantly with the descriptive error. Verified: reintroducing the raw-proxy
bug fails the test immediately with the #11030 message; fixed transport never
touches `then` (asserted).

Suites: packages/ui 544 files / 5532 pass; packages/app 37 files / 316 pass;
app-core transport suite 30 pass; typecheck green (ui, app, app-core); biome
clean on touched files.
lalalune pushed a commit that referenced this pull request Jul 3, 2026
…w cases

The plugin-view-llm-mock-coverage ratchet drifted against the view-case
source of truth: the visual smoke matrix (plugin-view-cases.ts) and the XR
ratchet (route-coverage.test.ts KNOWN_XR_VIEW_CASES) both register the
birdclaw view (gui/tui/xr from #11385) but PLUGIN_VIEW_LLM_MOCK_CASES and the
hardcoded lockstep counts had not been updated, so three assertions failed
(visual 57!=55, xr 29!=28, missing birdclaw mock journey).

Add the three birdclaw cases to PLUGIN_VIEW_LLM_MOCK_CASES (journeys derive
automatically via the .map) and bump the ratchet counts to 57 / 29 / 86.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gqo5uTsE25GyPTQHvfAAbj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: birdclaw (birdclaw.sh) local-first Twitter/X memory — plugin + launcher view + agent action

2 participants