lifecycle

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package lifecycle implements USER-DRIVEN session lifecycle operations over Claude Code transcripts: archiving a session's .jsonl out of the active projects tree, and deleting sessions behind a filter gate.

Two safety invariants drive the design (prior-art consensus, all LLM-free):

  • Delete is FILTER-GATED and DRY-RUN-FIRST: it refuses to delete every session (>=1 filter must be set), and a dry run reports a plan without touching disk.
  • A real delete writes a TOMBSTONE sidecar (a plain text file, one session id per line) so a later index pass can skip a deleted session instead of resurrecting it. The tombstone is NOT a schema change — it is a flat file the indexer consults.

This package is intentionally self-contained: it resolves sessions by walking .jsonl files directly and counts messages by JSONL line count, so it carries no dependency on the index/parse internals. The CLI layer wires the subcommands, flags, and the y/N confirm around these functions.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoFilter = errors.New("refusing to delete all sessions: set at least one filter")

ErrNoFilter is returned by Delete when the caller supplies no filter. Deleting every session must be an explicit, narrowed act — never the default.

Functions

func Archive

func Archive(sessionPathOrID, archiveDir string) (string, error)

Archive moves the session identified by sessionPathOrID into archiveDir, returning the new path. sessionPathOrID may be an absolute/relative path to a .jsonl, or a bare session id resolved against the projects tree (top-level sessions only).

If archiveDir is empty it defaults to ~/.claude/archive/. The move is idempotent: if the source is already inside archiveDir (or the destination already exists and the source is gone), Archive reports success with the archived path rather than erroring.

func IsTombstoned

func IsTombstoned(cacheDir, sessionID string) (bool, error)

IsTombstoned reports whether sessionID appears in the tombstone under cacheDir. A missing tombstone means nothing is tombstoned (returns false, nil).

func LoadTombstones

func LoadTombstones(cacheDir string) (map[string]struct{}, error)

LoadTombstones reads the tombstone at <cacheDir>/.deleted and returns the set of deleted session ids. A missing file is not an error — it yields an empty set. Blank lines and surrounding whitespace are ignored.

func MatchesSessionID added in v0.5.0

func MatchesSessionID(stem, id string) bool

MatchesSessionID reports whether a session id (a .jsonl stem) is addressed by id: exact equality always, prefix only from 8 characters up (the session8 form search output prints). Anything shorter must match exactly — a 3-char prefix silently fanning out to many sessions is how a targeted delete becomes a massacre. Shared by the live walk, the retained scan, and the archive's foreign-session probe so all three answer alike.

func TombstoneIDs added in v0.3.0

func TombstoneIDs(cacheDir string, ids []string) error

TombstoneIDs appends each id in ids to the tombstone sidecar under cacheDir, wrapping appendTombstones (same atomicity: create-dir, open-append, write). Exported so the CLI can tombstone RETAINED sessions matched by index.RetainedMatches — those have no backing file to os.Remove, so Delete's normal remove-then-tombstone path does not apply; this is the tombstone-only half for that case.

func TombstonePath

func TombstonePath(cacheDir string) string

TombstonePath returns the path to the tombstone sidecar file: <cacheDir>/.deleted. If cacheDir is empty it defaults to ~/.cache/session-search.

Types

type DeleteOpts

type DeleteOpts struct {
	// Before, when non-zero, matches sessions whose transcript file was last
	// modified strictly before this instant.
	Before time.Time
	// Project, when non-empty, matches sessions whose transcript directory path
	// contains this substring (case-sensitive).
	Project string
	// MaxMessages, when > 0, matches sessions with at most this many messages
	// (JSONL lines). Use it to prune short/low-signal sessions.
	MaxMessages int
	// SessionID, when non-empty, matches the one session whose id equals it —
	// or starts with it when it is at least 8 characters (the search-output
	// session8 form). See MatchesSessionID for the exact rule.
	SessionID string
	// DryRun, when true, computes and returns the plan WITHOUT deleting anything
	// or writing a tombstone.
	DryRun bool
}

DeleteOpts carries the filter gate and the dry-run switch for Delete.

At least one of Before / Project / MaxMessages must be set or Delete returns ErrNoFilter. The filters are ANDed: a session must satisfy every set filter to match. A zero-value field is "unset" and does not constrain the match.

type DeletePlan

type DeletePlan struct {
	Matched       []PlanItem
	TotalBytes    int64
	Deleted       bool   // false for a dry run
	TombstonePath string // where ids were (or would be) appended
}

DeletePlan is the result of a Delete pass — what matched and how much disk it reclaims. For a dry run, Deleted is false and nothing on disk changed; for a real delete, Deleted is true and Matched lists the sessions that were removed and tombstoned.

func Delete

func Delete(projectsRoot, cacheDir string, opts DeleteOpts) (DeletePlan, error)

Delete removes the sessions under projectsRoot that match opts, gated by the filter requirement. projectsRoot is the Claude Code projects root (the dir holding the per-project transcript dirs). cacheDir is where the tombstone sidecar lives (typically ~/.cache/session-search); if empty it defaults to that path.

Behavior:

  • No filter set -> ErrNoFilter (never deletes everything).
  • opts.DryRun true -> returns the plan, touches nothing on disk.
  • Otherwise -> deletes each matched .jsonl and appends its id to the tombstone, then returns the plan with Deleted=true.

type FloorStats added in v0.10.0

type FloorStats struct {
	SubstantiveHumanTurns int
	TotalMessages         int
	AssistantProseBytes   int
}

FloorStats are the transcript metrics used by the deterministic routine floor. TotalMessages deliberately counts every parsed record: tool results and hook banners are part of the conservative message-count guard.

func EvaluateMathFloor added in v0.10.0

func EvaluateMathFloor(messages []model.Message) (bool, FloorStats)

EvaluateMathFloor marks only obviously small sessions as routine. The two conditions are intentionally conjunctive: a single substantive prompt does not pass when the transcript grew large or produced substantial prose.

type PlanItem

type PlanItem struct {
	SessionID string // .jsonl stem == the claude --resume id
	Path      string // absolute path to the .jsonl
	Project   string // friendly project label (basename of the transcript dir)
	Bytes     int64  // file size in bytes
	Messages  int    // JSONL line count
}

PlanItem is one session matched by a Delete pass.

Jump to

Keyboard shortcuts

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