@@ -12,16 +12,18 @@ import (
1212 "github.com/teabranch/agentfile/pkg/fsutil"
1313 "github.com/teabranch/agentfile/pkg/github"
1414 "github.com/teabranch/agentfile/pkg/registry"
15+ "github.com/teabranch/agentfile/pkg/runtimecfg"
1516)
1617
1718func newInstallCommand () * cobra.Command {
1819 var global bool
1920 var modelOverride string
21+ var runtimeFlag string
2022
2123 cmd := & cobra.Command {
2224 Use : "install <agent-name | github.com/owner/repo[/agent][@version]>" ,
2325 Short : "Install an agent binary (local or remote)" ,
24- Long : `Installs an agent binary and updates the MCP config.
26+ Long : `Installs an agent binary and updates the MCP config for detected runtimes .
2527
2628Local install (from ./build/):
2729 agentfile install my-agent
@@ -30,26 +32,32 @@ Remote install (from GitHub Releases):
3032 agentfile install github.com/owner/repo/agent
3133 agentfile install github.com/owner/repo/agent@1.0.0
3234
33- By default, installs to .agentfile/bin/ (project-local) and updates .mcp.json .
34- With --global, installs to /usr/local/bin/ and updates ~/.claude/mcp.json .
35+ By default, installs to .agentfile/bin/ (project-local) and updates MCP config .
36+ With --global, installs to /usr/local/bin/ and updates global MCP config .
3537
3638Override settings at install time:
37- agentfile install --model gpt-5 github.com/owner/repo/agent` ,
39+ agentfile install --model gpt-5 github.com/owner/repo/agent
40+ agentfile install --runtime codex github.com/owner/repo/agent` ,
3841 Args : cobra .ExactArgs (1 ),
3942 RunE : func (cmd * cobra.Command , args []string ) error {
43+ writers , err := runtimecfg .Resolve (runtimeFlag )
44+ if err != nil {
45+ return err
46+ }
47+
4048 var agentName string
4149 if github .IsRemoteRef (args [0 ]) {
4250 parsed , err := github .ParseRef (args [0 ])
4351 if err != nil {
4452 return err
4553 }
4654 agentName = parsed .Agent
47- if err := runRemoteInstall (args [0 ], global ); err != nil {
55+ if err := runRemoteInstall (args [0 ], global , writers ); err != nil {
4856 return err
4957 }
5058 } else {
5159 agentName = args [0 ]
52- if err := runLocalInstall (args [0 ], global ); err != nil {
60+ if err := runLocalInstall (args [0 ], global , writers ); err != nil {
5361 return err
5462 }
5563 }
@@ -67,17 +75,18 @@ Override settings at install time:
6775
6876 cmd .Flags ().BoolVarP (& global , "global" , "g" , false , "Install globally to /usr/local/bin" )
6977 cmd .Flags ().StringVar (& modelOverride , "model" , "" , "Override the agent's model in ~/.agentfile/<name>/config.yaml" )
78+ cmd .Flags ().StringVar (& runtimeFlag , "runtime" , "auto" , "Target runtime: auto, all, claude-code, codex, gemini" )
7079
7180 return cmd
7281}
7382
74- func runLocalInstall (name string , global bool ) error {
83+ func runLocalInstall (name string , global bool , writers []runtimecfg. ConfigWriter ) error {
7584 src := filepath .Join ("build" , name )
7685 if _ , err := os .Stat (src ); err != nil {
7786 return fmt .Errorf ("binary not found: %s (run 'agentfile build' first)" , src )
7887 }
7988
80- binDir , mcpPath := installPaths (global )
89+ binDir := installBinDir (global )
8190
8291 if err := os .MkdirAll (binDir , 0o755 ); err != nil {
8392 return fmt .Errorf ("creating bin dir: %w" , err )
@@ -92,21 +101,20 @@ func runLocalInstall(name string, global bool) error {
92101 }
93102 fmt .Printf ("Installed %s → %s\n " , name , dst )
94103
95- // Update mcp.json .
104+ // Update MCP configs for target runtimes .
96105 absDst , err := filepath .Abs (dst )
97106 if err != nil {
98107 return fmt .Errorf ("resolving absolute path: %w" , err )
99108 }
100- entries := map [string ]MCPServerEntry {
109+ entries := map [string ]runtimecfg. ServerEntry {
101110 name : {
102111 Command : absDst ,
103112 Args : []string {"serve-mcp" },
104113 },
105114 }
106- if err := mergeMCPJSON ( mcpPath , entries ); err != nil {
107- return fmt . Errorf ( "updating %s: %w" , mcpPath , err )
115+ if err := mergeRuntimeConfigs ( writers , global , entries ); err != nil {
116+ return err
108117 }
109- fmt .Printf ("Updated %s\n " , mcpPath )
110118
111119 // Track in registry.
112120 version := ""
@@ -120,7 +128,7 @@ func runLocalInstall(name string, global bool) error {
120128 return trackInstall (name , "local" , version , absDst , scope )
121129}
122130
123- func runRemoteInstall (ref string , global bool ) error {
131+ func runRemoteInstall (ref string , global bool , writers []runtimecfg. ConfigWriter ) error {
124132 parsed , err := github .ParseRef (ref )
125133 if err != nil {
126134 return err
@@ -196,7 +204,7 @@ func runRemoteInstall(ref string, global bool) error {
196204 }
197205
198206 // Move to install location.
199- binDir , mcpPath := installPaths (global )
207+ binDir := installBinDir (global )
200208 if err := os .MkdirAll (binDir , 0o755 ); err != nil {
201209 return fmt .Errorf ("creating bin dir: %w" , err )
202210 }
@@ -210,21 +218,20 @@ func runRemoteInstall(ref string, global bool) error {
210218 }
211219 fmt .Printf ("Installed %s → %s\n " , parsed .Agent , dst )
212220
213- // Wire MCP.
221+ // Wire MCP for target runtimes .
214222 absDst , err := filepath .Abs (dst )
215223 if err != nil {
216224 return fmt .Errorf ("resolving absolute path: %w" , err )
217225 }
218- entries := map [string ]MCPServerEntry {
226+ entries := map [string ]runtimecfg. ServerEntry {
219227 parsed .Agent : {
220228 Command : absDst ,
221229 Args : []string {"serve-mcp" },
222230 },
223231 }
224- if err := mergeMCPJSON ( mcpPath , entries ); err != nil {
225- return fmt . Errorf ( "updating %s: %w" , mcpPath , err )
232+ if err := mergeRuntimeConfigs ( writers , global , entries ); err != nil {
233+ return err
226234 }
227- fmt .Printf ("Updated %s\n " , mcpPath )
228235
229236 // Track in registry.
230237 source := fmt .Sprintf ("github.com/%s/%s/%s" , parsed .Owner , parsed .Repo , parsed .Agent )
@@ -235,16 +242,34 @@ func runRemoteInstall(ref string, global bool) error {
235242 return trackInstall (parsed .Agent , source , manifest .Version , absDst , scope )
236243}
237244
238- func installPaths (global bool ) (binDir , mcpPath string ) {
245+ // installBinDir returns the binary install directory.
246+ // Binary location is agentfile-internal, independent of runtime.
247+ func installBinDir (global bool ) string {
239248 if global {
240- binDir = "/usr/local/bin"
241- home , _ := os .UserHomeDir ()
242- mcpPath = filepath .Join (home , ".claude" , "mcp.json" )
243- } else {
244- binDir = filepath .Join (".agentfile" , "bin" )
245- mcpPath = ".mcp.json"
249+ return "/usr/local/bin"
250+ }
251+ return filepath .Join (".agentfile" , "bin" )
252+ }
253+
254+ // mergeRuntimeConfigs writes MCP server entries to all target runtime configs.
255+ func mergeRuntimeConfigs (writers []runtimecfg.ConfigWriter , global bool , entries map [string ]runtimecfg.ServerEntry ) error {
256+ for _ , w := range writers {
257+ var cfgPath string
258+ if global {
259+ var err error
260+ cfgPath , err = w .GlobalPath ()
261+ if err != nil {
262+ return fmt .Errorf ("resolving global path for %s: %w" , w .Runtime (), err )
263+ }
264+ } else {
265+ cfgPath = w .LocalPath ()
266+ }
267+ if err := w .Merge (cfgPath , entries ); err != nil {
268+ return fmt .Errorf ("updating %s for %s: %w" , cfgPath , w .Runtime (), err )
269+ }
270+ fmt .Printf ("Updated %s (%s)\n " , cfgPath , w .Runtime ())
246271 }
247- return
272+ return nil
248273}
249274
250275func trackInstall (name , source , version , path , scope string ) error {
0 commit comments