🇺🇸 English. Leia em português: README.pt-BR.md.
A local panel that turns your saved x.com bookmarks into an actionable queue. It reads your curated bookmarks from an HTML file, stores them in SQLite, and drops an Execute button on every card — firing up either Claude Code (in a fresh project folder, with git and optional GitHub repo) or the Claude desktop app (Cowork), depending on the task type.
Local-first. Nothing leaves your machine. No tokens, no external API, no telemetry.
X bookmarks become a graveyard. You save a tweet at midnight swearing you'll come back to it, and it disappears in the scroll. This panel takes your triage output — whether manual, from an agent, from a scheduled task, or any pipeline that produces the expected HTML — and forces you to decide: act now, study later, or archive. One click dispatches the execution.
It assumes you already have Claude Code installed and, optionally, gh authenticated for automatic private repo creation.
- Imports bookmarks from an HTML file (
relatorio-bookmarks-x.html) containing aconst BOOKMARKS = [...]array. - Renders each item as a card with insight, suggested action, priority, and category.
- Classifies each item automatically between Claude Code (code task) and Cowork (desktop/visual task) via a simple heuristic — you can always override.
- On Execute:
- Claude Code: copies the prompt to the clipboard and opens Terminal at the project path running
claude "<prompt>". - Cowork: copies the prompt and opens the Claude desktop app — paste with ⌘V.
- + New project: creates
<repo>/<slug>/, writesREADME.md+CLAUDE.md, runsgit init+ initial commit. Optional:gh repo create --private.
- Claude Code: copies the prompt to the clipboard and opens Terminal at the project path running
- Tracks progress: installed, applied, project started (three independent flags per card).
- Debounced text search (
/focuses,Escclears). - Always-on via launchd: panel restarts in ≤30s if it crashes, watchdog runs every 5min.
| Layer | Technology |
|---|---|
| Backend | Python 3.11+ · Flask 3 · SQLite |
| Frontend | HTML + CSS + vanilla JS (no build step) |
| Runtime | macOS (launchd + Terminal.app + pbcopy + open -a) |
| External deps (optional) | claude CLI, gh CLI, Claude desktop app |
The panel itself only needs Python and Flask. Claude/gh/Cowork come in when you click Execute — if they're missing, the panel still works and just skips those actions.
git clone https://github.com/wesleysimplicio/x-bookmarks-panel.git
cd x-bookmarks-panel
chmod +x setup.sh
./setup.sh
open http://localhost:8765setup.sh is idempotent and does:
- Copies
.env.example→.env(if missing). - Creates
.venv/and installs Flask. - Initializes SQLite at
data/bookmarks.db. - Imports from HTML if present (
relatorio-bookmarks-x.html). - Registers two launchd agents: the panel itself and a watchdog.
If you don't have a triage source yet, copy the sample:
cp examples/sample-relatorio.html relatorio-bookmarks-x.html
curl -X POST http://localhost:8765/api/oportunidades/importThat populates the panel with two synthetic bookmarks.
The panel expects a file named relatorio-bookmarks-x.html with a JavaScript block like this:
<script>
const BOOKMARKS = [
{
"link": "https://x.com/username/status/123456789",
"autor": "Display Name",
"handle": "@username",
"texto": "tweet body",
"data": "2026-04-20",
"midia": "texto|imagem|video|link",
"categoria": "Claude Code",
"prioridade": "agir-agora",
"insight": "why this bookmark matters",
"acao_sugerida": "what to do in practice",
"vale_executar": true
}
];
</script>How you produce that HTML is up to you: manual curation, your own scraper, a scheduled task, a curation agent, whatever. See examples/sample-relatorio.html for the full format. The importer is idempotent — already-imported links are updated, new ones are inserted, and your panel-edited fields (status, tipo_execucao, notas) are preserved.
Note on field names. Internally the schema uses Portuguese names (
oportunidades,acao_sugerida,tipo_execucao). They stay for backward compatibility with the existing SQLite schema and API payload. Feel free to open an issue if you want an English-aliased API on top.
Triage HTML → importer.py → SQLite (oportunidades)
↓
Flask app.py ←→ index.html (fetch)
↓
executor.py → pbcopy + osascript + gh
(Claude Code / Cowork / git / GitHub)
| File | Purpose |
|---|---|
| server/app.py | Flask + REST routes |
| server/db.py | SQLite schema + helpers |
| server/importer.py | Reads the BOOKMARKS array from HTML |
| server/executor.py | Opens Terminal/Cowork, scaffolds folder + git + gh repo create |
| index.html | Vanilla-JS UI |
More architectural detail in DESIGN.md.
The UI consumes these endpoints. You can automate outside the panel the same way:
| Method | Path | Body |
|---|---|---|
| GET | /api/healthz |
— |
| GET | /api/stats |
— |
| GET | /api/oportunidades?prioridade=&status=&categoria= |
— |
| GET | /api/oportunidades/<id> |
— |
| POST | /api/oportunidades/<id> |
{status?, tipo_execucao?, notas?, prioridade?, instalado?, aplicado?, projeto_iniciado?} |
| POST | /api/oportunidades/<id>/executar |
{tipo?: 'claude'|'cowork'|'auto', criar_projeto?: bool, com_github?: bool} |
| POST | /api/oportunidades/import |
— |
| GET | /api/projetos |
— |
Example:
# Re-import on demand
curl -X POST http://localhost:8765/api/oportunidades/import
# Fire Claude Code + new folder + private GitHub repo
curl -X POST http://localhost:8765/api/oportunidades/1/executar \
-H 'Content-Type: application/json' \
-d '{"tipo":"claude","criar_projeto":true,"com_github":true}'Edit .env (auto-created by setup.sh):
| Variable | Default | Purpose |
|---|---|---|
BOOKMARKS_PORT |
8765 |
Panel HTTP port |
BOOKMARKS_HOST |
127.0.0.1 |
Bind address (keep local; do not expose) |
BOOKMARKS_LABEL_PREFIX |
com.bookmarks.panel |
launchd label prefix |
COWORK_APP |
Claude |
Desktop app name that open -a targets |
BOOKMARKS_HTML |
<repo>/relatorio-bookmarks-x.html |
Alternate HTML source path |
./setup.sh # full setup (idempotent)
./install-launchd.sh # (re)register as always-on service
./start.sh # run in foreground for debug (Ctrl+C to exit)
./stop.sh # unload launchd + kill process
./healthcheck.sh # manual ping + restart if down
launchctl list | grep bookmarks # agent status
tail -f data/painel.log # server logs
tail -f data/healthcheck.log # watchdog logs| Scenario | What happens |
|---|---|
Server crashes (exception, kill -9) |
launchd restarts in ≤30s (KeepAlive + ThrottleInterval) |
| Server goes zombie (alive but unresponsive) | watchdog detects in ≤5min and runs kickstart -k |
| Plist missing / launchd unloaded | watchdog runs install-launchd.sh |
| Mac reboots | comes back on login (RunAtLoad) |
| Mac sleeps/wakes | comes back on network availability (NetworkState=true) |
- "Server offline" —
tail -n 50 data/painel.err.log. If empty,launchctl list | grep bookmarks. - Watchdog not recovering —
tail data/healthcheck.log. - Cowork button doesn't open the app — confirm the app is called
Claudein Finder. If not, adjustCOWORK_APPin.env. - Terminal can't find
claude—which claude. If not under/opt/homebrew/binor/usr/local/bin, update thePATHinsidescripts/launchd/panel.plist.templateand run./install-launchd.sh. gh repo createfails — rungh auth loginonce.- DB corrupted —
./stop.sh && rm data/bookmarks.db && ./setup.sh.
- The panel binds to
127.0.0.1— nobody on your network reaches it. - Zero telemetry. Zero remote auth. Zero tokens in the repo.
.gitignoreblocksdata/,*.db,relatorio-bookmarks-x.html,.env, captures, triages, profile analyses, and the folders generated by the + New project button.- Before publishing your own fork: confirm
git statusdoesn't list anything sensitive.
- Filterable execution history in the UI.
- Webhook that re-imports when the external triage pipeline runs (instead of polling).
-
POST /api/projetos/<id>/abrirendpoint to reopen Claude Code in an existing project. - CSV/JSON export for bookmarks.
- Linux support (currently depends on
launchctl+pbcopy+ AppleScript).
PRs welcome. Read CONTRIBUTING.md.
MIT.