// Package store persists workspace-level PR review confirmation state. // // Confirmations live in /.spx/pr-review-confirmed.json as a // map[string][]string. File-level confirmations use key ":" // and hold the confirmed file paths of that commit; commit-level confirmations // use key ":commits" and hold the confirmed commit SHAs. Directories // are not tracked separately. A missing or malformed file reads as empty. package store import ( "encoding/json" "fmt" "os" "path/filepath" "strconv" "superwork-tui/internal/config" ) type confirmedMap = map[string][]string func confirmedFile(workspaceRoot string) string { return filepath.Join(config.SpxDir(workspaceRoot), "pr-review-confirmed.json") } func fileKey(issueNumber int, sha string) string { return strconv.Itoa(issueNumber) + ":" + sha } func commitsKey(issueNumber int) string { return strconv.Itoa(issueNumber) + ":commits" } // readMap returns the whole confirmation map. A missing or unparseable file // reads as an empty map rather than an error. func readMap(workspaceRoot string) confirmedMap { raw, err := os.ReadFile(confirmedFile(workspaceRoot)) if err != nil { return confirmedMap{} } var parsed confirmedMap if err := json.Unmarshal(raw, &parsed); err != nil { return confirmedMap{} } return parsed } // writeMap persists the confirmation map, creating .spx if needed. func writeMap(workspaceRoot string, m confirmedMap) error { if err := os.MkdirAll(config.SpxDir(workspaceRoot), 0o755); err != nil { return fmt.Errorf("create .spx dir: %w", err) } data, err := json.MarshalIndent(m, "", " ") if err != nil { return fmt.Errorf("marshal confirmations: %w", err) } if err := os.WriteFile(confirmedFile(workspaceRoot), append(data, '\n'), 0o644); err != nil { return fmt.Errorf("write confirmations: %w", err) } return nil } // ReadConfirmed returns the confirmed file paths for a (issueNumber, sha) commit. func ReadConfirmed(workspaceRoot string, issueNumber int, sha string) ([]string, error) { return readMap(workspaceRoot)[fileKey(issueNumber, sha)], nil } // WriteConfirmed sets the confirmed file paths for a (issueNumber, sha) commit. // An empty slice deletes the key. func WriteConfirmed(workspaceRoot string, issueNumber int, sha string, paths []string) error { m := readMap(workspaceRoot) key := fileKey(issueNumber, sha) if len(paths) > 0 { m[key] = paths } else { delete(m, key) } return writeMap(workspaceRoot, m) } // ReadConfirmedCommits returns the confirmed commit SHAs for an issue. func ReadConfirmedCommits(workspaceRoot string, issueNumber int) ([]string, error) { return readMap(workspaceRoot)[commitsKey(issueNumber)], nil } // WriteConfirmedCommits sets the confirmed commit SHAs for an issue. // An empty slice deletes the key. func WriteConfirmedCommits(workspaceRoot string, issueNumber int, shas []string) error { m := readMap(workspaceRoot) key := commitsKey(issueNumber) if len(shas) > 0 { m[key] = shas } else { delete(m, key) } return writeMap(workspaceRoot, m) }