Skip to content

Add zen_review and zen_review_resume MCP tools#5

Open
eslerm wants to merge 5 commits intomgreau:mainfrom
eslerm:pr/mcp-review-tools
Open

Add zen_review and zen_review_resume MCP tools#5
eslerm wants to merge 5 commits intomgreau:mainfrom
eslerm:pr/mcp-review-tools

Conversation

@eslerm
Copy link
Copy Markdown
Contributor

@eslerm eslerm commented Mar 20, 2026

  • Auto-install /review-pr Claude command before launching sessions
  • Extract worktree creation logic into shared internal/review package
  • Add MCP zen_review and zen_review_resume tools
  • Refactor cmd/review.go to use shared review.CreateWorktree()

eslerm and others added 5 commits March 16, 2026 11:49
Prevent indefinite hangs when GitHub is slow or unreachable by setting
a 30s timeout on the OAuth2 HTTP client and all exec.CommandContext calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When `zen review` or `zen review resume` launches Claude with `/review-pr`,
the command file may not exist if the user skipped `zen setup`. This adds
`ensureClaudeCommand()` which silently installs from the embedded FS when
missing, so the user never hits an unknown-command error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract worktree creation logic from cmd/review.go into shared
internal/review package so both the CLI and MCP server can use it.

- internal/review: CreateWorktree() and DetectRepo() shared functions
- MCP zen_review: creates worktree for a PR (not read-only, open-world)
- MCP zen_review_resume: returns worktree path + sessions (read-only)
- Refactor cmd/review.go runReview() to use review.CreateWorktree()
- Tests for missing params and non-existent worktree cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The codebase had a smart lock removal function (removeStaleLock) that
checks if the holding PID is still alive before removing, but three
call sites were using a bare os.Remove which could race with concurrent
git operations. Export RemoveStaleLock and use it everywhere.

Affected: internal/review, cmd/work, internal/reconciler/setup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ensureClaudeCommand: use os.UserHomeDir() instead of os.Getenv("HOME")
  for robustness in unusual environments
- DetectRepo: replace single-field match struct with plain []string

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Owner

@mgreau mgreau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal/review/review.go lines 81 & 89: git commands ignore context cancellation

The git fetch and git worktree add calls use exec.Command instead of exec.CommandContext, so a cancelled context (e.g. MCP client disconnects) won't interrupt them. Since GitMu is still held while they run, a hung git operation blocks all other worktree operations indefinitely.

Suggested fix:

fetchCmd := exec.CommandContext(ctx, "git", "fetch", "origin", fmt.Sprintf("+pull/%d/head:%s", prNumber, branchName))
// ...
wtCmd := exec.CommandContext(ctx, "git", "worktree", "add", worktreePath, branchName)

Copy link
Copy Markdown
Owner

@mgreau mgreau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal/mcp/server.go: zen_review_resume missing repo parameter

The tool searches all worktrees by pr_number only. If two configured repos both have a PR #N (e.g. a fork and upstream), handleReviewResume returns whichever ListAll surfaces first — silently wrong.

Suggest adding an optional repo parameter to match the pattern already established by zen_review:

mcpgo.WithString("repo", mcpgo.Description("Short repo name (auto-detected if omitted)")),

Then filter worktrees by repo name before matching on PRNumber.

Copy link
Copy Markdown
Owner

@mgreau mgreau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal/review/review.go + internal/mcp/tools.go: Result doesn't distinguish new vs resumed

When CreateWorktree returns early because a worktree already exists, the Result is identical to a freshly-created one. MCP consumers (e.g. an agent calling zen_review) can't tell whether to say "created" or "resumed", and can't know they skipped the fetch/inject steps.

Suggest adding a boolean field to Result:

type Result struct {
    WorktreePath string `json:"worktree_path"`
    PRNumber     int    `json:"pr_number"`
    Title        string `json:"title"`
    Author       string `json:"author"`
    Resumed      bool   `json:"resumed"` // true if worktree already existed
}

Set Resumed: true in the early-return path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants