diff --git a/.claude/plugins/code-review-tea/.claude-plugin/commands/.gitkeep b/.claude/plugins/code-review-tea/commands/.gitkeep similarity index 100% rename from .claude/plugins/code-review-tea/.claude-plugin/commands/.gitkeep rename to .claude/plugins/code-review-tea/commands/.gitkeep diff --git a/.claude/plugins/code-review-tea/.claude-plugin/commands/code-review.md b/.claude/plugins/code-review-tea/commands/code-review.md similarity index 100% rename from .claude/plugins/code-review-tea/.claude-plugin/commands/code-review.md rename to .claude/plugins/code-review-tea/commands/code-review.md diff --git a/docs/superpowers/plans/2026-06-27-code-review-tea-plugin.md b/docs/superpowers/plans/2026-06-27-code-review-tea-plugin.md index 4958377..ff60a27 100644 --- a/docs/superpowers/plans/2026-06-27-code-review-tea-plugin.md +++ b/docs/superpowers/plans/2026-06-27-code-review-tea-plugin.md @@ -44,14 +44,16 @@ These were confirmed by probing the live `tea` 0.14.1 binary during planning — ``` .claude/plugins/code-review-tea/ ├── .claude-plugin/ -│ ├── plugin.json # the manifest (credits Anthropic/Boris Cherny) -│ └── commands/ -│ ├── code-review.md # the forked, tea-rewritten command -│ └── RESOLVED_INVOCATION.md # verified tea call notes (from Task 2) +│ └── plugin.json # the manifest (credits Anthropic/Boris Cherny) — ONLY file in .claude-plugin/ +├── commands/ +│ ├── code-review.md # the forked, tea-rewritten command (plugin root, NOT in .claude-plugin/) +│ └── RESOLVED_INVOCATION.md # verified tea call notes (from Task 2) └── README.md # what this fork is, why, how it differs ``` -Rationale: a project-local plugin under `.claude/plugins/` is version-controlled with the repo and applies only here (this is a Gitea-only repo; a global install would mis-fire on the user's GitHub-hosted repos). The name `code-review-tea` is distinct from the upstream `code-review` so it never shadows or confuses the GitHub plugin if that ever gets enabled elsewhere. +Rationale: per Claude Code's plugin spec, only `plugin.json` lives in `.claude-plugin/`; `commands/`, `skills/`, `agents/` live at the plugin root (verified against the upstream `code-review` and `superpowers` plugins). A project-local plugin under `.claude/plugins/` is version-controlled with the repo and applies only here. The name `code-review-tea` is distinct from the upstream `code-review` so it never shadows or confuses the GitHub plugin if that ever gets enabled elsewhere. + +**Discovery note (verified via docs):** a plugin placed in `/.claude/plugins//` is NOT auto-discovered by Claude Code. It loads explicitly via `claude --plugin-dir .claude/plugins/code-review-tea` (per-session) or by `claude plugin init` installing into `~/.claude/skills/code-review-tea/` (auto-loads as `code-review-tea@skills-dir`). plugin commands are namespaced, so the command is invoked as **`/code-review-tea:code-review`**, not `/code-review-tea`. We create **no** Python source changes and **no** test files — this is a tooling-only change. The `allowed-tools` substitution and command-file rewrite are the entire substance. @@ -61,7 +63,7 @@ We create **no** Python source changes and **no** test files — this is a tooli **Files:** - Create: `.claude/plugins/code-review-tea/.claude-plugin/plugin.json` -- Create: `.claude/plugins/code-review-tea/.claude-plugin/commands/.gitkeep` +- Create: `.claude/plugins/code-review-tea/commands/.gitkeep` - [ ] **Step 1: Create the plugin manifest** @@ -91,7 +93,7 @@ Create `.claude/plugins/code-review-tea/.claude-plugin/plugin.json`: - [ ] **Step 2: Create the commands directory placeholder** -Create `.claude/plugins/code-review-tea/.claude-plugin/commands/.gitkeep` as an empty file so the directory is tracked by git. +Create `.claude/plugins/code-review-tea/commands/.gitkeep` as an empty file so the directory is tracked by git. - [ ] **Step 3: Verify the directory tree** @@ -173,7 +175,7 @@ Make a mental/scratch note of: (a) which login-context flag worked (`--remote or The substantive change. Fork the upstream command, substitute the five `gh` contact points and the link format, keep the parallel-agent + confidence-score + 80-threshold logic identical, and add a header crediting the original. **Files:** -- Create: `.claude/plugins/code-review-tea/.claude-plugin/commands/code-review.md` +- Create: `.claude/plugins/code-review-tea/commands/code-review.md` - [ ] **Step 1: Read the upstream command file for reference** @@ -185,7 +187,7 @@ Expected: the 93-line original. Keep it open; this is the template to fork. - [ ] **Step 2: Write the command file** -Create `.claude/plugins/code-review-tea/.claude-plugin/commands/code-review.md`. The full content: +Create `.claude/plugins/code-review-tea/commands/code-review.md`. The full content: ````markdown --- @@ -305,7 +307,7 @@ No issues found. Checked for bugs and CLAUDE.md compliance. Run: ```bash -head -6 .claude/plugins/code-review-tea/.claude-plugin/commands/code-review.md +head -6 .claude/plugins/code-review-tea/commands/code-review.md ``` Expected: the `---` fence, `allowed-tools:` line containing only `Bash(tea ...:*), Bash(git ...:*)` entries (NO `gh`), then `description:` and `disable-model-invocation: false`, then closing `---`. @@ -313,70 +315,75 @@ Expected: the `---` fence, `allowed-tools:` line containing only `Bash(tea ...:* Run: ```bash -grep -nE '\bgh\b|github\.com|/blob/' .claude/plugins/code-review-tea/.claude-plugin/commands/code-review.md || echo "clean: no gh/github/blob references" +grep -nE '\bgh\b|github\.com|/blob/' .claude/plugins/code-review-tea/commands/code-review.md || echo "clean: no gh/github/blob references" ``` Expected: `clean: no gh/github/blob references`. If anything matches, fix it before committing. (Note: the credit comment intentionally mentions "GitHub/gh-only" to describe the upstream — that line references `gh` as a word, so adjust the grep if it fires on the credit line; the credit line is correct and should stay. The grep is meant to catch *operational* `gh` calls, which would look like `gh pr` or `gh issue`.) A more precise check: ```bash -grep -nE 'gh pr|gh issue|gh search|gh api|github\.com/alee/vigilar/blob|/blob/<' .claude/plugins/code-review-tea/.claude-plugin/commands/code-review.md || echo "clean: no operational gh calls or blob links" +grep -nE 'gh pr|gh issue|gh search|gh api|github\.com/alee/vigilar/blob|/blob/<' .claude/plugins/code-review-tea/commands/code-review.md || echo "clean: no operational gh calls or blob links" ``` Expected: `clean: no operational gh calls or blob links`. - [ ] **Step 5: Commit** ```bash -git add .claude/plugins/code-review-tea/.claude-plugin/commands/code-review.md +git add .claude/plugins/code-review-tea/commands/code-review.md git commit -m "feat(plugin): gitea/tea port of code-review command" ``` --- -## Task 4: Enable the plugin in this project's settings +## Task 4: Load and verify the plugin **Files:** -- Modify: `.claude/settings.json` +- No file changes in this task (the plugin files from Tasks 1 & 3 are already in place; `.claude/settings.json` is NOT modified — repo-local `.claude/plugins//` dirs are not auto-discovered, so an `enabledPlugins` entry would not load it). This task is about confirming the plugin loads and its command resolves. -- [ ] **Step 1: Read the current settings** +**Discovery model (verified via docs at https://code.claude.com/docs/en/plugins):** a plugin placed at `/.claude/plugins//` is NOT auto-discovered by Claude Code. Two ways to load it: +- **`--plugin-dir`** (per-session, recommended for this fork): launch Claude Code as `claude --plugin-dir .claude/plugins/code-review-tea`. The plugin loads for that session; its command is `/code-review-tea:code-review`. +- **`claude plugin init`** (persistent, user-scope): installs into `~/.claude/skills/code-review-tea/`, auto-loads on subsequent sessions as `code-review-tea@skills-dir` (no marketplace needed). This makes it available across all your projects, not just Vigilar — pick this only if you want that. + +plugin commands are namespaced, so the invocation is `/code-review-tea:code-review` (plugin name `:` command file name), NOT `/code-review-tea`. + +- [ ] **Step 1: Validate the plugin structure with the official validator** Run: ```bash -cat .claude/settings.json -``` -Expected: -```json -{ - "enabledPlugins": { - "superpowers@claude-plugins-official": true - } -} +claude plugin validate .claude/plugins/code-review-tea 2>&1 ``` +Expected: a success/valid report. If it reports a structural error (e.g. "commands/ must be at plugin root, not in .claude-plugin/"), fix the layout before proceeding. This catches exactly the nesting mistake that was corrected earlier (commands/ now at plugin root). If `claude plugin validate` is unavailable in your version, skip to Step 2 (the layout was already verified against the upstream `code-review` plugin in planning). -- [ ] **Step 2: Add the new plugin to enabledPlugins** - -Change `.claude/settings.json` to: - -```json -{ - "enabledPlugins": { - "superpowers@claude-plugins-official": true, - "code-review-tea@vigilar": true - } -} -``` - -The `@vigilar` suffix denotes a project-local (non-marketplace) plugin discovered from `.claude/plugins/code-review-tea/`. If Claude Code resolves local plugins by bare directory name (no `@source` suffix), drop it to `"code-review-tea": true`. Step 3 verifies which form is accepted. - -- [ ] **Step 3: Verify the plugin is discoverable** - -Restart/reload Claude Code for this project and run `/code-review-tea`. If the command is not found, Claude Code's local-plugin resolution may need the bare plugin name — change settings to `"code-review-tea": true` and re-verify. If still not found, confirm the plugin is discovered at all by checking whether `/code-review-tea` appears in the command list (if not, the loader may not scan `.claude/plugins/` — in that case, the plugin can still be invoked by directly reading the command file; document this fallback in the README). - -- [ ] **Step 4: Commit** +- [ ] **Step 2: Load the plugin for a session and confirm the command is registered** +Run: ```bash -git add .claude/settings.json -git commit -m "chore(plugin): enable project-local code-review-tea plugin" +claude --plugin-dir .claude/plugins/code-review-tea ``` +Then, inside that Claude Code session, run `/help` (or check the command list) and confirm `/code-review-tea:code-review` appears. Do NOT attempt to run the full review here — the smoke test (Task 6) does the end-to-end run. This step only confirms the command is registered and discoverable. + +If `/code-review-tea:code-review` does NOT appear: run `claude --plugin-dir .claude/plugins/code-review-tea --debug` (or whatever the version's verbose flag is) and read the plugin-load errors. Most likely cause at this point would be malformed `plugin.json` or `commands/code-review.md` frontmatter — re-validate in Step 1. + +- [ ] **Step 3: (Optional) Make it persistent via claude plugin init** + +If you want the plugin available in every session (not just when you remember `--plugin-dir`), install it to your skills directory: +```bash +claude plugin init code-review-tea +``` +This creates `~/.claude/skills/code-review-tea/` and loads it as `code-review-tea@skills-dir` on subsequent sessions. Only do this if you want Vigilar's review command available outside this repo too; otherwise rely on `--plugin-dir` and skip this step. + +- [ ] **Step 4: Commit the corrected layout (if not already committed)** + +The Tasks 1 & 3 commits placed `commands/` in the wrong nested location; the restructure moved it to plugin root via `git mv`. Commit that move if it's still staged: +```bash +git status --short +git add .claude/plugins/code-review-tea +git commit -m "fix(plugin): move commands/ to plugin root (correct structure)" +``` +Append the coauthor trailer (after a blank line): +``` +Co-Authored-By: Claude Opus 4.8 (1M context) +``` +If `git status` shows nothing to commit (already committed), skip. --- @@ -384,31 +391,40 @@ git commit -m "chore(plugin): enable project-local code-review-tea plugin" **Files:** - Create: `.claude/plugins/code-review-tea/README.md` -- Create: `.claude/plugins/code-review-tea/.claude-plugin/commands/RESOLVED_INVOCATION.md` +- Create: `.claude/plugins/code-review-tea/commands/RESOLVED_INVOCATION.md` - Modify: `CLAUDE.md` — append a short "## Code Review" subsection - [ ] **Step 1: Write the verified invocation notes** -Create `.claude/plugins/code-review-tea/.claude-plugin/commands/RESOLVED_INVOCATION.md`. Fill in with the variants verified in Task 2 (replace `` placeholders with the actually-confirmed values): +Create `.claude/plugins/code-review-tea/commands/RESOLVED_INVOCATION.md`. Fill in with the variants verified in Task 2 (replace `` placeholders with the actually-confirmed values): ```markdown # Resolved tea post-back invocation (verified via Task 2 smoke run) Gitea comment posting that survives large multi-line Markdown bodies: - tea api --remote origin "{owner}/{repo}/issues//comments" -f body=@- # body via stdin - # fallback: -f body=@/tmp/review-tea-body.md # body via file + tea api --remote origin "/repos/{owner}/{repo}/issues//comments" -F body=@- # body via stdin [VERIFIED] + # fallback: -F body=@/tmp/review-tea-body.md # body via file [VERIFIED] -Code link format (verify it renders in Gitea web): - https://git.adlee.work/alee/vigilar/src/commit//#L-L # path segment confirms src/commit +CRITICAL: use `-F` (typed field), NOT `-f`. `-f key=@file` posts the literal string `@file`; +only `-F key=@file` / `-F key=@-` reads the file/stdin. The endpoint MUST include the `/repos/` +prefix: `/repos/{owner}/{repo}/issues//comments` (without /repos/ it 404s). `tea api` already +prints JSON to stdout — do NOT pass `-o json` (that writes the body to a file named `json`). + +Code link format (verified to render / resolve to HTTP 200 when SHA is remote-present): + https://git.adlee.work/alee/vigilar/src/commit//#L-L # path segment src/commit [VERIFIED] Eligibility metadata fetch: - tea pulls --remote origin --fields state,draft,title,mergeable,base,head,body,diff -o json + tea pulls --remote origin --fields index,state,draft,title,mergeable,base,head,body -o json + # NOTE: tea v0.14.1 returns empty `diff`/`patch` fields even when requested — do not rely on them. -Diff source (preferred, local): - git diff origin/...origin/ +Diff source (preferred, local — the reliable source in this tea build): + git diff origin/...origin/ # base/head come from the PR JSON's base/head fields -Login context: the `alee` tea login is NOT default (tea login list shows default=false). +Already-reviewed check: + tea pulls review-comments --remote origin -o json # scan body for "### Code review" marker + +Login context: the `alee` tea login is NOT default (tea login list shows default=false). [VERIFIED: --remote origin resolves context; --login alee fallback not needed] ``` - [ ] **Step 2: Write the plugin README** @@ -432,7 +448,7 @@ Vigilar is hosted on a self-hosted Gitea instance (`git.adlee.work`), not GitHub On a branch with an open Gitea pull request against `main`: - /code-review-tea + /code-review-tea:code-review Claude will: - Verify the PR is reviewable (skip closed/draft/trivial/already-reviewed) @@ -449,18 +465,28 @@ Claude will: - The `alee` login need NOT be the default — the command passes `--remote origin` to resolve context - An open Gitea pull request to review +## Load (this plugin is NOT auto-discovered) + +A plugin placed in `/.claude/plugins//` is not auto-discovered by Claude Code. Load it explicitly: + + # per-session (recommended): + claude --plugin-dir .claude/plugins/code-review-tea + + # persistent, user-scope (across all your projects): + claude plugin init code-review-tea # installs to ~/.claude/skills/code-review-tea/ + ## Differences from upstream | Area | Upstream (GitHub) | This fork (Gitea) | |---|---|---| | CLI | `gh` | `tea` | | PR metadata | `gh pr view` | `tea pulls --fields ... -o json` | -| Diff | `gh pr diff` | `git diff origin/...origin/` (local) | -| Comment post | `gh pr comment --body` | `tea api {owner}/{repo}/issues//comments -f body=@-` | -| Code link | `github.com/o/r/blob/SHA/path#L` | `git.adlee.work/alee/vigilar/src/commit/SHA/path#L` | +| Diff | `gh pr diff` | `git diff origin/...origin/` (local; `tea pulls --fields diff` returns empty in 0.14.1) | +| Comment post | `gh pr comment --body` | `tea api /repos/{owner}/{repo}/issues//comments -F body=@-` (`-F` typed field reads stdin; `/repos/` prefix required) | +| Code link | `github.com/o/r/blob/SHA/path#L` | `git.adlee.work/alee/vigilar/src/commit/SHA/path#L` (SHA must be remote-present) | | Inline line comments | (upstream posts one summary comment) | NOT attempted — single summary comment only (Gitea inline-comment API needs `tea api` review endpoints; out of scope) | -See `.claude-plugin/commands/RESOLVED_INVOCATION.md` for the verified `tea` calls. +See `commands/RESOLVED_INVOCATION.md` for the verified `tea` calls. ```` - [ ] **Step 3: Append a Code Review subsection to CLAUDE.md** @@ -469,13 +495,13 @@ Append to `CLAUDE.md`, as a new `## Code Review` section near the existing `## C ```markdown ## Code Review -- `/code-review-tea` — review the current Gitea PR (project-local fork of Anthropic's code-review plugin, adapted from `gh` to `tea`; credit: Boris Cherny). See `.claude/plugins/code-review-tea/README.md`. +- `/code-review-tea:code-review` — review the current Gitea PR (project-local fork of Anthropic's code-review plugin, adapted from `gh` to `tea`; credit: Boris Cherny). Load via `claude --plugin-dir .claude/plugins/code-review-tea`. See `.claude/plugins/code-review-tea/README.md`. ``` - [ ] **Step 4: Commit** ```bash -git add .claude/plugins/code-review-tea/README.md .claude/plugins/code-review-tea/.claude-plugin/commands/RESOLVED_INVOCATION.md CLAUDE.md +git add .claude/plugins/code-review-tea/README.md .claude/plugins/code-review-tea/commands/RESOLVED_INVOCATION.md CLAUDE.md git commit -m "docs(plugin): document code-review-tea fork and CLAUDE.md note" ``` @@ -517,7 +543,7 @@ Expected: a new PR index. Record it as `$SMOKE`. - [ ] **Step 3: Run the adapted command against the scratch PR** -Run `/code-review-tea` and provide `$SMOKE` as the PR index. Watch for each stage: +Run `/code-review-tea:code-review` and provide `$SMOKE` as the PR index. Watch for each stage: - eligibility fetch via `tea pulls` ✓ - CLAUDE.md gather ✓ - summary ✓ @@ -553,9 +579,9 @@ git rm vigilar/_scratch_review.py 2>/dev/null && git commit -m "chore: remove re - [ ] **Step 7: Update RESOLVED_INVOCATION.md with the end-to-end confirmation** -Edit `.claude/plugins/code-review-tea/.claude-plugin/commands/RESOLVED_INVOCATION.md` to replace each `` placeholder with the actual confirmed result from this smoke run (stdin worked vs file fallback, `--remote origin` resolved, `src/commit` rendered). Commit: +Edit `.claude/plugins/code-review-tea/commands/RESOLVED_INVOCATION.md` to replace each `` placeholder with the actual confirmed result from this smoke run (stdin worked vs file fallback, `--remote origin` resolved, `src/commit` rendered). Commit: ```bash -git add .claude/plugins/code-review-tea/.claude-plugin/commands/RESOLVED_INVOCATION.md +git add .claude/plugins/code-review-tea/commands/RESOLVED_INVOCATION.md git commit -m "docs(plugin): confirm resolved tea invocation from smoke test" ``` @@ -565,7 +591,7 @@ git commit -m "docs(plugin): confirm resolved tea invocation from smoke test" 1. **Spec coverage** — The user asked: make the `code-review` plugin work on Gitea (`tea`), name it `code-review-tea`, give credit to the original. → Tasks 1–6: scaffold, resolve invocation, write command, enable, document, smoke-test. Credit appears in `plugin.json` (contributors), command file header comment, and README. ✓ No hot-path review (dropped per revised scope). ✓ 2. **Placeholder scan** — Command file content is verbatim in Task 3 (full file with 4-backtick fence preserving inner 3-backtick fences). Test bodies are concrete (`is_armed` scratch bug). The only "verify which form" items (Task 4 Step 3 plugin discovery; Task 5 RESOLVED_INVOCATION `` placeholders) are real, intentional verifications — each has a concrete fallback and a step that resolves it, not an unfilled "TBD". -3. **Name consistency** — `code-review-tea` used consistently in directory, `plugin.json` `name`, settings key, README title, CLAUDE.md note, and `/code-review-tea` invocation. No leftover `supertea` or `code-review-gitea`. +3. **Name consistency** — `code-review-tea` used consistently in directory, `plugin.json` `name`, README title, and CLAUDE.md note. Command invoked as `/code-review-tea:code-review` (namespaced). No leftover `supertea` or `code-review-gitea`. No stale `.claude-plugin/commands/` nested paths. 4. **Credit correctness** — Original author Boris Cherny / Anthropic credited in `plugin.json` contributors, command file header comment, and README "Credit" section. Fork relationship stated (upstream = GitHub/gh-only). 5. **Uncertainty surfaced honestly** — `tea comment` vs `tea api` posting (Task 2 verifies, fallback documented); local-plugin `enabledPlugins` key form (Task 4 Step 3 verifies); Gitea `src/commit` path segment (Task 2/6 verifies render); `plugin.json` unknown-key tolerance (Task 1 Step 1 note + Task 6 smoke exercises loading). None silently assumed. ```