Skip to main content

Repo

Struct Repo 

Source
pub struct Repo {
    pub style: Style,
    pub branch_prefix: String,
    pub state_store: StateStore,
    pub followups: Followups,
    /* private fields */
}

Fields§

§style: Style§branch_prefix: String§state_store: StateStore§followups: Followups

Implementations§

Source§

impl Repo

Source

pub fn open(root: impl AsRef<Path>, cfg: &Config) -> Result<Self>

Source

pub fn root(&self) -> &Path

Source

pub fn clean(&self, text: &str) -> Result<String>

Scrub, then verify. A leak here reaches GitHub, so it is a hard error rather than a warning: silent partial compliance is how a style rule erodes over a long run.

Source

pub fn clean_body(&self, text: &str) -> Result<String>

Clean, and hold to a length budget. For anything a model wrote.

Source

pub fn clean_issue_body(&self, text: &str) -> Result<String>

The same, with an issue’s far larger budget and its exemption for code.

Source

pub fn clean_title(&self, text: &str) -> Result<String>

The single transform every outbound title goes through.

Scrub first, clip second, and never the other way round. Clipping first lets the scrub lengthen the result past the budget (an em dash becomes two characters), so a second pass would clip again and produce a different string. That broke follow-up deduplication silently: the lookup searched for one title while GitHub had stored another, no match was ever found, and a fresh duplicate issue was filed every review round. Doing it in this order makes the transform idempotent, which the tests assert.

Source

pub fn git(&self, args: &[&str]) -> Result<String>

Source

pub fn git_at(&self, cwd: Option<&Path>, args: &[&str]) -> Result<String>

Source

pub fn git_try(&self, args: &[&str]) -> String

Run git, tolerating failure. Returns whatever landed on stdout.

Source

pub fn git_try_at(&self, cwd: Option<&Path>, args: &[&str]) -> String

Source

pub fn default_branch(&self, configured: &str) -> String

The base branch the remote actually points at, rather than assuming main. Falls back to the configured value when there is no origin.

Source

pub fn branch_for_issue(&self, issue: i64) -> String

Source

pub fn branch_for_pr(&self, number: i64) -> String

Source

pub fn known_branches(&self) -> BTreeMap<String, BranchRecord>

Source

pub fn record_branch(&self, branch: &str, kind: &str, number: i64)

Source

pub fn forget_branch(&self, branch: &str)

Source

pub fn worktree_add(&self, issue: i64, base: &str) -> Result<(PathBuf, String)>

Isolate an issue so a failed run cannot poison the next one’s base.

Source

pub fn worktree_remove(&self, issue: i64)

Source

pub fn worktree_for_pr(&self, pr: &PrView) -> Result<(PathBuf, String)>

Check an existing PR branch out into an isolated worktree.

Source

pub fn worktree_for_pr_head(&self, number: i64) -> Result<PathBuf>

Check a pull request’s head out read only, detached, with no branch.

Fetches refs/pull/N/head, which GitHub serves for every pull request including one from a fork whose branch is not in this repository at all. That is what makes reviewing an outside contribution possible when pushing to it is not.

Detached on purpose. Review only mode has nothing to push, and a branch would only invite something to try.

Source

pub fn release_review_worktree(&self, number: i64)

Source

pub fn release_pr_worktree(&self, number: i64)

Source

pub fn base_ref(&self, cwd: &Path, base: &str) -> String

What to diff against: the remote tracking branch when it resolves, the local branch when it does not.

This is not a nicety. Every “did the agent do anything” check hangs off this ref, and git log against a ref that does not exist fails silently and reads as “no commits”. A checkout whose origin/main was never fetched would report every implementation as abandoned and throw the work away.

Source

pub fn has_changes(&self, cwd: &Path, base: &str) -> bool

Source

pub fn diff_stat(&self, cwd: &Path, base: &str) -> String

Source

pub fn rewrite_commits_if_needed(&self, cwd: &Path, base: &str) -> Result<()>

Scrub commit messages that slipped past the prompt.

git filter-branch calls back into this same binary, so there is no interpreter to find and no second copy of the rules to drift.

Source

pub fn push(&self, cwd: &Path, branch: &str) -> Result<()>

Push by explicit refspec from HEAD.

A resumed PR is checked out under a local name (pr-N) that does not match its remote branch, so pushing by branch name would resolve the wrong local ref or fail outright.

Source

pub fn gh(&self, args: &[&str]) -> Result<String>

Source

pub fn gh_at(&self, cwd: Option<&Path>, args: &[&str]) -> Result<String>

Source

pub fn gh_try(&self, args: &[&str]) -> String

Source

pub fn fetch_issues(&self, numbers: &[i64]) -> Result<Vec<Issue>>

Source

pub fn list_open_issues( &self, limit: usize, min_number: i64, ) -> Result<Vec<i64>>

Open issues, lowest numbered first. gh issue list excludes PRs.

Source

pub fn list_open_prs(&self, limit: usize, min_number: i64) -> Result<Vec<i64>>

Source

pub fn pr_for_branch(&self, branch: &str) -> Option<PrRef>

Source

pub fn item_kind(&self, number: i64) -> Result<ItemKind>

Whether a number names an issue or a pull request.

gh issue view happily returns a pull request when handed its number, so it cannot be used to tell them apart. The issues API carries both and marks a pull request with a pull_request key, which is definitive.

Source

pub fn open_pr_for_issue(&self, issue: i64) -> Option<PrRef>

An open pull request that would close this issue, whoever opened it.

spar’s own branch naming is checked first because it is exact and cheap. Falling back to GitHub’s own issue linkage is what lets spar pick up a pull request a person started on a branch named anything at all.

Source

pub fn pr_view(&self, number: i64) -> Result<PrView>

Source

pub fn pr_state(&self, number: i64) -> String

Source

pub fn create_pr( &self, cwd: &Path, branch: &str, base: &str, title: &str, body: &str, ) -> Result<PrRef>

Source

pub fn comment_pr(&self, number: i64, body: &str) -> Result<()>

Source

pub fn comment_issue(&self, number: i64, body: &str) -> Result<()>

Source

pub fn close_issue(&self, number: i64, body: &str) -> Result<()>

Comment, then close as not planned.

Only ever called when both agents independently declined the issue: one agent’s opinion is not enough to close somebody’s report.

Source

pub fn create_issue(&self, title: &str, body: &str) -> Result<String>

Source§

impl Repo

Source

pub fn find_similar_issue( &self, title: &str, body: &str, ) -> Option<ExistingIssue>

An issue that already describes this defect, however it was worded.

Exact title matching let duplicates through: two agents, or two runs a week apart, never word one defect identically. A real run filed two duplicates that way, and each had to be closed by hand afterwards. Titles alone are too thin to match on, so this compares titles and bodies together.

Source

pub fn find_issue_by_title(&self, title: &str) -> Option<String>

Avoid filing a duplicate when a follow-up already exists.

Source

pub fn merge_pr(&self, number: i64) -> Result<()>

Squash merge, tolerating cleanup failures after a successful merge.

gh pr merge --delete-branch exits non-zero when it cannot delete the local branch, which happens after the merge has already landed. Treating that as a failure reports work as lost when it is not.

Source

pub fn append_local_followup(&self, title: &str, body: &str) -> Option<String>

Append a follow-up to a local note instead of the tracker.

Deduplicated on the title, matching the issue path. Returns a display string, or None when it was already recorded. The body arrives with its provenance already stamped by the caller, so nothing is added here.

Source

pub fn pending_comment_path(&self, number: i64) -> PathBuf

Where a comment spar produced but did not post is kept.

Source

pub fn save_pending_comment(&self, number: i64, text: &str) -> Result<PathBuf>

Keep a comment spar decided not to post.

A dry run that prints and forgets means agreeing with what you read costs a second full review. Saving it makes the whole point of reading it first: look, edit if you like, then post what you already paid for.

Source

pub fn read_pending_comment(&self, number: i64) -> Option<String>

Source

pub fn state_path(&self, number: i64) -> PathBuf

Source

pub fn read_state(&self, pr: &PrView) -> Option<PersistedState>

Source

pub fn write_state(&self, number: i64, state: &PersistedState) -> Result<()>

Source

pub fn clear_state(&self, number: i64)

Drop state once the PR is finished and there is nothing to resume.

Source

pub fn prune_state(&self) -> Vec<String>

Remove state files whose PR is merged or closed.

Source

pub fn prune_pr_state(&self, numbers: Option<Vec<i64>>) -> Vec<String>

Delete state comments from PRs that are finished.

Open PRs are left alone: their state may still be live.

Source

pub fn prune_worktrees(&self, force_all: bool) -> Vec<String>

Drop worktrees whose PR is finished, then the branches they left behind.

With auto_merge off, which is the default, a run ends at “approved”, so nothing would ever clean these up on its own and they accumulate one per run. A stranded worktree also holds its branch checked out, which makes a later gh pr merge --delete-branch fail to clean up.

Source

pub fn prune_branches(&self, force_all: bool) -> Vec<String>

Delete leftover branches spar created whose worktree is already gone.

Deletion is driven by the ledger of branches spar actually created, not by a name pattern. Names default to issue-N, which is exactly what a person would call a branch themselves, so a name alone can never establish ownership. This is the data loss guard.

Trait Implementations§

Source§

impl Debug for Repo

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Repo

§

impl RefUnwindSafe for Repo

§

impl Send for Repo

§

impl Sync for Repo

§

impl Unpin for Repo

§

impl UnsafeUnpin for Repo

§

impl UnwindSafe for Repo

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.