@mrkaran/hodor 0.4.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,100 +2,166 @@
2
2
 
3
3
  # Hodor
4
4
 
5
- > Agentic code reviewer for GitHub PRs, GitLab MRs, and local diffs. Powered by the [pi-coding-agent](https://github.com/badlogic/pi-mono) SDK.
5
+ > Agentic code reviewer for GitHub PRs, GitLab MRs, Gitea/Forgejo PRs, and local diffs. Powered by the [pi-coding-agent](https://github.com/badlogic/pi-mono) SDK.
6
6
 
7
7
  Hodor runs as a stateful agent with tools (`bash`, `grep`, `read`, `git diff`) to autonomously analyze code changes, find bugs, and post structured reviews.
8
8
 
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- # npx (zero install, always latest)
12
+ # Just run it (zero install, always latest)
13
13
  npx @mrkaran/hodor <PR_URL>
14
14
 
15
- # Global install
15
+ # Or install globally
16
16
  npm install -g @mrkaran/hodor
17
+ ```
18
+
19
+ Docker images are also available at `ghcr.io/mr-karan/hodor:latest` for CI environments.
17
20
 
18
- # Docker
19
- docker pull ghcr.io/mr-karan/hodor:latest
21
+ ## Setup
20
22
 
21
- # From source
22
- git clone https://github.com/mr-karan/hodor && cd hodor
23
- bun install && bun run build
23
+ ```bash
24
+ # Set an API key for your LLM provider
25
+ export ANTHROPIC_API_KEY=sk-... # Anthropic (default)
26
+ export OPENAI_API_KEY=sk-... # OpenAI
27
+ export OPENROUTER_API_KEY=sk-or-... # OpenRouter (e.g., Kimi K2.6)
28
+ export AWS_PROFILE=default # AWS Bedrock (no API key needed)
29
+
30
+ # For posting reviews as comments
31
+ gh auth login # GitHub
32
+ glab auth login # GitLab
33
+
34
+ # For Gitea/Forgejo private repos or posting comments
35
+ export GITEA_TOKEN=your-token # or FORGEJO_TOKEN
24
36
  ```
25
37
 
26
38
  ## Usage
27
39
 
28
40
  ```bash
29
41
  # Review a GitHub PR
30
- hodor https://github.com/owner/repo/pull/123
42
+ npx @mrkaran/hodor https://github.com/owner/repo/pull/123
31
43
 
32
44
  # Review a GitLab MR (including self-hosted)
33
- hodor https://gitlab.example.com/org/project/-/merge_requests/42
45
+ npx @mrkaran/hodor https://gitlab.example.com/org/project/-/merge_requests/42
34
46
 
35
- # Post the review as a comment
36
- hodor <PR_URL> --post
47
+ # Review a Gitea or Forgejo PR
48
+ npx @mrkaran/hodor https://git.example.com/owner/repo/pulls/123
37
49
 
38
- # Review local changes (no PR URL needed)
39
- hodor --local # diff against origin/main
40
- hodor --local --diff-against HEAD~1 # diff against specific ref
41
- hodor --local --diff-against feature-branch
50
+ # Post the review as a PR/MR comment
51
+ npx @mrkaran/hodor <PR_URL> --post
42
52
 
43
53
  # Use a different model
44
- hodor <PR_URL> --model openai/gpt-5
45
- hodor <PR_URL> --model bedrock/converse/anthropic.claude-sonnet-4-5-v2
54
+ npx @mrkaran/hodor <PR_URL> --model openai/gpt-5
55
+ npx @mrkaran/hodor <PR_URL> --model bedrock/converse/anthropic.claude-sonnet-4-5-v2
46
56
 
47
57
  # Extended reasoning for complex PRs
48
- hodor <PR_URL> --reasoning-effort high
49
- hodor <PR_URL> --ultrathink
58
+ npx @mrkaran/hodor <PR_URL> --reasoning-effort high
50
59
 
51
60
  # Custom review instructions
52
- hodor <PR_URL> --prompt "Focus on SQL injection and auth bypasses"
53
- hodor <PR_URL> --prompt-file .hodor/security-review.md
61
+ npx @mrkaran/hodor <PR_URL> --prompt "Focus on SQL injection and auth bypasses"
54
62
 
55
63
  # Verbose mode (watch the agent think)
56
- hodor <PR_URL> -v
64
+ npx @mrkaran/hodor <PR_URL> -v
57
65
  ```
58
66
 
59
- **Docker:**
67
+ > If you installed globally with `npm install -g`, replace `npx @mrkaran/hodor` with `hodor`.
68
+
69
+ ## Local Mode
70
+
71
+ Review local git changes without a PR URL. Useful for pre-push reviews, Bitbucket PRs, or any git repo.
72
+
60
73
  ```bash
61
- docker run --rm \
62
- -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
63
- -e GITHUB_TOKEN=$GITHUB_TOKEN \
64
- ghcr.io/mr-karan/hodor:latest \
65
- https://github.com/owner/repo/pull/123 --post
74
+ # Review uncommitted changes against origin/main (default)
75
+ npx @mrkaran/hodor --local
76
+
77
+ # Review against a specific branch or ref
78
+ npx @mrkaran/hodor --local --diff-against develop
79
+ npx @mrkaran/hodor --local --diff-against HEAD~3
80
+
81
+ # Review a feature branch against main
82
+ git checkout feature-branch
83
+ npx @mrkaran/hodor --local --diff-against origin/main
84
+
85
+ # Use a specific workspace directory
86
+ npx @mrkaran/hodor --local --workspace /path/to/repo
87
+
88
+ # Combine with other flags
89
+ npx @mrkaran/hodor --local --diff-against origin/main --model openai/gpt-5 -v
66
90
  ```
67
91
 
68
- ## Configuration
92
+ Local mode:
93
+ - Includes **uncommitted changes** (staged + unstaged), not just commits
94
+ - Auto-resolves to the **git repo root** (works from subdirectories)
95
+ - Skips PR metadata fetching and workspace cloning
96
+ - `--post` is disabled (no remote to post to)
69
97
 
70
- ### CLI Flags
98
+ ## CLI Flags
71
99
 
72
100
  | Flag | Default | Description |
73
101
  |------|---------|-------------|
74
- | `--model` | `anthropic/claude-sonnet-4-5` | LLM model (Anthropic, OpenAI, or Bedrock) |
102
+ | `--model` | `anthropic/claude-sonnet-4-5-20250929` | LLM model as `provider/model-id`. Recommended: Anthropic, OpenAI, Bedrock, OpenRouter. Other pi-ai providers (e.g., Mistral, Gemini, xAI, Groq) are best-effort. See [docs/MODELS.md](./docs/MODELS.md). |
75
103
  | `--reasoning-effort` | – | Extended thinking: `low`, `medium`, `high` |
76
104
  | `--ultrathink` | Off | Maximum reasoning effort |
77
- | `--local` | Off | Review local changes (no PR URL required) |
78
- | `--diff-against` | `origin/main` | Git ref to diff against in local mode |
79
- | `--post` | Off | Post review comment to GitHub/GitLab |
105
+ | `--local` | Off | Review local git changes (no PR URL required) |
106
+ | `--diff-against` | `origin/main` | Git ref to diff against in `--local` mode |
107
+ | `--post` | Off | Post review as a comment on the PR/MR |
108
+ | `--review-style` | `hybrid` | GitLab posting style: `summary`, `inline`, or `hybrid` |
109
+ | `--code-quality` | – | Write a CodeClimate JSON artifact for GitLab code quality reports |
110
+ | `--commit-status` | Off | Post a pass/fail commit status to the GitLab MR head SHA |
80
111
  | `--prompt` | – | Append custom instructions to the review prompt |
81
- | `--prompt-file` | – | Replace the review prompt entirely |
82
- | `--workspace` | Temp dir | Workspace directory (re-use for faster multi-PR reviews) |
112
+ | `--prompt-file` | – | Use a custom prompt file |
113
+ | `--workspace` | Temp dir | Workspace directory (reuse for faster multi-PR reviews) |
83
114
  | `--bedrock-tags` | – | JSON cost allocation tags for AWS Bedrock |
84
- | `--prometheus-push` | – | Push metrics to a Prometheus Pushgateway URL |
115
+ | `--prometheus-push` | – | Push review metrics to a Prometheus Pushgateway or VictoriaMetrics import endpoint |
85
116
  | `-v, --verbose` | Off | Stream agent reasoning and tool calls |
86
117
 
87
- ### Environment Variables
118
+ ## Environment Variables
88
119
 
89
120
  | Variable | Purpose |
90
121
  |----------|---------|
91
122
  | `ANTHROPIC_API_KEY` | Claude API key |
92
123
  | `OPENAI_API_KEY` | OpenAI API key |
93
- | `LLM_API_KEY` | Generic fallback (used when provider-specific key is not set) |
94
- | `GITHUB_TOKEN` / `GITLAB_TOKEN` | Post comments to PRs/MRs (only with `--post`) |
124
+ | `OPENROUTER_API_KEY` | OpenRouter API key (for `openrouter/...` models, e.g. `openrouter/moonshotai/kimi-k2.6`) |
125
+ | Provider-specific keys | For best-effort pi-ai providers, use the env var pi-ai expects (e.g. `MISTRAL_API_KEY`, `GEMINI_API_KEY`, `XAI_API_KEY`, `GROQ_API_KEY`) |
126
+ | `LLM_API_KEY` | Generic fallback (when provider-specific key is not set) |
127
+ | `GITHUB_TOKEN` / `GITLAB_TOKEN` | Post comments to GitHub PRs / GitLab MRs (with `--post`) |
128
+ | `GITEA_TOKEN` / `FORGEJO_TOKEN` | Read private repos and post comments on Gitea/Forgejo PRs |
129
+ | `GITEA_HOST` / `FORGEJO_HOST` | Hostname for Gitea/Forgejo when not inferable from a full PR URL |
95
130
  | `AWS_PROFILE` or `AWS_ACCESS_KEY_ID` | AWS Bedrock auth (no API key needed) |
96
131
 
132
+ See [docs/MODELS.md](./docs/MODELS.md) for the full model/provider matrix and [docs/OPENROUTER.md](./docs/OPENROUTER.md) for an end-to-end Kimi K2.6 example.
133
+
134
+ ## Gitea / Forgejo
135
+
136
+ Hodor supports Gitea and Forgejo pull request URLs in this format:
137
+
138
+ ```bash
139
+ npx @mrkaran/hodor https://git.example.com/owner/repo/pulls/123
140
+ ```
141
+
142
+ For public repositories, metadata fetching may work without a token. Set `GITEA_TOKEN` or `FORGEJO_TOKEN` for private repositories, higher API limits, and `--post`:
143
+
144
+ ```bash
145
+ export GITEA_TOKEN=your-token
146
+ npx @mrkaran/hodor https://git.example.com/owner/repo/pulls/123 --post
147
+ ```
148
+
149
+ Fork PRs are checked out from the PR source repository when Gitea exposes the source clone URL. If the source branch or fork has been deleted, checkout will fail with a workspace error.
150
+
97
151
  ## CI/CD
98
152
 
153
+ ### Metrics in CI
154
+
155
+ Hodor can push per-review metrics at the end of a CI run to either a Prometheus Pushgateway base URL or a VictoriaMetrics Prometheus import endpoint (`/api/v1/import/prometheus`):
156
+
157
+ ```bash
158
+ hodor "$MR_OR_PR_URL" --prometheus-push "$METRICS_PUSH_URL"
159
+ ```
160
+
161
+ In CI, set `METRICS_PUSH_URL` as a secret/variable and add `--prometheus-push "$METRICS_PUSH_URL"` to the Hodor command. Metrics are best-effort: push failures are logged as warnings and do not fail the review job.
162
+
163
+ Each metric is labeled with `platform`, `model`, `verdict`, and for PR/MR URLs also `project` (`owner/repo`) and `mr_iid`/PR number. Exported metrics include token usage, cache read/write tokens, cache hit ratio, cost, turns, tool calls, duration, and findings by priority (`P0`–`P3`). A generic Grafana dashboard is available in [`docs/grafana/`](./docs/grafana/).
164
+
99
165
  ### GitHub Actions
100
166
 
101
167
  ```yaml
@@ -113,42 +179,66 @@ jobs:
113
179
  env:
114
180
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
115
181
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
182
+ METRICS_PUSH_URL: ${{ secrets.METRICS_PUSH_URL }} # optional
116
183
  run: |
117
- hodor "https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}" --post
184
+ EXTRA_ARGS=""
185
+ if [ -n "${METRICS_PUSH_URL:-}" ]; then EXTRA_ARGS="--prometheus-push $METRICS_PUSH_URL"; fi
186
+ bun run /app/dist/cli.js "https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}" --post $EXTRA_ARGS
118
187
  ```
119
188
 
120
189
  ### GitLab CI
121
190
 
122
191
  ```yaml
123
- include:
124
- - project: 'commons/gitlab-templates'
125
- ref: master
126
- file: '/hodor/.gitlab-ci-template.yml'
192
+ # .gitlab-ci.yml
193
+ workflow:
194
+ rules:
195
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
127
196
 
128
197
  hodor-review:
129
- extends: .hodor-review
198
+ stage: test
199
+ image:
200
+ name: ghcr.io/mr-karan/hodor:latest
201
+ entrypoint: [""]
202
+ variables:
203
+ HODOR_MODEL: "anthropic/claude-sonnet-4-5-20250929"
204
+ before_script:
205
+ - glab auth login --hostname $CI_SERVER_HOST --token $GITLAB_TOKEN
206
+ script:
207
+ - MR_URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
208
+ - |
209
+ EXTRA_ARGS=""
210
+ if [ -n "${METRICS_PUSH_URL:-}" ]; then EXTRA_ARGS="--prometheus-push $METRICS_PUSH_URL"; fi
211
+ bun run /app/dist/cli.js "$MR_URL" --model "$HODOR_MODEL" --post --code-quality gl-code-quality-report.json --commit-status $EXTRA_ARGS
212
+ artifacts:
213
+ reports:
214
+ codequality: gl-code-quality-report.json
215
+ when: always
216
+ allow_failure: true
217
+ timeout: 15m
130
218
  ```
131
219
 
220
+ This posts inline comments on the diff, a summary note, a pass/fail commit status, and a code quality report visible in the MR widget.
221
+
132
222
  See [AUTOMATED_REVIEWS.md](./docs/AUTOMATED_REVIEWS.md) for advanced workflows.
133
223
 
134
224
  ## Token Optimization
135
225
 
136
226
  Hodor automatically optimizes token usage:
137
227
 
138
- - **Diff embedding**: For PRs under 200KB, the diff is embedded directly in the prompt, cutting turns from ~60 to ~5.
139
- - **Incremental reviews**: On re-runs, only reviews changes since the last hodor comment (detected via SHA markers).
140
- - **Compaction**: SDK auto-summarizes older turns when context grows too large.
228
+ - **Diff embedding**: For PRs under 200KB, the diff is embedded directly in the prompt, cutting agent turns from ~60 to ~5.
229
+ - **Incremental reviews**: On re-runs, only reviews changes since the last hodor comment (detected via SHA markers in posted comments).
230
+ - **Compaction**: SDK auto-summarizes older conversation turns when context grows too large.
141
231
 
142
232
  ## Skills
143
233
 
144
- Hodor discovers repository-specific review guidelines from `.pi/skills/` or `.hodor/skills/`. Create a skill file to enforce conventions:
234
+ Hodor discovers repository-specific review guidelines from `.agents/skills/`, the cross-client Agent Skills convention:
145
235
 
146
236
  ```bash
147
- mkdir -p .hodor/skills/review-guidelines
237
+ mkdir -p .agents/skills/review-guidelines
148
238
  ```
149
239
 
150
240
  ```markdown
151
- # .hodor/skills/review-guidelines/SKILL.md
241
+ # .agents/skills/review-guidelines/SKILL.md
152
242
  ---
153
243
  name: review-guidelines
154
244
  description: Security and performance review checklist.
@@ -170,6 +260,40 @@ bun run test # Run tests
170
260
  bun run dev -- <url> # Run from source
171
261
  ```
172
262
 
263
+ ---
264
+
265
+ ## Architecture
266
+
267
+ Hodor is written in TypeScript and runs on [Bun](https://bun.sh). Key components:
268
+
269
+ | Module | Purpose |
270
+ |--------|---------|
271
+ | `src/cli.ts` | Commander.js CLI entry point |
272
+ | `src/agent.ts` | Core review orchestration, URL parsing, comment posting |
273
+ | `src/workspace.ts` | CI detection, repo cloning, branch checkout |
274
+ | `src/prompt.ts` | Prompt template building and interpolation |
275
+ | `src/model.ts` | Model string parsing, API key resolution |
276
+ | `src/gitlab.ts` | GitLab API via `glab` CLI (comments, inline notes, draft notes, commit status) |
277
+ | `src/github.ts` | GitHub API via `gh` CLI |
278
+ | `src/render.ts` | JSON review output → markdown rendering |
279
+ | `src/codequality.ts` | CodeClimate JSON artifact for GitLab code quality widget |
280
+ | `src/metrics.ts` | Token usage and cost formatting |
281
+ | `templates/` | Review prompt template (JSON schema) |
282
+
283
+ The agent runtime is provided by [`@mariozechner/pi-coding-agent`](https://github.com/badlogic/pi-mono) with [`@mariozechner/pi-ai`](https://github.com/badlogic/pi-mono) for LLM access. The agent session gets read-only tools (bash, read, grep, find, ls) and a review prompt, then autonomously analyzes the PR.
284
+
285
+ ---
286
+
287
+ ## Learn More
288
+
289
+ ### Hodor Documentation
290
+ - **[SKILLS.md](./docs/SKILLS.md)** - Creating repository-specific review guidelines
291
+ - **[AUTOMATED_REVIEWS.md](./docs/AUTOMATED_REVIEWS.md)** - Advanced CI/CD workflows
292
+
293
+ ### Contributing
294
+ Found a bug? Want to add a feature? Open an issue at https://github.com/mr-karan/hodor/issues.
295
+
296
+ ---
173
297
  ## License
174
298
 
175
299
  MIT
@@ -0,0 +1,23 @@
1
+ // src/utils/path.ts
2
+ function relativizeWorkspacePath(absolutePath, workspacePrefix) {
3
+ let filePath = absolutePath;
4
+ const prefix = workspacePrefix ?? process.env.CI_PROJECT_DIR;
5
+ if (prefix) {
6
+ const trimmed = prefix.replace(/\/+$/, "");
7
+ if (filePath.startsWith(`${trimmed}/`)) {
8
+ return filePath.slice(trimmed.length + 1);
9
+ }
10
+ }
11
+ const buildsMatch = filePath.match(/\/builds\/[^/]+\/[^/]+\/(.+)/);
12
+ if (buildsMatch) return buildsMatch[1];
13
+ if (filePath.includes("/workspace/")) {
14
+ return filePath.slice(filePath.indexOf("/workspace/") + "/workspace/".length);
15
+ }
16
+ const stripped = filePath.replace(/^.*\/hodor-review-[^/]+\//, "");
17
+ return stripped !== filePath ? stripped : filePath;
18
+ }
19
+
20
+ export {
21
+ relativizeWorkspacePath
22
+ };
23
+ //# sourceMappingURL=chunk-AMUK6GDX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/path.ts"],"sourcesContent":["/**\n * Strip workspace/CI prefixes from an absolute path so it becomes repo-relative.\n *\n * Used both for inline-comment paths posted to GitLab (must match the diff path)\n * and for the CodeClimate code quality artifact (GitLab compares against repo\n * paths, not the temp-dir absolute paths Hodor sees during a review).\n *\n * Honors `CI_PROJECT_DIR` first when set (GitLab CI sets it to the checkout root).\n * Otherwise, falls through generic patterns: GitLab `/builds/<group>/<project>/...`,\n * a `/workspace/...` segment, and Hodor's own `/tmp/hodor-review-<id>/...` temp dirs.\n */\nexport function relativizeWorkspacePath(absolutePath: string, workspacePrefix?: string): string {\n let filePath = absolutePath;\n\n const prefix = workspacePrefix ?? process.env.CI_PROJECT_DIR;\n if (prefix) {\n const trimmed = prefix.replace(/\\/+$/, \"\");\n if (filePath.startsWith(`${trimmed}/`)) {\n return filePath.slice(trimmed.length + 1);\n }\n }\n\n const buildsMatch = filePath.match(/\\/builds\\/[^/]+\\/[^/]+\\/(.+)/);\n if (buildsMatch) return buildsMatch[1];\n\n if (filePath.includes(\"/workspace/\")) {\n return filePath.slice(filePath.indexOf(\"/workspace/\") + \"/workspace/\".length);\n }\n\n const stripped = filePath.replace(/^.*\\/hodor-review-[^/]+\\//, \"\");\n return stripped !== filePath ? stripped : filePath;\n}\n"],"mappings":";AAWO,SAAS,wBAAwB,cAAsB,iBAAkC;AAC9F,MAAI,WAAW;AAEf,QAAM,SAAS,mBAAmB,QAAQ,IAAI;AAC9C,MAAI,QAAQ;AACV,UAAM,UAAU,OAAO,QAAQ,QAAQ,EAAE;AACzC,QAAI,SAAS,WAAW,GAAG,OAAO,GAAG,GAAG;AACtC,aAAO,SAAS,MAAM,QAAQ,SAAS,CAAC;AAAA,IAC1C;AAAA,EACF;AAEA,QAAM,cAAc,SAAS,MAAM,8BAA8B;AACjE,MAAI,YAAa,QAAO,YAAY,CAAC;AAErC,MAAI,SAAS,SAAS,aAAa,GAAG;AACpC,WAAO,SAAS,MAAM,SAAS,QAAQ,aAAa,IAAI,cAAc,MAAM;AAAA,EAC9E;AAEA,QAAM,WAAW,SAAS,QAAQ,6BAA6B,EAAE;AACjE,SAAO,aAAa,WAAW,WAAW;AAC5C;","names":[]}