feat(plugin-birdclaw): birdclaw.sh local-first Twitter/X memory — service + agent action + launcher view (+ view package-dir resolution fix) - #11385
Conversation
…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
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
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. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
…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.
…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.
…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
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--jsonenvelopes.execFileargv (no shell), spawn-env allowlist (PATH/HOME/BIRDCLAW_HOME+ a dedicatedBIRDCLAW_OPENAI_API_KEYknob only — the agent's own provider keys never reach the CLI), 30s-cached availability probe, typedBirdclawCliError(not-installed/timeout/failed/bad-json), parsers that skip malformed rows (upstream declares schema churn).BIRDCLAWaction (OWNER role-gated) —search | inbox | sync | digest | statusover the local archive.validate()requires the binary via the cached probe, so the planner never sees a dead action./api/birdclaw/*routes —status(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, 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.BIRDCLAW_BIN/BIRDCLAW_HOME, or~/.birdclaw);ELIZA_BIRDCLAW=1/0+ configbirdclaw: true|falseoverride; 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
registerPluginViewsresolved a plugin's package dir by probing the bare short name first. A plugin short name can collide with an unrelated npm package — concretely, pluginbirdclawvs thebirdclawCLI 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.tsnow probes the canonical@elizaos/plugin-<name>first (bare name stays as the community-plugin fallback), covered byviews-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/status→installed:true, version 0.8.5, counts {home:4, mentions:2, dms:4, needsReply:2}+ transport stateGET /api/birdclaw/tweets/inbox→ real flattened rows from the archivePOST /api/birdclaw/syncwith no live transport → 502 with the CLI's real diagnostic; bad params → 400sGET /api/views→ birdclaw entry with correctpluginDir(after the resolver fix),GET /api/views/birdclaw/bundle.js→ 200 (9.9 kB single chunk), hero → 200Boot log:
[BirdclawService] started (bin=…/birdclaw, installed=true, version=0.8.5)via the collector auto-detect path.Tests & gates
test:real, 5/5: real spawn →birdclaw init-seeded SQLite → real envelopes → parsers; graceful not-installed).@elizaos/agentdep created an agent↔plugin cycle — removed; the plugin depends only on core+ui).— [cloud-frontdoor]