diff --git a/proc.go b/proc.go new file mode 100644 index 0000000..adde78e --- /dev/null +++ b/proc.go @@ -0,0 +1,62 @@ +package main + +import ( + "os" + "path/filepath" + "strconv" + "strings" +) + +// FindClaudeProcesses scans procDir (typically "/proc") for active Claude Code processes. +// It returns a Process for each PID whose cmdline starts with "claude". +// Read errors (permission denied, vanished process) are silently skipped. +func FindClaudeProcesses(procDir string) ([]Process, error) { + entries, err := os.ReadDir(procDir) + if err != nil { + return nil, err + } + + var procs []Process + + for _, entry := range entries { + pid, err := strconv.Atoi(entry.Name()) + if err != nil { + continue // not a PID directory + } + + cmdlineData, err := os.ReadFile(filepath.Join(procDir, entry.Name(), "cmdline")) + if err != nil { + continue // permission denied or process vanished + } + + parts := strings.Split(strings.TrimRight(string(cmdlineData), "\x00"), "\x00") + if len(parts) == 0 { + continue + } + + // Match if the binary name ends with "claude" or is exactly "claude" + bin := filepath.Base(parts[0]) + if bin != "claude" { + continue + } + + cwd, err := os.Readlink(filepath.Join(procDir, entry.Name(), "cwd")) + if err != nil { + continue // cannot read cwd + } + + procs = append(procs, Process{ + PID: pid, + Cmd: parts, + Cwd: cwd, + }) + } + + return procs, nil +} + +// EncodePath converts an absolute path to the folder name format used by +// ~/.claude/projects/. Slashes and dots are replaced with dashes. +func EncodePath(path string) string { + return strings.NewReplacer("/", "-", ".", "-").Replace(path) +}