Designed with create-cli defaults:
- humans first
- scriptable
- stable
--json - diagnostics on stderr
- prompts only on TTY
birdclaw
birdclaw imports, syncs, searches, and operates on a local Twitter archive.
birdclaw [global flags] <subcommand> [args]
-h, --help--version--json
Command flags > environment overrides > user config
User config:
~/.birdclaw/config.json
BIRDCLAW_HOMEBIRDCLAW_CONFIGBIRDCLAW_ACTIONS_TRANSPORT
birdclaw init
birdclaw auth status
birdclaw auth use <transport>
birdclaw import archive [path]
birdclaw sync all
birdclaw sync tweets
birdclaw sync authored
birdclaw sync dms
birdclaw sync bookmarks
birdclaw sync likes
birdclaw sync timeline
birdclaw sync mentions
birdclaw sync mention-threads
birdclaw sync followers
birdclaw sync following
birdclaw search tweets <query>
birdclaw search dms <query>
birdclaw discuss <query>
birdclaw today
birdclaw digest [today|24h|yesterday|week]
birdclaw mentions export [query]
birdclaw media fetch
birdclaw dms list
birdclaw mute <handle-or-id>
birdclaw unmute <handle-or-id>
birdclaw mutes list
birdclaw blocks list
birdclaw blocks add <handle-or-id>
birdclaw blocks remove <handle-or-id>
birdclaw ban <handle-or-id>
birdclaw unban <handle-or-id>
birdclaw show tweet <id>
birdclaw show thread <id>
birdclaw show dm <conversation-id>
birdclaw inbox
birdclaw serve
birdclaw graph summary
birdclaw graph events
birdclaw graph top-followers
birdclaw graph unfollowed
birdclaw graph non-mutual-following
birdclaw graph mutuals
birdclaw compose post
birdclaw compose reply <tweet-id>
birdclaw db stats
birdclaw db vacuum
birdclaw backup export --repo <path>
birdclaw backup sync --repo <path> --remote <url>
birdclaw backup import <path>
birdclaw backup validate <path>
birdclaw debug transport
- alias for
digest today - streams Markdown as model tokens arrive
- uses
gpt-5.5, medium reasoning, and priority service tier by default - requires
OPENAI_API_KEY - excludes DMs unless
--include-dmsis passed - supports
--refresh,--model,--language <locale-id>,--max-tweets, and--max-links - reads the default report language from
BIRDCLAW_DIGEST_LANGUAGE
- period:
today,24h,yesterday, orweek - accepts explicit
--since <iso>and--until <iso>windows - caches the final structured result by local context hash, model, reasoning effort, service tier, and canonical report language
- accepts the same language tag through
GET /api/period-digest?language=zh-CN --jsonsuppresses token streaming and emits the final envelope
- create app dir
- create DB
- write default config if absent
- optionally detect
xurlandbird
- show transport availability
- show active account/profile
- never print secrets
- set preferred moderation action transport
- allowed:
auto,xurl,bird
- writes Git-friendly canonical JSONL text shards
- removes and rewrites the
data/directory in the backup repo - validates the manifest and file hashes by default
--commitcreates a Git commit in the backup repo--pushimplies commit and pushes the backup repo
birdclaw backup export --repo ~/Projects/birdclaw-store --commit --push- clones/configures the backup Git repo when needed
- pulls the backup repo before reading
- merge-imports remote backup rows into local SQLite
- exports the local union back into deterministic text shards
- commits and pushes the backup repo
birdclaw backup sync --repo ~/Projects/backup-birdclaw --remote https://github.com/steipete/backup-birdclaw.git --jsonShard contract:
- tweets:
data/tweets/YYYY.jsonl - unknown tweet dates:
data/tweets/unknown.jsonl - profiles:
data/profiles.jsonlincludes bio, follower/following counts, profile URL, location, verification type, structured URL entities, and raw profile JSON - affiliations:
data/profile_affiliations.jsonlincludes X badge/highlighted-label organization edges - identity history:
data/profile_snapshots.jsonlanddata/profile_bio_entities.jsonlpreserve profile-change states and extracted bio identity hints - collections:
data/collections/likes.jsonl,data/collections/bookmarks.jsonl - DMs:
data/dms/conversations.jsonlplusdata/dms/YYYY.jsonl - moderation:
data/moderation/blocks.jsonl,data/moderation/mutes.jsonl - no SQLite WAL/SHM, FTS shadow tables, or transient live cache rows
Backup auto-sync config lives in ~/.birdclaw/config.json:
{
"backup": {
"repoPath": "/Users/steipete/Projects/backup-birdclaw",
"remote": "https://github.com/steipete/backup-birdclaw.git",
"autoSync": true,
"staleAfterSeconds": 900
}
}Read commands pull + merge only when the last backup check is stale. Data-changing commands run a full backup sync afterward. Set BIRDCLAW_BACKUP_AUTO_SYNC=0 to disable backup auto-sync for one process.
Live local likes, bookmarks, DMs, and moderation verification use bird on PATH
by default. Override it with BIRDCLAW_BIRD_COMMAND or:
{
"mentions": {
"birdCommand": "/absolute/path/to/bird"
}
}- validates the backup first unless
--no-validateis passed - merge-imports by default so local-only rows are not deleted
--replacerestores exactly from backup and deletes local portable rows first- rebuilds tweet and DM FTS from the JSONL text
birdclaw backup import ~/Projects/birdclaw-store --json- checks
manifest.json - checks every listed shard hash, byte count, row count, and JSONL parseability
- exits non-zero on validation failure
birdclaw backup validate ~/Projects/birdclaw-store --json- validate archive
- analyze contents
- import selected slices
- stream bundled media files from
data/tweets_media/,data/direct_messages_media/,data/community_tweet_media/,data/deleted_tweets_media/,data/profile_media/,data/moments_tweets_media/, anddata/direct_messages_group_media/into~/.birdclaw/media/originals/archive/<kind>/<id>/<filename> - extract
extended_entities.media[].video_info.variants[]onto each tweet media row for archive video and animated GIFs - parse
data/follower.jsanddata/following.jsinto the local follow graph - idempotent
- path is optional; without one, macOS archive autodiscovery picks the newest likely ZIP
Flags:
--select <kinds>— comma-separated subset oftweets,likes,bookmarks,profiles,directMessages,followers,following
--select details:
- selected re-imports preserve unselected slices
- valid aliases for
directMessages:directmessages,direct-messages,dms - duplicate names are ignored
- empty values and unknown names exit as invalid usage
- selected imports validate that existing
acct_primarymatches the archive account before writing - selected imports preserve compatible existing profile rows unless
profilesis selected
Examples:
birdclaw import archive --json
birdclaw import archive ~/Downloads/twitter-archive.zip --json
birdclaw import archive ~/Downloads/twitter-archive.zip --select tweets --json
birdclaw import archive ~/Downloads/twitter-archive.zip --select likes,bookmarks --json
birdclaw import archive ~/Downloads/twitter-archive.zip --select dms --json
birdclaw import archive ~/Downloads/twitter-archive.zip --select followers,following --json- fetch deltas
- update canonical tables
- refresh cursors
- refresh FTS incrementally
sync likesandsync bookmarksuse cached live transport;autotriesxurl, thenbird;--early-stopcaps at 10 pages unless paired with--allor--max-pagessync authoredusesxurl, includes retweets, and resumes from a storedsince_idsync timelinestores the live home timeline throughbird; it defaults to the chronological Following feedsync mentionsingests recent mentions throughxurl(default) orbirdand writeskind='mention'rows into the canonical store; this is the cron-friendly ingest path that replaces relying onmentions export --refreshsync mention-threadsfetches conversation context for recent mentions throughbird threadorxurl; pass--mode xurlwhen thebirdCLI is unavailable, otherwise use--delay-msand--timeout-msto stay gentle on live Xsync followersandsync followingdefault to dry-run and require--yesfor live sync or fresh-cache merge;autoprefersbird, then falls back toxurl
Common flags:
--since <cursor-or-id>--limit <n>--transport <kind>--dry-run--mode auto|xurl|bird--all--max-pages <n>--early-stop(onsync likesandsync bookmarks)--refresh--cache-ttl <seconds>
Examples:
birdclaw sync authored --mode xurl --limit 100 --json
birdclaw sync likes --mode auto --limit 100 --refresh --json
birdclaw sync likes --mode auto --limit 100 --max-pages 5 --early-stop --refresh --json
birdclaw sync bookmarks --mode auto --limit 100 --refresh --json
birdclaw sync bookmarks --mode auto --limit 100 --max-pages 5 --early-stop --refresh --json
birdclaw sync bookmarks --mode bird --all --max-pages 5 --limit 100 --refresh --json
birdclaw sync timeline --limit 100 --refresh --json
birdclaw sync mentions --mode xurl --limit 100 --max-pages 3 --refresh --json
birdclaw sync mention-threads --mode bird --limit 30 --delay-ms 1500 --timeout-ms 15000 --json
birdclaw sync mention-threads --mode xurl --limit 30 --jsonFollow graph examples:
birdclaw sync followers --json
birdclaw sync following --json
birdclaw sync followers --yes --json
birdclaw sync following --yes --json
birdclaw sync followers --mode bird --yes --json
birdclaw sync followers --yes --max-pages 1 --allow-partial --json
birdclaw sync followers --yes --refresh --jsonFollow graph sync uses a 24-hour cache by default. Repeating the same sync command with --yes reuses fresh cache unless --refresh is passed, which prevents duplicate live reads during agent workflows.
--allow-partial acknowledges capped/incomplete snapshots and suppresses the warning. Incomplete snapshots are still recorded for audit, but they are not used for churn events.
- refreshes home timeline, mentions, mention threads, likes, bookmarks, and DMs for one account
- appends one JSONL audit entry per run to
~/.birdclaw/audit/account-sync.jsonl - records each step independently with count, source, and error
- uses
~/.birdclaw/locks/account-sync.lockto skip overlapping runs - requires
--allow-bird-accountbefore Bird-backed steps write to a non-default--account - exits non-zero when any step failed
Examples:
birdclaw --json jobs sync-account --account acct_openclaw --limit 100 --max-pages 3 --refresh --allow-bird-account
tail -n 20 ~/.birdclaw/audit/account-sync.jsonl | jq .- writes
~/Library/LaunchAgents/com.steipete.birdclaw.account-sync.plist - runs
jobs sync-accountevery 30 minutes by default - uses
launchctl load -wunless--no-loadis passed --steps <steps>narrows the scheduled surfaces--env-path <path>sources account-specificbirdcookies for launchd--allow-bird-accountasserts those cookies match--accountfor Bird-backed timeline, mentions, and DM steps
birdclaw --json jobs install-account-launchd --account acct_openclaw --program /opt/homebrew/bin/birdclaw --env-path ~/.config/bird/openclaw.env --allow-bird-account- runs a live bookmark refresh with scheduler-friendly defaults
- appends one JSONL audit entry per run
- records host, timestamps, duration, before/after bookmark counts, transport source, fetched count, backup sync result, and errors
- uses
~/.birdclaw/locks/bookmarks-sync.lockto skip overlapping runs - exits non-zero when the sync failed
Default audit log:
~/.birdclaw/audit/bookmarks-sync.jsonl
Examples:
birdclaw --json jobs sync-bookmarks --mode auto --limit 100 --max-pages 5 --refresh
tail -n 20 ~/.birdclaw/audit/bookmarks-sync.jsonl | jq .- writes
~/Library/LaunchAgents/com.steipete.birdclaw.bookmarks-sync.plist - runs
jobs sync-bookmarksevery 3 hours by default - uses
launchctl load -wunless--no-loadis passed - writes launchd stdout/stderr to
~/.birdclaw/logs/bookmarks-sync.*.log --env-path <path>sources an export-only shell env file inside the scheduled process, useful whenbirdneedsAUTH_TOKEN/CT0outside an interactive browser session
birdclaw --json jobs install-bookmarks-launchd --program /opt/homebrew/bin/birdclawFlags:
--author <handle-or-id>--since <date>--until <date>--originals-only--hide-low-quality--liked--bookmarked--limit <n>
Examples:
birdclaw search tweets --liked --limit 20 --json
birdclaw search tweets --bookmarked --limit 20 --jsonFlags:
--participant <handle-or-id>--min-followers <n>--max-followers <n>--min-influence-score <n>--max-influence-score <n>--sort recent|followers--context <n>--resolve-profiles--expand-urls--refresh-profile-cache--refresh-url-cache--no-xurl-fallback--replied--unreplied--limit <n>
Profile resolution reads the local profile row first, then the persistent lookup
cache, then bird user, then xurl unless --no-xurl-fallback is set. Failed
lookups are cached briefly so repeated searches do not keep spending live calls.
Resolved profile rows store bio, profile URL, location, verification type,
structured X URL entities, raw profile JSON, and affiliation badge metadata when
the live transport exposes it.
Fetch live keyword matches through bird or xurl, store them as local
search tweets, then stream an OpenAI Markdown summary and discussion. DMs stay
out unless explicitly requested.
Flags:
--account <account-id>--source all|search|home|mentions|authored|likes|bookmarks--mode auto|bird|xurl|local--include-dms--since <date>/--until <date>--question <prompt>--originals-only--hide-low-quality--refresh--model <model>--limit <n>--max-pages <n>
Examples:
birdclaw discuss "local-first" --mode bird
birdclaw discuss "sync engine" --question "what changed over time?"
birdclaw discuss "prototype" --include-dms --limit 500 --max-pages 5 --jsonFind likely people or orgs from local DM and optional tweet evidence.
Candidates include structured profileEvidence entries for profile bio, profile
URL, bio URLs, location, verified type, first-class affiliations, bio entities,
profile-history snapshots, DM context, and expanded URLs. whois also searches
significant terms from fuzzy prompts, so blacksmith guy can rank a match from
@useblacksmith and blacksmith.sh even when the literal phrase was not stored
in a DM. Query intent changes ranking: @github emphasizes handle and
affiliation evidence, github.com emphasizes URL/domain evidence, and
github guy emphasizes people/org affiliation evidence. Human output explains
"why this person?" and buckets candidates as likely affiliated, ecosystem,
profile/link-only, DM context, or other local matches.
Flags:
--account <account-id>--no-dms--tweets--no-resolve-profiles--no-expand-urls--refresh-profile-cache--refresh-url-cache--no-xurl-fallback--affiliation <query>- require current/bio/history affiliation evidence--current-affiliation <query>- require an active affiliation badge edge--exclude-domain-only- drop candidates that only matched domains or URLs--context <n>--limit <n>
Examples:
birdclaw whois blacksmith --context 4 --no-xurl-fallback --json
birdclaw whois "blacksmith guy" --context 4 --no-xurl-fallback --json
birdclaw whois "github guy" --current-affiliation github --exclude-domain-only
birdclaw whois blacksmith --tweets --no-xurl-fallback- export local mention tweets for scripts and agents
- always emits JSON
- supports
birdclaw, cachedxurl, or cachedbirdoutput - each item includes:
- raw
text - rendered
plainText - rendered
markdown - canonical tweet URL
- author and reply-state metadata
- raw
Flags:
--account <account-id>--mode birdclaw|xurl|bird--replied--unreplied--refresh--cache-ttl <seconds>--all--max-pages <n>--limit <n>
Examples:
birdclaw mentions export "agent" --unreplied --limit 10
birdclaw mentions export --mode bird --limit 20
birdclaw mentions export --mode xurl --limit 5
birdclaw mentions export "codex" --mode xurl --limit 5
birdclaw mentions export --mode xurl --refresh --cache-ttl 30 --limit 5
birdclaw mentions export --mode xurl --refresh --all --max-pages 9 --limit 100Notes:
--mode xurlmirrors thexurl mentionsresponse shape:data,includes.users,meta--mode birdshells out to your localbirdCLI, normalizes the JSON to that samexurl-compatible shape, then caches it in SQLite- payload is cached in local SQLite and reused until the cache TTL expires
--refreshbypasses the cache and fetches live mentions immediately--allkeeps paginating until the retrievable mentions window is exhausted--max-pageslimits that paged xurl scan and implies--all- in paged
xurlmode,--limitis the page size, not the total returned item count - query and reply-state filters still work in
xurlmode, but the filtered response is rebuilt from the local canonical store after sync - default live source can live in
~/.birdclaw/config.jsonundermentions.dataSource
- fill the local originals cache for images, videos, and animated GIFs whose tweets already live in the local SQLite store
- reuse bytes already extracted by
import archivebefore falling back to the CDN; reuses are counted in JSON output asreused_from_archiveand spend zero CDN bandwidth - only fetch URLs birdclaw already has from an archive or live sync record; never enumerate, crawl, or derive CDN URLs
- skip files already present on disk; resume partial downloads with
Range: bytes=<size>- - back off on
429; cap each file at--max-bytes
Flags:
--account <accountId>--limit <n>- stop after N tweets processed--kind <kind>- tweet/collection kind, e.g.home,like,bookmark,mention--since <isoDate>- only consider tweets created at or after this date--parallel <n>- concurrent image workers, capped at 5 (default1)--pacing-ms <n>- delay between image request starts (default250)--video-pacing-ms <n>- separate delay between video request starts--retry-max <n>- retries per file after rate limiting (default3)--include-video/--no-include-video- videos and animated GIFs are on by default--max-bytes <n>- per-file size cap in bytes (default104857600)--dry-run- list what would be fetched without downloading--json
JSON output carries images_fetched, videos_fetched, gifs_fetched, reused_from_archive, skipped_cached, failed, rate_limited, per-kind byte counters, and a failures[] array. See Media for the full pipeline.
Examples:
birdclaw media fetch --json
birdclaw media fetch --dry-run --limit 20
birdclaw media fetch --include-video --video-pacing-ms 1500 --max-bytes 209715200 --json
birdclaw media fetch --no-include-video --parallel 3 --pacing-ms 250 --json- inspect a profile's recent authored replies when one mention feels borderline
- moderation-first: scans the live authored tweet timeline, excludes retweets, keeps reply tweets only
- good for spotting templated AI cadence across unrelated conversations
- supports
--json
Flags:
--limit <n>
Examples:
birdclaw profiles replies @jpctan --limit 12 --json- list DM conversations or events without requiring a full-text query
- optimized for agent and operator filtering
- optionally refreshes live DMs before listing
Flags:
--refresh--mode bird|xurl|auto--cache-ttl <seconds>--participant <handle-or-id>--min-followers <n>--max-followers <n>--min-influence-score <n>--max-influence-score <n>--sort recent|followers--replied--unreplied--account <name>--limit <n>
- refresh live direct messages
- merge conversations/messages into the local SQLite store
- supports
--json
Flags:
--account <account-id>--mode bird|xurl|auto--limit <n>--refresh--cache-ttl <seconds>
--mode bird is the default and the only mode that can sync message requests. --mode xurl reads recent OAuth2 /2/dm_events as accepted conversations; --mode auto tries xurl first for accepted DMs and falls back to bird.
- show AI-ranked actionable queue
- supports
--json - supports
--limit - supports
--kind mentions|dms|mixed - supports replied/unreplied filters
- supports
--scoreto refresh stored OpenAI scores before listing - supports
--min-scoreand--hide-low-signal
- list current local blocked profiles
- account-scoped
- supports
--json
Flags:
--account <account-id>--search <query>--limit <n>
- add a local block entry for one account
- accepts handle,
@handle, Twitter URL, local profile id, or numeric Twitter user id - attempts live block transport via
xurlwhen resolvable - falls back to the Twitter web cookie session if
xurlis rejected for OAuth2 block writes - still records the local block if live transport is unavailable
Flags:
--account <account-id>
- import a blocklist file in one call
- reads newline-delimited handles, ids, or Twitter URLs
- ignores blank lines and
#comments - tolerates markdown bullets like
- @handle - returns per-entry success/failure in
--json
Flags:
--account <account-id>
- remove a local block entry for one account
- attempts live unblock transport via
xurlwhen resolvable - falls back to the Twitter web cookie session if
xurlis rejected for OAuth2 block writes
Flags:
--account <account-id>
- shorthand aliases for
blocks addandblocks remove - useful when you want one obvious moderation verb from the CLI
Flags:
--account <account-id>
- list current local muted profiles
- account-scoped
- supports
--json
Flags:
--account <account-id>--search <query>--limit <n>
- add a local mute entry for one account
- accepts handle,
@handle, Twitter URL, local profile id, or numeric Twitter user id - resolves remote targets via
bird user --jsonbefore falling back toxurl /2/users --transport autotriesbirdfirst, thenxurl- still records the local mute if live transport is unavailable
Flags:
--account <account-id>
- remove a local mute entry for one account
--transport autotriesbirdfirst, thenxurl
Flags:
--account <account-id>
- starts local app server
- starts the built production SSR and static-asset server
- stdout prints the listening URL
Flags:
--host <host>--port <port>
birdclaw serve binds the production server to 127.0.0.1:3000 by default and
enables local loopback web APIs without a token. BIRDCLAW_HOST and
BIRDCLAW_PORT provide environment defaults. Remote access through a trusted
private proxy requires BIRDCLAW_ALLOW_REMOTE_WEB=1. To require an app-level
token too, set BIRDCLAW_WEB_TOKEN and send it as x-birdclaw-token or a
birdclaw_token cookie.
- cache-only SQLite read
- current followers/following counts
- mutuals and non-mutual following counts
- last complete and incomplete snapshot times
- cache-only SQLite read
- current followers sorted by their
public_metrics.followers_count - supports
--limit
- cache-only SQLite read
- append-only ended follow edges since
--date - defaults to
followers; pass--direction followingfor outbound ended edges
- cache-only SQLite read
- append-only
startedandendedfollow graph history - supports
--direction followers|following,--kind started|ended,--since,--until, and--limit
- cache-only SQLite read
- current mutuals
- sorted by follower size
- cache-only SQLite read
- current following profiles that are not current followers
- supports
--sort followers|handle
Agent rule: use graph commands for analysis. Ask for explicit user approval before sync ... --yes --refresh, because that can spend live X API reads.
stdout:
- primary data
- URLs
- JSON output
stderr:
- progress
- warnings
- diagnostics
- auth hints
- default human output
--jsonstable machine-readable envelopes--plainstable line-oriented text, no color
0success1runtime failure2invalid usage / validation3auth unavailable4transport unavailable5partial sync failure
birdclaw init
birdclaw auth status
birdclaw import archive ~/Downloads/twitter-archive.zip --select tweets,directMessages
birdclaw sync all --transport xurl
birdclaw search tweets "openai" --since 2024-01-01 --limit 20
birdclaw search tweets --since 2020-01-01 --until 2021-01-01 --originals-only --hide-low-quality --limit 500
birdclaw search dms "invoice" --participant @someone --min-followers 1000
birdclaw dms list --unreplied --min-followers 500 --min-influence-score 90 --sort followers
birdclaw inbox --json
birdclaw serve
birdclaw graph events --json
birdclaw compose reply 1891234567890