Merge pull request #1682 from entireio/soph/enable-preselect-detected-agents · Entire
Merge pull request #1682 from entireio/soph/enable-preselect-detected-agents
772dae3→main·
peyton-alt·1w ago·2 files·+111 added/-41 removed
enable: pre-select detected agents instead of skipping the picker
Changes
2
cmd/entire/cli
Msetup.go+55/-41
Msetup_test.go+56
1457 unmodified lines
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1465
1466
1467
1491
1492
1493
1494
1495
1496
1470
1497
1498
1499
1500
1473
1474
1501
1502
1503
1504
1505
1506
6 unmodified lines
1513
1514
1515
1487
1488
1489
1490
1491
1492
1493
1516
1517
1518
1519
1520
1521
1522
1523
1524
59 unmodified lines
1584
1585
1586
1559
1560
1561
1562
1563
1564
1587
1588
1589
1590
1591
1592
1593
1594
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1457 unmodified lines
return count, nil
}
// promptAgentSelection shows the interactive multi-select agent picker and
// returns the chosen agent names. It is a package-level var so tests can
// substitute it — no real TTY/form is available under `go test`.
var promptAgentSelection = func(options []huh.Option[string]) ([]string, error) {
var selected []string
form := NewAccessibleForm(
huh.NewGroup(
huh.NewMultiSelect[string]().
Title("Select the agents you want to use").
Description("Use space to select, enter to confirm.").
Options(options...).
Validate(func(sel []string) error {
if len(sel) == 0 {
return errors.New("please select at least one agent")
}
return nil
}).
Value(&selected),
),
)
if err := form.Run(); err != nil {
return nil, fmt.Errorf("agent selection cancelled: %w", err)
}
return selected, nil
}
// detectOrSelectAgent tries to auto-detect agents, or prompts the user to select.
// Returns the detected/selected agents and any error.
//
// On first run (no hooks installed):
// - Single detected built-in agent: used automatically
// - Single detected external agent: interactive multi-select prompt
// - Multiple/no detected agents: interactive multi-select prompt
// - Shows the interactive multi-select (TTY available and no selectFn override)
// - Pre-selects detected built-in agents so the user can confirm with enter
// or add more; detected external agents are shown but not pre-selected
// - Non-interactive (no TTY): uses detected agents, else the default agent
//
// On re-run (hooks already installed):
// - Always shows the interactive multi-select
// - Shows the interactive multi-select (TTY available and no selectFn override)
// - Pre-selects only agents that have hooks installed (respects prior deselection)
// - Non-interactive (no TTY): keeps the currently installed agents
//
// selectFn overrides the interactive prompt for testing. When nil, the real form is used.
// It receives available agent names and returns the selected names.
// selectFn overrides the prompt with a caller-supplied selection (--yes uses
// selectAllAgents; tests inject their own), bypassing the form even on a TTY.
// When nil, the real multi-select form is shown.
func detectOrSelectAgent(ctx context.Context, w io.Writer, selectFn func(available []string) ([]string, error)) ([]agent.Agent, error) {
// Check for agents with hooks already installed (re-run detection)
installedAgentNames := GetAgentsWithHooksInstalled(ctx)
6 unmodified lines
if !hasInstalledHooks {
switch {
case len(detected) == 1:
if isBuiltInAgent(detected[0]) {
// When a selectFn is provided (e.g. --yes), skip the single-agent
// shortcut so the caller's selection logic runs instead.
if selectFn == nil {
fmt.Fprintf(w, "Detected agent: %s\n\n", detected[0].Type())
return detected, nil
}
// Announce the single detected built-in agent; it is pre-selected
// in the multi-select form below so the user can confirm it or add
// more. --yes (selectFn != nil) uses the caller's selection and
// skips the announcement.
if selectFn == nil && isBuiltInAgent(detected[0]) {
fmt.Fprintf(w, "Detected agent: %s\n\n", detected[0].Type())
}
case len(detected) > 1:
59 unmodified lines
availableNames = append(availableNames, opt.Value)
}
var selectedAgentNames []string
if selectFn != nil {
var err error
selectedAgentNames, err = selectFn(availableNames)
if err != nil {
return nil, err
// selectFn overrides the prompt with a caller-supplied selection (--yes,
// tests). When nil, show the real interactive multi-select. Routing both
// through selectFn keeps a single selection step, so there is no "skip the
// picker" path a lone detected agent can slip back into.
if selectFn == nil {
selectFn = func([]string) ([]string, error) {
return promptAgentSelection(options)
}
if len(selectedAgentNames) == 0 {
return nil, errors.New("no agents selected")
}
} else {
form := NewAccessibleForm(
huh.NewGroup(
huh.NewMultiSelect[string]().
Title("Select the agents you want to use").
Description("Use space to select, enter to confirm.").
Options(options...).
Validate(func(selected []string) error {
if len(selected) == 0 {
return errors.New("please select at least one agent")
}
return nil
}).
Value(&selectedAgentNames),
),
)
if err := form.Run(); err != nil {
return nil, fmt.Errorf("agent selection cancelled: %w", err)
}
}
selectedAgentNames, err := selectFn(availableNames)
if err != nil {
return nil, err
}
if len(selectedAgentNames) == 0 {
return nil, errors.New("no agents selected")
}
selectedAgents := make([]agent.Agent, 0, len(selectedAgentNames))
for _, name := range selectedAgentNames {