{"id":51525549,"url":"https://github.com/jacklvd/sidekick-cat","last_synced_at":"2026-07-08T21:01:05.619Z","repository":{"id":369427046,"uuid":"1289792763","full_name":"jacklvd/sidekick-cat","owner":"jacklvd","description":"Self-hosted GitHub review bot for your public and private repos — its own App identity, ~$0/mo on free-tier LLMs + Cloud Run.","archived":false,"fork":false,"pushed_at":"2026-07-05T08:25:45.000Z","size":135,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-05T10:09:49.452Z","etag":null,"topics":["agent","ai-assistant","ai-tool","bot","firestore","gcloud","streamlit"],"latest_commit_sha":null,"homepage":"https://sidekick-cat-guide.streamlit.app/","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/jacklvd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"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":null,"dco":null,"cla":null}},"created_at":"2026-07-05T07:55:14.000Z","updated_at":"2026-07-05T08:28:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jacklvd/sidekick-cat","commit_stats":null,"previous_names":["jacklvd/sidekick-cat"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/jacklvd/sidekick-cat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Fsidekick-cat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Fsidekick-cat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Fsidekick-cat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Fsidekick-cat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jacklvd","download_url":"https://codeload.github.com/jacklvd/sidekick-cat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacklvd%2Fsidekick-cat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35278166,"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-07-08T02:00:06.796Z","response_time":61,"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":["agent","ai-assistant","ai-tool","bot","firestore","gcloud","streamlit"],"created_at":"2026-07-08T21:01:05.099Z","updated_at":"2026-07-08T21:01:05.607Z","avatar_url":"https://github.com/jacklvd.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sidekick-cat 🐱\n\n**A code-review bot with its own name, its own face, and a $0 hosting bill.**\n\nMost review bots either cost money per seat or post as the anonymous\n`github-actions[bot]`. sidekick-cat is neither: it's a real, branded GitHub App\nthat reviews every PR, answers to slash commands, and never sends you an invoice\n— because every LLM provider behind it is free-tier by design.\n\n![Python 3.13](https://img.shields.io/badge/python-3.13-blue)\n![managed with uv](https://img.shields.io/badge/managed%20with-uv-de5fe9)\n![Cloud Run](https://img.shields.io/badge/runs%20on-Cloud%20Run-4285F4)\n![LLM: NVIDIA → Groq → GitHub Models](https://img.shields.io/badge/LLM-NVIDIA%20%E2%86%92%20Groq%20%E2%86%92%20GitHub%20Models-f55036)\n![cost](https://img.shields.io/badge/cost-~%240%2Fmo-brightgreen)\n![MIT license](https://img.shields.io/badge/license-MIT-green)\n\n## Why you'd want this\n\n- 🆓 **Actually free** — NVIDIA NIM, Groq, and GitHub Models are all no-card free\n  tiers, so inference can only ever 429, never bill. Cloud Run scales to zero\n  between events.\n- 🏷️ **A bot with a face, not `github-actions[bot]`** — its own GitHub App, its\n  own avatar, its own voice in every comment.\n- 🌐 **Zero per-repo setup** — install once on your whole account; every repo gets\n  reviews, no workflow files or secrets to copy around.\n- 🧵 **Reviews that don't spam** — inline threads reconcile on every re-run\n  (edited, not duplicated), and `/merge` won't ship until they're resolved.\n- 🛡️ **Can't run up a bill even if it wanted to** — daily caps, a circuit breaker,\n  and a loop guard are baked into the request path, not bolted on after.\n- 🧠 **Actually knows your repo** — caches a project-context brief (file tree, key\n  files) as a GitHub Issue so reviews are grounded, not generic.\n\n## See it in action\n\nOpen a PR, and sidekick-cat introduces itself:\n\n\u003e 👋 Hey @you — welcome, I'm Sidekick! Thank you so much for opening this pull\n\u003e request!\n\u003e\n\u003e - [ ] The description has a **TL;DR** — the gist in a line or two\n\u003e - [ ] The description covers **What** changed\n\u003e - [ ] The description covers **Why** it changed\n\u003e - [ ] The description includes a **Test** section so reviewers can verify it\n\u003e\n\u003e - 🏷️ I'll label this PR based on the files you touched\n\u003e - 🐱 Comment `/review` whenever you'd like my code review\n\nComment `/review`, and it leaves inline notes on the exact changed lines, plus a\nsummary:\n\n\u003e ### 🐱 Sidekick's code review\n\u003e VERDICT: comment\n\u003e\n\u003e Solid change overall — one race condition worth a look before merge, and a\n\u003e couple of nits inline. Nothing blocking.\n\n## What it does\n\n- **Open a PR** → Sidekick welcomes + assigns the author, checks the description has\n  the required sections (TL;DR / What / Why / Test), labels by file type, and posts\n  a short AI summary.\n- **Comment `/review`** → a full AI code review: inline threads on the exact changed\n  lines (severity + a fix), plus an idempotent summary comment. Re-running\n  reconciles threads instead of duplicating them; an unchanged head is skipped free.\n- **Comment `/context`** → (re)generates the project-context brief Sidekick uses to\n  ground its reviews: a summary, folder structure, and file highlights, cached as a\n  hidden GitHub Issue (`bot:context`) so it costs nothing to store. `/review` reuses\n  it automatically, refreshing it lazily if it's missing or older than 30 days.\n- **Comment `/merge`** → if the PR is open, conflict-free, approved (when required),\n  all checks pass, every review thread is resolved, and the description passes the\n  template check (re-checked live, so a stale ❌ can't be bypassed), Sidekick\n  squash-merges and deletes the branch; otherwise it comments exactly why it can't.\n\nSlash commands work only for OWNER / MEMBER / COLLABORATOR. All comments carry a\nhidden `\u003c!-- bot:* --\u003e` marker and are upserted, so retries edit instead of spam.\n\n## Architecture\n\n```mermaid\nflowchart TB\n    subgraph GH[\"🐙 GitHub · all installed repos\"]\n        EV[\"PR opened/reopened\u003cbr/\u003e· /review · /merge · /context\"]\n    end\n\n    EV --\u003e|\"App webhook · HMAC-signed\"| WH\n\n    subgraph CR[\"☁️ Cloud Run service · sidekick-cat (us-west1)\"]\n        WH[\"POST /webhook\u003cbr/\u003everify HMAC · loop guard ·\u003cbr/\u003edelivery dedup · ack 202 fast\"]\n        DI[\"background dispatch\u003cbr/\u003e(CPU always-on)\"]\n        WH --\u003e|hand off| DI\n    end\n\n    DI --\u003e|\"App JWT → installation token (cached)\"| AUTH[\"gh_app_auth\"]\n    DI --\u003e FLOWS\n\n    subgraph FLOWS[\"🐱 Reused flow logic · scripts/\"]\n        PO[\"pr_open:\u003cbr/\u003ewelcome · validate · label · summary\"]\n        RV[\"/review:\u003cbr/\u003ediff anchors · inline reconcile\"]\n        MG[\"/merge:\u003cbr/\u003emerge gate via GraphQL\"]\n        CX[\"/context:\u003cbr/\u003erepo brief, cached as a GitHub Issue\"]\n    end\n\n    PO --\u003e LIM\n    RV --\u003e LIM\n    PO --\u003e LLM\n    RV --\u003e LLM\n    CX --\u003e LLM\n\n    LLM[\"llm_client\u003cbr/\u003e🆓 NVIDIA → Groq → GitHub Models\"]\n    LIM[\"limits\u003cbr/\u003eFirestore: daily caps + breaker\"]\n\n    DI -.-\u003e|secrets| SM[\"🔐 Secret Manager\u003cbr/\u003eAPP_KEY · WEBHOOK_SECRET\u003cbr/\u003eNVIDIA_API_KEY · GROQ_API_KEY · MODELS_PAT\"]\n    LIM --\u003e FS[\"(Firestore · us-west1)\"]\n    LLM --\u003e|inference, $0| EXT[\"NVIDIA / Groq / GitHub Models\"]\n\n    FLOWS --\u003e|\"comments · labels · review · merge\"| GH\n```\n\n**Edges that make it host-agnostic:** the flow logic in `scripts/` only depends on\nfour *edges* — trigger (webhook), GitHub auth (mint the installation token in\nPython), diff fetch (REST), and inference (`llm_client`) — so it isn't tied to\nCloud Run specifically.\n\n### Layout\n\n```\nserver/         FastAPI adapter\n  app.py          /webhook (verify+filter+ack+handoff), background dispatch, /health\n  security.py     HMAC-SHA256 verify on the raw body\n  router.py       (event, payload) → intent (pr_open | command | ignore)\n  gh_app_auth.py  App JWT → installation token, cached per installation id\nscripts/        reused flow logic (host-agnostic)\n  welcome · validate_pr · label_pr · summarize_pr · review_pr · merge_pr\n  diff_anchors · gh · llm_client · limits · config\n  repo_context    per-repo project-context doc, cached as a GitHub Issue\ninfra/terraform/ optional `terraform destroy` button for the GCP half (see the wizard's Teardown tab)\ntools/          setup_wizard.py — guided GCP/GitHub App/token setup + a Teardown tab (not part of the bot itself)\n```\n\n### Cost safeguards\n\nThe LLM side is **structurally free** — all three providers are on no-card free tiers, so\nthey can only ever return `429`, never bill. The guards bound Cloud Run instead:\nloop guard (drop bot-authored events), Firestore daily caps (global / per-repo /\nper-PR) + a circuit breaker, `--max-instances=3`, `--min-instances=0`, and a $5/mo\nbilling budget with alerts.\n\n## Local development\n\nUses [`uv`](https://docs.astral.sh/uv/) and Python 3.13. The pure logic self-checks\nrun fully offline — no network, no token, no Firestore (the limiter falls back to\nin-memory when `LIMITS_BACKEND` is unset):\n\n```bash\nuv sync\nuv run python -m scripts.tests.test_pr_logic     # flow logic\nuv run python -m scripts.tests.test_limits       # caps + breaker\nuv run python -m scripts.tests.test_server       # HMAC + routing\nuv run python -m scripts.tests.test_llm_client   # provider fallback\n```\n\nAnything that talks to a real provider or GitHub (the module smoke mains, or\nrunning `server/app.py` locally) needs credentials: copy\n[`.env.example`](.env.example) to `.env`, fill in the keys you have, and run\nwith `uv run --env-file .env ...`.\n\n## Guided setup\n\nFirst time setting this up? `tools/setup_wizard.py` is a Streamlit page that\nwalks through the GCP project/APIs, the exact GitHub App permissions this bot\nneeds (traced from the actual API calls, not guessed), a webhook-secret\ngenerator, and the NVIDIA / Groq / GitHub Models tokens — ending in a ready-to-copy\n`.env` and first-deploy command. A final **Teardown** tab covers pausing or\nfully deleting everything the bot spun up, with the delete commands pre-filled\nfrom your project id.\n\n**[▶ Try the live wizard](https://sidekick-cat-guide.streamlit.app/)** to read\nthrough the steps, permissions, and commands. When you're ready to enter *real*\nsecrets (webhook secret, tokens), run it **locally** instead — a hosted\nStreamlit app runs server-side, so your input reaches Streamlit's servers,\nwhereas the local run keeps everything on your machine:\n\n```bash\nuv run --extra setup-wizard streamlit run tools/setup_wizard.py\n```\n\n## Deploy\n\nRoutine redeploys: `GCP_PROJECT=\u003cyour-gcp-project\u003e ./infra/deploy.sh` (runs the\noffline suites, then a source deploy that keeps the live env vars/secrets/scaling).\nFirst-time deploys need the full command:\n\n```bash\ngcloud run deploy sidekick-cat --source . --region us-west1 \\\n  --allow-unauthenticated --min-instances=0 --max-instances=3 --timeout=300 \\\n  --no-cpu-throttling \\\n  --set-env-vars APP_ID=\u003capp_id\u003e,LIMITS_BACKEND=firestore \\\n  --set-secrets APP_KEY=APP_KEY:latest,WEBHOOK_SECRET=WEBHOOK_SECRET:latest,NVIDIA_API_KEY=NVIDIA_API_KEY:latest,GROQ_API_KEY=GROQ_API_KEY:latest,MODELS_PAT=MODELS_PAT:latest\n```\n\nThen set the GitHub App webhook URL to the service URL + `/webhook`, subscribe to\n**Pull request** + **Issue comment** events, and install on **All repositories**.\n\n## Secrets \u0026 config (Cloud Run)\n\n| Name | Where | Purpose |\n|------|-------|---------|\n| `APP_ID` | env var | GitHub App ID — JWT issuer for the installation token |\n| `LIMITS_BACKEND` | env var | `firestore` to share caps/dedup across instances; unset → in-memory |\n| `APP_KEY` | Secret Manager | App private key (PEM) → mints installation tokens |\n| `WEBHOOK_SECRET` | Secret Manager | verifies `X-Hub-Signature-256` |\n| `NVIDIA_API_KEY` | Secret Manager | primary inference for `/review` and `/context` (NVIDIA NIM: GLM-5.2 → MiniMax-M2.7). `nvapi-...` key from build.nvidia.com |\n| `GROQ_API_KEY` | Secret Manager | summaries + first fallback (Groq) |\n| `MODELS_PAT` | Secret Manager | last fallback (GitHub Models; needs the `models` scope) |\n\nSee [`.env.example`](.env.example) for the full list, including the legacy\nGitHub Actions workflow vars.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacklvd%2Fsidekick-cat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacklvd%2Fsidekick-cat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacklvd%2Fsidekick-cat/lists"}