Skip to content

feat: add command for local code review#226

Merged
sohankshirsagar merged 7 commits into
mainfrom
sohan/tusk-review-cli
Apr 23, 2026
Merged

feat: add command for local code review#226
sohankshirsagar merged 7 commits into
mainfrom
sohan/tusk-review-cli

Conversation

@sohankshirsagar

@sohankshirsagar sohankshirsagar commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Introduces tusk review, a new top-level CLI parent command for the Tusk AI code review against the
user's local working tree — before they push or open a PR. Ships two subcommands: tusk review run
(submit a review of the current working tree) and tusk review status <run-id> (check an existing
run, with --watch to block).

tusk review run generates a binary git patch, uploads it via three RPCs on the new
CodeReviewService (CreateLocalCodeReviewRun, GetCodeReviewRunStatus, CancelCodeReviewRun),
polls for completion, and writes the backend-rendered result to stdout.

The CLI is a dumb pipe for presentation: the backend renders both the human-readable text and
the JSON. The only structured field the CLI interprets is the run-status enum (so it knows when to
stop polling).

Command structure

tusk review               # parent group — prints help + subcommand list
tusk review run           # submit a review of the working tree
tusk review run --no-poll # submit and exit immediately with the run id
tusk review status <id>   # one-shot status snapshot
tusk review status <id> --watch  # block until terminal state

Changes

CLI commands

  • New cmd/review.go — parent reviewCmd (group only, no RunE), shared flag binding, runReview
    function, result writers.
  • New cmd/review_run.goreviewRunCmd (Use: "run", RunE: runReview) registered under
    reviewCmd.
  • New cmd/review_status.gotusk review status <run-id> (single snapshot by default; --watch
    to block, reusing the same poll loop).
  • New cmd/short_docs/review/overview.md — parent-group help (intro + subcommand list + auth
    pointer).
  • New cmd/short_docs/review/run.md — full docs for the run subcommand.
  • cmd/errors.goExitCodeError wrapper so specific user-actionable failures surface as exit
    code 2.
  • main.go — unwraps ExitCodeError to pick the right os.Exit value (non-wrapped errors still
    exit 1).

RPC client

  • New internal/api/code_review.go — three RPC methods plus five sentinel error types the command
    layer switches on for exit-code mapping and verbatim message rendering:
    • RateLimitError (with RetryAfterIso8601), RepoNotFoundError, PatchInvalidError,
      NoSeatError, NotAuthorizedError.
  • internal/api/client.go — added CodeReviewServiceAPIPath and a makeCodeReviewServiceRequest
    helper, mirroring the existing pattern for TestRunService / ClientService.

Patch builder + preflight (all in internal/review/)

  • patch.goBuildPatch generates a binary-clean patch:
    • git add -N for untracked paths (signal-safe cleanup via RegisterCleanup).
    • Clone-pivot resolution order: @{u} upstream tracking ref → git merge-base origin/HEAD HEAD fallback.
    • git diff --binary <pivot> + git diff --numstat -z <pivot> against the working tree.
    • Submodule detection, size caps, top-contributors rendering for oversize-patch errors.
    • Captures branch_name (git branch --show-current) and local_head_sha (git rev-parse HEAD)
      for the request.
  • poll.go — decoupled spinner (100ms redraw, TTY only) from backend polling (5s default). Spinner
    ticks locally off cached display_message; backend is hit only every 5s for heartbeat and
    terminal-status checks.
  • filter_test.go — pins list sizes (84 / 9 / 37) as a drift-detector against the backend list.

Summary of git commands tusk review run executes

  1. Locate the repo (rev-parse --show-toplevel, rev-parse --git-dir).
  2. Validate it's in a reviewable state (no mid-rebase/merge/cherry-pick/revert flags in .git/).
  3. Identify the repo for the backend (remote get-url origin, unless --repo is passed).
  4. Resolve the clone pivot (@{u}merge-base origin/HEAD HEAD).
  5. git add -N untracked files so they show up in the diff.
  6. git diff --binary <pivot> + git diff --numstat -z <pivot>.
  7. Cleanup: git restore --staged <paths> to undo the intent-to-add (registered via
    RegisterCleanup so Ctrl+C mid-build still cleans up).

Request shape

CreateLocalCodeReviewRunRequest carries:

  • owner_name, repo_name
  • last_pushed_sha — the clone pivot (what the sandbox fetches); guaranteed reachable on origin.
  • patch — the delta between last_pushed_sha and the working tree.
  • branch_name — for backend PR lookup + seat resolution.
  • local_head_sha — informational / audit.
  • min_severity (optional), cli_version.

Client-side limits (enforced before upload)

The CLI aborts before sending the request when the patch exceeds any of these caps. The user gets
an explicit error with a top-contributors list and remediation text (exit code 2), not a silent
server-side reject:

  • Hard caps: 10,000 changed lines / 200 files / 1 MiB patch bytes.
  • Soft warnings (stderr, continues): 2,000 lines / 50 files.
  • Filter always applied, regardless of patch size — default skip list matches the backend's
    shouldSkipFile, plus .tuskignore and --exclude / --include flags.
  • Submodules explicitly refused client-side rather than letting them slip through into an opaque
    git apply failure in the sandbox.
  • Empty-after-filter patches exit 0 with "Nothing to review" — no wasted server-side run.

Exit codes

  • 0 — review completed (issues may or may not be found), or nothing to review after filtering.
  • 1 — run failed (sandbox error, patch-apply failure, timeout, auth, network).
  • 2 — user-actionable pre-flight or request failure:
    • mid-rebase / merge / cherry-pick / revert refused
    • couldn't determine clone pivot (no @{u}, no origin/HEAD, shallow clone)
    • patch too large (bytes / lines / files)
    • submodule changes in patch
    • repo not connected / not under caller's org
    • rate limit hit (with retry_after)
    • no active seat for the resolved identity
    • plan doesn't entitle the client to code review

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Found 3 issues

Comment thread cmd/review.go
Comment thread internal/review/patch.go Outdated
Comment thread internal/review/poll.go Outdated
Comment thread cmd/review.go
Comment thread go.mod Outdated
Comment thread internal/review/poll.go Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

8 issues found across 14 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/review.go">

<violation number="1" location="cmd/review.go:43">
P2: `reviewCmd` sets `SilenceUsage: true` but not `SilenceErrors: true`. When `errSilentFail` is returned (whose `Error()` returns `""`), Cobra will still print `"Error: \n"` to stderr because `SilenceErrors` defaults to `false`. Add `SilenceErrors: true` to prevent the stray output line after the backend's failure text has already been written.</violation>

<violation number="2" location="cmd/review.go:123">
P2: Build the local patch before cloud setup; current ordering can fail `tusk review` on auth/network even when there is nothing to review.</violation>
</file>

<file name="internal/review/preflight.go">

<violation number="1" location="internal/review/preflight.go:100">
P2: Detached-HEAD check swallows all git errors as a warning instead of only handling the detached-head exit case.</violation>
</file>

<file name="internal/review/patch.go">

<violation number="1" location="internal/review/patch.go:106">
P2: BuildPatch ignores its context, so git commands cannot be canceled or timed out by callers.</violation>

<violation number="2" location="internal/review/patch.go:305">
P2: Use tab-delimited parsing for `git diff --numstat`; `strings.Fields` can corrupt file paths (spaces/renames).</violation>
</file>

<file name="go.mod">

<violation number="1" location="go.mod:5">
P1: Don't commit the local replace directive; it breaks builds outside the sibling-repo development layout.</violation>
</file>

<file name="internal/review/poll.go">

<violation number="1" location="internal/review/poll.go:46">
P2: TTY detection checks `os.Stderr` instead of the configured `opts.Stderr`, so polling output mode can be wrong when stderr is redirected.</violation>
</file>

<file name="cmd/review_status.go">

<violation number="1" location="cmd/review_status.go:52">
P2: `status --watch` should not cancel the backend run on Ctrl+C; it should detach local polling only.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread go.mod Outdated
Comment thread cmd/review.go Outdated
Comment thread internal/review/preflight.go Outdated
Comment thread internal/review/patch.go Outdated
Comment thread internal/review/patch.go
Comment thread internal/review/poll.go Outdated
Comment thread cmd/review_status.go
Comment thread cmd/review.go
@ghost

ghost commented Apr 22, 2026

Copy link
Copy Markdown

Tip

New to Tusk? Learn more here.

Code Review

Tusk Review: No issues found


Unit Tests

Tusk is paused for this PR

View output

Last run on 9a52f20. Comment @use-tusk generate to create a test suite on the latest commit. Configure here.

View check history

Commit Unit Tests Created (UTC)
9a52f20 Tusk is paused for this PR · Output Apr 22, 2026 4:58AM

@sohankshirsagar sohankshirsagar added the Tusk - Pause For Current PR Pause Tusk for future commits in the current PR label Apr 22, 2026
Comment thread internal/review/patch.go
Comment thread cmd/review_status.go
Comment thread cmd/review.go
Comment thread internal/review/filter.go
Comment thread cmd/short_docs/review/overview.md

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Found 2 issues

Comment thread internal/review/patch.go Outdated
Comment thread internal/review/preflight.go Outdated
Comment thread cmd/review.go

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 89b76b5. Configure here.

Comment thread internal/review/filter.go
@sohankshirsagar sohankshirsagar requested a review from jy-tan April 23, 2026 21:00
@jy-tan

jy-tan commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Add review docs

@sohankshirsagar sohankshirsagar merged commit 27d6cdb into main Apr 23, 2026
15 checks passed
@sohankshirsagar sohankshirsagar deleted the sohan/tusk-review-cli branch April 23, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Tusk - Pause For Current PR Pause Tusk for future commits in the current PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants