Archive your Twitter/X bookmarks (and/or optionally, likes) to markdown. Automatically.
Like a dragon hoarding treasure, Smaug collects the valuable things you bookmark and like.
- Quick Start
- Requirements
- Platform Support
- OpenCode CLI Support
- Getting Twitter Credentials
- What It Does
- Running
- Categories
- Automation
- Output
- Configuration
- AI CLI Integration
- Troubleshooting
- Credits
🔥 🔥 🔥 🔥 🔥 🔥 🔥 🔥 🔥 🔥 🔥 🔥
_____ __ __ _ _ _ ____
/ ____| \/ | / \ | | | |/ ___|
\___ \| |\/| |/ _ \| | | | | _
___) | | | / ___ \ |_| | |_| |
|____/|_| |_/_/ \_\___/ \____|
🐉 The dragon stirs... treasures to hoard!
# 1. Install bird CLI (Twitter API wrapper)
# See https://github.com/steipete/bird for installation
# 2. Install an AI CLI (choose one):
# Claude Code: npm install -g @anthropic-ai/claude-code
# OpenCode: npm install -g @opencode/cli
# 3. Clone and install Smaug
git clone https://github.com/alexknowshtml/smaug
cd smaug
npm install
# 4. Run the setup wizard
npx smaug setup
# 5. Run the full job (fetch + process)
npx smaug runThe setup wizard will:
- Create required directories
- Guide you through getting Twitter credentials
- Create your config file
- Auto-detect your installed AI CLI
- Node.js 20+ (uses native
fetchAPI) - bird CLI - Twitter API wrapper (install globally)
- AI CLI - For bookmark analysis (Claude Code or OpenCode, optional, auto-detected)
- Git - Optional, for committing changes to version control
No shell utilities (curl, jq, etc.) needed - pure Node.js!
Smaug works on all major platforms:
| Platform | Status | Notes |
|---|---|---|
| Windows 10/11 | ✅ Fully supported | Native Node.js APIs, no shell dependencies |
| macOS 12+ | ✅ Fully supported | Native Node.js APIs |
| Linux | ✅ Fully supported | Native Node.js APIs |
Smaug supports both Claude Code and OpenCode CLI tools for bookmark analysis. You can choose which CLI to use based on your preferences and API access.
Configure which CLI tool to use in smaug.config.json:
{
"cliTool": "opencode", // or "claude"
"opencodeModel": "opencode/glm-4.7-free",
"claudeModel": "sonnet",
"autoInvokeOpencode": true,
"autoInvokeClaude": true
}Or set via environment variable:
export CLI_TOOL=opencode
export OPENCODE_MODEL=opencode/glm-4.7-free
export AUTO_INVOKE_OPENCODE=trueOpenCode is a versatile CLI that supports multiple AI models including free options.
Setup:
# Install OpenCode
npm install -g @opencode/cli
# Configure Smaug to use OpenCode
npx smaug init
# Then edit smaug.config.json to set:
# "cliTool": "opencode"
# "opencodeModel": "opencode/glm-4.7-free"Usage Examples:
# Run with OpenCode (configured in smaug.config.json)
npx smaug run
# Or override model for one run
export OPENCODE_MODEL=opencode/glm-4.7-pro
npx smaug run
# Fetch and process with OpenCode
npx smaug fetch 20
npx smaug runClaude Code provides advanced reasoning with Anthropic's models (Sonnet, Haiku, Opus).
Setup:
# Install Claude Code
npm install -g @anthropic-ai/claude-code
# Configure Smaug to use Claude
npx smaug init
# Then edit smaug.config.json to set:
# "cliTool": "claude"
# "claudeModel": "sonnet"Usage Examples:
# Run with Claude (default)
npx smaug run
# Use Haiku for faster, cheaper processing
export CLAUDE_MODEL=haiku
npx smaug run
# Track token usage and costs
npx smaug run --track-tokens| Feature | OpenCode | Claude Code |
|---|---|---|
| Models | Multiple (GLM-4.7, etc.) | Sonnet, Haiku, Opus |
| Cost | Free tier available | Paid API (Anthropic) |
| Setup | npm install -g @opencode/cli |
npm install -g @anthropic-ai/claude-code |
| Token Tracking | ✅ Supported | ✅ Supported |
| Parallel Processing | ✅ Via Task tool | ✅ Via Task tool |
| Auto-detection | ✅ Cross-platform | ✅ Cross-platform |
Both CLIs support these environment variables:
| Variable | Description | Example |
|---|---|---|
CLI_TOOL |
Which CLI to use | opencode or claude |
OPENCODE_MODEL |
OpenCode model name | opencode/glm-4.7-free |
CLAUDE_MODEL |
Claude model name | sonnet, haiku, opus |
AUTO_INVOKE_OPENCODE |
Auto-run OpenCode after fetch | true or false |
AUTO_INVOKE_CLAUDE |
Auto-run Claude after fetch | true or false |
AI_PATH |
Custom path to CLI binary | /usr/local/bin/opencode |
CLAUDE_TIMEOUT |
Processing timeout (ms) | 900000 (15 min) |
OpenCode with free model:
{
"cliTool": "opencode",
"opencodeModel": "opencode/glm-4.7-free",
"autoInvokeOpencode": true,
"autoInvokeClaude": false,
"claudeTimeout": 900000
}Claude with Sonnet:
{
"cliTool": "claude",
"claudeModel": "sonnet",
"autoInvokeClaude": true,
"allowedTools": "Read,Write,Edit,Glob,Grep,Bash,Task,TodoWrite",
"claudeTimeout": 900000
}Mixed usage (switch between them):
{
"cliTool": "opencode",
"opencodeModel": "opencode/glm-4.7-free",
"claudeModel": "sonnet",
"autoInvokeOpencode": true,
"autoInvokeClaude": true
}- ✅ No shell command dependencies - Uses native Node.js
fetchAPI - ✅ Automatic path detection - Finds Claude CLI on Windows/Mac/Linux
- ✅ Native HTTP handling - Better error handling and performance
- ✅ Exponential retry logic - Automatic retries with backoff for failed requests
- ✅ GitHub API rate limiting - Respects API limits (5000 req/hour authenticated)
-
AI CLI Location:
- Smaug automatically searches these Windows paths for Claude:
%LOCALAPPDATA%\Programs\claude.exe%PROGRAMFILES%\Claude\claude.exe%USERPROFILE%\AppData\Local\Programs\claude.exe
- And for OpenCode:
%APPDATA%\Roaming\npm\opencode.cmd%LOCALAPPDATA%\npm\opencode.cmd%LOCALAPPDATA%\Programs\opencode.exe
- Smaug automatically searches these Windows paths for Claude:
-
No additional tools required:
- No
curlneeded - No
gitneeded (unless using git automation) - Works out of the box with just Node.js
- No
-
PowerShell vs CMD:
- All commands work in both PowerShell and CMD
- No shell-specific syntax used
"Cannot find Claude/OpenCode binary":
# Check if Claude is installed
Get-Command claude
# Or check OpenCode
Get-Command opencode
# Or manually set path in smaug.config.json:
{
"claudePath": "C:\\Users\\YourName\\AppData\\Local\\Programs\\claude.exe"
}"Bird CLI not found":
- Install bird CLI globally:
npm install -g @steipete/bird@latest
- Verify installation:
bird --version
Smaug uses the bird CLI which needs your Twitter session cookies.
If you don't want to use the wizard to make it easy, you can manually put your seession info into the config.
- Open Twitter/X in your browser
- Open Developer Tools → Application → Cookies
- Find and copy these values:
auth_tokenct0
- Add them to
smaug.config.json:
{
"twitter": {
"authToken": "your_auth_token_here",
"ct0": "your_ct0_here"
}
}- Fetches bookmarks from Twitter/X using the bird CLI (can also fetch likes, or both)
- Expands t.co links to reveal actual URLs
- Extracts content from linked pages (GitHub repos, articles, quote tweets)
- Invokes Claude Code or OpenCode to analyze and categorize each tweet
- Saves to markdown organized by date with rich context
- Files to knowledge library - GitHub repos to
knowledge/tools/, articles toknowledge/articles/
# Full job (fetch + process with configured CLI)
npx smaug run
# Fetch from bookmarks (default)
npx smaug fetch 20
# Fetch from likes instead
npx smaug fetch --source likes
# Fetch from both bookmarks AND likes
npx smaug fetch --source both
# Process already-fetched tweets
npx smaug process
# Force re-process (ignore duplicates)
npx smaug process --force
# Track token usage and costs
npx smaug run --track-tokens
# Check what's pending
cat .state/pending-bookmarks.json | jq '.count'Categories define how different bookmark types are handled. Smaug comes with sensible defaults, but you can customize them in smaug.config.json.
| Category | Matches | Action | Destination |
|---|---|---|---|
| article | blogs, news sites, papers, medium.com, substack, etc | file | ./knowledge/articles/ |
| github | github.com | file | ./knowledge/tools/ |
| tweet | (fallback) | capture | bookmarks.md only |
🔜 Note: Transcription coming soon for podcasts, videos, etc but feel free to edit your own and submit back suggestions!
- file: Create a separate markdown file with rich metadata
- capture: Add to bookmarks.md only (no separate file)
- transcribe: Flag for future transcription (auto-transcription coming soon! PRs welcome)
Add your own categories in smaug.config.json:
{
"categories": {
"research": {
"match": ["arxiv.org", "papers.", "scholar.google"],
"action": "file",
"folder": "./knowledge/research",
"template": "article",
"description": "Academic papers"
},
"newsletter": {
"match": ["buttondown.email", "beehiiv.com"],
"action": "file",
"folder": "./knowledge/newsletters",
"template": "article",
"description": "Newsletter issues"
}
}
}Your custom categories merge with the defaults. To override a default, use the same key (e.g., github, article).
Run Smaug automatically every 30 minutes:
npm install -g pm2
pm2 start "npx smaug run" --cron "*/30 * * * *" --name smaug
pm2 save
pm2 startup # Start on bootcrontab -e
# Add:
*/30 * * * * cd /path/to/smaug && npx smaug run >> smaug.log 2>&1# Create /etc/systemd/system/smaug.service
# See docs/systemd-setup.md for detailsYour bookmarks organized by date:
# Thursday, January 2, 2026
## @simonw - Gist Host Fork for Rendering GitHub Gists
> I forked the wonderful gistpreview.github.io to create gisthost.github.io
- **Tweet:** https://x.com/simonw/status/123456789
- **Link:** https://gisthost.github.io/
- **Filed:** [gisthost-gist-rendering.md](./knowledge/articles/gisthost-gist-rendering.md)
- **What:** Free GitHub Pages-hosted tool that renders HTML files from Gists.
---
## @tom_doerr - Whisper-Flow Real-time Transcription
> This is amazing - real-time transcription with Whisper
- **Tweet:** https://x.com/tom_doerr/status/987654321
- **Link:** https://github.com/dimastatz/whisper-flow
- **Filed:** [whisper-flow.md](./knowledge/tools/whisper-flow.md)
- **What:** Real-time speech-to-text using OpenAI Whisper with streaming support.GitHub repos get their own files:
---
title: "whisper-flow"
type: tool
date_added: 2026-01-02
source: "https://github.com/dimastatz/whisper-flow"
tags: [ai, transcription, whisper, streaming]
via: "Twitter bookmark from @tom_doerr"
---
Real-time speech-to-text transcription using OpenAI Whisper...
## Key Features
- Streaming audio input
- Multiple language support
- Low latency output
## Links
- [GitHub](https://github.com/dimastatz/whisper-flow)
- [Original Tweet](https://x.com/tom_doerr/status/987654321)Create smaug.config.json:
{
"source": "bookmarks",
"archiveFile": "./bookmarks.md",
"pendingFile": "./.state/pending-bookmarks.json",
"stateFile": "./.state/bookmarks-state.json",
"timezone": "America/New_York",
"twitter": {
"authToken": "your_auth_token",
"ct0": "your_ct0"
},
"autoInvokeClaude": true,
"claudeModel": "sonnet",
"claudeTimeout": 900000,
"allowedTools": "Read,Write,Edit,Glob,Grep,Bash,Task,TodoWrite",
"webhookUrl": null,
"webhookType": "discord"
}| Option | Default | Description |
|---|---|---|
source |
bookmarks |
What to fetch: bookmarks (default), likes, or both |
includeMedia |
false |
EXPERIMENTAL: Include media attachments (photos, videos, GIFs) |
archiveFile |
./bookmarks.md |
Main archive file |
timezone |
America/New_York |
For date formatting |
cliTool |
claude |
CLI tool to use: claude or opencode |
autoInvokeClaude |
true |
Auto-run Claude Code for analysis |
autoInvokeOpencode |
true |
Auto-run OpenCode for analysis |
claudeModel |
sonnet |
Claude model: sonnet, haiku, or opus |
opencodeModel |
opencode/glm-4.7-free |
OpenCode model (any OpenCode-compatible model) |
claudePath |
null |
Custom path to CLI binary (auto-detected if null) |
claudeTimeout |
900000 |
Max processing time (15 min) |
webhookUrl |
null |
Discord/Slack webhook for notifications |
webhookType |
discord |
Webhook type: discord, slack, or generic |
Environment variables also work: AUTH_TOKEN, CT0, SOURCE, INCLUDE_MEDIA, ARCHIVE_FILE, TIMEZONE, CLI_TOOL, OPENCODE_MODEL, CLAUDE_MODEL, AUTO_INVOKE_OPENCODE, AUTO_INVOKE_CLAUDE, AI_PATH, CLAUDE_TIMEOUT, WEBHOOK_URL, WEBHOOK_TYPE, etc.
Media extraction (photos, videos, GIFs) is available but disabled by default. To enable:
# One-time with flag
npx smaug fetch --media
# Or in config
{
"includeMedia": true
}When enabled, the media[] array is included in the pending JSON with:
type: "photo", "video", or "animated_gif"url: Full-size media URLpreviewUrl: Thumbnail (smaller, faster)width,height: DimensionsvideoUrl,durationMs: For videos only
- Requires bird with media support - PR #14 adds media extraction. Until merged, you'll need a fork with this PR or wait for an upstream release. Without it,
--mediais a no-op (empty array). - Workflow still being refined - Short screengrabs (< 30s) don't need transcripts, but longer videos might. We're still figuring out the best handling.
Smaug uses either Claude Code or OpenCode for intelligent bookmark processing (configurable via cliTool setting). Both CLIs use the same processing instructions in .claude/commands/process-bookmarks.md:
- Generating descriptive titles (not generic "Article" or "Tweet")
- Filing GitHub repos to
knowledge/tools/ - Filing articles to
knowledge/articles/ - Handling quote tweets with full context
- Processing reply threads with parent context
- Parallel processing for 3+ bookmarks (using Haiku subagents for cost efficiency)
You can also run processing manually with your configured CLI:
# With Claude Code
claude
> Run /process-bookmarks
# With OpenCode
opencode
> Run /process-bookmarksTrack your API costs with the -t flag:
npx smaug run -t
# or
npx smaug run --track-tokensThis displays a breakdown at the end of each run:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 TOKEN USAGE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Main (sonnet):
Input: 85 tokens <$0.01
Output: 5,327 tokens $0.08
Cache Read: 724,991 tokens $0.22
Cache Write: 62,233 tokens $0.23
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💰 TOTAL COST: $0.53
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
For batches of 3+ bookmarks, Smaug spawns parallel subagents. By default, these use Haiku instead of Sonnet, which cuts costs nearly in half:
| Configuration | 20 Bookmarks | Time |
|---|---|---|
| Sonnet subagents | $1.00 | 4m 12s |
| Haiku subagents | $0.53 | 4m 18s |
Same speed, ~50% cheaper. The categorization and filing tasks don't require Sonnet-level reasoning, so Haiku handles them well.
This is configured in .claude/commands/process-bookmarks.md with model="haiku" in the Task calls.
This means either:
- No bookmarks were fetched (check bird CLI credentials)
- All fetched bookmarks already exist in
bookmarks.md
To start fresh:
rm -rf .state/ bookmarks.md knowledge/
mkdir -p .state knowledge/tools knowledge/articles
npx smaug runYour Twitter cookies may have expired. Get fresh ones from your browser.
For Claude:
# Check if Claude is installed
claude --version
# Or manually set path in smaug.config.json:
{
"claudePath": "C:\\Users\\YourName\\AppData\\Local\\Programs\\claude.exe"
}For OpenCode:
# Check if OpenCode is installed
opencode --version
# Or manually set path in smaug.config.json:
{
"claudePath": "/usr/local/bin/opencode"
}- Try
haikumodel instead ofsonnetin config for faster (but less thorough) processing - Switch to OpenCode with
opencode/glm-4.7-freefor faster processing - Make sure you're not re-processing with
--force(causes edits instead of appends)
- Ensure OpenCode is installed:
npm install -g @opencode/cli - Verify model is valid:
opencode modelsto see available models - Check that
cliToolis set to"opencode"in config
MIT