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) }