{"id":50932998,"url":"https://github.com/wesleysimplicio/x-bookmarks-panel","last_synced_at":"2026-06-17T06:03:58.929Z","repository":{"id":353661822,"uuid":"1220392128","full_name":"wesleysimplicio/x-bookmarks-panel","owner":"wesleysimplicio","description":"Local-first Flask panel to triage x.com bookmarks into an actionable queue — Claude Code / Cowork dispatch, SQLite, zero cloud.","archived":false,"fork":false,"pushed_at":"2026-05-18T05:54:50.000Z","size":163,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-18T07:47:11.235Z","etag":null,"topics":["ai-agent","bookmarks","claude","claude-code","cowork","flask","local-first","mit-license","productivity","python","sqlite","twitter","x-twitter"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wesleysimplicio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-24T21:15:35.000Z","updated_at":"2026-05-18T05:54:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wesleysimplicio/x-bookmarks-panel","commit_stats":null,"previous_names":["wesleysimplicio/x-bookmarks-panel"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/wesleysimplicio/x-bookmarks-panel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysimplicio%2Fx-bookmarks-panel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysimplicio%2Fx-bookmarks-panel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysimplicio%2Fx-bookmarks-panel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysimplicio%2Fx-bookmarks-panel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wesleysimplicio","download_url":"https://codeload.github.com/wesleysimplicio/x-bookmarks-panel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysimplicio%2Fx-bookmarks-panel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34435992,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-17T02:00:05.408Z","response_time":127,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","bookmarks","claude","claude-code","cowork","flask","local-first","mit-license","productivity","python","sqlite","twitter","x-twitter"],"created_at":"2026-06-17T06:03:58.141Z","updated_at":"2026-06-17T06:03:58.923Z","avatar_url":"https://github.com/wesleysimplicio.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# X Bookmarks Panel\n\n\u003e 🇺🇸 English. Leia em português: [README.pt-BR.md](README.pt-BR.md).\n\nA 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.\n\n\u003e Local-first. Nothing leaves your machine. No tokens, no external API, no telemetry.\n\n---\n\n## Why this exists\n\nX 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.\n\nIt assumes you already have Claude Code installed and, optionally, `gh` authenticated for automatic private repo creation.\n\n---\n\n## What it does\n\n- Imports bookmarks from an HTML file (`relatorio-bookmarks-x.html`) containing a `const BOOKMARKS = [...]` array.\n- Renders each item as a card with **insight**, **suggested action**, **priority**, and **category**.\n- Classifies each item automatically between **Claude Code** (code task) and **Cowork** (desktop/visual task) via a simple heuristic — you can always override.\n- On **Execute**:\n  - **Claude Code**: copies the prompt to the clipboard and opens Terminal at the project path running `claude \"\u003cprompt\u003e\"`.\n  - **Cowork**: copies the prompt and opens the Claude desktop app — paste with ⌘V.\n  - **+ New project**: creates `\u003crepo\u003e/\u003cslug\u003e/`, writes `README.md` + `CLAUDE.md`, runs `git init` + initial commit. Optional: `gh repo create --private`.\n- Tracks progress: **installed**, **applied**, **project started** (three independent flags per card).\n- Debounced text search (`/` focuses, `Esc` clears).\n- Always-on via launchd: panel restarts in ≤30s if it crashes, watchdog runs every 5min.\n\n---\n\n## Stack\n\n| Layer | Technology |\n|-------|------------|\n| Backend | Python 3.11+ · Flask 3 · SQLite |\n| Frontend | HTML + CSS + vanilla JS (no build step) |\n| Runtime | macOS (launchd + Terminal.app + pbcopy + `open -a`) |\n| External deps (optional) | [`claude`](https://docs.claude.com/en/docs/claude-code) CLI, [`gh`](https://cli.github.com/) CLI, Claude desktop app |\n\nThe 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.\n\n---\n\n## Setup\n\n```bash\ngit clone https://github.com/wesleysimplicio/x-bookmarks-panel.git\ncd x-bookmarks-panel\nchmod +x setup.sh\n./setup.sh\nopen http://localhost:8765\n```\n\n`setup.sh` is idempotent and does:\n\n1. Copies `.env.example` → `.env` (if missing).\n2. Creates `.venv/` and installs Flask.\n3. Initializes SQLite at `data/bookmarks.db`.\n4. Imports from HTML if present (`relatorio-bookmarks-x.html`).\n5. Registers two launchd agents: the panel itself and a watchdog.\n\n### First run without your own HTML\n\nIf you don't have a triage source yet, copy the sample:\n\n```bash\ncp examples/sample-relatorio.html relatorio-bookmarks-x.html\ncurl -X POST http://localhost:8765/api/oportunidades/import\n```\n\nThat populates the panel with two synthetic bookmarks.\n\n---\n\n## Your bookmarks HTML\n\nThe panel expects a file named `relatorio-bookmarks-x.html` with a JavaScript block like this:\n\n```html\n\u003cscript\u003e\nconst BOOKMARKS = [\n  {\n    \"link\": \"https://x.com/username/status/123456789\",\n    \"autor\": \"Display Name\",\n    \"handle\": \"@username\",\n    \"texto\": \"tweet body\",\n    \"data\": \"2026-04-20\",\n    \"midia\": \"texto|imagem|video|link\",\n    \"categoria\": \"Claude Code\",\n    \"prioridade\": \"agir-agora\",\n    \"insight\": \"why this bookmark matters\",\n    \"acao_sugerida\": \"what to do in practice\",\n    \"vale_executar\": true\n  }\n];\n\u003c/script\u003e\n```\n\nHow 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`](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.\n\n\u003e **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.\n\n---\n\n## Architecture in 30 seconds\n\n```\nTriage HTML  →  importer.py  →  SQLite (oportunidades)\n                                     ↓\n                                Flask app.py  ←→  index.html (fetch)\n                                     ↓\n                                executor.py  →  pbcopy + osascript + gh\n                                              (Claude Code / Cowork / git / GitHub)\n```\n\n| File | Purpose |\n|------|---------|\n| [server/app.py](server/app.py) | Flask + REST routes |\n| [server/db.py](server/db.py) | SQLite schema + helpers |\n| [server/importer.py](server/importer.py) | Reads the `BOOKMARKS` array from HTML |\n| [server/executor.py](server/executor.py) | Opens Terminal/Cowork, scaffolds folder + git + `gh repo create` |\n| [index.html](index.html) | Vanilla-JS UI |\n\nMore architectural detail in [DESIGN.md](DESIGN.md).\n\n---\n\n## API\n\nThe UI consumes these endpoints. You can automate outside the panel the same way:\n\n| Method | Path | Body |\n|--------|------|------|\n| GET  | `/api/healthz` | — |\n| GET  | `/api/stats` | — |\n| GET  | `/api/oportunidades?prioridade=\u0026status=\u0026categoria=` | — |\n| GET  | `/api/oportunidades/\u003cid\u003e` | — |\n| POST | `/api/oportunidades/\u003cid\u003e` | `{status?, tipo_execucao?, notas?, prioridade?, instalado?, aplicado?, projeto_iniciado?}` |\n| POST | `/api/oportunidades/\u003cid\u003e/executar` | `{tipo?: 'claude'\\|'cowork'\\|'auto', criar_projeto?: bool, com_github?: bool}` |\n| POST | `/api/oportunidades/import` | — |\n| GET  | `/api/projetos` | — |\n\nExample:\n\n```bash\n# Re-import on demand\ncurl -X POST http://localhost:8765/api/oportunidades/import\n\n# Fire Claude Code + new folder + private GitHub repo\ncurl -X POST http://localhost:8765/api/oportunidades/1/executar \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"tipo\":\"claude\",\"criar_projeto\":true,\"com_github\":true}'\n```\n\n---\n\n## Environment variables\n\nEdit `.env` (auto-created by `setup.sh`):\n\n| Variable | Default | Purpose |\n|----------|---------|---------|\n| `BOOKMARKS_PORT` | `8765` | Panel HTTP port |\n| `BOOKMARKS_HOST` | `127.0.0.1` | Bind address (keep local; do not expose) |\n| `BOOKMARKS_LABEL_PREFIX` | `com.bookmarks.panel` | launchd label prefix |\n| `COWORK_APP` | `Claude` | Desktop app name that `open -a` targets |\n| `BOOKMARKS_HTML` | `\u003crepo\u003e/relatorio-bookmarks-x.html` | Alternate HTML source path |\n\n---\n\n## Commands\n\n```bash\n./setup.sh                 # full setup (idempotent)\n./install-launchd.sh       # (re)register as always-on service\n./start.sh                 # run in foreground for debug (Ctrl+C to exit)\n./stop.sh                  # unload launchd + kill process\n./healthcheck.sh           # manual ping + restart if down\n\nlaunchctl list | grep bookmarks    # agent status\ntail -f data/painel.log             # server logs\ntail -f data/healthcheck.log        # watchdog logs\n```\n\n---\n\n## Always-on guarantees\n\n| Scenario | What happens |\n|----------|--------------|\n| Server crashes (exception, `kill -9`) | launchd restarts in ≤30s (`KeepAlive` + `ThrottleInterval`) |\n| Server goes zombie (alive but unresponsive) | watchdog detects in ≤5min and runs `kickstart -k` |\n| Plist missing / launchd unloaded | watchdog runs `install-launchd.sh` |\n| Mac reboots | comes back on login (`RunAtLoad`) |\n| Mac sleeps/wakes | comes back on network availability (`NetworkState=true`) |\n\n---\n\n## Troubleshooting\n\n- **\"Server offline\"** — `tail -n 50 data/painel.err.log`. If empty, `launchctl list | grep bookmarks`.\n- **Watchdog not recovering** — `tail data/healthcheck.log`.\n- **Cowork button doesn't open the app** — confirm the app is called `Claude` in Finder. If not, adjust `COWORK_APP` in `.env`.\n- **Terminal can't find `claude`** — `which claude`. If not under `/opt/homebrew/bin` or `/usr/local/bin`, update the `PATH` inside `scripts/launchd/panel.plist.template` and run `./install-launchd.sh`.\n- **`gh repo create` fails** — run `gh auth login` once.\n- **DB corrupted** — `./stop.sh \u0026\u0026 rm data/bookmarks.db \u0026\u0026 ./setup.sh`.\n\n---\n\n## Privacy and security\n\n- The panel binds to `127.0.0.1` — nobody on your network reaches it.\n- Zero telemetry. Zero remote auth. Zero tokens in the repo.\n- `.gitignore` blocks `data/`, `*.db`, `relatorio-bookmarks-x.html`, `.env`, captures, triages, profile analyses, and the folders generated by the **+ New project** button.\n- Before publishing your own fork: confirm `git status` doesn't list anything sensitive.\n\n---\n\n## Roadmap\n\n- [ ] Filterable execution history in the UI.\n- [ ] Webhook that re-imports when the external triage pipeline runs (instead of polling).\n- [ ] `POST /api/projetos/\u003cid\u003e/abrir` endpoint to reopen Claude Code in an existing project.\n- [ ] CSV/JSON export for bookmarks.\n- [ ] Linux support (currently depends on `launchctl` + `pbcopy` + AppleScript).\n\n---\n\n## Contributing\n\nPRs welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\n[MIT](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwesleysimplicio%2Fx-bookmarks-panel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwesleysimplicio%2Fx-bookmarks-panel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwesleysimplicio%2Fx-bookmarks-panel/lists"}