@weareikko/code-review 0.8.2 → 0.8.4
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 +60 -38
- package/dist/{cli-DBkaU14C.js → cli-FDpPZaNn.js} +125 -19
- package/dist/cli-FDpPZaNn.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/github.d.ts +15 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/gitlab-review.d.ts +6 -0
- package/dist/gitlab-review.d.ts.map +1 -1
- package/dist/platforms/github.d.ts +5 -1
- package/dist/platforms/github.d.ts.map +1 -1
- package/dist/posting.d.ts +5 -0
- package/dist/posting.d.ts.map +1 -1
- package/dist/review.js +1 -1
- package/package.json +1 -1
- package/dist/cli-DBkaU14C.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,15 +5,19 @@
|
|
|
5
5
|
[](https://bundlephobia.com/package/@weareikko/code-review)
|
|
6
6
|

|
|
7
7
|
|
|
8
|
-
Run an agent-driven code review
|
|
8
|
+
Run an agent-driven code review on **GitLab merge requests and GitHub pull requests** — the same review engine on both. It parses inline comments, posts deduplicated review discussions, upserts a summary, and reports per-run token usage and cost. The platform is auto-detected from the environment (GitLab CI vs. GitHub Actions) and can be forced with `--platform github|gitlab`.
|
|
9
9
|
|
|
10
|
-
The reviewer reads the
|
|
10
|
+
The reviewer reads the merge/pull request **title and description** as the author's declared intent: it reads the diff against the stated purpose and surfaces code/intent mismatches (the change does something the description never claimed, or omits something it promised) as a summary note. A missing or empty description degrades gracefully — the review still runs.
|
|
11
11
|
|
|
12
12
|
## Requirements
|
|
13
13
|
|
|
14
14
|
- Node.js `>=24`
|
|
15
|
-
- `git` available in the runtime
|
|
16
|
-
- A
|
|
15
|
+
- `git` available in the runtime (full history — the review diffs against the merge base)
|
|
16
|
+
- A run in one of the two supported contexts, which the tool auto-detects:
|
|
17
|
+
- **GitLab CI** in a merge-request pipeline (`GITLAB_CI`, `CI_PROJECT_ID`, `CI_MERGE_REQUEST_IID`), or
|
|
18
|
+
- **GitHub Actions** on a `pull_request` event (`GITHUB_ACTIONS`, `GITHUB_REPOSITORY`, the PR number)
|
|
19
|
+
|
|
20
|
+
Detection precedence: an explicit `--platform github|gitlab` (or `CODE_REVIEW_PLATFORM`) always wins; otherwise `GITHUB_ACTIONS=true` selects GitHub and `GITLAB_CI=true` (or a present `CI_PROJECT_ID` / `CI_SERVER_URL`) selects GitLab; failing that, the tool infers the platform from whichever side's identifiers are present.
|
|
17
21
|
|
|
18
22
|
## Install / Run
|
|
19
23
|
|
|
@@ -79,13 +83,17 @@ review:
|
|
|
79
83
|
- review-usage.json
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
## GitHub Actions
|
|
86
|
+
## GitHub Actions example
|
|
87
|
+
|
|
88
|
+
On GitHub the same engine reviews **pull requests** — auto-detected from the Actions environment (`GITHUB_ACTIONS`, `GITHUB_REPOSITORY`, the `pull_request` event); pass `--platform github` to force it. Findings post as one batched PR review with inline comments plus an upserted summary comment.
|
|
83
89
|
|
|
84
|
-
|
|
90
|
+
### Composite action (recommended)
|
|
85
91
|
|
|
86
|
-
|
|
92
|
+
The bundled composite action is the primary path: it checks out the repository, sets up Node, installs the CLI, and runs the review in one step. It needs:
|
|
87
93
|
|
|
88
|
-
|
|
94
|
+
- **Permissions:** `pull-requests: write` (to post the review and summary) and `contents: read` (to check out the code). The default `GITHUB_TOKEN` is enough; the action reads it as `${{ github.token }}` by default.
|
|
95
|
+
- **Full git history:** the action checks out the repository by default with `fetch-depth: 0` so the merge-base diff and commit log resolve (tune with `fetch-depth`, or set `checkout: false` and check out yourself first).
|
|
96
|
+
- **A model + its key:** pass the model via the `model` input and the provider's key via the `api-key` input (or expose the provider's standard env var, e.g. `ANTHROPIC_API_KEY`, to the step).
|
|
89
97
|
|
|
90
98
|
```yml
|
|
91
99
|
name: code-review
|
|
@@ -98,19 +106,32 @@ permissions:
|
|
|
98
106
|
|
|
99
107
|
jobs:
|
|
100
108
|
review:
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
steps:
|
|
111
|
+
# Checkout (full history) is bundled — no separate checkout step needed.
|
|
112
|
+
# Opt out with `checkout: false` if your job already checked out the code.
|
|
113
|
+
- uses: weareikko/code-review@0.8 # moving minor tag — auto patch updates (see Pinning below)
|
|
114
|
+
with:
|
|
115
|
+
model: anthropic/claude-sonnet-4-5
|
|
116
|
+
api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
117
|
+
# github-token defaults to ${{ github.token }}
|
|
118
|
+
# args: --min-severity warn --dry-run
|
|
103
119
|
```
|
|
104
120
|
|
|
105
|
-
|
|
121
|
+
Inputs: `model` (required), `api-key`, `github-token` (default `${{ github.token }}`), `version` (npm dist-tag/version, default `latest`), `node-version` (default `24`), `working-directory`, `args` (extra CLI flags forwarded verbatim), `checkout` (default `true` — bundled repository checkout), and `fetch-depth` (default `0` — full history, required for the merge-base diff).
|
|
106
122
|
|
|
107
|
-
|
|
123
|
+
Because the composite action references your secrets directly (`${{ secrets.ANTHROPIC_API_KEY }}`), it works from **any** repository, including consumers in a different organization from this one.
|
|
108
124
|
|
|
109
|
-
|
|
125
|
+
> **Pinning the ref.** Two moving tags are maintained, each re-pointed to the latest release it covers:
|
|
126
|
+
>
|
|
127
|
+
> - `@0.8` — **minor series**: newest `0.8.x`, patches only. Because a `0.x` minor bump marks a breaking change, this is the non-breaking channel and the recommended pin while the project is pre-1.0.
|
|
128
|
+
> - `@0` — **major series**: newest stable release. **Caveat:** in 0.x a minor bump _is_ a breaking change, so `@0` may advance across breaking releases (`0.8 → 0.9`); it becomes a true semver compatibility boundary only once `1.0` ships.
|
|
129
|
+
>
|
|
130
|
+
> For a frozen build, pin an exact patch (`@0.8.3`) or a commit SHA (immutable; strongest supply-chain posture); to track the tip, use `@main`. GitHub `uses:` refs do not support wildcards, so `@0.8.x` is not valid — use a moving tag instead.
|
|
110
131
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
132
|
+
### Reusable workflow (same org/enterprise)
|
|
133
|
+
|
|
134
|
+
The bundled reusable workflow lets a caller enable reviews with no `steps:` of its own — it checks out the code, installs the CLI, and runs the review for you:
|
|
114
135
|
|
|
115
136
|
```yml
|
|
116
137
|
name: code-review
|
|
@@ -123,24 +144,23 @@ permissions:
|
|
|
123
144
|
|
|
124
145
|
jobs:
|
|
125
146
|
review:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
# Checkout (full history) is bundled — opt out with `checkout: false` if
|
|
129
|
-
# your job already checked out the code with its own options.
|
|
130
|
-
- uses: weareikko/code-review@main # pin to a release tag in production
|
|
131
|
-
with:
|
|
132
|
-
model: anthropic/claude-sonnet-4-5
|
|
133
|
-
api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
134
|
-
# github-token defaults to ${{ github.token }}
|
|
135
|
-
# args: --min-severity warn --dry-run
|
|
147
|
+
uses: weareikko/code-review/.github/workflows/code-review.yml@0.8 # moving minor tag — auto patch updates
|
|
148
|
+
secrets: inherit
|
|
136
149
|
```
|
|
137
150
|
|
|
138
|
-
|
|
151
|
+
It reads review settings from repo/org **variables** (`CODE_REVIEW_MODEL`, `CODE_REVIEW_DEPTH`, `CODE_REVIEW_THINKING_LEVEL`, `CODE_REVIEW_VERIFY_MODEL`) and provider credentials from **secrets** named with the `CODE_REVIEW_` prefix (e.g. `CODE_REVIEW_ANTHROPIC_API_KEY`), which the CLI's env shim de-prefixes for the provider. Optional `with:` inputs: `model` (overrides the `CODE_REVIEW_MODEL` variable), `version`, `node-version`, `working-directory`, `args`, and `runs-on`.
|
|
139
152
|
|
|
140
|
-
|
|
153
|
+
> **Caveat — `secrets: inherit` is same-organization (or enterprise) only.** Organization secrets are **not** inherited across organizations, so this pattern only works when the caller repository lives in the same org (or enterprise) as `weareikko/code-review`. Cross-organization consumers must use the **composite action** above, which references their own secrets directly. (The reusable workflow relies on `secrets: inherit` and declares no `workflow_call.secrets`, so there is no explicit-secrets path for it.)
|
|
154
|
+
|
|
155
|
+
### Running the CLI directly
|
|
156
|
+
|
|
157
|
+
Prefer no action at all? `GITHUB_TOKEN`, `GITHUB_REPOSITORY`, and the PR number are read straight from the Actions environment. Check out the code first (full history) so the merge-base diff resolves:
|
|
141
158
|
|
|
142
159
|
```yml
|
|
143
|
-
- uses: actions/
|
|
160
|
+
- uses: actions/checkout@v5
|
|
161
|
+
with:
|
|
162
|
+
fetch-depth: 0
|
|
163
|
+
- uses: actions/setup-node@v5
|
|
144
164
|
with:
|
|
145
165
|
node-version: 24
|
|
146
166
|
- run: npx @weareikko/code-review
|
|
@@ -159,17 +179,17 @@ The README covers getting started. Reference material lives in [`docs/`](https:/
|
|
|
159
179
|
- [Skills](https://github.com/weareikko/code-review/blob/main/docs/skills.md) — built-in, external (`npm:`/`file:`/`git:`), and project auto-discovered review skills.
|
|
160
180
|
- [Multi-stage review](https://github.com/weareikko/code-review/blob/main/docs/multi-stage-review.md) — the staged Find / Verify / Synthesize pipeline behind `--review-depth`.
|
|
161
181
|
- [Observability](https://github.com/weareikko/code-review/blob/main/docs/observability.md) — diagnostics-channel tracing and the opt-in OpenTelemetry bridge (spans, metrics, logs).
|
|
162
|
-
- [Output format](https://github.com/weareikko/code-review/blob/main/docs/output-format.md) — inline-comment shape,
|
|
182
|
+
- [Output format](https://github.com/weareikko/code-review/blob/main/docs/output-format.md) — inline-comment shape, the upserted summary (a note on GitLab, an issue comment on GitHub), footer, and duplicate prevention.
|
|
163
183
|
|
|
164
184
|
## Configuration
|
|
165
185
|
|
|
166
|
-
The CLI auto-resolves most values from GitLab CI variables and provider-standard env vars. The two things you must provide are a model and its provider's API key:
|
|
186
|
+
The CLI auto-resolves most values from the CI environment (GitLab CI variables or the GitHub Actions context) and provider-standard env vars. The two things you must provide are a model and its provider's API key:
|
|
167
187
|
|
|
168
188
|
```bash
|
|
169
189
|
code-review --model anthropic/claude-sonnet-4-5 --api-key "$ANTHROPIC_API_KEY"
|
|
170
190
|
```
|
|
171
191
|
|
|
172
|
-
Equivalently, set `CODE_REVIEW_MODEL` and the provider's key (e.g. `ANTHROPIC_API_KEY`) as CI/CD variables. Common knobs include `--min-severity`, `--thinking`, `--posting-mode draft`, `--no-summary`, and `--dry-run`. See the full [environment-variable and flag reference](https://github.com/weareikko/code-review/blob/main/docs/configuration.md).
|
|
192
|
+
Equivalently, set `CODE_REVIEW_MODEL` and the provider's key (e.g. `ANTHROPIC_API_KEY`) as CI/CD variables (GitLab) or repository/organization variables and secrets (GitHub). Common knobs include `--min-severity`, `--thinking`, `--posting-mode draft`, `--no-summary`, and `--dry-run`. See the full [environment-variable and flag reference](https://github.com/weareikko/code-review/blob/main/docs/configuration.md).
|
|
173
193
|
|
|
174
194
|
## Providers
|
|
175
195
|
|
|
@@ -182,7 +202,7 @@ Equivalently, set `CODE_REVIEW_MODEL` and the provider's key (e.g. `ANTHROPIC_AP
|
|
|
182
202
|
- parsed comment payload
|
|
183
203
|
- computed fingerprints
|
|
184
204
|
- duplicate status
|
|
185
|
-
- final GitLab discussion payload
|
|
205
|
+
- final platform-specific posting payload (a GitLab discussion payload, or a GitHub review-comment payload)
|
|
186
206
|
- `review-usage.json`: token and cost breakdown for the run (`tokens.{input,output,cacheRead,cacheWrite,total}`, `cost.{input,output,cacheRead,cacheWrite,total}`, `model`)
|
|
187
207
|
|
|
188
208
|
The CLI also prints a one-line summary at the end of the run:
|
|
@@ -198,15 +218,17 @@ Use these files for CI debugging and auditing.
|
|
|
198
218
|
- **`Node.js >=24 is required`**
|
|
199
219
|
- Use `node:24` (or newer) in CI.
|
|
200
220
|
- **`Missing required configuration`**
|
|
201
|
-
- Provide required flags or ensure
|
|
221
|
+
- Provide required flags or ensure the platform's identifiers/token are available: on GitLab `CI_PROJECT_ID`, `CI_MERGE_REQUEST_IID`, and a GitLab token; on GitHub `GITHUB_REPOSITORY`, the PR number, and `GITHUB_TOKEN`. A model and its API key are required on both.
|
|
222
|
+
- **`Could not detect the review platform` / `Ambiguous review platform`**
|
|
223
|
+
- Set `--platform github|gitlab` (or `CODE_REVIEW_PLATFORM`) to force the platform.
|
|
202
224
|
- **`--min-severity must be one of: info, warn, critical`**
|
|
203
225
|
- Fix `--min-severity` or `CODE_REVIEW_MIN_SEVERITY`.
|
|
204
226
|
- **Git history errors / merge-base failures**
|
|
205
|
-
-
|
|
227
|
+
- Fetch full history: `GIT_DEPTH: 0` on GitLab, `fetch-depth: 0` on `actions/checkout` (the composite action does this by default).
|
|
206
228
|
- Ensure source and target branches are fetchable from `origin`.
|
|
207
|
-
- **
|
|
208
|
-
-
|
|
209
|
-
-
|
|
229
|
+
- **API 401/403 when posting**
|
|
230
|
+
- GitLab: ensure the token can read MR metadata/discussions and create MR discussions; with `CI_JOB_TOKEN`, check that project settings allow the required API access.
|
|
231
|
+
- GitHub: ensure the token has `pull-requests: write` (the default `GITHUB_TOKEN` with that permission is enough).
|
|
210
232
|
- **No comments posted**
|
|
211
233
|
- Check `review-comments.json` for `duplicate: true` or empty parsed comments.
|
|
212
234
|
- Run with `--dry-run` and inspect `code-review.md` formatting (`== Inline Comments ==`).
|
|
@@ -155,6 +155,20 @@ function parseNextLink(header) {
|
|
|
155
155
|
}
|
|
156
156
|
return null;
|
|
157
157
|
}
|
|
158
|
+
var REVIEW_THREADS_QUERY = `
|
|
159
|
+
query ($owner: String!, $repo: String!, $pull: Int!, $cursor: String) {
|
|
160
|
+
repository(owner: $owner, name: $repo) {
|
|
161
|
+
pullRequest(number: $pull) {
|
|
162
|
+
reviewThreads(first: 100, after: $cursor) {
|
|
163
|
+
pageInfo { hasNextPage endCursor }
|
|
164
|
+
nodes {
|
|
165
|
+
isResolved
|
|
166
|
+
comments(first: 100) { nodes { databaseId } }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}`;
|
|
158
172
|
var GitHubClient = class {
|
|
159
173
|
base;
|
|
160
174
|
token;
|
|
@@ -290,6 +304,65 @@ var GitHubClient = class {
|
|
|
290
304
|
getCurrentUser() {
|
|
291
305
|
return this.request("/user");
|
|
292
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Derive the GraphQL endpoint from the REST base. github.com exposes GraphQL
|
|
309
|
+
* at `<origin>/graphql`, while GitHub Enterprise Server exposes it at
|
|
310
|
+
* `<origin>/api/graphql` (its REST base is `<origin>/api/v3`).
|
|
311
|
+
*/
|
|
312
|
+
graphqlEndpoint() {
|
|
313
|
+
if (this.base.endsWith("/api/v3")) return `${this.base.slice(0, -7)}/api/graphql`;
|
|
314
|
+
return `${this.base}/graphql`;
|
|
315
|
+
}
|
|
316
|
+
async graphql(query, variables) {
|
|
317
|
+
const url = this.graphqlEndpoint();
|
|
318
|
+
const response = await this.fetchWithTimeout(url, {
|
|
319
|
+
method: "POST",
|
|
320
|
+
headers: this.headers({ "Content-Type": "application/json" }),
|
|
321
|
+
body: JSON.stringify({
|
|
322
|
+
query,
|
|
323
|
+
variables
|
|
324
|
+
})
|
|
325
|
+
}, "POST", "/graphql");
|
|
326
|
+
if (!response.ok) this.failure("POST", "/graphql", response, await response.text());
|
|
327
|
+
const text = await response.text();
|
|
328
|
+
const parsed = JSON.parse(text);
|
|
329
|
+
if (parsed.errors && parsed.errors.length > 0) throw new GitHubApiError(`GitHub API POST /graphql failed: ${parsed.errors.map((e) => e.message ?? "").join("; ")}`, {
|
|
330
|
+
method: "POST",
|
|
331
|
+
path: "/graphql",
|
|
332
|
+
responseBody: text,
|
|
333
|
+
hint: "Ensure the token can read pull-request review threads (pull-requests: read / repo scope)."
|
|
334
|
+
});
|
|
335
|
+
return parsed.data;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Return the database IDs of review comments that belong to a **resolved**
|
|
339
|
+
* review thread. GitHub's REST comment endpoints omit thread-resolution state;
|
|
340
|
+
* it is only exposed via GraphQL `reviewThreads.isResolved`. Callers use this
|
|
341
|
+
* set to mark normalized notes resolved so resolved threads are excluded from
|
|
342
|
+
* summary carry-over and prior-thread context. Paginates over threads.
|
|
343
|
+
*/
|
|
344
|
+
async listResolvedReviewCommentIds(owner, repo, pull) {
|
|
345
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
346
|
+
let cursor = null;
|
|
347
|
+
let hasNext = true;
|
|
348
|
+
while (hasNext) {
|
|
349
|
+
const threads = (await this.graphql(REVIEW_THREADS_QUERY, {
|
|
350
|
+
owner,
|
|
351
|
+
repo,
|
|
352
|
+
pull,
|
|
353
|
+
cursor
|
|
354
|
+
})).repository?.pullRequest?.reviewThreads;
|
|
355
|
+
if (!threads) break;
|
|
356
|
+
for (const thread of threads.nodes ?? []) {
|
|
357
|
+
if (!thread.isResolved) continue;
|
|
358
|
+
for (const comment of thread.comments?.nodes ?? []) if (typeof comment.databaseId === "number") resolved.add(comment.databaseId);
|
|
359
|
+
}
|
|
360
|
+
hasNext = threads.pageInfo?.hasNextPage ?? false;
|
|
361
|
+
cursor = threads.pageInfo?.endCursor ?? null;
|
|
362
|
+
if (!cursor) hasNext = false;
|
|
363
|
+
}
|
|
364
|
+
return resolved;
|
|
365
|
+
}
|
|
293
366
|
};
|
|
294
367
|
//#endregion
|
|
295
368
|
//#region src/fingerprints.ts
|
|
@@ -439,7 +512,12 @@ function buildSizeNoticeBlock(notice) {
|
|
|
439
512
|
if (sizeSkippedFiles.length > 0) {
|
|
440
513
|
const fileList = sizeSkippedFiles.map((file) => `- \`${file.path}\` (${formatChars(file.chars)})`).join("\n");
|
|
441
514
|
const cov = notice.coverage;
|
|
442
|
-
const
|
|
515
|
+
const retrieved = notice.retrieved === true;
|
|
516
|
+
let coverageLine;
|
|
517
|
+
if (cov && cov.totalLines > 0) {
|
|
518
|
+
const pct = Math.round(cov.reviewedLines / cov.totalLines * 100);
|
|
519
|
+
coverageLine = retrieved ? `> **Large diff — ~${pct}% of changed lines fit the inline budget** (${cov.reviewedLines} of ${cov.totalLines}). The files below exceeded it and were staged for on-demand retrieval; see the review summary for which were read. Absence from the findings is not a clean bill of health.` : `> **Partial review — ~${pct}% of changed lines reviewed** (${cov.reviewedLines} of ${cov.totalLines}). The files below were NOT reviewed; their absence from the findings is not a clean bill of health.`;
|
|
520
|
+
} else coverageLine = retrieved ? `> **${sizeSkippedFiles.length} file(s) exceeded the size budget** — their diffs were staged for on-demand retrieval; see the review summary for which were read:` : `> **${sizeSkippedFiles.length} file(s) were not reviewed** — the diff exceeded the size budget, so these files were dropped from the review:`;
|
|
443
521
|
blocks.push([
|
|
444
522
|
`> [!WARNING]`,
|
|
445
523
|
coverageLine,
|
|
@@ -470,7 +548,7 @@ function buildSummaryBody(summary, costFooter, options = {}) {
|
|
|
470
548
|
return `${withFooter}\n\n${buildSummaryHistoryBlock(historyEntries)}`;
|
|
471
549
|
}
|
|
472
550
|
function buildReviewedCommitFooter(commitSha) {
|
|
473
|
-
return `Reviewed by ${PRODUCT_LINK} v0.8.
|
|
551
|
+
return `Reviewed by ${PRODUCT_LINK} v0.8.4 for commit ${commitSha}.`;
|
|
474
552
|
}
|
|
475
553
|
function extractReviewedCommitSha(body) {
|
|
476
554
|
return REVIEWED_COMMIT_FOOTER_PATTERN.exec(body)?.[1] ?? null;
|
|
@@ -863,6 +941,7 @@ var BOOLEAN_FLAGS = new Set([
|
|
|
863
941
|
"no-summary",
|
|
864
942
|
"force-review",
|
|
865
943
|
"retrieve-skipped",
|
|
944
|
+
"no-retrieve-skipped",
|
|
866
945
|
"verbose",
|
|
867
946
|
"help",
|
|
868
947
|
"version"
|
|
@@ -921,6 +1000,22 @@ function resolvePostSummary(args, env) {
|
|
|
921
1000
|
}
|
|
922
1001
|
return true;
|
|
923
1002
|
}
|
|
1003
|
+
function resolveRetrieveSkipped(args, env) {
|
|
1004
|
+
if (args.noRetrieveSkipped === true) return false;
|
|
1005
|
+
if (args.retrieveSkipped === true) return true;
|
|
1006
|
+
const raw = env.CODE_REVIEW_RETRIEVE_SKIPPED;
|
|
1007
|
+
if (typeof raw === "string") {
|
|
1008
|
+
const normalized = raw.trim().toLowerCase();
|
|
1009
|
+
if ([
|
|
1010
|
+
"0",
|
|
1011
|
+
"false",
|
|
1012
|
+
"no",
|
|
1013
|
+
"off"
|
|
1014
|
+
].includes(normalized)) return false;
|
|
1015
|
+
if (normalized.length > 0) return true;
|
|
1016
|
+
}
|
|
1017
|
+
return true;
|
|
1018
|
+
}
|
|
924
1019
|
function normalizeChoice(value) {
|
|
925
1020
|
return String(value ?? "").trim().toLowerCase();
|
|
926
1021
|
}
|
|
@@ -1124,7 +1219,7 @@ function resolveConfig(argv = process.argv.slice(2), env = process.env) {
|
|
|
1124
1219
|
maxDiffChars,
|
|
1125
1220
|
decomposeHintLines,
|
|
1126
1221
|
diffContext,
|
|
1127
|
-
retrieveSkipped:
|
|
1222
|
+
retrieveSkipped: resolveRetrieveSkipped(args, env),
|
|
1128
1223
|
reviewFile: String(args.reviewFile ?? "code-review.md"),
|
|
1129
1224
|
output: String(args.output ?? "review-comments.json"),
|
|
1130
1225
|
dryRun: toBoolean(args.dryRun),
|
|
@@ -3715,10 +3810,10 @@ function buildUserPrompt(diff, skippedFiles = [], commitLog, priorThreads, inten
|
|
|
3715
3810
|
if (commitLog?.trim()) parts.push(`Commits in this MR (oldest first):\n<commits>\n${commitLog.trim()}\n</commits>`);
|
|
3716
3811
|
parts.push(`Review this diff:\n<diff>\n${diff}\n</diff>`);
|
|
3717
3812
|
if (retrievableSkipped && retrievableSkipped.length > 0) parts.push(renderRetrievableSkippedBlock(retrievableSkipped));
|
|
3718
|
-
else if (skippedFiles.length > 0) parts.push(`<skipped_files>\n${skippedFiles.map((file) => `- ${file}`).join("\n")}\n</skipped_files>\nThe above files were not included because the diff exceeded the size limit.
|
|
3813
|
+
else if (skippedFiles.length > 0) parts.push(`<skipped_files>\n${skippedFiles.map((file) => `- ${file}`).join("\n")}\n</skipped_files>\nThe above files were not included because the diff exceeded the size limit. They are already surfaced to the reader in the MR summary, so do not re-list them; just do not assume they are clean, since you did not see them.`);
|
|
3719
3814
|
if (coverage && coverage.totalLines > 0 && coverage.reviewedLines < coverage.totalLines) {
|
|
3720
3815
|
const pct = Math.round(coverage.reviewedLines / coverage.totalLines * 100);
|
|
3721
|
-
parts.push(`<coverage>You reviewed ${coverage.reviewedLines} of ${coverage.totalLines} changed lines (~${pct}%). The rest were dropped for the size budget and you did NOT see them.
|
|
3816
|
+
parts.push(`<coverage>You reviewed ${coverage.reviewedLines} of ${coverage.totalLines} changed lines (~${pct}%). The rest were dropped for the size budget and you did NOT see them. The MR summary already reports this partial coverage to the reader, so do not restate it; just do not imply the unreviewed files are clean — their absence from your findings is not a clearance.</coverage>`);
|
|
3722
3817
|
}
|
|
3723
3818
|
if (priorThreads && priorThreads.length > 0) {
|
|
3724
3819
|
const block = renderPriorThreadsBlock(priorThreads);
|
|
@@ -3950,13 +4045,14 @@ async function runReview(config, options) {
|
|
|
3950
4045
|
reviewedLines: reviewedChangedLines,
|
|
3951
4046
|
totalLines: reviewedChangedLines + skippedChangedLines
|
|
3952
4047
|
} : void 0;
|
|
4048
|
+
const retrievableSkipped = config.retrieveSkipped && sizeSkippedSections.length > 0 ? await writeSkippedDiffs(cwd, sizeSkippedSections) : [];
|
|
4049
|
+
if (retrievableSkipped.length > 0) logger.info(`Staged ${retrievableSkipped.length} dropped-file diff(s) on disk for retrieval.`);
|
|
3953
4050
|
const sizeNotice = {
|
|
3954
4051
|
sizeSkippedFiles,
|
|
3955
4052
|
decomposeHint,
|
|
3956
|
-
coverage
|
|
4053
|
+
coverage,
|
|
4054
|
+
retrieved: retrievableSkipped.length > 0
|
|
3957
4055
|
};
|
|
3958
|
-
const retrievableSkipped = config.retrieveSkipped && sizeSkippedSections.length > 0 ? await writeSkippedDiffs(cwd, sizeSkippedSections) : [];
|
|
3959
|
-
if (retrievableSkipped.length > 0) logger.info(`Staged ${retrievableSkipped.length} dropped-file diff(s) on disk for retrieval.`);
|
|
3960
4056
|
const context = await loadReviewContext(cwd, config.skills, (msg) => logger.warn(msg), { refreshGitSkills: config.refreshGitSkills });
|
|
3961
4057
|
const systemPrompt = buildJSONSystemPrompt(context, minSeverity);
|
|
3962
4058
|
const userPrompt = buildUserPrompt(diff, skippedFiles, options.commitLog, options.priorThreads, options.intent, coverage, retrievableSkipped);
|
|
@@ -4836,7 +4932,7 @@ async function loadDefaultRuntime() {
|
|
|
4836
4932
|
const [sdkNode, resources, semconv] = modules;
|
|
4837
4933
|
const serviceResource = resources.resourceFromAttributes({
|
|
4838
4934
|
[semconv.ATTR_SERVICE_NAME ?? "service.name"]: SERVICE_NAME,
|
|
4839
|
-
[semconv.ATTR_SERVICE_VERSION ?? "service.version"]: "0.8.
|
|
4935
|
+
[semconv.ATTR_SERVICE_VERSION ?? "service.version"]: "0.8.4"
|
|
4840
4936
|
});
|
|
4841
4937
|
process.env.OTEL_METRICS_EXPORTER = process.env.OTEL_METRICS_EXPORTER ?? "otlp";
|
|
4842
4938
|
process.env.OTEL_LOGS_EXPORTER = process.env.OTEL_LOGS_EXPORTER ?? "otlp";
|
|
@@ -5268,7 +5364,7 @@ function boldCommentTitle(body) {
|
|
|
5268
5364
|
*/
|
|
5269
5365
|
function buildCommentBody(body, commitSha, confidence) {
|
|
5270
5366
|
const confidenceLine = `_Confidence: ${confidence}._`;
|
|
5271
|
-
const footer = `<sub>Reviewed by ${PRODUCT_LINK} v0.8.
|
|
5367
|
+
const footer = `<sub>Reviewed by ${PRODUCT_LINK} v0.8.4 for commit ${commitSha}.</sub>`;
|
|
5272
5368
|
return `${boldCommentTitle(body.trim())}\n\n${confidenceLine}\n\n---\n\n${footer}`;
|
|
5273
5369
|
}
|
|
5274
5370
|
function buildPayload(comment, body, refs, resolved) {
|
|
@@ -5431,8 +5527,12 @@ function reviewCommentPosition(comment) {
|
|
|
5431
5527
|
* The fingerprint markers, summary marker, and reviewed-commit footer are HTML
|
|
5432
5528
|
* comments that render identically on GitHub, so `extractExistingFingerprints`,
|
|
5433
5529
|
* `findExistingSummaryNote`, and the reviewed-commit scan all work as-is.
|
|
5530
|
+
*
|
|
5531
|
+
* `resolvedCommentIds` carries the database ids of comments in resolved review
|
|
5532
|
+
* threads (from the GraphQL `reviewThreads` query, since REST omits resolution),
|
|
5533
|
+
* so each note gets a `resolved` flag mirroring GitLab's per-note field.
|
|
5434
5534
|
*/
|
|
5435
|
-
function normalizeGitHubDiscussions(reviewComments, issueComments) {
|
|
5535
|
+
function normalizeGitHubDiscussions(reviewComments, issueComments, resolvedCommentIds = /* @__PURE__ */ new Set()) {
|
|
5436
5536
|
const threads = /* @__PURE__ */ new Map();
|
|
5437
5537
|
const order = [];
|
|
5438
5538
|
for (const comment of reviewComments) {
|
|
@@ -5446,6 +5546,7 @@ function normalizeGitHubDiscussions(reviewComments, issueComments) {
|
|
|
5446
5546
|
notes.push({
|
|
5447
5547
|
id: comment.id,
|
|
5448
5548
|
body: comment.body ?? "",
|
|
5549
|
+
resolved: resolvedCommentIds.has(comment.id),
|
|
5449
5550
|
position: reviewCommentPosition(comment)
|
|
5450
5551
|
});
|
|
5451
5552
|
}
|
|
@@ -5512,8 +5613,12 @@ var GitHubPlatform = class {
|
|
|
5512
5613
|
return refs;
|
|
5513
5614
|
}
|
|
5514
5615
|
async getDiscussions() {
|
|
5515
|
-
const [reviewComments, issueComments] = await Promise.all([
|
|
5516
|
-
|
|
5616
|
+
const [reviewComments, issueComments, resolvedCommentIds] = await Promise.all([
|
|
5617
|
+
this.client.listReviewComments(this.owner, this.repo, this.pull),
|
|
5618
|
+
this.client.listIssueComments(this.owner, this.repo, this.pull),
|
|
5619
|
+
this.client.listResolvedReviewCommentIds(this.owner, this.repo, this.pull)
|
|
5620
|
+
]);
|
|
5621
|
+
return normalizeGitHubDiscussions(reviewComments, issueComments, resolvedCommentIds);
|
|
5517
5622
|
}
|
|
5518
5623
|
buildComments(comments, diff, refs, existingFingerprints) {
|
|
5519
5624
|
this.commitId = refs.head_sha;
|
|
@@ -5984,9 +6089,10 @@ Options:
|
|
|
5984
6089
|
context aids reasoning but inflates tokens and fits fewer files in
|
|
5985
6090
|
the budget; less fits more. 0 = built-in default (20).
|
|
5986
6091
|
(env: CODE_REVIEW_DIFF_CONTEXT)
|
|
5987
|
-
--retrieve-skipped
|
|
5988
|
-
|
|
5989
|
-
|
|
6092
|
+
--no-retrieve-skipped Disable staging diffs for files dropped by the size budget on disk.
|
|
6093
|
+
Retrieval is on by default: dropped diffs are staged so the reviewer
|
|
6094
|
+
can read them on demand instead of losing them.
|
|
6095
|
+
(env: CODE_REVIEW_RETRIEVE_SKIPPED=0)
|
|
5990
6096
|
--min-severity <level> info, warn, or critical (default: info)
|
|
5991
6097
|
--thinking <level> off, minimal, low, medium, high, or xhigh (default: off).
|
|
5992
6098
|
Higher levels add billable thinking tokens at the model output rate.
|
|
@@ -6346,10 +6452,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
6346
6452
|
return;
|
|
6347
6453
|
}
|
|
6348
6454
|
if (argv.includes("--version") || argv.includes("-v")) {
|
|
6349
|
-
console.log("0.8.
|
|
6455
|
+
console.log("0.8.4");
|
|
6350
6456
|
return;
|
|
6351
6457
|
}
|
|
6352
|
-
process.stderr.write(`[code-review] @weareikko/code-review v0.8.
|
|
6458
|
+
process.stderr.write(`[code-review] @weareikko/code-review v0.8.4\n`);
|
|
6353
6459
|
assertNodeVersion();
|
|
6354
6460
|
applyCodeReviewEnvPrefix();
|
|
6355
6461
|
applyDefaultCacheRetention();
|
|
@@ -6372,4 +6478,4 @@ if (isDirectRun()) main().catch((error) => {
|
|
|
6372
6478
|
//#endregion
|
|
6373
6479
|
export { normalizeBody as $, SUMMARY_HISTORY_END as A, buildSummaryHistoryEntries as B, createDiagnosticContext as C, traceDiagnosticPhase as D, traceDiagnostic as E, SUMMARY_MARKER as F, findExistingSummaryNoteId as G, extractSummaryHistoryEntries as H, buildArchivedSummaryEntry as I, upsertSummaryNote as J, stripSummaryHistory as K, buildReviewedCommitFooter as L, SUMMARY_HISTORY_ENTRY_START as M, SUMMARY_HISTORY_LIMIT as N, normalizeSeverity as O, SUMMARY_HISTORY_START as P, fingerprints as Q, buildSizeNoticeBlock as R, DIAGNOSTIC_CHANNEL_PREFIX as S, diagnosticChannels as T, findExistingReviewedCommitSha as U, extractReviewedCommitSha as V, findExistingSummaryNote as W, extractDiffHunkContext as X, appendFingerprintMarkers as Y, extractExistingFingerprints as Z, resolveNpmSkillDir as _, main as a, parseReviewMarkdownWithWarnings as b, buildGeneratedComments as c, startOtelBridge as d, sha256 as et, filterDiff as f, parseSkillSpec as g, loadNamedSkill as h, formatUsageLine as i, SUMMARY_HISTORY_ENTRY_END as j, toGitLabReviewSeverity as k, buildPayload as l, gitSkillCacheKey as m, formatPerModelUsage as n, run as o, runReview as p, stripSummaryMarker as q, formatSkillsFooter as r, withHttpStamping as s, countPostedBySeverity as t, isOtelEnabled as u, resolveSkillCacheDir as v, createDiagnosticRunId as w, DIAGNOSTIC_CHANNEL_NAMES as x, parseReviewMarkdown as y, buildSummaryBody as z };
|
|
6374
6480
|
|
|
6375
|
-
//# sourceMappingURL=cli-
|
|
6481
|
+
//# sourceMappingURL=cli-FDpPZaNn.js.map
|