From 66116173d56d82a0093c67ba2960f1f85b0ab0b5 Mon Sep 17 00:00:00 2001 From: Tom Nash Date: Thu, 2 Apr 2026 16:23:45 +1000 Subject: [PATCH] FEAT-init-flag: Adding the optional flag -y to the init command to allow auto-acceptance of sensible defaults for headless setups like in conductor setup scripts --- cli/cmd/init.go | 44 +++++++++++++++++++++++--------- cli/tests/init_test.go | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 cli/tests/init_test.go diff --git a/cli/cmd/init.go b/cli/cmd/init.go index 37d1fd0..c753b9f 100644 --- a/cli/cmd/init.go +++ b/cli/cmd/init.go @@ -30,6 +30,12 @@ installation and returns an informative message.`, RunE: runInit, } +var initYes bool + +func init() { + initCmd.Flags().BoolVarP(&initYes, "yes", "y", false, "Accept sensible defaults (non-interactive)") +} + type initResult struct { RepoRoot string `json:"repo_root"` PolicyDB string `json:"policy_db"` @@ -56,7 +62,7 @@ func runInit(cmd *cobra.Command, args []string) error { if flags.JSON { out, _ := json.MarshalIndent(map[string]interface{}{ "already_initialised": true, - "repo_root": absRoot, + "repo_root": absRoot, }, "", " ") fmt.Println(string(out)) return nil @@ -123,12 +129,17 @@ func runInit(cmd *cobra.Command, args []string) error { return fmt.Errorf("init: store installed agents: %w", err) } - // Standard guardrails — prompt user to opt in (skip in --json mode). + // Standard guardrails — prompt user to opt in. + // In --json mode this is skipped unless -y is set. var addedCommands []string var addedFiles []string - if !flags.JSON { + if !flags.JSON || initYes { var err error - addedCommands, addedFiles, err = promptAndAddGuardrails(cmd, policyDB) + if initYes { + addedCommands, addedFiles, err = addStandardGuardrails(policyDB) + } else { + addedCommands, addedFiles, err = promptAndAddGuardrails(cmd, policyDB) + } if err != nil { return fmt.Errorf("init: guardrails: %w", err) } @@ -169,12 +180,12 @@ func runInit(cmd *cobra.Command, args []string) error { return nil } -// selectAgents presents the interactive TUI (or auto-selects in --json/non-TTY). +// selectAgents presents the interactive TUI (or auto-selects in --json/-y). // Returns the selected agent IDs and display names. func selectAgents(cmd *cobra.Command) (ids []string, names []string, err error) { allAgents := agents.All() - if flags.JSON { + if flags.JSON || initYes { // Auto-select all installable agents. for _, a := range allAgents { if a.Installable() { @@ -264,6 +275,21 @@ func promptAndAddGuardrails(cmd *cobra.Command, policyDB *sql.DB) (addedCommands return nil, nil, nil } + addedCommands, addedFiles, err = addStandardGuardrails(policyDB) + if err != nil { + return nil, nil, err + } + + total := len(addedCommands) + len(addedFiles) + if total > 0 { + fmt.Fprintf(cmd.OutOrStdout(), " added %d guardrail(s).\n", total) + } else { + fmt.Fprintln(cmd.OutOrStdout(), " already configured.") + } + return addedCommands, addedFiles, nil +} + +func addStandardGuardrails(policyDB *sql.DB) (addedCommands []string, addedFiles []string, err error) { user := store.CurrentOSUser() for _, pattern := range store.StandardGuardrails { @@ -288,11 +314,5 @@ func promptAndAddGuardrails(cmd *cobra.Command, policyDB *sql.DB) (addedCommands addedFiles = append(addedFiles, f.Pattern) } - total := len(addedCommands) + len(addedFiles) - if total > 0 { - fmt.Fprintf(cmd.OutOrStdout(), " added %d guardrail(s).\n", total) - } else { - fmt.Fprintln(cmd.OutOrStdout(), " already configured.") - } return addedCommands, addedFiles, nil } diff --git a/cli/tests/init_test.go b/cli/tests/init_test.go new file mode 100644 index 0000000..51a943d --- /dev/null +++ b/cli/tests/init_test.go @@ -0,0 +1,58 @@ +package tests + +import "testing" + +func TestInitYesAddsDefaultGuardrails(t *testing.T) { + repo := testRepo{ + Dir: t.TempDir(), + Home: t.TempDir(), + } + + runCordon(t, repo, "init", "-y") + + var commandList struct { + Rules []struct { + Pattern string `json:"Pattern"` + } `json:"rules"` + } + r := runCordon(t, repo, "command", "list", "--json") + mustParseJSON(t, r.Stdout, &commandList) + + if len(commandList.Rules) == 0 { + t.Fatal("expected default command guardrails after init -y, got none") + } + + foundCommandGuardrail := false + for _, rule := range commandList.Rules { + if rule.Pattern == "git reset --hard*" { + foundCommandGuardrail = true + break + } + } + if !foundCommandGuardrail { + t.Fatal("expected guardrail 'git reset --hard*' after init -y") + } + + var fileList struct { + FileRules []struct { + Pattern string `json:"Pattern"` + } `json:"file_rules"` + } + r = runCordon(t, repo, "file", "list", "--json") + mustParseJSON(t, r.Stdout, &fileList) + + if len(fileList.FileRules) == 0 { + t.Fatal("expected default file guardrails after init -y, got none") + } + + foundFileGuardrail := false + for _, rule := range fileList.FileRules { + if rule.Pattern == ".env" { + foundFileGuardrail = true + break + } + } + if !foundFileGuardrail { + t.Fatal("expected file guardrail '.env' after init -y") + } +}