diff --git a/cmd/gh-aw/main.go b/cmd/gh-aw/main.go index 1f39cc62b10..3683a5a0966 100644 --- a/cmd/gh-aw/main.go +++ b/cmd/gh-aw/main.go @@ -482,7 +482,7 @@ This command only works with workflows that have workflow_dispatch triggers. return errors.New("workflow inputs cannot be specified in interactive mode (they will be collected interactively)") } - return cli.RunWorkflowInteractively(cmd.Context(), verboseFlag, repoOverride, refOverride, autoMergePRs, push, engineOverride, dryRun) + return cli.RunWorkflowInteractively(cmd.Context(), verboseFlag, repoOverride, refOverride, autoMergePRs, push, engineOverride, dryRun, approveRun) } return cli.RunWorkflowsOnGitHub(cmd.Context(), args, cli.RunOptions{ @@ -815,7 +815,7 @@ Use "` + string(constants.CLIExtensionPrefix) + ` help all" to show help for all runCmd.Flags().Bool("push", false, "Commit and push workflow files (including transitive imports) before running. Refuses to proceed when unrelated files are already staged.") runCmd.Flags().Bool("dry-run", false, "Preview workflow execution without triggering runs on GitHub Actions") runCmd.Flags().BoolP("json", "j", false, "Output results in JSON format") - runCmd.Flags().Bool("approve", false, "Approve safe update manifest changes when --push triggers an automatic recompile step. When strict mode is active (the default), the recompile step enforces safe update checking; pass this flag to approve those changes.") + runCmd.Flags().Bool("approve", false, "Approve all safe update changes. When strict mode is active (the default), the compiler emits warnings for new restricted secrets or unapproved action additions/removals not present in the existing gh-aw-manifest. Use this flag to approve and skip safe update enforcement") // Register completions for run command runCmd.ValidArgsFunction = cli.CompleteWorkflowNames cli.RegisterEngineFlagCompletion(runCmd) diff --git a/cmd/gh-aw/main_help_text_test.go b/cmd/gh-aw/main_help_text_test.go index 2f619a1d9f9..9adbdcdf3ac 100644 --- a/cmd/gh-aw/main_help_text_test.go +++ b/cmd/gh-aw/main_help_text_test.go @@ -20,8 +20,7 @@ func TestRunCommandHelpTextConsistency(t *testing.T) { require.NotNil(t, runPush, "run command should define --push") require.NotNil(t, runRawField, "run command should define --raw-field") require.NotNil(t, compileApprove, "compile command should define --approve") - assert.Contains(t, compileApprove.Usage, "safe update changes", "compile --approve should describe compiler safe update approval") - assert.Equal(t, "Approve safe update manifest changes when --push triggers an automatic recompile step. When strict mode is active (the default), the recompile step enforces safe update checking; pass this flag to approve those changes.", runApprove.Usage, "run --approve should explain the --push-triggered recompile behavior with strict mode context") + assert.Equal(t, compileApprove.Usage, runApprove.Usage, "run --approve and compile --approve should have identical usage descriptions") assert.Equal(t, "Commit and push workflow files (including transitive imports) before running. Refuses to proceed when unrelated files are already staged.", runPush.Usage, "run --push should describe the staged-files precondition precisely") assert.Equal(t, "F", runRawField.Shorthand, "run --raw-field should keep the legacy -F shorthand for compatibility") assert.Equal(t, "use the long form --raw-field instead", runRawField.ShorthandDeprecated, "run -F shorthand should be marked deprecated") diff --git a/pkg/cli/add_command.go b/pkg/cli/add_command.go index 5879ea311e2..a06acf1490b 100644 --- a/pkg/cli/add_command.go +++ b/pkg/cli/add_command.go @@ -20,7 +20,7 @@ import ( var addLog = logger.New("cli:add_command") var ( - addCommandLong = `Add one or more agentic workflows from repositories to .github/workflows. + addCommandLong = `Add one or more agentic workflows from repositories, local files, or URLs to .github/workflows. This command adds workflows directly without interactive prompts. Use 'add-wizard' for a guided setup that configures secrets, creates a pull request, and more. diff --git a/pkg/cli/experiments_command.go b/pkg/cli/experiments_command.go index a281c3627bb..3b22f34c578 100644 --- a/pkg/cli/experiments_command.go +++ b/pkg/cli/experiments_command.go @@ -90,7 +90,7 @@ func NewExperimentsCommand() *cobra.Command { cmd := &cobra.Command{ Use: "experiments", Short: "List and analyze experiment workflow branches in the repository", - Long: `Explore ongoing experiments in the repository. + Long: `List and analyze experiment workflow branches in the repository. Experiments are tracked via git branches with the "experiments/" prefix (e.g., experiments/my-workflow). Each branch stores a state.json file written by the diff --git a/pkg/cli/run_interactive.go b/pkg/cli/run_interactive.go index 904b2321eca..3d26b5f91e9 100644 --- a/pkg/cli/run_interactive.go +++ b/pkg/cli/run_interactive.go @@ -28,7 +28,7 @@ type WorkflowOption struct { } // RunWorkflowInteractively runs a workflow in interactive mode -func RunWorkflowInteractively(ctx context.Context, verbose bool, repoOverride string, refOverride string, autoMergePRs bool, push bool, engineOverride string, dryRun bool) error { +func RunWorkflowInteractively(ctx context.Context, verbose bool, repoOverride string, refOverride string, autoMergePRs bool, push bool, engineOverride string, dryRun bool, approve bool) error { runInteractiveLog.Print("Starting interactive workflow run") // Check if running in CI environment @@ -74,7 +74,7 @@ func RunWorkflowInteractively(ctx context.Context, verbose bool, repoOverride st } // Step 6: Build command string for display - cmdStr := buildCommandString(selectedWorkflow.Name, inputValues, repoOverride, refOverride, autoMergePRs, push, engineOverride) + cmdStr := buildCommandString(selectedWorkflow.Name, inputValues, repoOverride, refOverride, autoMergePRs, push, engineOverride, approve) fmt.Fprintln(os.Stderr, console.FormatInfoMessage("\nRunning workflow...")) fmt.Fprintln(os.Stderr, console.FormatCommandMessage("Equivalent command: "+cmdStr)) fmt.Fprintln(os.Stderr, "") @@ -90,6 +90,7 @@ func RunWorkflowInteractively(ctx context.Context, verbose bool, repoOverride st Inputs: inputValues, Verbose: verbose, DryRun: dryRun, + Approve: approve, }) if err != nil { return fmt.Errorf("failed to run workflow: %w", err) @@ -371,6 +372,7 @@ type RunWorkflowOptions struct { AutoMergePRs bool Push bool DryRun bool + Approve bool } // RunSpecificWorkflowInteractively runs a specific workflow in interactive mode @@ -422,7 +424,7 @@ func RunSpecificWorkflowInteractively(ctx context.Context, opts RunWorkflowOptio } // Build command string for display - cmdStr := buildCommandString(opts.WorkflowName, inputValues, opts.RepoOverride, opts.RefOverride, opts.AutoMergePRs, opts.Push, opts.EngineOverride) + cmdStr := buildCommandString(opts.WorkflowName, inputValues, opts.RepoOverride, opts.RefOverride, opts.AutoMergePRs, opts.Push, opts.EngineOverride, opts.Approve) fmt.Fprintln(os.Stderr, console.FormatInfoMessage("\nRunning workflow...")) fmt.Fprintln(os.Stderr, console.FormatCommandMessage("Equivalent command: "+cmdStr)) fmt.Fprintln(os.Stderr, "") @@ -439,6 +441,7 @@ func RunSpecificWorkflowInteractively(ctx context.Context, opts RunWorkflowOptio Inputs: inputValues, Verbose: opts.Verbose, DryRun: opts.DryRun, + Approve: opts.Approve, }) if err != nil { return fmt.Errorf("failed to run workflow: %w", err) @@ -448,7 +451,7 @@ func RunSpecificWorkflowInteractively(ctx context.Context, opts RunWorkflowOptio } // buildCommandString builds the equivalent command string for display -func buildCommandString(workflowName string, inputs []string, repoOverride, refOverride string, autoMergePRs, push bool, engineOverride string) string { +func buildCommandString(workflowName string, inputs []string, repoOverride, refOverride string, autoMergePRs, push bool, engineOverride string, approve bool) string { parts := []string{string(constants.CLIExtensionPrefix), "run", workflowName} // Add inputs @@ -472,6 +475,9 @@ func buildCommandString(workflowName string, inputs []string, repoOverride, refO if engineOverride != "" { parts = append(parts, "--engine", engineOverride) } + if approve { + parts = append(parts, "--approve") + } return strings.Join(parts, " ") } diff --git a/pkg/cli/run_interactive_test.go b/pkg/cli/run_interactive_test.go index 471f6a353fc..f73aa22c662 100644 --- a/pkg/cli/run_interactive_test.go +++ b/pkg/cli/run_interactive_test.go @@ -204,6 +204,7 @@ func TestBuildCommandString(t *testing.T) { autoMergePRs bool push bool engineOverride string + approve bool expected string }{ { @@ -259,11 +260,29 @@ func TestBuildCommandString(t *testing.T) { engineOverride: "copilot", expected: "gh aw run test-workflow -F name=value --repo owner/repo --ref main --auto-merge-prs --push --engine copilot", }, + { + name: "with approve", + workflowName: "test-workflow", + approve: true, + expected: "gh aw run test-workflow --approve", + }, + { + name: "all flags with approve", + workflowName: "test-workflow", + inputs: []string{"name=value"}, + repoOverride: "owner/repo", + refOverride: "main", + autoMergePRs: true, + push: true, + engineOverride: "copilot", + approve: true, + expected: "gh aw run test-workflow -F name=value --repo owner/repo --ref main --auto-merge-prs --push --engine copilot --approve", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := buildCommandString(tt.workflowName, tt.inputs, tt.repoOverride, tt.refOverride, tt.autoMergePRs, tt.push, tt.engineOverride) + result := buildCommandString(tt.workflowName, tt.inputs, tt.repoOverride, tt.refOverride, tt.autoMergePRs, tt.push, tt.engineOverride, tt.approve) assert.Equal(t, tt.expected, result) }) }