package session import ( "os" "path/filepath" "sort" "strings" ) // Info describes one session file in a directory. type Info struct { ID string Path string Name string CWD string Leaf string Updated string } // List returns session files in dir, newest first. func List(dir string) ([]Info, error) { ents, err := os.ReadDir(dir) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, err } var out []Info for _, ent := range ents { name := ent.Name() if ent.IsDir() || !strings.HasSuffix(name, ".jsonl") { continue } path := filepath.Join(dir, name) st, err := Open(path) if err != nil { continue } info, err := st.Info() if err != nil { continue } out = append(out, info) } sort.Slice(out, func(i, j int) bool { return out[i].Updated > out[j].Updated }) return out, nil } // Continue opens the newest session whose cwd matches. A second return // value of false means there is nothing to continue. func Continue(dir, cwd string) (*Store, bool, error) { list, err := List(dir) if err != nil { return nil, false, err } for _, info := range list { if cwd == "" || info.CWD == cwd { st, err := Open(info.Path) return st, err == nil, err } } return nil, false, nil } // Resume opens a session by id (filename stem) or by name. func Resume(dir, idOrName string) (*Store, error) { list, err := List(dir) if err != nil { return nil, err } for _, info := range list { if info.ID == idOrName || info.Name == idOrName { return Open(info.Path) } } return nil, os.ErrNotExist } // SetCWD records the working directory used by Continue. func (s *Store) SetCWD(cwd string) error { s.mu.Lock() defer s.mu.Unlock() return s.writeMetaKeyLocked("cwd", cwd) } // Info reads the catalog fields for this file. func (s *Store) Info() (Info, error) { s.mu.Lock() defer s.mu.Unlock() entries, err := s.readEntriesLocked() if err != nil { return Info{}, err } meta := s.readMetaLocked() updated := "" if len(entries) > 0 { updated = entries[len(entries)-1].Timestamp } id := strings.TrimSuffix(filepath.Base(s.path), ".jsonl") return Info{ ID: id, Path: s.path, Name: meta["name"], CWD: meta["cwd"], Leaf: s.leafLocked(entries), Updated: updated, }, nil }