package main import ( "encoding/json" "fmt" "os" "os/exec" "strings" ) const ( needRequired = "required" needAgent = "agent" needOptional = "optional" ) type check struct { Name string `json:"name"` Need string `json:"need"` For string `json:"for"` OK bool `json:"ok"` Command string `json:"command"` Fix string `json:"fix"` Blocking bool `json:"blocking"` } func checks() []check { return []check{ gitCheck(), command("claude", needAgent, "Claude models", "curl -fsSL https://claude.ai/install.sh | bash", "Then run claude once to sign in."), command("codex", needAgent, "GPT models", "brew install --cask codex", "Then run codex once to sign in."), command("agy", needAgent, "Gemini models", "brew install --cask antigravity-cli", "Then run agy once to sign in."), command("npx", needOptional, "reviewers' Playwright browser tools", "brew install node", ""), appCheck("Google Chrome", needOptional, "browser tools for agents and reviewers", "brew install --cask google-chrome"), } } func gitCheck() check { c := check{Name: "git", Need: needRequired, For: "worktrees, snapshots and merges", Command: "xcode-select --install"} path, err := exec.LookPath("git") // /usr/bin/git is a stub that offers to install the Command Line Tools when they're missing. c.OK = err == nil && (path != "/usr/bin/git" || exec.Command("xcode-select", "-p").Run() == nil) return c } func command(name, need, purpose, install, fix string) check { _, err := exec.LookPath(name) return check{Name: name, Need: need, For: purpose, OK: err == nil, Command: install, Fix: fix} } func appCheck(name, need, purpose, install string) check { _, err := os.Stat("/Applications/" + name + ".app") return check{Name: name, Need: need, For: purpose, OK: err == nil, Command: install} } func markBlocking(cs []check) bool { agentOK := false for _, c := range cs { agentOK = agentOK || (c.Need == needAgent && c.OK) } ready := true for i, c := range cs { cs[i].Blocking = !c.OK && (c.Need == needRequired || (c.Need == needAgent && !agentOK)) ready = ready && !cs[i].Blocking } return ready } func checkCmd() { cs := checks() ready := markBlocking(cs) out, err := json.Marshal(struct { Ready bool `json:"ready"` Loopback string `json:"loopback"` Checks []check `json:"checks"` }{ready, loopbackURL, cs}) if err != nil { fatal("encode checks", "err", err) } fmt.Println(string(out)) } func requireChecks() { cs := checks() if markBlocking(cs) { return } fmt.Println("byre can't run on this Mac yet. It needs git and at least one of claude, codex and agy:") for _, c := range cs { if c.Blocking { fmt.Printf(" %s: %s\n", c.Name, strings.TrimSpace(c.Command+" "+c.Fix)) } } os.Exit(1) }