discovery

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Mar 23, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

gitinfo.go provides lightweight git repository metadata for a working directory. It shells out to git with strict timeouts so a slow or missing repo never blocks session discovery.

Package discovery scans for active Claude Code sessions on the local machine. It reads session files, checks process liveness, parses transcripts, and determines each session's current state.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckSession

func CheckSession(pid int, startedAt time.Time) bool

CheckSession checks whether the process for a session is still the same process that started the session. It verifies both that the PID is alive and that the process start time is close to the session's startedAt.

This detects PID reuse: if a process dies and a new process gets the same PID, the start times won't match.

func EncodeCWD

func EncodeCWD(cwd string) string

EncodeCWD converts a filesystem path to the encoded directory name used by Claude Code under ~/.claude/projects/. Forward slashes are replaced with hyphens, and non-ASCII characters are also replaced with hyphens to match Claude Code's actual encoding behavior.

Example: "/Users/jason/project" -> "-Users-jason-project" Example: "/Users/jason/Documents/实习/理想实习/FusionSQL" -> "-Users-jason-Documents---------FusionSQL"

func FindSessionIndex

func FindSessionIndex(projectsDir, cwd string) string

FindSessionIndex locates the sessions-index.json file for a given project CWD under the projects directory.

Returns an empty string if the file does not exist.

func FindTranscriptPath

func FindTranscriptPath(projectsDir, cwd, sessionID string) string

FindTranscriptPath locates the transcript JSONL file for a given session. It tries two strategies:

  1. Direct path: projectsDir/<encodedCWD>/<sessionID>.jsonl
  2. Fallback scan: search all project dirs for the sessionID.jsonl file

Returns an empty string if the transcript file does not exist.

func IsProcessAlive

func IsProcessAlive(pid int) bool

IsProcessAlive checks whether a process with the given PID exists. On Linux, this uses kill(pid, 0) which returns an error if the process does not exist. EPERM (permission denied) still means the process exists.

func ParseSessionIndex

func ParseSessionIndex(path string) (map[string]IndexEntry, error)

ParseSessionIndex reads a sessions-index.json file and returns a map of session ID to IndexEntry. This is hints-only data used to enrich sessions with summaries and message counts.

Defensive behavior:

  • Returns empty map and no error if the file does not exist.
  • Returns empty map and no error if the file is malformed JSON.
  • Silently ignores unknown fields in the JSON.
  • Never fails. If anything goes wrong, you just get less data.

func ReadLastActivity

func ReadLastActivity(path string) (time.Time, error)

ReadLastActivity reads the tail of a JSONL transcript file and returns the timestamp of the most recent entry that has one.

If the file does not exist or is empty, it returns the zero time and no error. Malformed lines are silently skipped.

Types

type AttentionReason

type AttentionReason string

AttentionReason flags diagnostic issues that are separate from lifecycle state. Per Codex review: don't mix lifecycle states with data-integrity warnings.

const (
	// AttentionOrphaned means a transcript exists but no session file was found.
	AttentionOrphaned AttentionReason = "orphaned"

	// AttentionStale means the PID was reused by a different process.
	AttentionStale AttentionReason = "stale"

	// AttentionNoTranscript means a session file exists but no transcript was found.
	AttentionNoTranscript AttentionReason = "no-transcript"
)

type GitInfo added in v0.6.0

type GitInfo struct {
	// Branch is the current branch name (e.g. "main", "feat/foo").
	// Empty if not a git repo or on a detached HEAD.
	Branch string

	// IsDirty is true when the working tree has uncommitted changes
	// (staged, unstaged, or untracked files).
	IsDirty bool

	// Ahead is the number of commits ahead of the upstream tracking branch.
	// Zero if no upstream is configured or git fails.
	Ahead int

	// Behind is the number of commits behind the upstream tracking branch.
	// Zero if no upstream is configured or git fails.
	Behind int

	// LastCommit is the short hash and subject of the most recent commit
	// (e.g. "abc1234 Fix the thing"). Empty if there are no commits.
	LastCommit string

	// LatestTag is the most recent git tag (e.g. "v1.0.1").
	// Empty if no tags exist.
	LatestTag string
}

GitInfo holds git repository metadata for a session's working directory.

func GetGitInfo added in v0.6.0

func GetGitInfo(cwd string) GitInfo

GetGitInfo returns git metadata for the repository at cwd. It returns a zero GitInfo if cwd is empty, does not exist, or is not inside a git repository. All errors are swallowed -- this function is designed to be purely defensive like the rest of the discovery package.

type IndexEntry

type IndexEntry struct {
	SessionID    string `json:"sessionId"`
	Summary      string `json:"summary"`
	MessageCount int    `json:"messageCount"`
}

IndexEntry holds the subset of fields we extract from a sessions-index.json entry. Unknown fields are silently ignored.

type Project

type Project struct {
	// Name is the project identifier. Normally the basename of the shared CWD
	// (e.g. "QuantMind"). When multiple projects share the same basename,
	// GroupByProject expands it to "parent/basename" (e.g. "work/api") to
	// prevent identity collisions.
	Name string

	// Path is the full filesystem path to the project directory.
	Path string

	// Sessions contains all discovered sessions for this project.
	Sessions []Session
}

Project groups sessions that share the same working directory.

func GroupByProject

func GroupByProject(sessions []Session) []Project

GroupByProject groups a flat list of sessions into Project values, keyed by CWD. When multiple distinct CWDs share the same basename, their display names are expanded to include up to 3 path components (e.g. "work/api" instead of "api") until all names are unique.

The returned slice is sorted alphabetically by project name.

type Session

type Session struct {
	// ID is the session UUID from the session file (sessionId field).
	ID string

	// PID is the process ID of the Claude Code process.
	PID int

	// CWD is the working directory the session was started in.
	CWD string

	// ProjectName is the basename of CWD (or the git root if available).
	ProjectName string

	// StartedAt is when the session was created (from the session file).
	StartedAt time.Time

	// State is the classified lifecycle state.
	State SessionState

	// LastActivity is the timestamp of the most recent transcript entry.
	// Zero value means no activity information is available.
	LastActivity time.Time

	// Summary is the human-readable summary from sessions-index.json, if
	// available. Empty string if not found.
	Summary string

	// MessageCount is the number of messages from sessions-index.json.
	// Zero if not available.
	MessageCount int

	// Attention holds diagnostic flags (orphaned, stale, no-transcript).
	// Empty slice means no issues.
	Attention []AttentionReason
}

Session represents a single Claude Code session discovered on disk.

func ScanSessions

func ScanSessions(sessionsDir string) ([]Session, error)

ScanSessions reads all session files from the given directory and returns parsed Session values. It silently skips files that are not valid JSON, are not .json files, or are missing required fields.

If the directory does not exist, ScanSessions returns an empty slice and no error.

type SessionState

type SessionState string

SessionState describes the lifecycle state of a Claude Code session.

const (
	// StateBusy means the session's process is alive and had activity within
	// the last 30 seconds.
	StateBusy SessionState = "busy"

	// StateWaiting means the process is alive but activity was between 30
	// seconds and 5 minutes ago (likely waiting for user input).
	StateWaiting SessionState = "waiting"

	// StateIdle means the process is alive but no activity for over 5
	// minutes.
	StateIdle SessionState = "idle"

	// StateDead means the session's process is no longer running.
	StateDead SessionState = "dead"
)

func ClassifyState

func ClassifyState(processAlive bool, lastActivity time.Time) SessionState

ClassifyState determines the SessionState based on process liveness and last activity timestamp.

Decision logic:

  • Process not alive -> StateDead
  • Process alive, activity < 30s ago -> StateBusy
  • Process alive, activity 30s-5min ago -> StateWaiting
  • Process alive, activity > 5min ago -> StateIdle
  • Process alive, no activity info -> StateIdle

StateOrphaned and StateStale are not assigned by this function; they require additional context from the scanner.

func (SessionState) IsAlive

func (s SessionState) IsAlive() bool

IsAlive reports whether the state indicates an active, running session.

Jump to

Keyboard shortcuts

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