96 lines
2.7 KiB
Go
96 lines
2.7 KiB
Go
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)
|
|
}
|