zzz

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package zzz drives an autonomous overnight loop: clean-git-check → engine.Run → if tree changed, ONE commit; on any failure, git reset --hard back to last known good. Inspired by gnhf (github.com/kunchenguid/gnhf). Wakes you up to a branch full of individually-revertable commits and a notes.md log of every step.

Index

Constants

View Source
const (
	StatusRunning   = "running"
	StatusCompleted = "completed"
	StatusFailed    = "failed"
	StatusAborted   = "aborted"

	IterCommitted = "committed"
	IterReset     = "reset"
	IterFailed    = "failed"
	IterNoop      = "noop"

	ModeBranch   = "branch"
	ModeCurrent  = "current"
	ModeWorktree = "worktree"
)
View Source
const (
	SteerNote  = "note"  // append text to next prompt
	SteerStop  = "stop"  // graceful stop after current iter
	SteerAbort = "abort" // hard cancel ctx mid-iter
)

Variables

This section is empty.

Functions

func AcquireLock added in v0.1.1

func AcquireLock(workdir, runID string) (func(), error)

AcquireLock takes an exclusive lock on workdir (a run's RepoRoot — the worktree for worktree runs, the main repo for branch/current runs). It prevents two overnight loops from racing the same git index and prevents a double-resume of the same run. Returns a release func; call it on exit.

A lock left behind by a crashed process is detected via the recorded PID and stolen. On Windows there is no portable liveness probe, so a stale lock is kept (manual removal of ~/.bee/zzz/locks/<key>.lock required) rather than risk stealing a live one.

func AddAll

func AddAll(dir string) error

AddAll stages everything.

func AppendEvent

func AppendEvent(id string, ev Event) error

AppendEvent writes one line to events.jsonl.

func AppendNote

func AppendNote(id string, iter int, subject, body string) error

AppendNote tacks one section onto notes.md. Used after every iteration so later prompts can see what prior iterations did.

func CleanFD

func CleanFD(dir string) error

CleanFD removes untracked files + dirs. Pairs with ResetHard for a full rollback after a failed iteration.

func Commit

func Commit(dir, msg string, sign, noVerify bool) (string, error)

Commit creates ONE commit with msg. When sign is false (gnhf parity) the command is invoked with overrides that disable any configured signing — the run does NOT touch the user's git config. Returns the new short sha.

func CommitAll

func CommitAll(dir, msg string, sign, noVerify bool) (string, error)

CommitAll stages every tracked + untracked change and commits in a single git invocation. The atomicity is git's own (index set then commit). Returns the new short sha.

func CommitMessageFrom

func CommitMessageFrom(finalText string, iter, inTok, outTok int) string

CommitMessageFrom builds a conventional commit subject + body. Subject is the first non-empty line of finalText truncated to 70 chars and lowercased "fix:" / "feat:" prefix when missing. Body carries the remainder.

func CreateBranchAndSwitch

func CreateBranchAndSwitch(dir, name string) error

CreateBranchAndSwitch creates `name` and checks it out. Errors if it exists.

func CurrentBranch

func CurrentBranch(dir string) (string, error)

CurrentBranch returns the active branch name. Detached HEAD returns "HEAD".

func DiffStat

func DiffStat(dir, ref string) (string, error)

DiffStat returns the one-line `--shortstat` summary for HEAD~1..HEAD. Returns empty string when the commit didn't actually change anything.

func Drive

func Drive(ctx context.Context, stopCh <-chan struct{}, eng Runner, cfg Config, run *Run, ui UI) (retErr error)

Drive is the main overnight loop. Returns nil on a clean exit (objective reached / max-iter / stop signal); error only on unrecoverable failure.

ctx cancel = hard stop (mid-iteration). stopCh closed = graceful stop after current iteration finishes. If ui also satisfies Steerable, operator nudges are drained between iterations: notes get appended to the next prompt, stop closes the local graceful-stop path.

func Fetch

func Fetch(dir, remote string) error

Fetch updates remote-tracking refs. remote defaults to "origin". Best-effort: not having a remote configured is not fatal for local-only flows.

func HasBranch

func HasBranch(dir, name string) bool

HasBranch returns true when `name` exists locally.

func HeadSHA

func HeadSHA(dir string) (string, error)

HeadSHA returns the short HEAD sha.

func HomeDir

func HomeDir() (string, error)

HomeDir returns ~/.bee/zzz. Created on first call. BEE_HOME overrides ~/.bee (matches bgreg/agents and lets tests stay hermetic on Windows where os.UserHomeDir reads USERPROFILE, not HOME).

func IsClean

func IsClean(dir string) (bool, error)

IsClean returns true when `git status --porcelain` has no output.

func IsRebasing

func IsRebasing(dir string) bool

IsRebasing returns true when a rebase is in progress in dir. Checks for .git/rebase-merge and .git/rebase-apply via git status --porcelain=v2.

func LoadPrompt

func LoadPrompt(id string) (string, error)

LoadPrompt reads the saved objective.

func MergeFF

func MergeFF(dir, base, branch string) error

MergeFF fast-forwards branch onto base in dir. dir should be the main repo (not the worktree). Returns error if FF is not possible.

func NewID

func NewID() string

NewID returns a short timestamped id: 20260518-a1b2c3d4. Sortable, unique enough for human-facing dirs, no external deps. UUIDs exist elsewhere in bee but they read poorly in `ls` output for an overnight log dir.

func Push

func Push(dir, branch string) error

Push pushes the named branch with upstream tracking.

func ReadNotes

func ReadNotes(id string) (string, error)

ReadNotes returns the current notes.md contents (empty on first iter).

func Rebase

func Rebase(dir, onto string) error

Rebase replays HEAD commits onto onto. Returns the raw error including stderr on conflict so callers can surface it.

func RebaseAbort

func RebaseAbort(dir string) error

RebaseAbort aborts an in-progress rebase. Safe to call when no rebase is active — git exits non-zero but the caller can ignore it.

func RepoRoot

func RepoRoot(dir string) (string, error)

RepoRoot resolves dir's repo root via `git rev-parse --show-toplevel`.

func ResetHard

func ResetHard(dir, ref string) error

ResetHard discards uncommitted changes back to ref (defaults to HEAD).

func RunDir

func RunDir(id string) (string, error)

RunDir returns ~/.bee/zzz/runs/<id>, creating it.

func SaveBlockedPatch

func SaveBlockedPatch(id string, iter int, dir string) error

SaveBlockedPatch dumps the current tracked diff + porcelain status to ~/.bee/zzz/runs/<id>/blocked-<iter>.patch so the operator can inspect what the BLOCKED iteration produced before the run reset the tree. Untracked files are listed by name only (no content) to keep the patch self-explanatory.

func SaveMeta

func SaveMeta(r *Run) error

SaveMeta writes meta.json atomically.

func SavePrompt

func SavePrompt(id, objective string) error

SavePrompt persists the original objective so resume keeps context. Written atomically (tmp+rename) so a crash mid-write can't leave a truncated objective that a later resume would silently run with.

func TailNoteSections

func TailNoteSections(notes string, n int) string

TailNoteSections returns the last n "## iter " sections of notes. Used to cap prompt growth on long runs — without this, iter N's prompt embeds N prior sections, costing O(n²) tokens across the run. n<=0 returns notes unchanged.

func WorktreeAdd

func WorktreeAdd(repoRoot, path, branch string) error

WorktreeAdd creates a new worktree at path tracking new branch. path MUST NOT exist; git refuses otherwise.

func WorktreeDir

func WorktreeDir(id string) (string, error)

WorktreeDir returns ~/.bee/zzz/worktrees/<id>. NOT created — git worktree add wants a non-existent path.

func WorktreeRemove

func WorktreeRemove(repoRoot, path string, force bool) error

WorktreeRemove tears down a worktree. Force flag handles dirty trees.

Types

type Config

type Config struct {
	Objective     string
	MaxIterations int
	MaxTokens     int    // 0 = unlimited
	StopWhen      string // substring match against FinalText
	Worktree      bool
	CurrentBranch bool // commit on current branch, no auto-create
	Push          bool // git push after each commit
	Sign          bool // default false (gnhf parity, avoids overnight prompts)
	NoVerify      bool // skip pre-commit hooks (opt-in)

	// MaxConsecutiveFails ends the run after this many failed iters in a row.
	// 0 → default (3). Noop iters do not count.
	MaxConsecutiveFails int
	// MaxConsecutiveNoops ends the run after this many no-change iters in a row.
	// 0 → default (5). Stops a model that only ever surveys from spinning to
	// MaxIterations without committing anything.
	MaxConsecutiveNoops int
	// HardErrorRetries bounds engine.Run retries per iter on transient errors.
	// 0 → default (3).
	HardErrorRetries int
	// NotesTailIters caps how many prior iteration sections are echoed into
	// the next prompt. 0 → default (5). Negative → unlimited (legacy).
	NotesTailIters int
}

Config is the parsed flag set for one run.

type Event

type Event struct {
	Iter     int       `json:"iter"`
	Phase    string    `json:"phase"`
	Time     time.Time `json:"time"`
	Tokens   TokenStat `json:"tokens,omitempty"`
	DiffStat string    `json:"diff_stat,omitempty"`
	Commit   string    `json:"commit,omitempty"`
	Err      string    `json:"err,omitempty"`
}

Event is one row in events.jsonl.

type IterationResult

type IterationResult struct {
	Iter      int
	Status    string // "committed" | "reset" | "failed" | "noop"
	Subject   string
	CommitSHA string
	Tokens    TokenStat
	DiffStat  string
	Err       error
	Duration  time.Duration
}

IterationResult summarises one pass through the loop.

type PruneOpts

type PruneOpts struct {
	MaxAge          time.Duration
	KeepNewest      int
	StaleRunningAge time.Duration // 0 → never reap running runs
	// IncludeWorktree triggers `git worktree remove` per pruned worktree run.
	// MainRepoRoot must be set to the main repo (not a worktree) — git refuses
	// to remove a worktree from itself.
	IncludeWorktree bool
	MainRepoRoot    string
}

PruneOpts controls run-directory garbage collection. A run is removed when it satisfies BOTH thresholds — older than MaxAge AND beyond the KeepNewest most recent. Zero values disable the matching threshold:

  • MaxAge=0 → age never triggers; only excess beyond KeepNewest removed.
  • KeepNewest=0 → count never triggers; only age-based removal.

Only terminal runs (completed, failed, aborted) are candidates. Active runs are always retained, except runs stuck in "running" past StaleRunningAge — those are reaped because they almost always indicate a crashed process that never persisted a terminal status.

type PruneResult

type PruneResult struct {
	RemovedRunIDs        []string
	ReapedStaleRunIDs    []string
	RemovedWorktreePaths []string
	Errors               []error
}

PruneResult lists what Prune deleted.

func Prune

func Prune(opts PruneOpts) PruneResult

Prune sweeps ~/.bee/zzz/runs/ removing old terminal runs per opts. With IncludeWorktree, also removes the associated git worktree for each pruned worktree-mode run. StaleRunningAge>0 also reaps "running" runs older than that threshold (process crashed before persisting a terminal status).

type Run

type Run struct {
	ID              string    `json:"id"`
	Objective       string    `json:"objective"`
	Branch          string    `json:"branch"`
	Worktree        string    `json:"worktree,omitempty"`
	Mode            string    `json:"mode"` // "branch" | "current" | "worktree"
	RepoRoot        string    `json:"repo_root"`
	StartedAt       time.Time `json:"started_at"`
	EndedAt         time.Time `json:"ended_at,omitempty"`
	Status          string    `json:"status"` // "running" | "completed" | "failed" | "aborted"
	IterCount       int       `json:"iter_count"`
	Tokens          TokenStat `json:"tokens"`
	Commits         []string  `json:"commits"`
	PushedCommits   []string  `json:"pushed_commits,omitempty"`
	PushFailedIters []int     `json:"push_failed_iters,omitempty"`
	StopCause       string    `json:"stop_cause,omitempty"`
}

Run is the persisted metadata for one overnight session.

func LatestRun

func LatestRun() (*Run, error)

LatestRun returns the most recently started run, or nil.

func ListRuns

func ListRuns() ([]*Run, error)

ListRuns returns all runs in ~/.bee/zzz/runs/, newest first.

func LoadMeta

func LoadMeta(id string) (*Run, error)

LoadMeta reads meta.json for an existing run.

func (*Run) Summary

func (r *Run) Summary() string

Summary renders a one-line listing entry. Objective is truncated to keep columns aligned in `bee zzz --list` output.

type Runner

type Runner interface {
	Run(ctx context.Context, prompt string) (loop.RunResult, error)
	CostTotal() cost.Summary
}

Runner is the surface Drive needs from the engine. Real callers wrap *loop.Engine via NewLoopRunner; tests pass a stub.

func NewLoopRunner

func NewLoopRunner(eng *loop.Engine) Runner

NewLoopRunner adapts *loop.Engine to the Runner interface so Drive isn't bound to the concrete engine type.

type Status

type Status struct {
	// contains filtered or unexported fields
}

Status drives the single-line live status display + final summary. No TUI — just carriage-return overwrites to stderr so piping stdout still works (the engine's text deltas already go to stdout).

func NewStatus

func NewStatus(w io.Writer) *Status

NewStatus returns a status renderer writing to w. Pass os.Stderr in normal CLI use, io.Discard in tests.

func (*Status) Disable

func (s *Status) Disable()

Disable suppresses live rendering (useful when --json or non-tty).

func (*Status) IncCommits

func (s *Status) IncCommits()

func (*Status) Println

func (s *Status) Println(msg string)

Println prints msg on its own line, leaving the status line intact below.

func (*Status) RenderSummary

func (s *Status) RenderSummary(r *Run)

RenderSummary prints the multi-line exit summary. Called once at end.

func (*Status) SetIter

func (s *Status) SetIter(n, max int)

func (*Status) SetPhase

func (s *Status) SetPhase(p string)

func (*Status) SetTokens

func (s *Status) SetTokens(t TokenStat)

type Steer

type Steer struct {
	Kind string
	Text string
}

Steer is one operator command pushed from the UI into Drive between iterations. Free-text falls under SteerNote — it gets appended verbatim to the next iteration's prompt as an "operator nudge" block.

type Steerable

type Steerable interface {
	Steer() <-chan Steer
}

Steerable is implemented by UIs that accept mid-run operator input. When Drive holds a UI that also satisfies this interface it drains Steer between iterations: notes get appended to the prompt, stop closes the graceful stop channel.

type TokenStat

type TokenStat struct {
	Input  int     `json:"input"`
	Output int     `json:"output"`
	USD    float64 `json:"usd"`
}

TokenStat is the running tally across all iterations.

type UI

type UI interface {
	SetIter(n, max int)
	SetPhase(p string)
	SetTokens(t TokenStat)
	IncCommits()
	Println(msg string)
	RenderSummary(r *Run)
}

UI abstracts the live status sink so Drive can be driven by either the terminal-line Status renderer or a bubbletea TUI. Methods mirror the original *Status surface 1:1 so the existing impl is automatically a UI.

Directories

Path Synopsis
Package tui drives the bee-zzz live interface: animated sleeping bee at the footer, moon-phase iteration timeline, transcript pane, and a textarea for steering commands (/stop /abort /note <text>).
Package tui drives the bee-zzz live interface: animated sleeping bee at the footer, moon-phase iteration timeline, transcript pane, and a textarea for steering commands (/stop /abort /note <text>).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL