elek

Read-only AI code review for pull requests.
A self-hosted GitHub Action. Bring your own model key.

ci License: MIT

elek reviews every pull request in your repository. When a PR is opened, updated, reopened, or marked ready, the Action runs one [Pi](https://github.com/earendil-works/pi) session against the diff. The session uses the model you choose, with your key. elek then posts a summary comment and inline comments on the changed lines. elek is open source and runs entirely in your own GitHub Actions workflow. There is no hosted service, account, or telemetry. See it review a real tenant-isolation regression in [elek-demo#1](https://github.com/selimozten/elek-demo/pull/1). ## Safety model - The model gets Pi's `read`, `grep`, `find`, and `ls` tools and nothing else: no shell, no file writes, no MCP. A guard blocks paths outside the repository, `.git`, secret files, and symlink escapes. - The model never receives `GITHUB_TOKEN`. elek posts the review itself. - elek has no code path that approves, merges, closes, or pushes. Reviews post with the `COMMENT` event. - elek redacts token-shaped secrets from the review before it posts. - When the base branch has a `.elek.yml`, elek loads policy from it, so a PR cannot change the rules that review it. ## One-minute setup From your repository root, run: ```bash npx --package github:selimozten/elek elek-init --provider deepseek ``` This writes `.github/workflows/elek.yml` and `.elek.yml`. Add the repository secret `DEEPSEEK_API_KEY`, commit both files, and open a PR. To use another provider, pass `--provider` and optionally `--model`: ```bash npx --package github:selimozten/elek elek-init --provider anthropic --model claude-sonnet-4-6 npx --package github:selimozten/elek elek-init --provider openrouter --model moonshotai/kimi-k2.7-code ``` `elek-init --help` lists every flag. It refuses to overwrite existing files unless you pass `--force`. ## Manual workflow ```yaml name: elek on: pull_request: types: [opened, synchronize, reopened, ready_for_review] permissions: contents: read pull-requests: write issues: write concurrency: group: elek-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: review: # Fork PRs get no secrets and a read-only token, so elek skips them. if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v6.0.3 with: fetch-depth: 0 - uses: selimozten/elek@v1 with: deepseek_api_key: ${{ secrets.DEEPSEEK_API_KEY }} provider: deepseek model: deepseek-v4-pro thinking: high ``` `fetch-depth: 0` lets elek build the diff and read the base-branch config from the checkout. ## Inputs | Input | Default | Description | |---|---|---| | `provider` | `anthropic` | Any Pi provider, for example `deepseek`, `openrouter`, `openai`, `google`, `opencode-go`. | | `model` | provider default | Model ID, for example `deepseek-v4-pro` or `claude-sonnet-4-6`. | | `thinking` | `medium` | Pi thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`. | | `*_api_key` | none | The key for your provider. See [Providers](#providers). | | `github_token` | `${{ github.token }}` | Token elek uses to post comments. The model never sees it. | | `prompt` | built-in request | Replaces the default review request. The safety rules, review lenses, and response format still apply. | | `run_timeout_seconds` | `0` | Stop the model run after this many seconds and report a failure. `0` means no timeout. | | `config_path` | `.elek.yml` | Repository config file. Set `none`, `off`, or `false` to disable it. | | `sticky_comment` | `true` | Show live progress and the review in one tracking comment. With `false`, elek posts a PR review only. | | `severity_threshold` | from `.elek.yml` | Lowest severity to report: `critical`, `important`, or `minor`. | ## Repository config `.elek.yml` keeps review policy next to the code: ```yaml severity_threshold: important knowledge_paths: - AGENTS.md - CONTRIBUTING.md - docs/ARCHITECTURE.md ignore_paths: - docs/** - "*.md" instructions: - Treat auth and permission changes as security-sensitive. - Require tests for parser and config changes. ``` | Key | Description | |---|---| | `severity_threshold` | Lowest severity to report. The `severity_threshold` input overrides it. | | `knowledge_paths` | Repository docs to include as project context. When unset, elek tries `AGENTS.md`, `CONTRIBUTING.md`, `docs/ARCHITECTURE.md`, and `docs/adr`. elek caps the file count and size. | | `ignore_paths` | Globs whose patches elek leaves out of the prompt. elek still reports a finding in an ignored path that breaks something elsewhere. | | `instructions` | Extra review rules for your repository. | On a pull request, elek reads `.elek.yml` and the knowledge files from the base branch. If elek cannot load `.elek.yml` from the base branch, for example because the base branch has none, it uses the checked-out `.elek.yml` and knowledge files instead. Commit `.elek.yml` to your default branch so PRs cannot change their own review policy. elek fails the run when the config file has malformed YAML, so a typo cannot drop your policy without notice. Unknown keys log a warning. ## Providers elek works with any provider Pi supports. Set `provider`, `model`, and the matching key input: | Input | Environment variable | |---|---| | `anthropic_api_key` | `ANTHROPIC_API_KEY` | | `openai_api_key` | `OPENAI_API_KEY` | | `google_api_key` | `GEMINI_API_KEY` | | `deepseek_api_key` | `DEEPSEEK_API_KEY` | | `groq_api_key` | `GROQ_API_KEY` | | `mistral_api_key` | `MISTRAL_API_KEY` | | `together_api_key` | `TOGETHER_API_KEY` | | `xai_api_key` | `XAI_API_KEY` | | `openrouter_api_key` | `OPENROUTER_API_KEY` | | `opencode_api_key` | `OPENCODE_API_KEY` (OpenCode Zen and Go) | For Amazon Bedrock, set `AWS_REGION`, `AWS_ACCESS_KEY_ID`, and `AWS_SECRET_ACCESS_KEY` as job-level `env`. For Vertex, set `GOOGLE_APPLICATION_CREDENTIALS` and `ANTHROPIC_VERTEX_PROJECT_ID`. ## Outputs | Output | Description | |---|---| | `conclusion` | `success`, `failure`, or `skipped`. | | `comment_id` | ID of the tracking comment. | | `summary` | First 1,000 characters of the posted review. | | `input_tokens` | Input tokens the review used. | | `output_tokens` | Output tokens the review used. | ## Permissions ```yaml permissions: contents: read pull-requests: write issues: write ``` `contents: read` is enough because elek never pushes. `pull-requests: write` posts the review and inline comments. `issues: write` posts the tracking comment. ## FAQ **Does elek review pull requests from forks?** No. GitHub gives fork `pull_request` runs no repository secrets and a read-only token, so elek has no model key and cannot post. The workflow above skips forks. Without that guard, elek exits with a `skipped` conclusion. Do not switch to `pull_request_target` to get secrets: that event runs with your secrets while the PR author controls the code. **What about draft pull requests?** elek skips drafts and reviews the PR when you mark it ready for review. **Which events does elek handle?** Only `pull_request` with the `opened`, `synchronize`, `reopened`, or `ready_for_review` action. Other events end with a warning and a `skipped` conclusion. elek also skips runs that a bot triggers or that a user who is not a repository owner, member, or collaborator triggers. **What does a review cost?** elek prints the token counts in the review footer and in the `input_tokens` and `output_tokens` outputs. Multiply them by your provider's prices. When the provider reports no usage, elek estimates the counts from the prompt and response length and labels the line as estimated. **What does the model check?** Every review applies two lenses. The risk lens looks for bugs, security holes, breaking behavior, and side effects. The design lens looks for structural regressions and needless complexity. The model then drops findings it cannot verify against the changed code. `prompt` changes the request but not these rules. **Why are comments from `github-actions[bot]`?** The default `GITHUB_TOKEN` posts as that account. If you pass a GitHub App or bot token as `github_token`, set the job-level env `ELEK_TRACKING_COMMENT_AUTHORS` to that login so elek reuses its own tracking comment on later pushes. ## Documentation - [`docs/setup.md`](docs/setup.md) explains setup in more detail and covers troubleshooting. - [`docs/examples.md`](docs/examples.md) has workflow recipes. - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) describes how a review run works. - [`CONTRIBUTING.md`](CONTRIBUTING.md) and [`AGENTS.md`](AGENTS.md) are for people and agents working on elek. ## License MIT. Built on [Pi](https://github.com/earendil-works/pi).