{"id":47881893,"url":"https://github.com/markphelps/sherpa","last_synced_at":"2026-04-04T01:50:43.420Z","repository":{"id":340900027,"uuid":"1168010617","full_name":"markphelps/sherpa","owner":"markphelps","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-27T02:32:47.000Z","size":2155,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-27T09:45:32.821Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/markphelps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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-02-26T23:21:13.000Z","updated_at":"2026-02-27T02:32:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/markphelps/sherpa","commit_stats":null,"previous_names":["markphelps/sherpa"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/markphelps/sherpa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markphelps%2Fsherpa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markphelps%2Fsherpa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markphelps%2Fsherpa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markphelps%2Fsherpa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/markphelps","download_url":"https://codeload.github.com/markphelps/sherpa/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markphelps%2Fsherpa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31384846,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T01:22:39.193Z","status":"ssl_error","status_checked_at":"2026-04-04T01:22:33.970Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2026-04-04T01:50:42.762Z","updated_at":"2026-04-04T01:50:43.404Z","avatar_url":"https://github.com/markphelps.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sherpa\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./.github/images/demo.png\" alt=\"Sherpa\" width=\"800\" /\u003e\n\u003c/p\u003e\n\nA browser extension that guides you through pull requests with live, LLM-powered explanations as you review code. Renders a sidebar with layered progressive disclosure: PR summary, file-level explanations, and hunk-level detail.\n\n## How it works\n\nWhen you open a GitHub PR's \"Files changed\" tab, Sherpa:\n\n1. **Summarizes the PR** — one LLM call using the title, description, commit messages, and file list\n2. **Explains files on demand** — click or scroll to a file to get a contextual explanation using the full diff and file content\n3. **Explains hunks on demand** — drill into individual code changes for line-level detail\n\nExplanations are streamed in real time and cached locally so revisiting a PR is instant. API keys are obfuscated at rest (AES-GCM-based, not true encryption) and only held in memory during the active session.\n\n## Architecture\n\nSee [ARCHITECTURE.md](ARCHITECTURE.md) for a detailed breakdown of the extension's internals.\n\n## Security\n\nSee [SECURITY.md](SECURITY.md) for details on how Sherpa handles API keys, OAuth tokens, and other security considerations.\n\n## Tech stack\n\n| Layer               | Technology                                       |\n| ------------------- | ------------------------------------------------ |\n| Extension framework | [WXT](https://wxt.dev) (Manifest V3, Vite-based) |\n| Side panel UI       | Preact + Tailwind CSS                            |\n| LLM                 | Vercel AI SDK with provider adapters             |\n| Backend             | Cloudflare Worker (OAuth + explanation cache)    |\n| Language            | TypeScript                                       |\n| Tests               | Vitest                                           |\n\n## Setup\n\n**For a complete step-by-step guide** covering the extension, Cloudflare Worker, GitHub App, and LLM provider setup, see **[SETUP.md](SETUP.md)**.\n\nRequires [mise](https://mise.jdx.dev) for tool management (installs node + pnpm from `.mise.toml`). After [installing mise](https://mise.jdx.dev/getting-started.html), activate it in your shell:\n\n```bash\necho 'eval \"$(mise activate zsh)\"' \u003e\u003e ~/.zshrc   # or bash/fish — see mise docs\n```\n\nQuick start for development:\n\n```bash\nmise install                           # install node + pnpm (from .mise.toml)\npnpm install\ncp browser/.env.example browser/.env   # edit with your values\ncd browser \u0026\u0026 pnpm run dev             # opens Chrome with hot reload\n```\n\n| Variable                | Description                                                      | Required |\n| ----------------------- | ---------------------------------------------------------------- | -------- |\n| `VITE_WORKER_URL`       | Cloudflare Worker URL (or `http://localhost:8787` for local dev) | Yes      |\n| `VITE_GITHUB_CLIENT_ID` | GitHub App client ID                                             | Yes      |\n\n```bash\ncd browser\npnpm run build    # builds to .output/chrome-mv3/\npnpm run zip      # builds and creates a zip for distribution\npnpm test         # run tests\n```\n\n## Local development (extension + worker)\n\nTo run the full stack locally you need two terminals — one for the extension and one for the Cloudflare Worker.\n\n### 1. Extension\n\n```bash\nmise install         # install node + pnpm (one-time)\npnpm install        # from root — installs all workspace deps\ncd browser\npnpm run dev\n```\n\nThis starts the WXT dev server with hot reload and opens Chrome with the extension loaded. Note your extension ID from `chrome://extensions`.\n\n### 2. Worker\n\n```bash\ncd worker\npnpm run dev   # starts wrangler dev on localhost:8787\n```\n\nCreate a `worker/.dev.vars` file with your secrets:\n\n```\nGITHUB_CLIENT_ID=your_github_app_client_id\nGITHUB_CLIENT_SECRET=your_github_app_client_secret\nEXTENSION_ID=your_chrome_extension_id\n```\n\nSet `EXTENSION_ID` to the value from `chrome://extensions` so that CORS allows requests from your local extension.\n\n### 3. Point the extension at the local worker\n\nSet `VITE_WORKER_URL` in your `browser/.env` file:\n\n```\nVITE_WORKER_URL=http://localhost:8787\n```\n\nAlternatively, you can change the Worker URL in the extension's settings panel after loading it.\n\n### 4. Tunnel for GitHub OAuth\n\nGitHub OAuth requires a publicly reachable callback URL, so you need to tunnel your local worker. Using [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/):\n\n```bash\ncloudflared tunnel --url http://localhost:8787\n```\n\nOr with [ngrok](https://ngrok.com/):\n\n```bash\nngrok http 8787\n```\n\nThen:\n\n1. Set `VITE_WORKER_URL` in your `browser/.env` to the tunnel URL (e.g. `https://abc123.trycloudflare.com`)\n2. Update your GitHub App's **Authorization callback URL** to `\u003ctunnel-url\u003e/callback`\n3. Restart `pnpm run dev` (in `browser/`) so the extension picks up the new URL\n\n## Cost estimate\n\nFor a ~2400-line, 30-file PR, a full read-through costs roughly $0.10–0.30 depending on the LLM provider. Lazy loading means most reviews cost far less since you won't expand every file and hunk.\n\n## Generated files\n\nGenerated and lock files (e.g. `package-lock.json`, `yarn.lock`, `*.min.js`) are automatically kept collapsed during scroll sync so they don't clutter the sidebar. A small \"gen\" badge appears next to their filename. You can still expand them manually by clicking.\n\nDefault patterns:\n\n- `package-lock.json`\n- `yarn.lock`\n- `pnpm-lock.yaml`\n- `*.lock`\n- `go.sum`\n- `*.min.js`\n- `*.min.css`\n- `*.generated.*`\n- `*.g.dart`\n- `*.pb.go`\n\n## Known limitations\n\n- **GitHub DOM fragility** — The content script relies on GitHub's CSS class names, which are not a stable API. All selectors are isolated in `browser/src/providers/github/selectors.ts` for easy updates when GitHub changes their markup. A validation check on load warns if expected elements aren't found.\n- **Token budget** — Very large PRs with full file context can approach provider token limits. Mitigated by lazy loading at the file/hunk level rather than sending entire PRs.\n- **GitHub only** — The provider abstraction (`CodeReviewProvider` + `DOMAdapter`) is designed for adding GitLab and other platforms, but only GitHub is implemented today.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkphelps%2Fsherpa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarkphelps%2Fsherpa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkphelps%2Fsherpa/lists"}