Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions cmd/agentsview/parse_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func parseDiffAgentTypes(names []string) ([]parser.AgentType, error) {
strings.Join(parseDiffSupportedAgents(), ", "),
)
}
if !def.FileBased || def.DiscoverFunc == nil {
if !parseDiffAgentSupported(def) {
return nil, fmt.Errorf(
"agent %q is not supported by parse-diff "+
"(no on-disk source to re-parse)",
Expand All @@ -253,18 +253,33 @@ func parseDiffAgentTypes(names []string) ([]parser.AgentType, error) {
return out, nil
}

// parseDiffSupportedAgents lists the agent types parse-diff can
// re-parse: file-based agents with a discovery function.
// parseDiffSupportedAgents lists the agent types parse-diff can re-parse.
func parseDiffSupportedAgents() []string {
var names []string
for _, def := range parser.Registry {
if def.FileBased && def.DiscoverFunc != nil {
if parseDiffAgentSupported(def) {
names = append(names, string(def.Type))
}
}
return names
}

func parseDiffAgentSupported(def parser.AgentDef) bool {
if !def.FileBased {
return false
}
if def.DiscoverFunc != nil {
return true
}
switch parser.ProviderMigrationModes()[def.Type] {
case parser.ProviderMigrationProviderAuthoritative:
_, ok := parser.ProviderFactoryByType(def.Type)
return ok
default:
return false
}
}

// renderParseDiffReport writes the human-readable report. An empty
// archive renders a zero-count summary with no tables. Every value
// that originates in session files or archive rows (IDs, paths,
Expand Down
10 changes: 10 additions & 0 deletions cmd/agentsview/parse_diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ func TestParseDiffAgentTypes(t *testing.T) {
in: []string{"claude"},
want: []string{"claude"},
},
{
name: "provider authoritative agent",
in: []string{"pi"},
want: []string{"pi"},
},
{
name: "provider authoritative shared provider family agent",
in: []string{"omp"},
want: []string{"omp"},
},
{
name: "trims and lowercases",
in: []string{" Claude "},
Expand Down
94 changes: 0 additions & 94 deletions internal/parser/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -1426,100 +1426,6 @@ func IsPiSessionFile(path string) bool {
return false
}

// DiscoverPiSessions finds JSONL files under piDir that are
// valid pi sessions. Pi sessions live in
// <piDir>/<encoded-cwd>/<session-id>.jsonl; the encoded-cwd
// format is ambiguous between pi versions, so discovery
// validates by reading the session header rather than parsing
// the directory name. Project is left empty so ParsePiSession
// can derive it from the header cwd field.
func DiscoverPiSessions(piDir string) []DiscoveredFile {
return discoverPiLikeSessions(piDir, AgentPi)
}

// DiscoverOMPSessions finds JSONL files under an OhMyPi session root.
// OMP uses the same layout and file format as Pi, rooted by default at
// ~/.omp/agent/sessions.
func DiscoverOMPSessions(ompDir string) []DiscoveredFile {
return discoverPiLikeSessions(ompDir, AgentOMP)
}

func discoverPiLikeSessions(piDir string, agent AgentType) []DiscoveredFile {
if piDir == "" {
return nil
}
entries, err := os.ReadDir(piDir)
if err != nil {
return nil
}
var files []DiscoveredFile
for _, entry := range entries {
if !isDirOrSymlink(entry, piDir) {
continue
}
cwdDir := filepath.Join(piDir, entry.Name())
sessionFiles, err := os.ReadDir(cwdDir)
if err != nil {
continue
}
for _, sf := range sessionFiles {
if sf.IsDir() {
continue
}
if !strings.HasSuffix(sf.Name(), ".jsonl") {
continue
}
path := filepath.Join(cwdDir, sf.Name())
if !IsPiSessionFile(path) {
continue
}
files = append(files, DiscoveredFile{
Path: path,
Agent: agent,
// Project intentionally empty; ParsePiSession
// derives project from the header cwd field.
})
}
}
sort.Slice(files, func(i, j int) bool {
return files[i].Path < files[j].Path
})
return files
}

// FindPiSourceFile finds the original JSONL file for a pi
// session ID by searching all encoded-cwd subdirectories
// under piDir for a file named <sessionID>.jsonl.
func FindPiSourceFile(piDir, sessionID string) string {
return findPiLikeSourceFile(piDir, sessionID)
}

// FindOMPSourceFile finds the original JSONL file for an OMP session ID.
func FindOMPSourceFile(ompDir, sessionID string) string {
return findPiLikeSourceFile(ompDir, sessionID)
}

func findPiLikeSourceFile(piDir, sessionID string) string {
if piDir == "" || !IsValidSessionID(sessionID) {
return ""
}
entries, err := os.ReadDir(piDir)
if err != nil {
return ""
}
target := sessionID + ".jsonl"
for _, entry := range entries {
if !isDirOrSymlink(entry, piDir) {
continue
}
candidate := filepath.Join(piDir, entry.Name(), target)
if _, err := os.Stat(candidate); err == nil {
return candidate
}
}
return ""
}

// isRegularFile returns true if path exists and is a regular
// file (not a symlink, directory, or other special file).
// IsRegularFile reports whether path is a regular file (not
Expand Down
18 changes: 4 additions & 14 deletions internal/parser/pi.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,12 @@ import (
"github.com/tidwall/gjson"
)

// ParsePiSession parses a pi-agent JSONL session file.
// The file format uses a leading session-header entry followed by
// message, model_change, and compaction entries.
func ParsePiSession(
func (p *piProvider) parseSession(
path, project, machine string,
) (*ParsedSession, []ParsedMessage, error) {
return parsePiLikeSession(path, project, machine, AgentPi, "pi:")
}

// ParseOMPSession parses an OhMyPi JSONL session file. OMP uses the
// same on-disk session format as Pi, but sessions are identified with
// the omp agent type and omp: session ID prefix.
func ParseOMPSession(
path, project, machine string,
) (*ParsedSession, []ParsedMessage, error) {
return parsePiLikeSession(path, project, machine, AgentOMP, "omp:")
return parsePiLikeSession(
path, project, machine, p.Def.Type, p.Def.IDPrefix,
)
}

func parsePiLikeSession(
Expand Down
Loading