X Bookmark Triage
by @jeremyknows
Automatically triages X/Twitter bookmarks into structured knowledge cards and posts them to a Discord channel (#knowledge-intake or similar). Captures tweet...
clawhub install x-bookmark-triageπ About This Skill
name: x-bookmark-triage description: > Automatically triages X/Twitter bookmarks into structured knowledge cards and posts them to a Discord channel (#knowledge-intake or similar). Captures tweet content via fxtwitter, scores relevance using Claude Haiku, assigns tier (1β10), freshness (evergreen/dated/expired), and topic tags, then optionally unbookmarks after capture. Includes a scheduled poll for new bookmarks, a one-time backlog sweep, and a manual drop handler. Use when: (1) setting up automated bookmark β knowledge card pipeline, (2) draining an existing bookmark backlog, (3) dropping a URL manually for immediate triage, (4) packaging this as a publishable skill for other OpenClaw users. Requires X OAuth 2.0 User Context with bookmark.read scope (+ bookmark.write to unbookmark). Triggers on: "triage my bookmarks", "set up bookmark pipeline", "drain bookmark backlog", "x-bookmark-triage", "knowledge intake".
x-bookmark-triage
Turns X/Twitter bookmarks into searchable, tiered knowledge cards posted to a Discord channel. Captures, scores, and optionally unbookmarks β so your bookmark list stays clean and your knowledge base grows automatically.
> Works standalone β no OpenClaw required for core triage. OpenClaw integration is optional (adds scheduled polling via cron and bus events). See references/adapting.md for standalone setup.
Architecture
X Bookmarks
β
βββ Scheduled poll (2Γ/day) β bookmark-poll.js
βββ Backlog sweep (one-time) β backlog-sweep.js
βββ Manual drop (on mention) β poll-channel.js (launchd poller)
β
βΌ
triage-url.js (fetch β Claude Haiku β card)
β
βΌ
Discord #knowledge-intake (structured card)
β
βΌ
X API DELETE /bookmarks/:id (unbookmark)
Prerequisites
| Requirement | Notes |
|-------------|-------|
| X OAuth 2.0 app | developer.x.com β Free tier works |
| Scopes | bookmark.read (required) + bookmark.write (for unbookmark) |
| Discord bot token | Needs Send Messages permission in target channel |
| Anthropic API key | ANTHROPIC_DEFAULT_KEY env var |
| Node.js 18+ | Scripts use spawnSync, no npm deps |
Quick Start (8-step MVP)
1. Get credentials:
- X OAuth 2.0 app: Create at developer.x.com β Free tier. See references/oauth-setup.md for step-by-step.
- Discord bot token: Create at discord.com/developers β Bot needs Send Messages permission in your target channel.
- Anthropic API key: From console.anthropic.com
2. Run auth flow: node scripts/x-oauth2-authorize.js β saves refresh token to data/x-oauth2-token-cache.json
3. Set env vars: Export X_OAUTH2_CLIENT_ID, X_OAUTH2_CLIENT_SECRET, X_OAUTH2_REFRESH_TOKEN, DISCORD_BOT_TOKEN, ANTHROPIC_DEFAULT_KEY
4. Set your Discord channel: Set env var KNOWLEDGE_INTAKE_CHANNEL_ID= (preferred), OR edit scripts/triage-url.js line 19 to hardcode it.
5. Test triage: node scripts/triage-url.js https://x.com/@username/status/1234567890 --source "manual drop"
6. Check Discord: You should see a formatted card in your channel
7. Verify dedup: Run the command again β should skip (already seen)
8. Done: Your first card is live. Next: set up scheduling (see Steps 3β4 below)
Setup
1. OAuth 2.0 Authorization
# Run the interactive auth flow
node scripts/x-oauth2-authorize.js
β Opens browser, captures callback, saves to data/x-oauth2-token-cache.json
Store credentials in your gateway plist (or env):
X_OAUTH2_CLIENT_ID β app OAuth 2.0 client ID
X_OAUTH2_CLIENT_SECRET β app OAuth 2.0 client secret
X_OAUTH2_REFRESH_TOKEN β from auth flow (auto-rotated by scripts)
The scripts also fall back to reading refresh_token from a secrets JSON file at runtime. See references/oauth-setup.md for full details.
2. Configure Target Channel
Set CHANNEL_ID at the top of scripts/triage-url.js to your Discord channel ID.
3. Register the Scheduled Poll (Optional β for automated polling)
# Register a cron job (9 AM + 5 PM daily)
In OpenClaw: cron add β see references/cron-setup.md
You can also run manually: node scripts/bookmark-poll.js
Alternatively, drain all bookmarks one-time:
node scripts/backlog-sweep.js --delay 3
4. Register the launchd Poller (Optional β for automated polling)
Note: scripts/ai.watson.knowledge-intake-poll.plist is a template. Customize it first:
1. Edit the file to replace /PATH/TO/skills/x-bookmark-triage/scripts/poll-channel.js with the actual path
2. Replace YOUR_BOT_TOKEN, YOUR_ANTHROPIC_KEY, YOUR_CHANNEL_ID, /PATH/TO/workspace with real values
3. Copy to LaunchAgents and load:
# After customizing the plist:
cp scripts/ai.watson.knowledge-intake-poll.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/ai.watson.knowledge-intake-poll.plist
Or use OpenClaw to manage it β see references/cron-setup.md.
Scripts
triage-url.js β Core triage engine
node scripts/triage-url.js [--source "bookmark poll"|"manual drop"|"backlog sweep"]
data/knowledge-intake-seen.jsonbookmark-poll.js β Scheduled sweep (latest 20)
node scripts/bookmark-poll.js
triage-url.js for each unseen URLbookmark_sweep_complete to busbacklog-sweep.js β One-time full drain
node scripts/backlog-sweep.js [--dry-run] [--limit 50] [--delay 3] [--no-unbookmark]
--dry-run: list URLs without triaging or unbookmarking--limit N: stop after N bookmarks--delay N: seconds between triage calls (default: 3)backlog_sweep_complete to buspoll-channel.js β Discord drop handler
node scripts/poll-channel.js
Or via wrapper: bash scripts/run-poll.sh
triage-url.js for each unseen URLCard Format
Tier 3β10 (work-relevant):
ποΈ Title @handleβ’ The Miracle: what's new/impressive
β’ Why it matters: connection to your work
β’ Key Features: [list]
β’ Proposed Action: specific next step
β’ Tier: π₯ Tier 2 β Worth tracking (7/10) β relevance summary
β’ Freshness: πΏ evergreen | Topic: #ai
β’ Tags: #tool #infra
β’ Link:
Tier 1β2 (off-topic/personal):
π Title @handle β one-liner summary #personal
Tier Guide
| Tier | Label | Meaning | |------|-------|---------| | 9β10 | π₯ Act now | Implement this week | | 7β8 | π₯ Worth tracking | Thread + investigate soon | | 5β6 | π₯ Knowledge doc | Revisit in a sprint | | 3β4 | π Low priority | Interesting, not actionable | | 1β2 | π Off-topic | Personal/entertainment β one-liner only |
Freshness Values
evergreen πΏ β timeless, still valid in 2 yearsdated π
β relevant but will age (benchmarks, roadmaps)expired β° β time-sensitive, likely already staleTopic Values (one per card)
#ai Β· #veefriends Β· #product Β· #revenue Β· #openclaw Β· #personal Β· #x-strategy
Token / Cost Estimate
Each triage call: ~3.5K avg input + 300 output tokens (claude-haiku-4-5)
Seen-URL Deduplication
URLs are tracked in data/knowledge-intake-seen.json. Cap: 2,000 entries (FIFO). Bookmarks already in the seen cache are still unbookmarked β they were captured in a prior run.
Refresh Token Rotation
X rotates refresh tokens on each use. The scripts:
1. Detect rotation (new token in response)
2. Save new token to data/x-oauth2-new-refresh-token.txt
3. Log a warning to update the plist env var
Watson auto-detects and updates the secrets file. For non-Watson deployments, monitor for this warning and update X_OAUTH2_REFRESH_TOKEN in your env.
References
references/oauth-setup.md β Full OAuth 2.0 setup walkthroughreferences/cron-setup.md β OpenClaw cron registration for scheduled pollsreferences/adapting.md β How to adapt channel IDs, topics, triage prompt for your workspaceβοΈ Configuration
1. OAuth 2.0 Authorization
# Run the interactive auth flow
node scripts/x-oauth2-authorize.js
β Opens browser, captures callback, saves to data/x-oauth2-token-cache.json
Store credentials in your gateway plist (or env):
X_OAUTH2_CLIENT_ID β app OAuth 2.0 client ID
X_OAUTH2_CLIENT_SECRET β app OAuth 2.0 client secret
X_OAUTH2_REFRESH_TOKEN β from auth flow (auto-rotated by scripts)
The scripts also fall back to reading refresh_token from a secrets JSON file at runtime. See references/oauth-setup.md for full details.
2. Configure Target Channel
Set CHANNEL_ID at the top of scripts/triage-url.js to your Discord channel ID.
3. Register the Scheduled Poll (Optional β for automated polling)
# Register a cron job (9 AM + 5 PM daily)
In OpenClaw: cron add β see references/cron-setup.md
You can also run manually: node scripts/bookmark-poll.js
Alternatively, drain all bookmarks one-time:
node scripts/backlog-sweep.js --delay 3
4. Register the launchd Poller (Optional β for automated polling)
Note: scripts/ai.watson.knowledge-intake-poll.plist is a template. Customize it first:
1. Edit the file to replace /PATH/TO/skills/x-bookmark-triage/scripts/poll-channel.js with the actual path
2. Replace YOUR_BOT_TOKEN, YOUR_ANTHROPIC_KEY, YOUR_CHANNEL_ID, /PATH/TO/workspace with real values
3. Copy to LaunchAgents and load:
# After customizing the plist:
cp scripts/ai.watson.knowledge-intake-poll.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/ai.watson.knowledge-intake-poll.plist
Or use OpenClaw to manage it β see references/cron-setup.md.