strategy

package
v0.4.6 Latest Latest
Warning

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

Go to latest
Published: Feb 24, 2026 License: MIT Imports: 45 Imported by: 0

Documentation

Overview

Package strategy provides an interface for different git strategies that can be used to save and manage Claude Code session changes.

Index

Constants

View Source
const (
	StrategyNameManualCommit = "manual-commit"
	StrategyNameAutoCommit   = "auto-commit"
)

Strategy name constants

View Source
const (

	// DefaultAgentType is the generic fallback agent type name
	DefaultAgentType = agent.AgentTypeUnknown
)
View Source
const DefaultStrategyName = StrategyNameManualCommit

DefaultStrategyName is the name of the default strategy. Manual-commit is the recommended strategy for most workflows.

View Source
const MaxDescriptionLength = 60

MaxDescriptionLength is the maximum length for descriptions in commit messages before truncation occurs.

View Source
const NoDescription = "No description"

NoDescription is the default description for sessions without one.

Variables

View Source
var ErrBranchNotFound = errors.New("branch not found")

ErrBranchNotFound is returned by DeleteBranchCLI when the branch does not exist.

View Source
var ErrEmptyRepository = errors.New("repository has no commits yet")

ErrEmptyRepository is returned when the repository has no commits yet.

View Source
var ErrNoMetadata = errors.New("commit has no entire metadata")

ErrNoMetadata is returned when a commit does not have an Entire metadata trailer.

View Source
var ErrNoSession = errors.New("no session info available")

ErrNoSession is returned when no session info is available.

View Source
var ErrNotTaskCheckpoint = errors.New("not a task checkpoint")

ErrNotTaskCheckpoint is returned when a rewind point is not a task checkpoint.

Functions

func CalculateAttributionWithAccumulated

func CalculateAttributionWithAccumulated(
	baseTree *object.Tree,
	shadowTree *object.Tree,
	headTree *object.Tree,
	filesTouched []string,
	promptAttributions []PromptAttribution,
) *checkpoint.InitialAttribution

CalculateAttributionWithAccumulated computes final attribution using accumulated prompt data. This provides more accurate attribution than tree-only comparison because it captures user edits that happened between checkpoints (which would otherwise be mixed into the checkpoint snapshots).

The calculation: 1. Sum user edits from PromptAttributions (captured at each prompt start) 2. Add user edits after the final checkpoint (shadow → head diff) 3. Calculate agent lines from base → shadow 4. Estimate user self-modifications vs agent modifications using per-file tracking 5. Compute percentages

Note: Binary files (detected by null bytes) are silently excluded from attribution calculations since line-based diffing only applies to text files.

See docs/architecture/attribution.md for details on the per-file tracking approach.

func CheckAndWarnHookManagers added in v0.4.5

func CheckAndWarnHookManagers(w io.Writer, localDev bool)

CheckAndWarnHookManagers detects external hook managers and writes a warning to w if any are found. localDev controls whether the warning references "go run" or the "entire" binary.

func ClearHooksDirCache added in v0.4.6

func ClearHooksDirCache()

ClearHooksDirCache clears the cached hooks directory. This is primarily useful for testing when changing directories.

func ClearSessionState

func ClearSessionState(sessionID string) error

ClearSessionState removes the session state file for the given session ID.

func CountTodos

func CountTodos(todosJSON []byte) int

CountTodos returns the number of todo items in the JSON array. Returns 0 if the JSON is invalid or empty.

func DeleteBranchCLI added in v0.3.13

func DeleteBranchCLI(branchName string) error

DeleteBranchCLI deletes a git branch using the git CLI. Uses `git branch -D` instead of go-git's RemoveReference because go-git v5 doesn't properly persist deletions when refs are packed (.git/packed-refs) or in a worktree context. This is the same class of go-git v5 bug that affects checkout and reset --hard (see HardResetWithProtection).

Returns ErrBranchNotFound if the branch does not exist, allowing callers to use errors.Is for idempotent deletion patterns.

func DeleteOrphanedCheckpoints

func DeleteOrphanedCheckpoints(checkpointIDs []string) (deleted []string, failed []string, err error)

DeleteOrphanedCheckpoints removes checkpoint directories from the entire/checkpoints/v1 branch.

func DeleteOrphanedSessionStates

func DeleteOrphanedSessionStates(sessionIDs []string) (deleted []string, failed []string, err error)

DeleteOrphanedSessionStates deletes the specified session state files.

func DeleteShadowBranches

func DeleteShadowBranches(branches []string) (deleted []string, failed []string, err error)

DeleteShadowBranches deletes the specified branches from the repository. Returns two slices: successfully deleted branches and branches that failed to delete. Individual branch deletion failures do not stop the operation - all branches are attempted.

func EnsureEntireGitignore

func EnsureEntireGitignore() error

EnsureEntireGitignore ensures all required entries are in .entire/.gitignore Works correctly from any subdirectory within the repository.

func EnsureMetadataBranch

func EnsureMetadataBranch(repo *git.Repository) error

ensureMetadataBranch creates the orphan entire/checkpoints/v1 branch if it doesn't exist. This branch has no parent and starts with an empty tree.

func ExtractFirstPrompt

func ExtractFirstPrompt(content string) string

ExtractFirstPrompt extracts and truncates the first meaningful prompt from prompt content. Prompts are separated by "\n\n---\n\n". Skips empty prompts and separator-only content. Returns empty string if no valid prompt is found.

func ExtractInProgressTodo

func ExtractInProgressTodo(todosJSON []byte) string

ExtractInProgressTodo extracts the content of the in-progress todo item from tool_input. This is used for commit messages in incremental checkpoints.

Priority order:

  1. in_progress item (current work)
  2. first pending item (next work - fallback)
  3. last completed item (final work just finished)
  4. first item with unknown status (edge case)
  5. empty string (no items)

Returns empty string if no suitable item is found or JSON is invalid.

func ExtractLastCompletedTodo

func ExtractLastCompletedTodo(todosJSON []byte) string

ExtractLastCompletedTodo extracts the content of the last completed todo item from tool_input. This represents the work that was just finished and is used for commit messages.

When TodoWrite is called in PostToolUse, the NEW list is provided which has the just-completed work marked as "completed". The last completed item is the most recently finished task.

Returns empty string if no completed items exist or JSON is invalid.

func ExtractSessionIDFromCommit

func ExtractSessionIDFromCommit(commit *object.Commit) string

ExtractSessionIDFromCommit extracts the session ID from a commit's trailers. It checks the Entire-Session trailer first, then falls back to extracting from the metadata directory path in the Entire-Metadata trailer. Returns empty string if no session ID is found.

func ExtractToolUseIDFromTaskMetadataDir

func ExtractToolUseIDFromTaskMetadataDir(metadataDir string) string

ExtractToolUseIDFromTaskMetadataDir extracts the ToolUseID from a task metadata directory path. Task metadata dirs have format: .entire/metadata/<session>/tasks/<toolUseID> Returns empty string if not a task metadata directory.

func FindMostRecentSession added in v0.3.13

func FindMostRecentSession() string

FindMostRecentSession returns the session ID of the most recently interacted session (by LastInteractionTime) in the current worktree. Returns empty string if no sessions exist. Scoping to the current worktree prevents cross-worktree pollution in log routing. Falls back to unfiltered search if the worktree path can't be determined.

func FormatIncrementalMessage

func FormatIncrementalMessage(todoContent string, sequence int, toolUseID string) string

FormatIncrementalMessage formats a commit message for an incremental checkpoint. Format: "<todo-content> (<tool-use-id>)"

If todoContent is empty, falls back to: "Checkpoint #<sequence>: <tool-use-id>"

func FormatIncrementalSubject

func FormatIncrementalSubject(
	incrementalType string,
	subagentType string,
	taskDescription string,
	todoContent string,
	incrementalSequence int,
	shortToolUseID string,
) string

FormatIncrementalSubject formats the commit message subject for incremental checkpoints. Delegates to FormatIncrementalMessage.

Note: The incrementalType, subagentType, and taskDescription parameters are kept for API compatibility but are not currently used. They may be used in the future for different checkpoint types.

func FormatSubagentEndMessage

func FormatSubagentEndMessage(agentType, description, toolUseID string) string

FormatSubagentEndMessage formats a commit message for when a subagent completes. Format: "Completed '<agent-type>' agent: <description> (<tool-use-id>)"

Edge cases:

  • Empty description: "Completed '<agent-type>' agent (<tool-use-id>)"
  • Empty agentType: "Completed agent: <description> (<tool-use-id>)"
  • Both empty: "Task: <tool-use-id>"

func GetCurrentBranchName

func GetCurrentBranchName(repo *git.Repository) string

GetCurrentBranchName returns the short name of the current branch if HEAD points to a branch. Returns an empty string if in detached HEAD state or if there's an error reading HEAD. This is used to capture branch metadata for checkpoints.

func GetDefaultBranchName

func GetDefaultBranchName(repo *git.Repository) string

GetDefaultBranchName returns the name of the default branch. First checks origin/HEAD, then falls back to checking if main/master exists. Returns empty string if unable to determine. NOTE: Duplicated from cli/git_operations.go - see ENT-129 for consolidation.

func GetGitAuthorFromRepo

func GetGitAuthorFromRepo(repo *git.Repository) (name, email string)

GetGitAuthorFromRepo retrieves the git user.name and user.email, checking both the repository-local config and the global ~/.gitconfig. Delegates to checkpoint.GetGitAuthorFromRepo — this wrapper exists so callers within the strategy package don't need a qualified import.

func GetGitCommonDir

func GetGitCommonDir() (string, error)

GetGitCommonDir returns the path to the shared git directory. In a regular checkout, this is .git/ In a worktree, this is the main repo's .git/ (not .git/worktrees/<name>/) Uses git rev-parse --git-common-dir for reliable handling of worktrees.

func GetGitDir

func GetGitDir() (string, error)

GetGitDir returns the actual git directory path by delegating to git itself. This handles both regular repositories and worktrees, and inherits git's security validation for gitdir references.

func GetHooksDir added in v0.4.5

func GetHooksDir() (string, error)

GetHooksDir returns the active hooks directory path. This respects core.hooksPath and correctly resolves to the common hooks directory when called from a linked worktree. The result is cached per working directory.

func GetMainBranchHash

func GetMainBranchHash(repo *git.Repository) plumbing.Hash

getMainBranchHash returns the hash of the main branch (main or master). Returns ZeroHash if no main branch is found.

func GetMainRepoRoot

func GetMainRepoRoot() (string, error)

GetMainRepoRoot returns the root directory of the main repository. In the main repo, this is the worktree path (repo root). In a worktree, this parses the .git file to find the main repo. This function works correctly from any subdirectory within the repository.

Per gitrepository-layout(5), a worktree's .git file is a "gitfile" containing "gitdir: <path>" pointing to $GIT_DIR/worktrees/<id> in the main repository. See: https://git-scm.com/docs/gitrepository-layout

func GetMetadataBranchTree

func GetMetadataBranchTree(repo *git.Repository) (*object.Tree, error)

GetMetadataBranchTree returns the tree object for the entire/checkpoints/v1 branch.

func GetRemoteMetadataBranchTree

func GetRemoteMetadataBranchTree(repo *git.Repository) (*object.Tree, error)

GetRemoteMetadataBranchTree returns the tree object for origin/entire/checkpoints/v1.

func GetWorktreePath

func GetWorktreePath() (string, error)

GetWorktreePath returns the absolute path to the current worktree root. This is the working directory path, not the git directory.

func HardResetWithProtection

func HardResetWithProtection(commitHash plumbing.Hash) (shortID string, err error)

HardResetWithProtection performs a git reset --hard to the specified commit. Uses the git CLI instead of go-git because go-git's HardReset incorrectly deletes untracked directories (like .entire/) even when they're in .gitignore. Returns the short commit ID (7 chars) on success for display purposes.

func InstallGitHook

func InstallGitHook(silent bool, localDev bool) (int, error)

InstallGitHook installs generic git hooks that delegate to `entire hook` commands. These hooks work with any strategy - the strategy is determined at runtime. If silent is true, no output is printed (except backup notifications, which always print). localDev controls whether hooks use "go run" (true) or the "entire" binary (false). Returns the number of hooks that were installed (0 if all already up to date).

func IsAncestorOf

func IsAncestorOf(repo *git.Repository, commit, target plumbing.Hash) bool

IsAncestorOf checks if commit is an ancestor of (or equal to) target. Returns true if target can reach commit by following parent links. Limits search to 1000 commits to avoid excessive traversal.

func IsEmptyRepository added in v0.4.3

func IsEmptyRepository(repo *git.Repository) bool

IsEmptyRepository returns true if the repository has no commits yet. After git-init, HEAD points to an unborn branch (e.g., refs/heads/main) whose target does not yet exist. repo.Head() returns ErrReferenceNotFound in this case.

func IsGitHookInstalled

func IsGitHookInstalled() bool

IsGitHookInstalled checks if all generic Entire CLI hooks are installed.

func IsGitHookInstalledInDir added in v0.4.4

func IsGitHookInstalledInDir(repoDir string) bool

IsGitHookInstalledInDir checks if all Entire CLI hooks are installed in the given repo directory. This is useful for tests that need to check hooks without changing the working directory.

func IsInsideWorktree

func IsInsideWorktree() bool

IsInsideWorktree returns true if the current directory is inside a git worktree (as opposed to the main repository). Worktrees have .git as a file pointing to the main repo, while the main repo has .git as a directory. This function works correctly from any subdirectory within the repository.

func IsOnDefaultBranch

func IsOnDefaultBranch(repo *git.Repository) (bool, string)

IsOnDefaultBranch checks if the repository HEAD is on the default branch. Returns (isOnDefault, currentBranchName). NOTE: Duplicated from cli/git_operations.go - see ENT-129 for consolidation.

func IsShadowBranch

func IsShadowBranch(branchName string) bool

IsShadowBranch returns true if the branch name matches the shadow branch pattern. Shadow branches have the format "entire/<commit-hash>-<worktree-hash>" where the commit hash is at least 7 hex characters and worktree hash is 6 hex characters. The "entire/checkpoints/v1" branch is NOT a shadow branch.

func List

func List() []string

List returns all registered strategy names in sorted order.

func ListShadowBranches

func ListShadowBranches() ([]string, error)

ListShadowBranches returns all shadow branches in the repository. Shadow branches match the pattern "entire/<commit-hash>" (7+ hex chars). The "entire/checkpoints/v1" branch is excluded as it stores permanent metadata. Returns an empty slice (not nil) if no shadow branches exist.

func ManagedGitHookNames added in v0.4.4

func ManagedGitHookNames() []string

ManagedGitHookNames returns the list of git hooks managed by Entire CLI. This is useful for tests that need to manipulate hooks.

func OpenRepository

func OpenRepository() (*git.Repository, error)

OpenRepository opens the git repository with linked worktree support enabled. It uses git.PlainOpenWithOptions with EnableDotGitCommonDir set to true, which is required for proper operation in git worktrees created via 'git worktree add'.

Without EnableDotGitCommonDir, go-git operations in worktrees can silently fail: - Commits appear to succeed but are not persisted - Refs are written to incorrect locations - The worktree's HEAD/index don't get updated properly

This happens because worktrees use .git as a file (pointing to the main repo) rather than a directory, and go-git needs to route paths correctly between shared (.git/) and per-worktree (.git/worktrees/<name>/) locations.

The function first uses 'git rev-parse --show-toplevel' to find the repository root, which works correctly even when called from a subdirectory within the repo.

func PromptOverwriteNewerLogs

func PromptOverwriteNewerLogs(sessions []SessionRestoreInfo) (bool, error)

PromptOverwriteNewerLogs asks the user for confirmation to overwrite local session logs that have newer timestamps than the checkpoint versions.

func ReadAgentTypeFromTree added in v0.4.4

func ReadAgentTypeFromTree(tree *object.Tree, checkpointPath string) agent.AgentType

ReadAgentTypeFromTree reads the agent type from a checkpoint's metadata.json file in a git tree. If metadata.json doesn't exist (shadow branches), it falls back to detecting the agent from the presence of agent-specific config files (.gemini/settings.json or .claude/). Returns agent.AgentTypeUnknown if the agent type cannot be determined.

func ReadAllSessionPromptsFromTree

func ReadAllSessionPromptsFromTree(tree *object.Tree, checkpointPath string, sessionCount int, sessionIDs []string) []string

ReadAllSessionPromptsFromTree reads the first prompt for all sessions in a multi-session checkpoint. Returns a slice of prompts parallel to sessionIDs (oldest to newest). For single-session checkpoints, returns a slice with just the root prompt.

func ReadSessionPromptFromTree

func ReadSessionPromptFromTree(tree *object.Tree, checkpointPath string) string

ReadSessionPromptFromTree reads the first meaningful prompt from a checkpoint's prompt.txt file in a git tree. Returns an empty string if the prompt cannot be read.

func Register

func Register(name string, factory Factory)

Register adds a strategy factory to the registry. This is typically called from init() functions in strategy implementations.

func RemoveGitHook

func RemoveGitHook() (int, error)

RemoveGitHook removes all Entire CLI git hooks from the repository. If a .pre-entire backup exists, it is restored. Returns the number of hooks removed.

func ResolveAgentForRewind added in v0.4.3

func ResolveAgentForRewind(agentType agent.AgentType) (agent.Agent, error)

ResolveAgentForRewind resolves the agent from checkpoint metadata. Falls back to the default agent (Claude) for old checkpoints that lack agent info.

func SaveSessionState

func SaveSessionState(state *SessionState) error

SaveSessionState saves the session state atomically.

func StageFiles

func StageFiles(worktree *git.Worktree, modified, newFiles, deleted []string, stageCtx StageFilesContext)

StageFiles stages modified, new, and deleted files to the git worktree.

This function handles three categories of file changes:

  1. Modified files: existing files that have been changed
  2. New files: files that were created during the session
  3. Deleted files: files that were removed during the session

Error Handling Strategy:

  • Individual file staging errors are logged to stderr but don't fail the operation
  • This ensures that partial staging succeeds even if some files have issues
  • If a modified file no longer exists, it's treated as a deletion

The stageCtx parameter is used for user-facing messages to indicate whether this is staging for a session checkpoint or a task checkpoint.

func StatusToText

func StatusToText(status SessionRestoreStatus) string

StatusToText returns a human-readable status string.

func TaskMetadataDir

func TaskMetadataDir(sessionMetadataDir, toolUseID string) string

TaskMetadataDir returns the path to a task's metadata directory within the session metadata directory.

func TransitionAndLog added in v0.3.13

func TransitionAndLog(state *SessionState, event session.Event, ctx session.TransitionContext, handler session.ActionHandler) error

TransitionAndLog runs a session phase transition, applies actions via the handler, and logs the transition. Returns the first handler error from ApplyTransition (if any) so callers can surface it. The error is also logged internally for diagnostics. This is the single entry point for all state machine transitions to ensure consistent logging of phase changes.

func TruncateDescription

func TruncateDescription(s string, maxLen int) string

TruncateDescription truncates a string to maxLen runes, adding "..." if truncated. Uses rune-based slicing to avoid splitting multi-byte UTF-8 characters. If maxLen is less than 3, truncates without ellipsis.

Types

type AutoCommitStrategy

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

AutoCommitStrategy implements the auto-commit strategy: - Code changes are committed to the active branch (like commit strategy) - Session logs are committed to a shadow branch (like manual-commit strategy) - Code commits can reference the shadow branch via trailers

func (*AutoCommitStrategy) CanRewind

func (s *AutoCommitStrategy) CanRewind() (bool, string, error)

func (*AutoCommitStrategy) Description

func (s *AutoCommitStrategy) Description() string

func (*AutoCommitStrategy) EnsureSetup

func (s *AutoCommitStrategy) EnsureSetup() error

EnsureSetup ensures the strategy's required setup is in place. For auto-commit strategy: - Ensure .entire/.gitignore has all required entries - Create orphan entire/checkpoints/v1 branch if it doesn't exist - Install git hooks if missing (self-healing for third-party overwrites)

func (*AutoCommitStrategy) GetCheckpointLog

func (s *AutoCommitStrategy) GetCheckpointLog(cp Checkpoint) ([]byte, error)

GetCheckpointLog returns the session transcript for a specific checkpoint. For auto-commit strategy, looks up checkpoint by ID on the entire/checkpoints/v1 branch using the checkpoint store.

func (*AutoCommitStrategy) GetMetadataRef

func (s *AutoCommitStrategy) GetMetadataRef(checkpoint Checkpoint) string

GetMetadataRef returns a reference to the metadata for the given checkpoint. For auto-commit strategy, returns the checkpoint path on entire/checkpoints/v1 branch.

func (*AutoCommitStrategy) GetRewindPoints

func (s *AutoCommitStrategy) GetRewindPoints(limit int) ([]RewindPoint, error)

func (*AutoCommitStrategy) GetSessionContext

func (s *AutoCommitStrategy) GetSessionContext(sessionID string) string

GetSessionContext returns the context.md content for a session. For auto-commit strategy, reads from the entire/checkpoints/v1 branch using the checkpoint store.

func (*AutoCommitStrategy) GetSessionInfo

func (s *AutoCommitStrategy) GetSessionInfo() (*SessionInfo, error)

GetSessionInfo returns session information for linking commits. For auto-commit strategy, we don't track active sessions - metadata is stored on entire/checkpoints/v1 branch when SaveStep is called. Active branch commits are kept clean (no trailers), so this returns ErrNoSession. Use ListSessions() or GetSession() to retrieve session info from the metadata branch.

func (*AutoCommitStrategy) GetSessionMetadataRef

func (s *AutoCommitStrategy) GetSessionMetadataRef(sessionID string) string

GetSessionMetadataRef returns a reference to the most recent metadata for a session.

func (*AutoCommitStrategy) GetTaskCheckpoint

func (s *AutoCommitStrategy) GetTaskCheckpoint(point RewindPoint) (*TaskCheckpoint, error)

GetTaskCheckpoint returns the task checkpoint for a given rewind point. For auto-commit strategy, checkpoints are stored on the entire/checkpoints/v1 branch in checkpoint directories. Returns ErrNotTaskCheckpoint if the point is not a task checkpoint.

func (*AutoCommitStrategy) GetTaskCheckpointTranscript

func (s *AutoCommitStrategy) GetTaskCheckpointTranscript(point RewindPoint) ([]byte, error)

GetTaskCheckpointTranscript returns the session transcript for a task checkpoint. For auto-commit strategy, transcripts are stored on the entire/checkpoints/v1 branch in checkpoint directories. Returns ErrNotTaskCheckpoint if the point is not a task checkpoint.

func (*AutoCommitStrategy) InitializeSession

func (s *AutoCommitStrategy) InitializeSession(sessionID string, agentType agent.AgentType, transcriptPath string, userPrompt string) error

InitializeSession creates session state for a new session. This is called during UserPromptSubmit hook to set up tracking for the session. For auto-commit strategy, this creates a SessionState file in .git/entire-sessions/ to track CheckpointTranscriptStart (transcript offset) across checkpoints. agentType is the human-readable name of the agent (e.g., "Claude Code"). transcriptPath is the path to the live transcript file (for mid-session commit detection). userPrompt is the user's prompt text (stored truncated as FirstPrompt for display).

func (*AutoCommitStrategy) ListOrphanedItems

func (s *AutoCommitStrategy) ListOrphanedItems() ([]CleanupItem, error)

ListOrphanedItems returns orphaned items created by the auto-commit strategy. For auto-commit, checkpoints are orphaned when no commit has an Entire-Checkpoint trailer referencing them (e.g., after rebasing or squashing).

func (*AutoCommitStrategy) Name

func (s *AutoCommitStrategy) Name() string

func (*AutoCommitStrategy) PrePush

func (s *AutoCommitStrategy) PrePush(remote string) error

PrePush is called by the git pre-push hook before pushing to a remote. It pushes the entire/checkpoints/v1 branch alongside the user's push. Configuration options (stored in .entire/settings.json under strategy_options.push_sessions):

  • "auto": always push automatically
  • "prompt" (default): ask user with option to enable auto
  • "false"/"off"/"no": never push

func (*AutoCommitStrategy) PreviewRewind

func (s *AutoCommitStrategy) PreviewRewind(_ RewindPoint) (*RewindPreview, error)

PreviewRewind returns what will happen if rewinding to the given point. For auto-commit strategy, this returns nil since git reset doesn't delete untracked files.

func (*AutoCommitStrategy) Rewind

func (s *AutoCommitStrategy) Rewind(point RewindPoint) error

func (*AutoCommitStrategy) SaveStep added in v0.4.6

func (s *AutoCommitStrategy) SaveStep(ctx StepContext) error

func (*AutoCommitStrategy) SaveTaskStep added in v0.4.6

func (s *AutoCommitStrategy) SaveTaskStep(ctx TaskStepContext) error

SaveTaskStep creates a checkpoint commit for a completed task. For auto-commit strategy: 1. Commit code changes to active branch (no trailers - clean history) 2. Commit task metadata to entire/checkpoints/v1 branch with checkpoint format

func (*AutoCommitStrategy) ValidateRepository

func (s *AutoCommitStrategy) ValidateRepository() error

type Checkpoint

type Checkpoint struct {
	// CheckpointID is the stable 12-hex-char identifier for this checkpoint.
	// Used to look up metadata at <id[:2]>/<id[2:]>/ on entire/checkpoints/v1 branch.
	CheckpointID id.CheckpointID

	// Message is the commit message or checkpoint description
	Message string

	// Timestamp is when this checkpoint was created
	Timestamp time.Time

	// IsTaskCheckpoint indicates if this is a task checkpoint (vs a session checkpoint)
	IsTaskCheckpoint bool

	// ToolUseID is the tool use ID for task checkpoints (empty for session checkpoints)
	ToolUseID string
}

Checkpoint represents a save point within a session. Checkpoints can be either session-level (on Stop) or task-level (on subagent completion).

type CheckpointInfo

type CheckpointInfo struct {
	CheckpointID     id.CheckpointID `json:"checkpoint_id"` // 12-hex-char from Entire-Checkpoint trailer, used as directory path
	SessionID        string          `json:"session_id"`
	CreatedAt        time.Time       `json:"created_at"`
	CheckpointsCount int             `json:"checkpoints_count"`
	FilesTouched     []string        `json:"files_touched"`
	Agent            agent.AgentType `json:"agent,omitempty"` // Human-readable agent name (e.g., "Claude Code")
	IsTask           bool            `json:"is_task,omitempty"`
	ToolUseID        string          `json:"tool_use_id,omitempty"`
	SessionCount     int             `json:"session_count,omitempty"` // Number of sessions (1 if omitted)
	SessionIDs       []string        `json:"session_ids,omitempty"`   // All session IDs in this checkpoint
}

CheckpointInfo represents checkpoint metadata stored on the sessions branch. Metadata is stored at sharded path: <checkpoint_id[:2]>/<checkpoint_id[2:]>/

func ListCheckpoints

func ListCheckpoints() ([]CheckpointInfo, error)

ListCheckpoints returns all checkpoints from the entire/checkpoints/v1 branch. Scans sharded paths: <id[:2]>/<id[2:]>/ directories containing metadata.json. Used by both manual-commit and auto-commit strategies.

func ReadCheckpointMetadata

func ReadCheckpointMetadata(tree *object.Tree, checkpointPath string) (*CheckpointInfo, error)

readCheckpointMetadata reads metadata.json from a checkpoint path on entire/checkpoints/v1. With the new format, root metadata.json is a CheckpointSummary with Agents array. This function reads the summary and extracts relevant fields into CheckpointInfo, also reading session-level metadata for IsTask/ToolUseID fields.

type CleanupItem

type CleanupItem struct {
	Type   CleanupType
	ID     string // Branch name, session ID, or checkpoint ID
	Reason string // Why this item is considered orphaned
}

CleanupItem represents an orphaned item that can be cleaned up.

func ListAllCleanupItems

func ListAllCleanupItems() ([]CleanupItem, error)

ListAllCleanupItems returns all orphaned items across all categories. It iterates over all registered strategies and calls ListOrphanedItems on those that implement OrphanedItemsLister. Returns an error if the repository cannot be opened.

func ListOrphanedSessionStates

func ListOrphanedSessionStates() ([]CleanupItem, error)

ListOrphanedSessionStates returns session state files that are orphaned. A session state is orphaned if:

  • No checkpoints on entire/checkpoints/v1 reference this session ID
  • No shadow branch exists for the session's base commit

This is strategy-agnostic as session states are shared by all strategies.

type CleanupResult

type CleanupResult struct {
	ShadowBranches    []string // Deleted shadow branches
	SessionStates     []string // Deleted session state files
	Checkpoints       []string // Deleted checkpoint metadata
	FailedBranches    []string // Shadow branches that failed to delete
	FailedStates      []string // Session states that failed to delete
	FailedCheckpoints []string // Checkpoints that failed to delete
}

CleanupResult contains the results of a cleanup operation.

func DeleteAllCleanupItems

func DeleteAllCleanupItems(items []CleanupItem) (*CleanupResult, error)

DeleteAllCleanupItems deletes all specified cleanup items. Logs each deletion for audit purposes.

type CleanupType

type CleanupType string

CleanupType identifies the type of orphaned item.

const (
	CleanupTypeShadowBranch CleanupType = "shadow-branch"
	CleanupTypeSessionState CleanupType = "session-state"
	CleanupTypeCheckpoint   CleanupType = "checkpoint"
)

type CommitMsgHandler

type CommitMsgHandler interface {
	// CommitMsg is called by the git commit-msg hook after the user edits the message.
	// Used to validate or modify the final commit message before the commit is created.
	// If this returns an error, the commit is aborted.
	CommitMsg(commitMsgFile string) error
}

CommitMsgHandler is an optional interface for strategies that need to handle the git commit-msg hook.

type ConcurrentSessionChecker

type ConcurrentSessionChecker interface {
	// CountOtherActiveSessionsWithCheckpoints returns the number of other active sessions
	// with uncommitted checkpoints on the same base commit.
	// Returns 0, nil if no such sessions exist.
	CountOtherActiveSessionsWithCheckpoints(currentSessionID string) (int, error)
}

ConcurrentSessionChecker is an optional interface for strategies that support counting concurrent sessions with uncommitted changes. This is used by the SessionStart hook to show an informational message about how many other active conversations will be included in the next commit.

type CondenseResult

type CondenseResult struct {
	CheckpointID         id.CheckpointID // 12-hex-char from Entire-Checkpoint trailer, used as directory path
	SessionID            string
	CheckpointsCount     int
	FilesTouched         []string
	TotalTranscriptLines int // Total lines in transcript after this condensation
}

CondenseResult contains the result of a session condensation operation.

type ExtractedSessionData

type ExtractedSessionData struct {
	Transcript          []byte   // Full transcript content for the session
	FullTranscriptLines int      // Total line count in full transcript
	Prompts             []string // All user prompts from this portion
	Context             []byte   // Generated context.md content
	FilesTouched        []string
	TokenUsage          *agent.TokenUsage // Token usage calculated from transcript (since CheckpointTranscriptStart)
}

ExtractedSessionData contains data extracted from a shadow branch.

type Factory

type Factory func() Strategy

Factory creates a new strategy instance

type LogsOnlyRestorer

type LogsOnlyRestorer interface {
	// RestoreLogsOnly restores session logs from a logs-only rewind point.
	// Does not modify the working directory - only restores the transcript
	// to the agent's session directory (determined per-session from checkpoint metadata).
	// If force is false, prompts for confirmation when local logs have newer timestamps.
	// Returns info about each restored session so callers can print correct resume commands.
	RestoreLogsOnly(point RewindPoint, force bool) ([]RestoredSession, error)
}

LogsOnlyRestorer is an optional interface for strategies that support restoring session logs without file state restoration. This is used for "logs-only" rewind points where only the session transcript can be restored (file state requires git checkout).

type ManualCommitStrategy

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

ManualCommitStrategy implements the manual-commit strategy for session management. It stores checkpoints on shadow branches and condenses session logs to a permanent sessions branch when the user commits.

func (*ManualCommitStrategy) CanRewind

func (s *ManualCommitStrategy) CanRewind() (bool, string, error)

CanRewind checks if rewinding is possible. For manual-commit strategy, rewind restores files from a checkpoint - uncommitted changes are expected and will be replaced by the checkpoint contents. Returns true with a warning message showing what changes will be reverted.

func (*ManualCommitStrategy) ClearSessionState

func (s *ManualCommitStrategy) ClearSessionState(sessionID string) error

ClearSessionState is the exported version of clearSessionState. Used by the rewind reset command to clean up session state files.

func (*ManualCommitStrategy) CommitMsg

func (s *ManualCommitStrategy) CommitMsg(commitMsgFile string) error

CommitMsg is called by the git commit-msg hook after the user edits the message. If the message contains only our trailer (no actual user content), strip it so git will abort the commit due to empty message.

func (*ManualCommitStrategy) CondenseSession

func (s *ManualCommitStrategy) CondenseSession(repo *git.Repository, checkpointID id.CheckpointID, state *SessionState, committedFiles map[string]struct{}) (*CondenseResult, error)

CondenseSession condenses a session's shadow branch to permanent storage. checkpointID is the 12-hex-char value from the Entire-Checkpoint trailer. Metadata is stored at sharded path: <checkpoint_id[:2]>/<checkpoint_id[2:]>/ Uses checkpoint.GitStore.WriteCommitted for the git operations.

For mid-session commits (no Stop/SaveStep called yet), the shadow branch may not exist. In this case, data is extracted from the live transcript instead.

func (*ManualCommitStrategy) CondenseSessionByID added in v0.3.13

func (s *ManualCommitStrategy) CondenseSessionByID(sessionID string) error

CondenseSessionByID force-condenses a session by its ID and cleans up. This is used by "entire doctor" to salvage stuck sessions.

func (*ManualCommitStrategy) CountOtherActiveSessionsWithCheckpoints added in v0.3.12

func (s *ManualCommitStrategy) CountOtherActiveSessionsWithCheckpoints(currentSessionID string) (int, error)

CountOtherActiveSessionsWithCheckpoints counts how many other active sessions from the SAME worktree (different from currentSessionID) have created checkpoints on the SAME base commit (current HEAD). This is used to show an informational message about concurrent sessions that will be included in the next commit. Returns 0, nil if no such sessions exist.

func (*ManualCommitStrategy) Description

func (s *ManualCommitStrategy) Description() string

Description returns the strategy description.

func (*ManualCommitStrategy) EnsureSetup

func (s *ManualCommitStrategy) EnsureSetup() error

EnsureSetup ensures the strategy is properly set up.

func (*ManualCommitStrategy) FindSessionsForCommit

func (s *ManualCommitStrategy) FindSessionsForCommit(baseCommitSHA string) ([]*SessionState, error)

FindSessionsForCommit is the exported version of findSessionsForCommit. Used by the rewind reset command to find sessions to clean up.

func (*ManualCommitStrategy) GetAdditionalSessions

func (s *ManualCommitStrategy) GetAdditionalSessions() ([]*Session, error)

GetAdditionalSessions implements SessionSource interface. Returns active sessions from .git/entire-sessions/ that haven't yet been condensed.

func (*ManualCommitStrategy) GetCheckpointLog

func (s *ManualCommitStrategy) GetCheckpointLog(checkpoint Checkpoint) ([]byte, error)

GetCheckpointLog returns the session transcript for a specific checkpoint. For manual-commit strategy, metadata is stored at sharded paths on entire/checkpoints/v1 branch.

func (*ManualCommitStrategy) GetLogsOnlyRewindPoints

func (s *ManualCommitStrategy) GetLogsOnlyRewindPoints(limit int) ([]RewindPoint, error)

GetLogsOnlyRewindPoints finds commits in the current branch's history that have condensed session logs on the entire/checkpoints/v1 branch. These are commits that were created with session data but the shadow branch has been condensed.

The function works by: 1. Getting all checkpoints from the entire/checkpoints/v1 branch 2. Building a map of checkpoint ID -> checkpoint info 3. Scanning the current branch history for commits with Entire-Checkpoint trailers 4. Matching by checkpoint ID (stable across amend/rebase)

func (*ManualCommitStrategy) GetMetadataRef

func (s *ManualCommitStrategy) GetMetadataRef(checkpoint Checkpoint) string

GetMetadataRef returns a reference to the metadata for the given checkpoint. For manual-commit strategy, returns the sharded path on entire/checkpoints/v1 branch.

func (*ManualCommitStrategy) GetRewindPoints

func (s *ManualCommitStrategy) GetRewindPoints(limit int) ([]RewindPoint, error)

GetRewindPoints returns available rewind points. Uses checkpoint.GitStore.ListTemporaryCheckpoints for reading from shadow branches.

func (*ManualCommitStrategy) GetSessionContext

func (s *ManualCommitStrategy) GetSessionContext(sessionID string) string

GetSessionContext returns the context.md content for a session. For manual-commit strategy, reads from the entire/checkpoints/v1 branch using the sessions map.

func (*ManualCommitStrategy) GetSessionInfo

func (s *ManualCommitStrategy) GetSessionInfo() (*SessionInfo, error)

GetSessionInfo returns the current session info.

func (*ManualCommitStrategy) GetSessionMetadataRef

func (s *ManualCommitStrategy) GetSessionMetadataRef(_ string) string

GetSessionMetadataRef returns a reference to the most recent metadata commit for a session. For manual-commit strategy, metadata lives on the entire/checkpoints/v1 branch.

func (*ManualCommitStrategy) GetTaskCheckpoint

func (s *ManualCommitStrategy) GetTaskCheckpoint(point RewindPoint) (*TaskCheckpoint, error)

GetTaskCheckpoint retrieves a task checkpoint.

func (*ManualCommitStrategy) GetTaskCheckpointTranscript

func (s *ManualCommitStrategy) GetTaskCheckpointTranscript(point RewindPoint) ([]byte, error)

GetTaskCheckpointTranscript retrieves the transcript for a task checkpoint.

func (*ManualCommitStrategy) HandleTurnEnd added in v0.3.13

func (s *ManualCommitStrategy) HandleTurnEnd(state *SessionState) error

HandleTurnEnd dispatches strategy-specific actions emitted when an agent turn ends. The primary job is to finalize all checkpoints from this turn with the full transcript.

During a turn, PostCommit writes provisional transcript data (whatever was available at commit time). HandleTurnEnd replaces that with the complete session transcript (from prompt to stop event), ensuring every checkpoint has the full context.

func (*ManualCommitStrategy) InitializeSession

func (s *ManualCommitStrategy) InitializeSession(sessionID string, agentType agent.AgentType, transcriptPath string, userPrompt string) error

InitializeSession creates session state for a new session or updates an existing one. This implements the optional SessionInitializer interface. Called during UserPromptSubmit to allow git hooks to detect active sessions.

If the session already exists and HEAD has moved (e.g., user committed), updates BaseCommit to the new HEAD so future checkpoints go to the correct shadow branch.

If there's an existing shadow branch with commits from a different session ID, returns a SessionIDConflictError to prevent orphaning existing session work.

agentType is the human-readable name of the agent (e.g., "Claude Code"). transcriptPath is the path to the live transcript file (for mid-session commit detection). userPrompt is the user's prompt text (stored truncated as FirstPrompt for display).

func (*ManualCommitStrategy) ListOrphanedItems

func (s *ManualCommitStrategy) ListOrphanedItems() ([]CleanupItem, error)

ListOrphanedItems returns orphaned items created by the manual-commit strategy. This includes:

  • Shadow branches that weren't auto-cleaned during commit condensation
  • Session state files with no corresponding checkpoints or shadow branches

func (*ManualCommitStrategy) Name

func (s *ManualCommitStrategy) Name() string

Name returns the strategy name.

func (*ManualCommitStrategy) PostCommit

func (s *ManualCommitStrategy) PostCommit() error

During rebase/cherry-pick/revert operations, phase transitions are skipped entirely.

func (*ManualCommitStrategy) PrePush

func (s *ManualCommitStrategy) PrePush(remote string) error

PrePush is called by the git pre-push hook before pushing to a remote. It pushes the entire/checkpoints/v1 branch alongside the user's push. Configuration options (stored in .entire/settings.json under strategy_options.push_sessions):

  • "auto": always push automatically
  • "prompt" (default): ask user with option to enable auto
  • "false"/"off"/"no": never push

func (*ManualCommitStrategy) PrepareCommitMsg

func (s *ManualCommitStrategy) PrepareCommitMsg(commitMsgFile string, source string) error

func (*ManualCommitStrategy) PreviewRewind

func (s *ManualCommitStrategy) PreviewRewind(point RewindPoint) (*RewindPreview, error)

PreviewRewind returns what will happen if rewinding to the given point. This allows showing warnings about untracked files that will be deleted.

func (*ManualCommitStrategy) Reset

func (s *ManualCommitStrategy) Reset() error

Reset deletes the shadow branch and session state for the current HEAD. This allows starting fresh without existing checkpoints.

func (*ManualCommitStrategy) ResetSession added in v0.3.13

func (s *ManualCommitStrategy) ResetSession(sessionID string) error

ResetSession clears a single session's state and removes the shadow branch if no other sessions reference it. File changes remain in the working directory.

func (*ManualCommitStrategy) RestoreLogsOnly

func (s *ManualCommitStrategy) RestoreLogsOnly(point RewindPoint, force bool) ([]RestoredSession, error)

RestoreLogsOnly restores session logs from a logs-only rewind point. This fetches the transcript from entire/checkpoints/v1 and writes it to the agent's session directory. Does not modify the working directory. When multiple sessions were condensed to the same checkpoint, ALL sessions are restored. If force is false, prompts for confirmation when local logs have newer timestamps. Returns info about each restored session so callers can print correct per-session resume commands.

func (*ManualCommitStrategy) Rewind

func (s *ManualCommitStrategy) Rewind(point RewindPoint) error

func (*ManualCommitStrategy) SaveStep added in v0.4.6

func (s *ManualCommitStrategy) SaveStep(ctx StepContext) error

SaveStep saves a checkpoint to the shadow branch. Uses checkpoint.GitStore.WriteTemporary for git operations.

func (*ManualCommitStrategy) SaveTaskStep added in v0.4.6

func (s *ManualCommitStrategy) SaveTaskStep(ctx TaskStepContext) error

SaveTaskStep saves a task step checkpoint to the shadow branch. Uses checkpoint.GitStore.WriteTemporaryTask for git operations.

func (*ManualCommitStrategy) ValidateRepository

func (s *ManualCommitStrategy) ValidateRepository() error

ValidateRepository validates that the repository is suitable for this strategy.

type OrphanedItemsLister

type OrphanedItemsLister interface {
	// ListOrphanedItems returns items created by this strategy that are now orphaned.
	// Each strategy defines what "orphaned" means for its own data structures.
	ListOrphanedItems() ([]CleanupItem, error)
}

OrphanedItemsLister is an optional interface for strategies that can identify orphaned items (shadow branches, session states, checkpoints) that should be cleaned up. This is used by the "entire session cleanup" command.

ListAllCleanupItems() automatically discovers all registered strategies, checks if they implement OrphanedItemsLister, and combines their orphaned items.

type PostCommitHandler

type PostCommitHandler interface {
	// PostCommit is called by the git post-commit hook after a commit is created.
	// Used to perform actions like condensing session data after commits.
	// Should return nil on errors to not block subsequent operations (log warnings to stderr).
	PostCommit() error
}

PostCommitHandler is an optional interface for strategies that need to handle the git post-commit hook.

type PrePushHandler

type PrePushHandler interface {
	// PrePush is called by the git pre-push hook before pushing to a remote.
	// Used to push session branches (e.g., entire/checkpoints/v1) alongside user pushes.
	// The remote parameter is the name of the remote being pushed to.
	// Should return nil on errors to not block pushes (log warnings to stderr).
	PrePush(remote string) error
}

PrePushHandler is an optional interface for strategies that need to handle the git pre-push hook.

type PrepareCommitMsgHandler

type PrepareCommitMsgHandler interface {
	// PrepareCommitMsg is called by the git prepare-commit-msg hook.
	// It can modify the commit message file to add trailers, etc.
	// The source parameter indicates how the commit was initiated:
	//   - "" or "template": normal editor flow
	//   - "message": using -m or -F flag
	//   - "merge": merge commit
	//   - "squash": squash merge
	//   - "commit": amend with -c/-C
	// Should return nil on errors to not block commits (log warnings to stderr).
	PrepareCommitMsg(commitMsgFile string, source string) error
}

PrepareCommitMsgHandler is an optional interface for strategies that need to handle the git prepare-commit-msg hook.

type PromptAttribution

type PromptAttribution = session.PromptAttribution

PromptAttribution is an alias for session.PromptAttribution.

func CalculatePromptAttribution

func CalculatePromptAttribution(
	baseTree *object.Tree,
	lastCheckpointTree *object.Tree,
	worktreeFiles map[string]string,
	checkpointNumber int,
) PromptAttribution

CalculatePromptAttribution computes line-level attribution at the start of a prompt. This captures user edits since the last checkpoint BEFORE the agent makes changes.

Parameters:

  • baseTree: the tree at session start (the base commit)
  • lastCheckpointTree: the tree from the previous checkpoint (nil if first checkpoint)
  • worktreeFiles: map of file path → current worktree content for files that changed
  • checkpointNumber: which checkpoint we're about to create (1-indexed)

Returns the attribution data to store in session state. For checkpoint 1 (when lastCheckpointTree is nil), AgentLinesAdded/Removed will be 0 since there's no previous checkpoint to measure cumulative agent work against.

Note: Binary files (detected by null bytes) in reference trees are silently excluded from attribution calculations since line-based diffing only applies to text files.

type RestoredSession added in v0.4.3

type RestoredSession struct {
	SessionID string
	Agent     agent.AgentType
	Prompt    string
	CreatedAt time.Time // From session metadata; used by resume to determine most recent
}

RestoredSession describes a single session that was restored by RestoreLogsOnly. Each session may come from a different agent, so callers use this to print per-session resume commands without re-reading the metadata tree.

type RewindPoint

type RewindPoint struct {
	// ID is the unique identifier for this rewind point
	// (commit hash, branch name, stash ref, etc.)
	ID string

	// Message is the human-readable description/summary
	Message string

	// MetadataDir is the path to the metadata directory
	MetadataDir string

	// Date is when this rewind point was created
	Date time.Time

	// IsTaskCheckpoint indicates if this is a task checkpoint (vs a session checkpoint)
	IsTaskCheckpoint bool

	// ToolUseID is the tool use ID for task checkpoints (empty for session checkpoints)
	ToolUseID string

	// IsLogsOnly indicates this is a commit with session logs but no shadow branch state.
	// The logs can be restored from entire/checkpoints/v1, but file state requires git checkout.
	IsLogsOnly bool

	// CheckpointID is the stable 12-hex-char identifier for logs-only points.
	// Used to retrieve logs from entire/checkpoints/v1/<id[:2]>/<id[2:]>/full.jsonl
	// Empty for shadow branch checkpoints (uncommitted).
	CheckpointID id.CheckpointID

	// Agent is the human-readable name of the agent that created this checkpoint
	// (e.g., "Claude Code", "Cursor")
	Agent agent.AgentType

	// SessionID is the session identifier for this checkpoint.
	// Used to distinguish checkpoints from different concurrent sessions.
	SessionID string

	// SessionPrompt is the initial prompt that started this session.
	// Used to help users identify which session a checkpoint belongs to.
	SessionPrompt string

	// SessionCount is the number of sessions in this checkpoint (1 for single-session).
	// Only populated for logs-only points with multi-session checkpoints.
	SessionCount int

	// SessionIDs contains all session IDs when this is a multi-session checkpoint.
	// The last entry is the most recent session (same as SessionID).
	// Only populated for logs-only points with multi-session checkpoints.
	SessionIDs []string

	// SessionPrompts contains the first prompt for each session (parallel to SessionIDs).
	// Used to display context when showing resume commands for multi-session checkpoints.
	SessionPrompts []string
}

RewindPoint represents a point to which the user can rewind. This abstraction allows different strategies to use different identifiers (commit hashes, branch names, stash refs, etc.)

type RewindPreview

type RewindPreview struct {
	// FilesToRestore are files from the checkpoint that will be written/restored.
	FilesToRestore []string

	// FilesToDelete are untracked files that will be removed.
	// These are files created after the checkpoint that aren't in the checkpoint tree
	// and weren't present at session start.
	FilesToDelete []string

	// TrackedChanges are tracked files with uncommitted changes that will be reverted.
	// These come from the existing CanRewind() warning.
	TrackedChanges []string
}

RewindPreview describes what will happen when rewinding to a checkpoint. Used to warn users about files that will be modified or deleted.

type Session

type Session struct {
	// ID is the unique session identifier (e.g., "2025-12-01-8f76b0e8-b8f1-4a87-9186-848bdd83d62e")
	ID string

	// Description is a human-readable summary of the session
	// (typically the first prompt or derived from commit messages)
	Description string

	// Strategy is the name of the strategy that created this session
	Strategy string

	// StartTime is when the session was started
	StartTime time.Time

	// Checkpoints is the list of save points within this session
	Checkpoints []Checkpoint
}

Session represents a Claude Code session with its checkpoints. A session is created when a user runs `claude` and tracks all changes made during that interaction.

func GetSession

func GetSession(sessionID string) (*Session, error)

GetSession finds a session by ID (supports prefix matching). Returns ErrNoSession if no matching session is found.

func ListSessions

func ListSessions() ([]Session, error)

ListSessions returns all sessions from the entire/checkpoints/v1 branch, plus any additional sessions from strategies implementing SessionSource. It automatically discovers all registered strategies and merges their sessions.

type SessionCondenser added in v0.3.13

type SessionCondenser interface {
	// CondenseSessionByID force-condenses a session and cleans up.
	// Generates a new checkpoint ID, condenses to entire/checkpoints/v1,
	// updates the session state, and removes the shadow branch
	// if no other active sessions need it.
	CondenseSessionByID(sessionID string) error
}

SessionCondenser is an optional interface for strategies that support force-condensing a session. This is used by "entire doctor" to salvage stuck sessions by condensing their data to permanent storage.

type SessionIDConflictError

type SessionIDConflictError struct {
	ExistingSession string // Session ID found in the shadow branch
	NewSession      string // Session ID being initialized
	ShadowBranch    string // The shadow branch name (e.g., "entire/abc1234")
}

SessionIDConflictError is returned when trying to start a new session but the shadow branch already has commits from a different session ID. This prevents orphaning existing session work.

func (*SessionIDConflictError) Error

func (e *SessionIDConflictError) Error() string

type SessionInfo

type SessionInfo struct {
	// SessionID is the session identifier extracted from the latest commit's metadata
	SessionID string

	// Reference is a strategy-specific reference string.
	// For manual-commit strategy: "entire/abc1234" (the shadow branch name)
	// Empty for commit strategy (metadata is in the same commit).
	Reference string

	// CommitHash is the full SHA of the commit containing the session metadata.
	// Empty for commit strategy.
	CommitHash string
}

SessionInfo contains information about the current session state. This is used to generate trailers for linking commits to their AI session.

type SessionInitializer

type SessionInitializer interface {
	// InitializeSession creates session state for a new session.
	// Called during UserPromptSubmit hook before any checkpoints are created.
	// agentType is the human-readable name of the agent (e.g., "Claude Code").
	// transcriptPath is the path to the live transcript file (for mid-session commit detection).
	// userPrompt is the user's prompt text (stored truncated as FirstPrompt for display).
	InitializeSession(sessionID string, agentType agent.AgentType, transcriptPath string, userPrompt string) error
}

SessionInitializer is an optional interface for strategies that need to initialize session state when a user prompt is submitted. Strategies like manual-commit use this to create session state files that the git prepare-commit-msg hook can detect.

type SessionResetter

type SessionResetter interface {
	// Reset deletes the shadow branch and session state for the current HEAD.
	// Returns nil if there's nothing to reset (no shadow branch).
	Reset() error

	// ResetSession clears the state for a single session and cleans up
	// the shadow branch if no other sessions reference it.
	// File changes remain in the working directory.
	ResetSession(sessionID string) error
}

SessionResetter is an optional interface for strategies that support resetting session state and shadow branches. This is used by the "reset" command to clean up shadow branches and session state when a user wants to start fresh.

type SessionRestoreInfo

type SessionRestoreInfo struct {
	SessionID      string
	Prompt         string               // First prompt preview for display
	Status         SessionRestoreStatus // Status of this session
	LocalTime      time.Time
	CheckpointTime time.Time
}

SessionRestoreInfo contains information about a session being restored.

type SessionRestoreStatus

type SessionRestoreStatus int

SessionRestoreStatus represents the status of a session being restored.

const (
	StatusNew             SessionRestoreStatus = iota // Local file doesn't exist
	StatusUnchanged                                   // Local and checkpoint are the same
	StatusCheckpointNewer                             // Checkpoint has newer entries
	StatusLocalNewer                                  // Local has newer entries (conflict)
)

func ClassifyTimestamps

func ClassifyTimestamps(localTime, checkpointTime time.Time) SessionRestoreStatus

ClassifyTimestamps determines the restore status based on local and checkpoint timestamps.

type SessionSource

type SessionSource interface {
	// GetAdditionalSessions returns sessions not yet on entire/checkpoints/v1 branch.
	GetAdditionalSessions() ([]*Session, error)
}

SessionSource is an optional interface for strategies that provide additional sessions beyond those stored on the entire/checkpoints/v1 branch. For example, manual-commit strategy provides active sessions from .git/entire-sessions/ that haven't yet been condensed to entire/checkpoints/v1.

ListSessions() automatically discovers all registered strategies, checks if they implement SessionSource, and merges their additional sessions by ID.

type SessionState

type SessionState = session.State

SessionState is an alias for session.State. Previously this was a separate struct with manual conversion functions.

func ListSessionStates added in v0.3.13

func ListSessionStates() ([]*SessionState, error)

ListSessionStates returns all session states from the state directory. This is a package-level function that doesn't require a specific strategy instance.

func LoadSessionState

func LoadSessionState(sessionID string) (*SessionState, error)

LoadSessionState loads the session state for the given session ID. Returns (nil, nil) when session file doesn't exist or session is stale (not an error condition). Stale sessions are automatically deleted by the underlying StateStore.

type StageFilesContext

type StageFilesContext string

StageFilesContext describes what type of staging operation this is (for messages).

const (
	// StageForSession is used when staging files for a session checkpoint.
	StageForSession StageFilesContext = "session"
	// StageForTask is used when staging files for a task checkpoint.
	StageForTask StageFilesContext = "task"
)

type StepContext added in v0.4.6

type StepContext struct {
	// SessionID is the Claude Code session identifier
	SessionID string

	// ModifiedFiles is the list of files modified during the session
	// (extracted from the transcript, already filtered and relative)
	ModifiedFiles []string

	// NewFiles is the list of new files created during the session
	// (pre-computed by CLI from pre-prompt state comparison)
	NewFiles []string

	// DeletedFiles is the list of files deleted during the session
	// (tracked files that no longer exist)
	DeletedFiles []string

	// MetadataDir is the path to the session metadata directory
	MetadataDir string

	// MetadataDirAbs is the absolute path to the session metadata directory
	MetadataDirAbs string

	// CommitMessage is the generated commit message
	CommitMessage string

	// TranscriptPath is the path to the transcript file
	TranscriptPath string

	// AuthorName is the name to use for commits
	AuthorName string

	// AuthorEmail is the email to use for commits
	AuthorEmail string

	// AgentType is the human-readable agent name (e.g., "Claude Code", "Cursor")
	AgentType agent.AgentType

	// Transcript position at step/turn start - tracks what was added during this step
	StepTranscriptIdentifier string // Last identifier when step started (UUID for Claude, message ID for Gemini)
	StepTranscriptStart      int    // Transcript line count when this step/turn started

	// TokenUsage contains the token usage for this checkpoint
	TokenUsage *agent.TokenUsage
}

StepContext contains all information needed for saving a step checkpoint. All file paths should be pre-filtered and normalized by the CLI layer.

type Strategy

type Strategy interface {
	// Name returns the strategy identifier (e.g., "commit", "branch", "stash")
	Name() string

	// Description returns a human-readable description for the setup wizard
	Description() string

	// ValidateRepository checks if the repository is in a valid state
	// for this strategy to operate. Returns an error if validation fails.
	ValidateRepository() error

	// SaveStep is called on Stop to save all session changes
	// using this strategy's approach (commit, branch, stash, etc.)
	SaveStep(ctx StepContext) error

	// SaveTaskStep is called by PostToolUse[Task] hook when a subagent completes.
	// Creates a checkpoint commit with task metadata for later rewind.
	// Different strategies may handle this differently:
	// - Commit strategy: commits to active branch
	// - Manual-commit strategy: commits to shadow branch
	// - Auto-commit strategy: commits logs to shadow only (code deferred to Stop)
	SaveTaskStep(ctx TaskStepContext) error

	// GetRewindPoints returns available points to rewind to.
	// The limit parameter controls the maximum number of points to return.
	GetRewindPoints(limit int) ([]RewindPoint, error)

	// Rewind restores the repository to the given rewind point.
	// The metadataDir in the point is used to restore the session transcript.
	Rewind(point RewindPoint) error

	// CanRewind checks if rewinding is currently possible.
	// Returns (canRewind, reason if not, error)
	CanRewind() (bool, string, error)

	// PreviewRewind returns what will happen if rewinding to the given point.
	// This allows showing warnings about files that will be deleted before the rewind.
	// Returns nil if preview is not supported (e.g., auto-commit strategy).
	PreviewRewind(point RewindPoint) (*RewindPreview, error)

	// GetTaskCheckpoint returns the task checkpoint for a given rewind point.
	// For strategies that store checkpoints in git (auto-commit), this reads from the branch.
	// For strategies that store checkpoints on disk (commit, manual-commit), this reads from the filesystem.
	// Returns nil, nil if not a task checkpoint or checkpoint not found.
	GetTaskCheckpoint(point RewindPoint) (*TaskCheckpoint, error)

	// GetTaskCheckpointTranscript returns the session transcript for a task checkpoint.
	// For strategies that store transcripts in git (auto-commit), this reads from the branch.
	// For strategies that store transcripts on disk (commit, manual-commit), this reads from the filesystem.
	GetTaskCheckpointTranscript(point RewindPoint) ([]byte, error)

	// GetSessionInfo returns session information for linking commits.
	// This is used by the context command to generate trailers.
	// Returns ErrNoSession if no session info is available.
	GetSessionInfo() (*SessionInfo, error)

	// EnsureSetup ensures the strategy's required setup is in place,
	// installing any missing pieces (git hooks, gitignore entries, etc.).
	// Returns nil if setup is complete or was successfully installed.
	EnsureSetup() error

	// GetMetadataRef returns a reference to the metadata commit for the given checkpoint.
	// Format: "<branch>@<commit-sha>" (e.g., "entire/checkpoints/v1@abc123").
	// Returns empty string if not applicable (e.g., commit strategy with filesystem metadata).
	GetMetadataRef(checkpoint Checkpoint) string

	// GetSessionMetadataRef returns a reference to the most recent metadata commit for a session.
	// Format: "<branch>@<commit-sha>" (e.g., "entire/checkpoints/v1@abc123").
	// Returns empty string if not applicable or session not found.
	GetSessionMetadataRef(sessionID string) string

	// GetSessionContext returns the context.md content for a session.
	// Returns empty string if not available.
	GetSessionContext(sessionID string) string

	// GetCheckpointLog returns the session transcript for a specific checkpoint.
	// For strategies that store transcripts in git branches (auto-commit, manual-commit),
	// this reads from the checkpoint's commit tree.
	// For strategies that store on disk (commit), reads from the filesystem.
	// Returns ErrNoMetadata if transcript is not available.
	GetCheckpointLog(checkpoint Checkpoint) ([]byte, error)
}

Strategy defines the interface for git operation strategies. Different implementations can use commits, branches, stashes, etc.

Note: State capture (tracking untracked files before a session) is handled by the CLI layer, not the strategy. The strategy receives pre-computed file lists in StepContext.

func Default

func Default() Strategy

Default returns the default strategy. Falls back to returning nil if no strategies are registered.

func Get

func Get(name string) (Strategy, error)

func NewAutoCommitStrategy

func NewAutoCommitStrategy() Strategy

func NewManualCommitStrategy

func NewManualCommitStrategy() Strategy

func NewShadowStrategy

func NewShadowStrategy() Strategy

type SubagentCheckpoint

type SubagentCheckpoint struct {
	Type      string          `json:"type"`        // Tool name: "TodoWrite", "Edit", "Write"
	ToolUseID string          `json:"tool_use_id"` // The tool use ID that created this checkpoint
	Timestamp time.Time       `json:"timestamp"`   // When the checkpoint was created
	Data      json.RawMessage `json:"data"`        // Type-specific payload (tool_input)
}

SubagentCheckpoint represents an intermediate checkpoint created during subagent execution. These are created by PostToolUse hooks for tools like TodoWrite, Edit, Write.

type TaskCheckpoint

type TaskCheckpoint struct {
	SessionID      string `json:"session_id"`
	ToolUseID      string `json:"tool_use_id"`
	CheckpointUUID string `json:"checkpoint_uuid"`
	AgentID        string `json:"agent_id,omitempty"`
}

TaskCheckpoint contains the checkpoint information written to checkpoint.json

func ReadTaskCheckpoint

func ReadTaskCheckpoint(taskMetadataDir string) (*TaskCheckpoint, error)

ReadTaskCheckpoint reads the checkpoint.json file from a task metadata directory. This is used during rewind to get the checkpoint UUID for transcript truncation.

type TaskStepContext added in v0.4.6

type TaskStepContext struct {
	// SessionID is the Claude Code session identifier
	SessionID string

	// ToolUseID is the unique identifier for this Task tool invocation
	ToolUseID string

	// AgentID is the subagent identifier (from tool_response.agentId)
	AgentID string

	// ModifiedFiles is the list of files modified by the subagent
	// (extracted from the subagent's transcript)
	ModifiedFiles []string

	// NewFiles is the list of new files created by the subagent
	// (computed from pre-task state comparison)
	NewFiles []string

	// DeletedFiles is the list of files deleted by the subagent
	DeletedFiles []string

	// TranscriptPath is the path to the main session transcript
	TranscriptPath string

	// SubagentTranscriptPath is the path to the subagent's transcript (if available)
	SubagentTranscriptPath string

	// CheckpointUUID is the UUID for transcript truncation when rewinding
	CheckpointUUID string

	// AuthorName is the name to use for commits
	AuthorName string

	// AuthorEmail is the email to use for commits
	AuthorEmail string

	// IsIncremental indicates this is an incremental checkpoint during task execution,
	// not a final task completion checkpoint. When true:
	// - Writes to checkpoints/NNN-{tool-use-id}.json instead of checkpoint.json
	// - Skips transcript handling
	// - Uses incremental commit message
	IsIncremental bool

	// IncrementalSequence is the checkpoint sequence number (1, 2, 3, ...)
	// Only used when IsIncremental is true
	IncrementalSequence int

	// IncrementalType is the tool that triggered this checkpoint ("TodoWrite", "Edit", etc.)
	// Only used when IsIncremental is true
	IncrementalType string

	// IncrementalData is the tool_input payload for this checkpoint
	// Only used when IsIncremental is true
	IncrementalData json.RawMessage

	// SubagentType is the type of subagent (e.g., "dev", "reviewer")
	// Extracted from tool_input.subagent_type in Task tool
	// Used for descriptive commit messages
	SubagentType string

	// TaskDescription is the task description provided to the subagent
	// Extracted from tool_input.description in Task tool
	// Used for descriptive commit messages
	TaskDescription string

	// TodoContent is the content of the in-progress todo item
	// Extracted from tool_input.todos where status == "in_progress"
	// Used for descriptive incremental checkpoint messages
	TodoContent string

	// AgentType is the human-readable agent name (e.g., "Claude Code", "Cursor")
	AgentType agent.AgentType
}

TaskStepContext contains all information needed for saving a task step checkpoint. This is called by the PostToolUse[Task] hook when a subagent completes. The strategy is responsible for creating metadata structures and storing them according to its storage approach.

type TurnEndHandler added in v0.3.13

type TurnEndHandler interface {
	// HandleTurnEnd performs strategy-specific cleanup at the end of a turn.
	// Work items are read from state (e.g. TurnCheckpointIDs), not from the
	// action list. The state has already been updated by ApplyTransition;
	// the caller saves it after this method returns.
	HandleTurnEnd(state *session.State) error
}

TurnEndHandler is an optional interface for strategies that need to perform work when an agent turn ends (ACTIVE → IDLE). For example, manual-commit strategy uses this to finalize checkpoints with the full session transcript.

Jump to

Keyboard shortcuts

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