1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
package aitool
import "os/exec"
type Tool string
const (
ToolOpencode Tool = "opencode"
ToolHexAI Tool = "hexai"
ToolClaude Tool = "claude"
ToolAmp Tool = "amp"
)
type LookPathFunc func(file string) (string, error)
func Chain(preferred string) []Tool {
switch preferred {
case "", string(ToolOpencode):
return []Tool{ToolOpencode, ToolHexAI, ToolClaude, ToolAmp}
case string(ToolHexAI):
return []Tool{ToolHexAI, ToolClaude, ToolAmp}
case string(ToolClaude), "claude-code":
return []Tool{ToolClaude, ToolAmp}
case string(ToolAmp):
return []Tool{ToolAmp}
default:
return nil
}
}
func FirstAvailable(preferred string, lookPath LookPathFunc) Tool {
for _, tool := range Chain(preferred) {
if IsAvailable(tool, lookPath) {
return tool
}
}
return ""
}
func AvailableChain(preferred string, lookPath LookPathFunc) []Tool {
chain := Chain(preferred)
available := make([]Tool, 0, len(chain))
for _, tool := range chain {
if IsAvailable(tool, lookPath) {
available = append(available, tool)
}
}
return available
}
func IsAvailable(tool Tool, lookPath LookPathFunc) bool {
if lookPath == nil {
lookPath = exec.LookPath
}
binary, ok := availabilityBinary(tool)
if !ok {
return false
}
_, err := lookPath(binary)
return err == nil
}
func availabilityBinary(tool Tool) (string, bool) {
switch tool {
case ToolOpencode:
return "ollama", true
case ToolHexAI, ToolClaude, ToolAmp:
return string(tool), true
default:
return "", false
}
}
|