paths

package
v0.10.5 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	EntireDir         = ".entire"
	EntireTmpDir      = ".entire/tmp"
	EntireMetadataDir = ".entire/metadata"
)

Directory constants

View Source
const (
	PromptFileName           = "prompt.txt"
	TranscriptFileName       = "full.jsonl"
	TranscriptFileNameLegacy = "full.log"
	// CompactTranscriptFileName is the compact transcript stored alongside
	// full.jsonl. It holds the full compacted session; this checkpoint's slice
	// begins at the session metadata's compact_transcript_start.
	CompactTranscriptFileName = "transcript.jsonl"
	MetadataFileName          = "metadata.json"
	CheckpointFileName        = "checkpoint.json"
	ContentHashFileName       = "content_hash.txt"
	SettingsFileName          = "settings.json"

	// AssetsDir is the per-session subfolder holding externalized transcript
	// assets (e.g. images); AssetsManifestFile indexes them. AssetsDirName is the
	// bare tree-entry name (no trailing slash) used when walking git trees.
	AssetsDirName      = "assets"
	AssetsDir          = "assets/"
	AssetsManifestFile = "assets/manifest.json"
)

Metadata file names

View Source
const MetadataBranchName = "entire/checkpoints/v1"

MetadataBranchName is the orphan branch used by manual-commit strategy to store metadata

View Source
const TrailsBranchName = "entire/trails/v1"

TrailsBranchName is the orphan branch used to store trail metadata. Trails are branch-centric work tracking abstractions that link to checkpoints by branch name.

Variables

View Source
var (
	// ErrEntireDirNotDirectory reports that `.entire` exists and is not a real
	// directory. The remedy is to inspect and replace the path.
	ErrEntireDirNotDirectory = errors.New("not a directory")

	// ErrEntireDirUnsupportedEntry reports that an entry directly under
	// `.entire` is neither a regular file nor a directory. The remedy is to
	// inspect that entry and replace it, which is the same shape as
	// ErrEntireDirNotDirectory's but not the same sentence:
	// `.entire/settings.json` is not required to be a directory, so telling
	// someone it is not one names the wrong problem.
	ErrEntireDirUnsupportedEntry = errors.New("not a regular file or directory")

	// ErrEntireDirUnreadable reports that `.entire` could not be inspected at
	// all — a permission failure, an I/O error, a dead mount. Nothing is known
	// about what is at the path. The remedy is ownership, permissions, or the
	// filesystem itself.
	ErrEntireDirUnreadable = errors.New("cannot be inspected")

	// ErrRepositoryUnresolved reports that the worktree root could not be
	// determined for a reason other than there being no repository, so there is
	// no `.entire` path to inspect yet. The remedy is git.
	ErrRepositoryUnresolved = errors.New("cannot determine which repository this directory belongs to")
)

The four ways validating `.entire` can fail. Each is identified positively and carries a different remedy, which is why they are separate sentinels rather than one error plus an else branch: callers print the fix, and telling someone to reinstall git because their filesystem returned EACCES sends them after the wrong thing. A caller matching none of these must offer no remedy rather than guess at one.

View Source
var ErrNotARepository = errors.New("not a git repository")

ErrNotARepository reports that git positively identified the working directory as being outside any repository. It is the ONLY worktree-root failure that means "there is nothing here to look at" — every other one (git missing from PATH, a cancelled context, a permission failure, dubious ownership, malformed output) means "we could not find out", which is a different thing and must not be treated as the benign case.

Functions

func AbsPath

func AbsPath(ctx context.Context, relPath string) (string, error)

AbsPath returns the absolute path for a relative path within the repository. If the path is already absolute, it is returned as-is. Uses WorktreeRoot() to resolve paths relative to the worktree root.

func AgentTranscriptFileName added in v0.10.0

func AgentTranscriptFileName(agentID string) string

AgentTranscriptFileName returns the file name an agent writes a subagent's transcript under: agent-<agentID>.jsonl.

func CaseInsensitiveFS added in v0.9.0

func CaseInsensitiveFS() bool

CaseInsensitiveFS reports whether path comparisons should be case-insensitive on the host OS. This is OS-based, not volume-based: Windows and macOS default to case-insensitive filesystems, Linux to case-sensitive. Keying on GOOS keeps the result deterministic. It must only influence EXCLUSION decisions (see IsProtectedSubpath / Equal): on an atypical volume (e.g. a case-sensitive macOS APFS volume) it treats a differently-cased path as matching, which is safe only when the effect is to exclude more, never to widen an allow gate.

func ClearWorktreeRootCache added in v0.4.8

func ClearWorktreeRootCache()

ClearWorktreeRootCache clears the cached worktree root. This is primarily useful for testing when changing directories.

func Equal added in v0.9.0

func Equal(a, b string) bool

Equal reports whether two paths refer to the same location, honoring the host OS's case sensitivity (see CaseInsensitiveFS). Both inputs are cleaned and slash-normalized before comparison. Like IsProtectedSubpath, this is intended for EXCLUSION matching (e.g. protected files), not fail-closed containment.

func ExtractSessionIDFromTranscriptPath

func ExtractSessionIDFromTranscriptPath(transcriptPath string) string

ExtractSessionIDFromTranscriptPath attempts to extract a session ID from a transcript path. Claude transcripts are stored at ~/.claude/projects/<project>/sessions/<id>.jsonl If the path doesn't match expected format, returns empty string.

func GetLastTimestampFromBytes

func GetLastTimestampFromBytes(data []byte) time.Time

GetLastTimestampFromBytes extracts the timestamp from the last non-empty line of JSONL content. Returns zero time if not found.

func GetLastTimestampFromFile

func GetLastTimestampFromFile(path string) time.Time

GetLastTimestampFromFile reads the last non-empty line from a JSONL file and extracts the timestamp field. Returns zero time if file doesn't exist or no valid timestamp is found.

func GetWorktreeID

func GetWorktreeID(worktreePath string) (string, error)

GetWorktreeID returns the internal git worktree identifier for the given path. For the main worktree (where .git is a directory), returns empty string. For linked worktrees (where .git is a file), extracts the name from .git/worktrees/<name>/ path. This name is stable across `git worktree move`.

func IsInfrastructurePath

func IsInfrastructurePath(path string) bool

IsInfrastructurePath returns true if the path is part of CLI infrastructure (i.e., inside the .entire directory). It is used only to EXCLUDE infra paths from checkpoints/tracking, so it matches case-insensitively on case-insensitive filesystems via IsProtectedSubpath. Do not use it as a containment/allow gate.

func IsProtectedSubpath added in v0.9.0

func IsProtectedSubpath(parent, child string) bool

IsProtectedSubpath reports whether child is under parent for the purpose of EXCLUDING protected/infrastructure content from checkpoints and tracking. Unlike IsSubpath it honors OS case-insensitivity (see CaseInsensitiveFS), so a case variant of a protected dir (".Claude" vs ".claude") is still excluded on Windows/macOS.

SECURITY: never use this for allow/containment decisions. Case-folding widens what counts as "inside" parent, which is safe only when the effect is to exclude more. On a case-sensitive volume under a case-insensitive GOOS it over-matches; for a fail-closed gate that would fail open. Use IsSubpath there.

func IsRelativeTraversal added in v0.7.7

func IsRelativeTraversal(rel string) bool

IsRelativeTraversal reports whether rel escapes its base directory. It accepts both OS-native paths and Git-style slash-normalized paths.

func IsSubpath added in v0.5.1

func IsSubpath(parent, child string) bool

IsSubpath reports whether child is lexically under parent (or equal to it). It uses filepath.Rel, which cleans both inputs and is traversal-resistant: a crafted child like "/a/b/../../../etc/passwd" that escapes parent will produce a relative path starting with ".." and be rejected.

Matching is case-SENSITIVE. This is the correct primitive for fail-closed containment/allow checks (e.g. validating an attacker-influenced path stays under an Entire-owned dir): on a case-sensitive volume a differently-cased path names a different directory, so folding it in would fail open. For EXCLUSION decisions that must also catch case variants on Windows/macOS, use IsProtectedSubpath instead.

func ParseTimestampFromJSONL

func ParseTimestampFromJSONL(line string) time.Time

ParseTimestampFromJSONL extracts the timestamp from a JSONL line. Returns zero time if the line is empty or doesn't contain a valid timestamp.

func RequireEntireDir added in v0.10.4

func RequireEntireDir(ctx context.Context) error

RequireEntireDir validates the current worktree's `.entire`.

Outside a git repository there is no worktree root and so nothing to validate, which is not an error: commands that need a repository report its absence themselves, with a message about the repository rather than about `.entire`. That skip requires git's positive ErrNotARepository verdict.

Every other discovery failure — git missing from PATH, a cancelled context, a permission failure, dubious ownership, malformed output — fails closed. Those mean "we could not find out", and the consequence of guessing "no repository" is not merely a skipped check: settings resolution falls back to a path relative to the current directory when the root will not resolve (settingsAbsPaths in the settings package), so a guess would read ./.entire/settings.json — through the very symlink this exists to reject. Refusing to run on a machine whose git is broken is the cheaper mistake.

Deliberately not memoized. The Lstat and the one-level listing are free next to the `git rev-parse` that WorktreeRoot runs — measured 8.2µs against a ~millisecond subprocess — and a cached "it was fine" is a stale answer in a long-lived process such as `entire mcp`.

func SessionMetadataDirFromSessionID

func SessionMetadataDirFromSessionID(sessionID string) string

SessionMetadataDirFromSessionID returns the path to a session's metadata directory for the given Entire session ID. The sessionID must be the full, already date-prefixed Entire session identifier as stored on disk, not an agent-specific or raw Claude ID.

func SubagentsDir added in v0.10.0

func SubagentsDir(transcriptDir, sessionID string) string

SubagentsDir returns the directory an agent stores a session's subagent transcripts in: <transcriptDir>/<sessionID>/subagents.

This layout lives here, in the leaf paths package, because it is needed on both sides of the import graph — the lifecycle dispatcher and the strategy, review, and agentimport packages all resolve it, and those cannot import each other. Before it was named it existed as five copies of the same filepath.Join, which is how the SubagentEnd path came to disagree with the turn-end path about where subagent transcripts live.

sessionID is the *agent's* session ID (the transcript file's own basename), not the date-prefixed Entire session ID.

func SymlinkedEntryError added in v0.10.4

func SymlinkedEntryError(path string) error

SymlinkedEntryError reports that path is a symbolic link, naming the target when it can be read. A Readlink that fails leaves the entry named, which is still enough to act on.

Exported because this sentence has two producers — the `.entire` entry scan here and the settings reader, which refuses a symlinked settings file at the read itself (see readConfined in the settings package). One function so the two cannot drift into describing the same condition differently.

func ToRelativePath

func ToRelativePath(absPath, cwd string) string

ToRelativePath converts an absolute path to relative. Returns empty string if the path is outside the working directory.

func ValidateEntireDirAt added in v0.10.4

func ValidateEntireDirAt(worktreeRoot string) error

ValidateEntireDirAt reports whether worktreeRoot's `.entire` is safe to read and write through. It is safe when the path is absent (Entire is not enabled here yet, or `enable` is about to create it), or is a real directory whose own entries are real files and directories. Anything else is a broken repo and the caller must not touch the path.

The stat is Lstat, not Stat, so a symlink is rejected even when it points at a perfectly good directory. `.entire` holds session metadata, transcripts, and the settings that decide what gets redacted before it is committed, so a path someone else controls the far end of is not a path we write through.

The same reasoning covers one level down, so the entries directly inside are checked too, and there the rule is an allowlist: Entire only ever creates regular files and directories under `.entire`, so anything else arrived some other way. A symlinked `.entire/metadata` redirects transcripts, and a symlinked `.entire/settings.local.json` redirects the file that names the command Entire executes at pre-push. See validateEntireDirEntries for why the scan stops there, and unsupportedEntryType for the one type it tolerates.

The settings package refuses a symlinked settings file at the read itself as well (readConfined). Neither check subsumes the other: this one stops a command before it does anything, and covers the subdirectories no settings read touches, while that one covers the many callers that reach settings.Load without passing through a command's pre-run.

A stat error other than "not exist" is also a failure. It is not evidence that the invariant is violated, but neither is it evidence that it holds, and the caller's next move is to write there.

func WorktreeRoot added in v0.4.8

func WorktreeRoot(ctx context.Context) (string, error)

WorktreeRoot returns the git worktree root directory — in a linked worktree that worktree's root, not the main repository's. The result is cached per working directory. Callers that need to distinguish "outside a repository" from "could not find out" match ErrNotARepository; nothing else may be read as the benign case.

Everything Entire stores is located from this path — .entire via the entiredir package, and the git common dir via gitdir — so a failure here used to be papered over by callers falling back to a relative path, which then resolved against wherever the process happened to be. From a subdirectory that silently meant a second .entire beside the agent instead of the repository's one.

The subprocess fails for more reasons than "no repository" — `git` off $PATH is the common one — so those two outcomes are separated rather than merged: only ErrNotARepository means "there is nothing here", and it is the only failure any caller is allowed to answer with a directory of its own.

Types

This section is empty.

Jump to

Keyboard shortcuts

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