> **code-review-local** — skill 10 of 83 in [bitwarden/ai-plugins](https://skillsdocs.com/bitwarden/ai-plugins).
>
> Book (all skills, one file): https://skillsdocs.com/bitwarden/ai-plugins.md
> Machine manifest: https://skillsdocs.com/bitwarden/ai-plugins/.well-known/agent-skills/index.json
> Install the book: `npx skills add bitwarden/ai-plugins`
> Upstream: https://github.com/bitwarden/ai-plugins/blob/main/plugins/bitwarden-code-review/skills/code-review-local/SKILL.md @ `main`
> Raw bytes, no header: https://raw.githubusercontent.com/bitwarden/ai-plugins/main/plugins/bitwarden-code-review/skills/code-review-local/SKILL.md
> Base for relative paths: https://raw.githubusercontent.com/bitwarden/ai-plugins/main/plugins/bitwarden-code-review/skills/code-review-local/
> Licence: Other — https://github.com/bitwarden/ai-plugins
>
> Bundled files (1), referenced from this skill's directory:
>   - `README.md` — https://raw.githubusercontent.com/bitwarden/ai-plugins/main/plugins/bitwarden-code-review/skills/code-review-local/README.md
>
> Content © its authors, served unmodified. Takedown: https://github.com/DreambaseAI/skillsdocs/issues/new?labels=takedown&title=Takedown+request

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: code-review-local
argument-hint: "[PR# | PR URL | --base <ref>] [--output-dir <path>] | (blank to choose interactively)"
allowed-tools: AskUserQuestion, Task
description: Review a GitHub pull request or local changes and write the review to local files instead of posting
---

**Take `--base <ref>` out of `$ARGUMENTS` first**, before anything below reads it. The accepted form is exactly that — `--base`, a space, then the ref — and nothing else. Remove the pair and resolve the target from what is left, so `--base origin/rc` on its own still reaches the interactive path instead of being read as a target. It names the ref local mode diffs against, and it applies to local changes only — with a pull request target, drop it and say you did. A pull request's base is the one GitHub records, and `gh pr diff` already uses it.

**Any other appearance of the token `--base` is an error**, `--base=<ref>` and a trailing `--base` with nothing after it and a second `--base` among them. Do not invoke the Task tool: say which form you were given and ask again. The rule is stated this way round on purpose — an enumeration of bad forms is a closed list, and the next one nobody thought of falls through it.

**Never continue with the default base once `--base` has appeared in any form.** An unrecognized form leaves no ref to validate, so it would otherwise reach the interactive path, emit no `BASE:` line, and let the agent review the trunk merge base under an ordinary verdict with nothing saying the requested base was dropped. That is the silent wrong scope this parameter exists to prevent, and the entry path is the only place it could still happen. Aborting here also keeps a leftover token out of target resolution, where a `--base=rc2` could have its `2` read as a pull request number.

**The ref must match `^[A-Za-z0-9][A-Za-z0-9._/-]*$` before it goes into the `BASE:` line.** It is written verbatim into `git diff <ref>...HEAD`, and `Bash(git diff:*)` is a prefix rule, so a value starting with `-` matches that rule as an option rather than a ref and reaches git as a flag. The leading character class is what rejects that; the rest keeps whitespace, shell metacharacters, and `@{` out of the command string. If the value does not match, **do not invoke the Task tool at all**: report what you were given and ask again.

**Take `--output-dir <path>` out of `$ARGUMENTS` too**, before resolving the target. The only accepted form is `--output-dir`, a space, then the path, which ends at the next whitespace; any other form is an error, handled as for `--base`. Unlike `--base`, it applies to both targets. Expand a leading `~` to the home directory, then make the path absolute, resolving a relative one against the working directory. Without the flag, emit no `OUTPUT_DIR:` line; the agent defaults to `${CLAUDE_PLUGIN_DATA}/code-reviews/`.

**Resolve the target next, in this turn.** Two ways in:

- `$ARGUMENTS` names a PR. Extract just the number — `123`, `https://github.com/org/repo/pull/456`, and `PR #789` all yield a bare integer.
- `$ARGUMENTS` is empty, or names nothing that parses as a PR. Use `AskUserQuestion` to ask whether to review a pull request or the local changes, and settle it here; the agent runs as a subagent and cannot prompt mid-run, so a question left for it has no one to answer. This turn cannot list open PRs, so take the number as free text rather than offering a menu.

**If the target is a pull request, the number must match `^[0-9]+$` before it goes into the `TARGET:` line.** (A local-changes target has no number; this check does not apply to it.) It is written verbatim into `gh pr view`, `gh pr diff`, and the GraphQL variable below, and `Bash(gh pr view:*)` is a prefix rule — `gh pr view 1 --repo attacker/repo` would match it and redirect the review. If the value does not match, **do not invoke the Task tool at all**: report what you were given and ask again. Never delegate without a resolved target.

**Then invoke the Task tool** with `subagent_type: "bitwarden-code-review:bitwarden-code-reviewer"`. Begin the prompt with the resolved target on its own line, in exactly one of these forms, followed by everything from **CRITICAL INSTRUCTIONS FOR THE AGENT** onward — not this paragraph, which is addressed to the command turn and would tell the agent not to do its own job:

```
TARGET: PR #<number>
TARGET: local changes
```

On the line after it, add `BASE: <ref>` — only when the target is local changes and a validated ref survived above. Omit the line entirely otherwise. There is no placeholder form: the agent defaults to `origin/HEAD` when the line is absent, and a placeholder would be diffed against as a literal ref name.

On the next line, always add `OUTPUT: local files` — both targets. This command writes to local files and never posts, so that declaration, not the target, is what `Skill(posting-review-summary)` routes on.

On the line after that, add `OUTPUT_DIR: <absolute dir>` — only when `--output-dir` was given. Omit the line entirely otherwise: there is no placeholder form, and the agent applies its default when the line is absent.

The `TARGET:` line is the only carrier — `$ARGUMENTS` is empty on the interactive path, so an agent left to re-derive the target from it would find nothing. This command's own turn holds only `AskUserQuestion` and `Task`: it settles the target and delegates. Thread pre-fetching belongs to the workflow-driven `/bitwarden-code-review:code-review`, not here. Do not run the `gh`, `git`, `Skill`, or `Write` operations described below yourself — they are the agent's, and it carries its own grants for them.

Invoke the bitwarden-code-reviewer agent now with the instructions below. When it returns, tell the user the full path of each file it wrote.

**CRITICAL INSTRUCTIONS FOR THE AGENT:**

1. **Read the target from the `TARGET:` line at the top of this prompt.** It is already resolved — the command turn settled it before delegating. Do not ask and do not re-derive it: you hold no `AskUserQuestion` grant and no one is there to answer.
   - `TARGET: PR #<number>` — use that number for thread detection and for fetching PR data with `gh pr view`
   - `TARGET: local changes` — follow the local-mode procedure in your `AGENT.md`, which defines how the base is resolved and what to do when there is nothing to review. Skip thread detection (step 2). The scope is whatever that procedure resolves, not both scopes at once
   - `BASE: <ref>` on the following line, when present — diff against that ref instead of `origin/HEAD`. Re-check it against `^[A-Za-z0-9][A-Za-z0-9._/-]*$` yourself; the command turn checked it, but you also run on prompts it did not write. Absent the line, use `origin/HEAD`

2. **Detect Existing Threads** (PR reviews only - skip for local changes):

   Fetch existing review threads to prevent duplicate comments. Capture BOTH comment sources:

   ```bash
   # General PR comments
   gh pr view <PR_NUMBER> --json comments

   # Inline review threads (resolved + open)
   gh api graphql -f query='
   query($owner: String!, $repo: String!, $pr: Int!) {
     repository(owner: $owner, name: $repo) {
       pullRequest(number: $pr) {
         reviewThreads(first: 100) {
           nodes {
             id
             isResolved
             isOutdated
             path
             line
             startLine
             diffSide
             comments(first: 10) {
               nodes {
                 id
                 body
                 author { login }
                 createdAt
               }
             }
           }
         }
       }
     }
   }
   ' -f owner="<OWNER>" -f repo="<REPO>" -F pr="<PR_NUMBER>"
   ```

   **Thread Matching Logic** - Before creating any new comment, check for matches:

   | Match Type   | Criteria                   | Action              |
   | ------------ | -------------------------- | ------------------- |
   | **Exact**    | Same file + same line      | Use existing thread |
   | **Nearby**   | Same file + line within ±5 | Use existing thread |
   | **Content**  | Body similarity >70%       | Use existing thread |
   | **No match** | None of above              | Create new comment  |

3. **Local Review Mode**: Writing to local files instead of GitHub. Invoke `Skill(posting-review-summary)` with local output context.

4. **Output Destination**: Both files go to the local-file destination your `AGENT.md` Step 1 resolves — the `OUTPUT_DIR:` directory when that line is present, its default otherwise — under the names it assigns.

5. **Format Exactly As PR Comments**: Both files MUST contain exactly what would be posted to GitHub
   - If no inline comments would be left, leave the inline-comments file blank.

6. **No GitHub Posting**: Do NOT use `gh pr review --comment` or `gh pr comment` to post anything. Only READ from GitHub, WRITE to local files.

7. **Include All Standard Review Elements**:
   - Pre-review protocol (read existing comments, understand changes, assess PR metadata)
   - All finding categories (❌ ⚠️ ♻️ 🎨 ❓)
   - Proper `<details>` sections for each finding
   - Final summary with overall assessment

**Note**: The output formats below mirror the standard GitHub review formats documented in your AGENT.md file, adapted for local file output instead of direct GitHub posting.

**File 1: the summary file**

Uses the same format as `Skill(posting-review-summary)`, including its `## No Verdict` form when nothing could be reviewed:

```markdown
## 🤖 Bitwarden Claude Code Review

**Overall Assessment:** APPROVE / REQUEST CHANGES

[1-2 neutral sentence describing what was reviewed]

<details>
<summary>Code Review Details</summary>

- [emoji]: [One-line description]
  - `filename.ts:42`

</details>
```

**File 2: the inline-comments file**

Contains all inline review comments with file and line references (same format as would be posted with `gh pr review --comment` in standard GitHub reviews, but written to local file). Format:

```markdown
## [file-path]:[line-number]

[Emoji]: [One-line description]

<details>
<summary>Details and fix</summary>

[Full details, code examples, rationale]

</details>

---

## [next-file]:[next-line]

[Next comment...]

---
```
