@@ -8,9 +8,11 @@ import (
88 "strings"
99
1010 tea "github.com/charmbracelet/bubbletea"
11+ "github.com/minicodemonkey/chief/internal/agent"
1112 "github.com/minicodemonkey/chief/internal/cmd"
1213 "github.com/minicodemonkey/chief/internal/config"
1314 "github.com/minicodemonkey/chief/internal/git"
15+ "github.com/minicodemonkey/chief/internal/loop"
1416 "github.com/minicodemonkey/chief/internal/prd"
1517 "github.com/minicodemonkey/chief/internal/tui"
1618)
@@ -26,6 +28,8 @@ type TUIOptions struct {
2628 Merge bool
2729 Force bool
2830 NoRetry bool
31+ Agent string // --agent claude|codex
32+ AgentPath string // --agent-path
2933}
3034
3135func main () {
@@ -118,6 +122,40 @@ func listAvailablePRDs() []string {
118122 return names
119123}
120124
125+ // parseAgentFlags extracts --agent and --agent-path from args[startIdx:],
126+ // returning the agent name, agent path, remaining args (with agent flags removed),
127+ // and the updated index offsets. It exits on missing values.
128+ func parseAgentFlags (args []string , startIdx int ) (agentName , agentPath string , remaining []string ) {
129+ for i := startIdx ; i < len (args ); i ++ {
130+ arg := args [i ]
131+ switch {
132+ case arg == "--agent" :
133+ if i + 1 < len (args ) {
134+ i ++
135+ agentName = args [i ]
136+ } else {
137+ fmt .Fprintf (os .Stderr , "Error: --agent requires a value (claude, codex, or opencode)\n " )
138+ os .Exit (1 )
139+ }
140+ case strings .HasPrefix (arg , "--agent=" ):
141+ agentName = strings .TrimPrefix (arg , "--agent=" )
142+ case arg == "--agent-path" :
143+ if i + 1 < len (args ) {
144+ i ++
145+ agentPath = args [i ]
146+ } else {
147+ fmt .Fprintf (os .Stderr , "Error: --agent-path requires a value\n " )
148+ os .Exit (1 )
149+ }
150+ case strings .HasPrefix (arg , "--agent-path=" ):
151+ agentPath = strings .TrimPrefix (arg , "--agent-path=" )
152+ default :
153+ remaining = append (remaining , arg )
154+ }
155+ }
156+ return
157+ }
158+
121159// parseTUIFlags parses command-line flags for TUI mode
122160func parseTUIFlags () * TUIOptions {
123161 opts := & TUIOptions {
@@ -129,6 +167,9 @@ func parseTUIFlags() *TUIOptions {
129167 NoRetry : false ,
130168 }
131169
170+ // Pre-extract agent flags so they don't interfere with positional arg parsing
171+ opts .Agent , opts .AgentPath , _ = parseAgentFlags (os .Args , 1 )
172+
132173 for i := 1 ; i < len (os .Args ); i ++ {
133174 arg := os .Args [i ]
134175
@@ -147,6 +188,10 @@ func parseTUIFlags() *TUIOptions {
147188 opts .Force = true
148189 case arg == "--no-retry" :
149190 opts .NoRetry = true
191+ case arg == "--agent" || arg == "--agent-path" :
192+ i ++ // skip value (already parsed by parseAgentFlags)
193+ case strings .HasPrefix (arg , "--agent=" ) || strings .HasPrefix (arg , "--agent-path=" ):
194+ // already parsed by parseAgentFlags
150195 case arg == "--max-iterations" || arg == "-n" :
151196 // Next argument should be the number
152197 if i + 1 < len (os .Args ) {
@@ -211,14 +256,23 @@ func parseTUIFlags() *TUIOptions {
211256func runNew () {
212257 opts := cmd.NewOptions {}
213258
214- // Parse arguments: chief new [name] [context...]
215- if len (os .Args ) > 2 {
216- opts .Name = os .Args [2 ]
259+ // Parse arguments: chief new [name] [context...] [--agent X] [--agent-path X]
260+ flagAgent , flagPath , positional := parseAgentFlags (os .Args , 2 )
261+ // Filter out remaining flags, keep only positional args
262+ var args []string
263+ for _ , a := range positional {
264+ if ! strings .HasPrefix (a , "-" ) {
265+ args = append (args , a )
266+ }
267+ }
268+ if len (args ) > 0 {
269+ opts .Name = args [0 ]
217270 }
218- if len (os . Args ) > 3 {
219- opts .Context = strings .Join (os . Args [ 3 :], " " )
271+ if len (args ) > 1 {
272+ opts .Context = strings .Join (args [ 1 :], " " )
220273 }
221274
275+ opts .Provider = resolveProvider (flagAgent , flagPath )
222276 if err := cmd .RunNew (opts ); err != nil {
223277 fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
224278 os .Exit (1 )
@@ -228,22 +282,22 @@ func runNew() {
228282func runEdit () {
229283 opts := cmd.EditOptions {}
230284
231- // Parse arguments: chief edit [name] [--merge] [--force]
232- for i := 2 ; i < len (os .Args ); i ++ {
233- arg := os . Args [ i ]
234- switch arg {
235- case "--merge" :
285+ // Parse arguments: chief edit [name] [--merge] [--force] [--agent X] [--agent-path X]
286+ flagAgent , flagPath , remaining := parseAgentFlags (os .Args , 2 )
287+ for _ , arg := range remaining {
288+ switch {
289+ case arg == "--merge" :
236290 opts .Merge = true
237- case "--force" :
291+ case arg == "--force" :
238292 opts .Force = true
239293 default :
240- // If not a flag, treat as PRD name (first non-flag arg)
241294 if opts .Name == "" && ! strings .HasPrefix (arg , "-" ) {
242295 opts .Name = arg
243296 }
244297 }
245298 }
246299
300+ opts .Provider = resolveProvider (flagAgent , flagPath )
247301 if err := cmd .RunEdit (opts ); err != nil {
248302 fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
249303 os .Exit (1 )
@@ -282,7 +336,33 @@ func runList() {
282336 }
283337}
284338
339+ // resolveProvider loads config and resolves the agent provider, exiting on error.
340+ func resolveProvider (flagAgent , flagPath string ) loop.Provider {
341+ cwd , err := os .Getwd ()
342+ if err != nil {
343+ fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
344+ os .Exit (1 )
345+ }
346+ cfg , err := config .Load (cwd )
347+ if err != nil {
348+ fmt .Fprintf (os .Stderr , "Error: failed to load .chief/config.yaml: %v\n " , err )
349+ os .Exit (1 )
350+ }
351+ provider , err := agent .Resolve (flagAgent , flagPath , cfg )
352+ if err != nil {
353+ fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
354+ os .Exit (1 )
355+ }
356+ if err := agent .CheckInstalled (provider ); err != nil {
357+ fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
358+ os .Exit (1 )
359+ }
360+ return provider
361+ }
362+
285363func runTUIWithOptions (opts * TUIOptions ) {
364+ provider := resolveProvider (opts .Agent , opts .AgentPath )
365+
286366 prdPath := opts .PRDPath
287367
288368 // If no PRD specified, try to find one
@@ -322,7 +402,8 @@ func runTUIWithOptions(opts *TUIOptions) {
322402
323403 // Create the PRD
324404 newOpts := cmd.NewOptions {
325- Name : result .PRDName ,
405+ Name : result .PRDName ,
406+ Provider : provider ,
326407 }
327408 if err := cmd .RunNew (newOpts ); err != nil {
328409 fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
@@ -344,19 +425,19 @@ func runTUIWithOptions(opts *TUIOptions) {
344425 fmt .Printf ("Warning: failed to check conversion status: %v\n " , err )
345426 } else if needsConvert {
346427 fmt .Println ("prd.md is newer than prd.json, running conversion..." )
347- convertOpts := prd .ConvertOptions {
348- PRDDir : prdDir ,
349- Merge : opts .Merge ,
350- Force : opts .Force ,
351- }
352- if err := prd . Convert ( convertOpts ); err != nil {
428+ if err := cmd . RunConvertWithOptions (cmd .ConvertOptions {
429+ PRDDir : prdDir ,
430+ Merge : opts .Merge ,
431+ Force : opts .Force ,
432+ Provider : provider ,
433+ } ); err != nil {
353434 fmt .Printf ("Error converting PRD: %v\n " , err )
354435 os .Exit (1 )
355436 }
356437 fmt .Println ("Conversion complete." )
357438 }
358439
359- app , err := tui .NewAppWithOptions (prdPath , opts .MaxIterations )
440+ app , err := tui .NewAppWithOptions (prdPath , opts .MaxIterations , provider )
360441 if err != nil {
361442 // Check if this is a missing PRD file error
362443 if os .IsNotExist (err ) || strings .Contains (err .Error (), "no such file" ) {
@@ -403,7 +484,8 @@ func runTUIWithOptions(opts *TUIOptions) {
403484 case tui .PostExitInit :
404485 // Run new command then restart TUI
405486 newOpts := cmd.NewOptions {
406- Name : finalApp .PostExitPRD ,
487+ Name : finalApp .PostExitPRD ,
488+ Provider : provider ,
407489 }
408490 if err := cmd .RunNew (newOpts ); err != nil {
409491 fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
@@ -416,9 +498,10 @@ func runTUIWithOptions(opts *TUIOptions) {
416498 case tui .PostExitEdit :
417499 // Run edit command then restart TUI
418500 editOpts := cmd.EditOptions {
419- Name : finalApp .PostExitPRD ,
420- Merge : opts .Merge ,
421- Force : opts .Force ,
501+ Name : finalApp .PostExitPRD ,
502+ Merge : opts .Merge ,
503+ Force : opts .Force ,
504+ Provider : provider ,
422505 }
423506 if err := cmd .RunEdit (editOpts ); err != nil {
424507 fmt .Fprintf (os .Stderr , "Error: %v\n " , err )
@@ -447,9 +530,11 @@ Commands:
447530 help Show this help message
448531
449532Global Options:
533+ --agent <provider> Agent CLI to use: claude (default), codex, or opencode
534+ --agent-path <path> Custom path to agent CLI binary
450535 --max-iterations N, -n N Set maximum iterations (default: dynamic)
451- --no-retry Disable auto-retry on Claude crashes
452- --verbose Show raw Claude output in log
536+ --no-retry Disable auto-retry on agent crashes
537+ --verbose Show raw agent output in log
453538 --merge Auto-merge progress on conversion conflicts
454539 --force Auto-overwrite on conversion conflicts
455540 --help, -h Show this help message
@@ -470,7 +555,8 @@ Examples:
470555 chief -n 20 Launch with 20 max iterations
471556 chief --max-iterations=5 auth
472557 Launch auth PRD with 5 max iterations
473- chief --verbose Launch with raw Claude output visible
558+ chief --verbose Launch with raw agent output visible
559+ chief --agent codex Use Codex CLI instead of Claude
474560 chief new Create PRD in .chief/prds/main/
475561 chief new auth Create PRD in .chief/prds/auth/
476562 chief new auth "JWT authentication for REST API"
0 commit comments