Skip to content

Add per-commit review mode#444

Closed
ruifm wants to merge 3 commits into
agavra:mainfrom
ruifm:main
Closed

Add per-commit review mode#444
ruifm wants to merge 3 commits into
agavra:mainfrom
ruifm:main

Conversation

@ruifm

@ruifm ruifm commented Jul 3, 2026

Copy link
Copy Markdown

Adds an initial per-commit review workflow for local commit ranges. Reviewers can run tuicr --commits -r main..HEAD, move through commits one at a time, leave comments on each commit’s diff or commit message, and export feedback grouped by commit for agent-friendly review output.

Example export:

## Local tuicr Comments

### Commit abc1234 - Refactor auth token parsing

Author: Jane Developer

Commit message:

```text
Refactor auth token parsing

Move token parsing into a dedicated helper before changing validation behavior.

1. [SUGGESTION] commit message:1 - Make the subject imperative.
2. [NOTE] src/auth.rs:42 - This split makes the follow-up behavior change easier to review.

### Commit def5678 - Reject expired auth tokens

Author: Jane Developer

Commit message:

Reject expired auth tokens

3. [ISSUE] src/auth.rs:88 - This should handle missing expiry claims explicitly.

Advantages of per-commit mode:

  • Keeps feedback attached to the exact commit that introduced the code.
  • Makes commit hygiene review practical, including comments on commit messages.
  • Helps reviewers evaluate whether each commit is internally coherent and reviewable.
  • Produces export output grouped by commit, which is easier for authors and coding agents to
    apply without mixing feedback across unrelated changes.
  • Still supports normal line, range, file, and review-level comments, but scoped to the
    active commit.

Copilot AI review requested due to automatic review settings July 3, 2026 13:57

Copilot AI 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.

Pull request overview

Adds an initial per-commit local review mode for commit ranges (tuicr --commits -r base..head), including per-commit comment scoping, per-commit export grouping, and updated CLI/UI/docs to support the workflow.

Changes:

  • Introduces a new session diff source (PerCommitRange) plus CommitReview storage to persist comments/files per commit while mirroring the active commit into the existing flat session fields.
  • Adds CLI flag parsing (--commits) and commit-cycling behavior in the TUI, plus slug/manifest updates for persisted sessions.
  • Extends export/CLI output to include per-commit grouped markdown and comment JSON with commit metadata.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/ui/status_bar.rs Displays per-commit mode status (active commit index / total).
src/ui/help_popup.rs Updates help text to reflect per-commit behavior for cycling commits.
src/slug.rs Adds slug source + parsing/formatting for per-commit sessions (per-commit/<base>..<head>).
src/review_store.rs Routes review add to the active commit for per-commit sessions + adds tests.
src/review_cli.rs Emits per-commit comment listings with commit metadata when in per-commit sessions + adds tests.
src/persistence/manifest.rs Computes comment/file counts correctly for per-commit sessions + labels per-commit diff source.
src/output/markdown.rs Adds per-commit grouped markdown export with commit message context + adds tests.
src/model/review.rs Introduces CommitReview and SessionDiffSource::PerCommitRange; updates session comment/review counters.
src/model/mod.rs Re-exports CommitReview.
src/main.rs Threads per_commit startup option into app initialization.
src/handler.rs Ensures active per-commit state is synced before export/copy-and-quit; updates commit cycling behavior.
src/cli.rs Adds --commits flag and conflict validation; adds CLI parsing tests.
src/app.rs Implements per-commit mode core behavior (session creation/loading, commit switching, syncing, reload/export integration) + adds tests.
README.md Documents --commits usage and per-commit export behavior.
docs/KEYBINDINGS.md Documents commit cycling behavior updates for per-commit mode.
AGENTS.md Documents how per-commit mode is stored/exported and how review CLI behaves with per-commit sessions.

Comment thread src/app.rs
Comment on lines +2626 to +2641
let mut empty_lines = Vec::new();
for (line, comments) in &mut review.line_comments {
if let Some(index) = comments.iter().position(|comment| comment.id == id) {
comments.remove(index);
if comments.is_empty() {
empty_lines.push(*line);
}
for line in empty_lines {
review.line_comments.remove(&line);
}
return true;
}
}
for line in empty_lines {
review.line_comments.remove(&line);
}
Comment thread src/review_cli.rs
Comment on lines +500 to +513
let mut files: Vec<_> = commit_review.files.iter().collect();
files.sort_by_key(|(path, _)| {
if path.as_path() == Path::new("Commit Message") {
String::new()
} else {
path.to_string_lossy().to_string()
}
});
for (path, review) in files {
let path_display = if path.as_path() == Path::new("Commit Message") {
"commit message".to_string()
} else {
path.to_string_lossy().to_string()
};
Comment thread src/output/markdown.rs
Comment on lines +575 to +589
let mut files: Vec<_> = review.files.iter().collect();
files.sort_by_key(|(path, _)| {
if path.as_path() == std::path::Path::new("Commit Message") {
String::new()
} else {
path.to_string_lossy().to_string()
}
});

for (path, file_review) in files {
let path_str = if path.as_path() == std::path::Path::new("Commit Message") {
"commit message".to_string()
} else {
path.display().to_string()
};
Comment thread src/review_cli.rs
Comment on lines +788 to +800
let mut msg_review = FileReview::new(PathBuf::from("Commit Message"), FileStatus::Added, 0);
msg_review.add_line_comment(
1,
Comment::new(
"Commit message note".to_string(),
CommentType::Suggestion,
Some(LineSide::New),
),
);
second
.files
.insert(PathBuf::from("Commit Message"), msg_review);
session.per_commit_reviews.insert(second_id.clone(), second);
Comment thread src/output/markdown.rs
Comment on lines +1190 to +1202
let mut msg_review = FileReview::new(PathBuf::from("Commit Message"), FileStatus::Added, 0);
msg_review.add_line_comment(
1,
Comment::new(
"Subject should be imperative".to_string(),
CommentType::Suggestion,
Some(LineSide::New),
),
);
first
.files
.insert(PathBuf::from("Commit Message"), msg_review);
session.per_commit_reviews.insert(oldest.clone(), first);
@thiblahute

Copy link
Copy Markdown
Contributor

We already have a very similar workflow and I added #442 yesterday which is very similar to what you are doing, maybe you approach is better though.

(Not that I have been using tuicr to do per commit review for a long time)

@agavra

agavra commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Thanks @thiblahute and @ruifm - I prefer the approach in #442 that fixes the bugs with the existing per-commit reviews rather than building workflow into tuicr itself. Workflows are highly personal, and you could code this up in userspace IIUC.

@ruifm ruifm closed this Jul 7, 2026
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.

4 participants