How to Use the ReviewStore Rust Library API for Programmatic Review Sessions in tuicr

The ReviewStore API in agavra/tuicr exposes a programmatic interface for creating, querying, and mutating code review sessions without launching the TUI, supporting atomic operations via add_comment, get_review, and save_review methods.

The tuicr crate ships a pure-Rust library in src/review_store.rs that enables developers to automate code review workflows. By leveraging the ReviewStore Rust library API, you can integrate persisted review sessions into custom tooling, CI pipelines, or headless automation scripts.

Core Types and Architecture

The library centers on a few key structs that manage persistence and in-memory representation.

ReviewStore and SessionRef

The ReviewStore struct acts as the main facade for managing the directory where sessions are stored. It provides constructors and methods to list, load, and mutate sessions. A SessionRef serves as an opaque handle to a single session file, implemented as a PathBuf wrapper.

According to the source in src/review_store.rs#L10-L41, you initialize the store using either the default platform directory or a custom path.

Data Models

  • SessionSummary: Lightweight metadata returned by listing methods, containing the slug, kind, timestamps, comment count, and file count (src/review_store.rs#L59-L71).
  • ReviewSession: The full in-memory representation of a session, including files, comments, and reviewed hunk state (src/model/review.rs#L89-L124).
  • Comment: Individual comment objects with content, type, author, lifecycle state, and optional commit scoping (src/model/comment.rs#L55-L78).

Request Types

The AddCommentRequest struct describes a new draft comment, specifying the target, content, type, author, and optional commit SHA (src/review_store.rs#L73-L88). The CommentTarget enum determines attachment points: review-level, file-level, single line, or line-range (src/review_store.rs#L90-L102).

Initializing the Store

You have two entry points for creating a ReviewStore instance.

ReviewStore::new() builds a store using the platform-default reviews directory, typically ~/.local/share/tuicr/reviews on Linux (src/review_store.rs#L16-L21).

ReviewStore::with_reviews_dir(dir) allows you to override the storage location, which is essential for tests or isolated tooling environments (src/review_store.rs#L22-L28).

use tuicr::review_store::ReviewStore;
use std::path::Path;

// Use the default platform directory
let store = ReviewStore::new();

// Or use a temporary isolated directory
let temp_dir = std::env::temp_dir().join("my_tuicr_reviews");
let store = ReviewStore::with_reviews_dir(temp_dir);

Querying Sessions

Listing Sessions

Use list_sessions_for_repo(selector) to retrieve all persisted sessions belonging to a specific checkout path or forge coordinate (e.g., owner/repo) (src/review_store.rs#L34-L45).

Use list_all_sessions() to retrieve every session ordered newest-first, including both local and PR sessions (src/review_store.rs#L47-L55).

use std::path::Path;

// List sessions for a specific repository checkout
let repo_path = Path::new("/home/me/project");
let sessions = store
    .list_sessions_for_repo(repo_path)
    .expect("failed to list sessions");

// Print session metadata
for s in sessions {
    println!(
        "{} | {} | {} comments | {} files",
        s.slug, s.kind.id(), s.comment_count, s.file_count
    );
}

Resolving PR Sessions

The resolve_pr_session(slug) method converts a PR slug (e.g., gh:owner/repo/pr/42) into a SessionRef if the session exists on disk (src/review_store.rs#L59-L66).

let slug = "gh:agavra/tuicr/pr/42";
if let Some(session_ref) = store
    .resolve_pr_session(slug)
    .expect("failed to resolve PR")
{
    let pr_review = store.get_review(&session_ref).expect("could not load PR session");
    // Work with pr_review...
}

Loading and Inspecting Sessions

Call get_review(&session_ref) to load the full ReviewSession from disk (src/review_store.rs#L67-L71).

// Assuming 'first' is a SessionSummary from list_sessions_for_repo
let session_ref = &first.session_ref;
let mut review = store
    .get_review(session_ref)
    .expect("could not load session");

// Inspect review-level comments
for c in &review.review_comments {
    println!("{}: {}", c.author, c.content);
}

// Inspect file-level and line-level comments
for (path, file_rev) in &review.files {
    for c in &file_rev.file_comments {
        println!("File {}: {}", path.display(), c.content);
    }
    for (line, comments) in &file_rev.line_comments {
        for c in comments {
            println!("Line {}:{} - {}", path.display(), line, c.content);
        }
    }
}

Mutating Sessions

Adding Comments

The add_comment(&session_ref, request) method appends a local draft comment to a session and persists it atomically. Internally, it delegates to add_comment_to_session, which validates content, creates a Comment object, inserts it based on the CommentTarget, and updates the session's updated_at timestamp (src/review_store.rs#L72-L84).

use tuicr::review_store::{AddCommentRequest, CommentTarget};
use tuicr::model::{CommentType, LineSide};

let req = AddCommentRequest {
    target: CommentTarget::Line {
        path: std::path::PathBuf::from("src/main.rs"),
        line: 42,
        side: LineSide::New,
    },
    content: "Consider extracting this block into a helper function".into(),
    comment_type: CommentType::from_id("suggestion"),
    author: "my-bot".into(),
    commit_id: None,
};

store
    .add_comment(&session_ref, req)
    .expect("failed to add comment");

Saving Modified Sessions

After in-place modifications to a ReviewSession, call save_review(&session) to persist changes and receive an updated SessionRef (src/review_store.rs#L86-L90).

// Modify the session in-place
review.session_notes = Some("Reviewed on 2026-08-02".into());

// Persist changes
let new_ref = store
    .save_review(&review)
    .expect("could not save session");

println!("Session saved at {}", new_ref.path().display());

Persistence Mechanics and Thread Safety

All I/O delegates to crate::persistence::storage, which writes sessions under $XDG_DATA_HOME/tuicr/reviews/ (or the custom directory) and maintains manifests for fast listing (src/review_store.rs#L92-L98).

add_comment and save_review use storage::update_session_in_dir and storage::save_session_in_dir, which lock the session file, merge external changes, and perform atomic rename operations on temporary files. This ensures that concurrent CLI tools and custom scripts do not corrupt session data.

Summary

  • Initialize the store with ReviewStore::new() or with_reviews_dir() to control storage location.
  • Query existing sessions using list_sessions_for_repo(), list_all_sessions(), or resolve_pr_session().
  • Load full session data with get_review() to access ReviewSession and its Comment collections.
  • Mutate sessions by constructing AddCommentRequest with CommentTarget variants and calling add_comment().
  • Persist changes atomically via save_review(), which handles file locking and conflict resolution.

Frequently Asked Questions

How do I configure a custom storage directory for ReviewStore?

Use the ReviewStore::with_reviews_dir(path) constructor instead of new(). This overrides the default platform directory and is useful for testing or isolated tooling environments. The method is defined in src/review_store.rs#L22-L28.

Is the ReviewStore API thread-safe for concurrent access?

Yes. The add_comment and save_review methods use file locking via storage::update_session_in_dir and atomic rename operations. Concurrent processes (such as multiple CLI invocations or background tasks) can safely read and write sessions without data corruption.

What is the difference between CommentTarget::Line and CommentTarget::LineRange?

CommentTarget::Line attaches a comment to a single specific line with a LineSide (Old or New), while CommentTarget::LineRange attaches to a span of lines. Both variants are used when constructing AddCommentRequest to specify where the comment appears in the diff.

How do I add a comment to a specific commit rather than the full diff?

Include the optional commit_id field in your AddCommentRequest. When commit_id is Some(sha), the comment targets that specific commit; when None, it applies to the full diff session. This aligns with the Comment struct's commit scoping as defined in [src/model/comment.rs](https://github.com/agavra/tuicr/blob/main/src/model/comment.rs).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →