From 4b077df31a3640fa60115b77686c1ae0b5b3f7a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 04:50:32 +0000 Subject: [PATCH 1/6] Initial plan From 3fda7fa88cc4a0dd368a049a80bb18f46e275c92 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 05:04:17 +0000 Subject: [PATCH 2/6] fix: surface add-wizard manifest config prompts earlier Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- pkg/cli/add_interactive_orchestrator.go | 16 +++++++-- pkg/cli/bootstrap_config.go | 44 ++++++++++++++++++++++++ pkg/cli/bootstrap_profile_runner_test.go | 43 +++++++++++++++++++++++ 3 files changed, 100 insertions(+), 3 deletions(-) diff --git a/pkg/cli/add_interactive_orchestrator.go b/pkg/cli/add_interactive_orchestrator.go index 866f6dc28cb..10f9f7fd29c 100644 --- a/pkg/cli/add_interactive_orchestrator.go +++ b/pkg/cli/add_interactive_orchestrator.go @@ -130,6 +130,16 @@ func RunAddInteractive(ctx context.Context, config *AddInteractiveConfig) error return err } + bootstrapProfile := (*resolvedBootstrapProfile)(nil) + if config.resolvedWorkflows != nil { + bootstrapProfile = config.resolvedWorkflows.BootstrapProfile + } + if config.hasWriteAccess { + if err := executeBootstrapConfigForAdd(ctx, config.RepoOverride, config.WorkflowSpecs, bootstrapProfileAddWizardPreInstall(bootstrapProfile), false, config.Verbose); err != nil { + return err + } + } + // Step 6: Select coding agent and collect API key if err := config.selectAIEngineAndKey(); err != nil { return err @@ -170,13 +180,13 @@ func RunAddInteractive(ctx context.Context, config *AddInteractiveConfig) error } // Step 9b: Apply bootstrap config steps interactively (if the package declares any) - if config.resolvedWorkflows != nil && config.resolvedWorkflows.BootstrapProfile != nil { + if bootstrapProfile != nil { if config.hasWriteAccess { - if err := executeBootstrapConfigForAdd(ctx, config.RepoOverride, config.WorkflowSpecs, config.resolvedWorkflows.BootstrapProfile, config.UseCopilotRequests, config.Verbose); err != nil { + if err := executeBootstrapConfigForAdd(ctx, config.RepoOverride, config.WorkflowSpecs, bootstrapProfileAddWizardPostInstall(bootstrapProfile), config.UseCopilotRequests, config.Verbose); err != nil { return err } } else { - printBootstrapConfigTODO(os.Stderr, config.resolvedWorkflows.BootstrapProfile) + printBootstrapConfigTODO(os.Stderr, bootstrapProfile) } } diff --git a/pkg/cli/bootstrap_config.go b/pkg/cli/bootstrap_config.go index bd8f2d766f9..65b3e703412 100644 --- a/pkg/cli/bootstrap_config.go +++ b/pkg/cli/bootstrap_config.go @@ -68,6 +68,50 @@ func printBootstrapConfigTODO(w io.Writer, profile *resolvedBootstrapProfile) { fmt.Fprintln(w, "") } +func bootstrapProfileAddWizardPreInstall(profile *resolvedBootstrapProfile) *resolvedBootstrapProfile { + return filterBootstrapProfileActions(profile, func(action repositoryPackageBootstrapAction) bool { + switch action.Type { + case "require-owner-type", "repo-variable", "repo-secret", "github-app": + return true + default: + return false + } + }) +} + +func bootstrapProfileAddWizardPostInstall(profile *resolvedBootstrapProfile) *resolvedBootstrapProfile { + return filterBootstrapProfileActions(profile, func(action repositoryPackageBootstrapAction) bool { + switch action.Type { + case "copilot-auth", "commit-and-push", "handoff": + return true + default: + return false + } + }) +} + +func filterBootstrapProfileActions(profile *resolvedBootstrapProfile, keep func(repositoryPackageBootstrapAction) bool) *resolvedBootstrapProfile { + if profile == nil || profile.Profile == nil || len(profile.Profile.Config) == 0 { + return nil + } + + filtered := make([]repositoryPackageBootstrapAction, 0, len(profile.Profile.Config)) + for _, action := range profile.Profile.Config { + if keep(action) { + filtered = append(filtered, action) + } + } + if len(filtered) == 0 { + return nil + } + + filteredProfile := *profile + filteredBootstrap := *profile.Profile + filteredBootstrap.Config = filtered + filteredProfile.Profile = &filteredBootstrap + return &filteredProfile +} + // executeBootstrapConfigForAdd runs the bootstrap config actions interactively. // Used by add-wizard after the workflow PR has been created and merged. func executeBootstrapConfigForAdd(ctx context.Context, repo string, sources []string, profile *resolvedBootstrapProfile, useCopilotRequests bool, verbose bool) error { diff --git a/pkg/cli/bootstrap_profile_runner_test.go b/pkg/cli/bootstrap_profile_runner_test.go index 8c0e2a67ed2..45dae4d2f05 100644 --- a/pkg/cli/bootstrap_profile_runner_test.go +++ b/pkg/cli/bootstrap_profile_runner_test.go @@ -69,3 +69,46 @@ func TestBootstrapProfileState(t *testing.T) { t.Fatal("expected SECRET_ONE secret") } } + +func TestBootstrapProfileAddWizardPhases(t *testing.T) { + profile := &resolvedBootstrapProfile{ + PackageID: "owner/repo", + Profile: &repositoryPackageBootstrap{ + Config: []repositoryPackageBootstrapAction{ + {Type: "require-owner-type"}, + {Type: "github-app"}, + {Type: "repo-variable"}, + {Type: "repo-secret"}, + {Type: "copilot-auth"}, + {Type: "commit-and-push"}, + {Type: "handoff"}, + }, + }, + } + + preInstall := bootstrapProfileAddWizardPreInstall(profile) + if preInstall == nil || preInstall.Profile == nil { + t.Fatal("expected pre-install bootstrap profile") + } + if got := len(preInstall.Profile.Config); got != 4 { + t.Fatalf("pre-install profile should contain 4 actions, got %d", got) + } + if preInstall.Profile.Config[0].Type != "require-owner-type" || preInstall.Profile.Config[3].Type != "repo-secret" { + t.Fatalf("unexpected pre-install action ordering: %+v", preInstall.Profile.Config) + } + + postInstall := bootstrapProfileAddWizardPostInstall(profile) + if postInstall == nil || postInstall.Profile == nil { + t.Fatal("expected post-install bootstrap profile") + } + if got := len(postInstall.Profile.Config); got != 3 { + t.Fatalf("post-install profile should contain 3 actions, got %d", got) + } + if postInstall.Profile.Config[0].Type != "copilot-auth" || postInstall.Profile.Config[2].Type != "handoff" { + t.Fatalf("unexpected post-install action ordering: %+v", postInstall.Profile.Config) + } + + if got := len(profile.Profile.Config); got != 7 { + t.Fatalf("original profile should remain unchanged, got %d actions", got) + } +} From 9fc8894c8f179fb8bb1f5c7adb8b313c7929035c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 05:25:08 +0000 Subject: [PATCH 3/6] fix: recognize manifest config earlier in add-wizard Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- pkg/cli/add_interactive_orchestrator.go | 10 +++-- pkg/cli/bootstrap_config.go | 15 +++++--- pkg/cli/bootstrap_profile_runner_test.go | 47 ++++++++++++++++++------ 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/pkg/cli/add_interactive_orchestrator.go b/pkg/cli/add_interactive_orchestrator.go index 10f9f7fd29c..2b35994f8f5 100644 --- a/pkg/cli/add_interactive_orchestrator.go +++ b/pkg/cli/add_interactive_orchestrator.go @@ -130,14 +130,16 @@ func RunAddInteractive(ctx context.Context, config *AddInteractiveConfig) error return err } - bootstrapProfile := (*resolvedBootstrapProfile)(nil) + var bootstrapProfile *resolvedBootstrapProfile if config.resolvedWorkflows != nil { bootstrapProfile = config.resolvedWorkflows.BootstrapProfile } + remainingBootstrapProfile := bootstrapProfile if config.hasWriteAccess { if err := executeBootstrapConfigForAdd(ctx, config.RepoOverride, config.WorkflowSpecs, bootstrapProfileAddWizardPreInstall(bootstrapProfile), false, config.Verbose); err != nil { return err } + remainingBootstrapProfile = bootstrapProfileAddWizardPostInstall(bootstrapProfile) } // Step 6: Select coding agent and collect API key @@ -180,13 +182,13 @@ func RunAddInteractive(ctx context.Context, config *AddInteractiveConfig) error } // Step 9b: Apply bootstrap config steps interactively (if the package declares any) - if bootstrapProfile != nil { + if remainingBootstrapProfile != nil { if config.hasWriteAccess { - if err := executeBootstrapConfigForAdd(ctx, config.RepoOverride, config.WorkflowSpecs, bootstrapProfileAddWizardPostInstall(bootstrapProfile), config.UseCopilotRequests, config.Verbose); err != nil { + if err := executeBootstrapConfigForAdd(ctx, config.RepoOverride, config.WorkflowSpecs, remainingBootstrapProfile, config.UseCopilotRequests, config.Verbose); err != nil { return err } } else { - printBootstrapConfigTODO(os.Stderr, bootstrapProfile) + printBootstrapConfigTODO(os.Stderr, remainingBootstrapProfile) } } diff --git a/pkg/cli/bootstrap_config.go b/pkg/cli/bootstrap_config.go index 65b3e703412..c22d95cc3d2 100644 --- a/pkg/cli/bootstrap_config.go +++ b/pkg/cli/bootstrap_config.go @@ -90,6 +90,9 @@ func bootstrapProfileAddWizardPostInstall(profile *resolvedBootstrapProfile) *re }) } +// filterBootstrapProfileActions returns a shallow clone of profile containing only +// actions for which keep returns true. It returns nil when the input profile is +// nil, has no bootstrap payload, or no actions survive filtering. func filterBootstrapProfileActions(profile *resolvedBootstrapProfile, keep func(repositoryPackageBootstrapAction) bool) *resolvedBootstrapProfile { if profile == nil || profile.Profile == nil || len(profile.Profile.Config) == 0 { return nil @@ -105,11 +108,13 @@ func filterBootstrapProfileActions(profile *resolvedBootstrapProfile, keep func( return nil } - filteredProfile := *profile - filteredBootstrap := *profile.Profile - filteredBootstrap.Config = filtered - filteredProfile.Profile = &filteredBootstrap - return &filteredProfile + return &resolvedBootstrapProfile{ + PackageID: profile.PackageID, + Source: profile.Source, + Profile: &repositoryPackageBootstrap{ + Config: filtered, + }, + } } // executeBootstrapConfigForAdd runs the bootstrap config actions interactively. diff --git a/pkg/cli/bootstrap_profile_runner_test.go b/pkg/cli/bootstrap_profile_runner_test.go index 45dae4d2f05..b29af3af704 100644 --- a/pkg/cli/bootstrap_profile_runner_test.go +++ b/pkg/cli/bootstrap_profile_runner_test.go @@ -5,6 +5,8 @@ package cli import ( "context" "testing" + + "github.com/stretchr/testify/assert" ) func TestBootstrapActionNeedsMutation(t *testing.T) { @@ -71,6 +73,10 @@ func TestBootstrapProfileState(t *testing.T) { } func TestBootstrapProfileAddWizardPhases(t *testing.T) { + expectedPreInstallActionTypes := []string{"require-owner-type", "github-app", "repo-variable", "repo-secret"} + expectedPostInstallActionTypes := []string{"copilot-auth", "commit-and-push", "handoff"} + expectedTotalActions := len(expectedPreInstallActionTypes) + len(expectedPostInstallActionTypes) + 1 // unsupported + profile := &resolvedBootstrapProfile{ PackageID: "owner/repo", Profile: &repositoryPackageBootstrap{ @@ -82,6 +88,7 @@ func TestBootstrapProfileAddWizardPhases(t *testing.T) { {Type: "copilot-auth"}, {Type: "commit-and-push"}, {Type: "handoff"}, + {Type: "unsupported"}, }, }, } @@ -90,25 +97,41 @@ func TestBootstrapProfileAddWizardPhases(t *testing.T) { if preInstall == nil || preInstall.Profile == nil { t.Fatal("expected pre-install bootstrap profile") } - if got := len(preInstall.Profile.Config); got != 4 { - t.Fatalf("pre-install profile should contain 4 actions, got %d", got) - } - if preInstall.Profile.Config[0].Type != "require-owner-type" || preInstall.Profile.Config[3].Type != "repo-secret" { - t.Fatalf("unexpected pre-install action ordering: %+v", preInstall.Profile.Config) - } + assert.Equal(t, expectedPreInstallActionTypes, bootstrapActionTypes(preInstall.Profile.Config)) postInstall := bootstrapProfileAddWizardPostInstall(profile) if postInstall == nil || postInstall.Profile == nil { t.Fatal("expected post-install bootstrap profile") } - if got := len(postInstall.Profile.Config); got != 3 { - t.Fatalf("post-install profile should contain 3 actions, got %d", got) + assert.Equal(t, expectedPostInstallActionTypes, bootstrapActionTypes(postInstall.Profile.Config)) + + if got := len(profile.Profile.Config); got != expectedTotalActions { + t.Fatalf("original profile should remain unchanged, got %d actions", got) } - if postInstall.Profile.Config[0].Type != "copilot-auth" || postInstall.Profile.Config[2].Type != "handoff" { - t.Fatalf("unexpected post-install action ordering: %+v", postInstall.Profile.Config) + + unsupportedOnlyProfile := &resolvedBootstrapProfile{ + PackageID: "owner/repo", + Profile: &repositoryPackageBootstrap{ + Config: []repositoryPackageBootstrapAction{{Type: "unsupported"}}, + }, } + for _, tt := range []struct { + name string + filter func(*resolvedBootstrapProfile) *resolvedBootstrapProfile + }{ + {name: "pre-install", filter: bootstrapProfileAddWizardPreInstall}, + {name: "post-install", filter: bootstrapProfileAddWizardPostInstall}, + } { + if tt.filter(unsupportedOnlyProfile) != nil { + t.Fatalf("unsupported actions should be excluded from the %s phase", tt.name) + } + } +} - if got := len(profile.Profile.Config); got != 7 { - t.Fatalf("original profile should remain unchanged, got %d actions", got) +func bootstrapActionTypes(actions []repositoryPackageBootstrapAction) []string { + types := make([]string, 0, len(actions)) + for _, action := range actions { + types = append(types, action.Type) } + return types } From 12e14f51535b820d92c5eee4bbe9c407d183df90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 05:38:15 +0000 Subject: [PATCH 4/6] fix: align add-wizard security scanner flags Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- pkg/cli/add_wizard_command.go | 7 ++++++- pkg/cli/add_wizard_command_test.go | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/cli/add_wizard_command.go b/pkg/cli/add_wizard_command.go index 7e1830a18dd..c35d5aef2f7 100644 --- a/pkg/cli/add_wizard_command.go +++ b/pkg/cli/add_wizard_command.go @@ -74,6 +74,8 @@ Note: To create a new workflow from scratch, use the 'new' command instead.`, skipSecret := noSecret || skipSecretLegacy appendText, _ := cmd.Flags().GetString("append") disableSecurityScanner, _ := cmd.Flags().GetBool("no-security-scanner") + disableSecurityScannerLegacy, _ := cmd.Flags().GetBool("disable-security-scanner") + disableSecurityScanner = disableSecurityScanner || disableSecurityScannerLegacy addWizardLog.Printf("Starting add-wizard: workflows=%v, engine=%s, verbose=%v", workflows, engineOverride, verbose) @@ -127,8 +129,11 @@ Note: To create a new workflow from scratch, use the 'new' command instead.`, // Add append flag (matches --append in add command) cmd.Flags().String("append", "", "Append extra content to the end of the agentic workflow on installation") - // Add no-security-scanner flag (matches --no-security-scanner in add command) + // Add no-security-scanner flag (--disable-security-scanner is kept as a deprecated alias + // for consistency with add and other install entry points) cmd.Flags().Bool("no-security-scanner", false, "Skip security scanning of workflow markdown content") + cmd.Flags().Bool("disable-security-scanner", false, "Skip security scanning of workflow markdown content") + _ = cmd.Flags().MarkDeprecated("disable-security-scanner", "use --no-security-scanner instead") // Register completions RegisterEngineFlagCompletion(cmd) diff --git a/pkg/cli/add_wizard_command_test.go b/pkg/cli/add_wizard_command_test.go index 40849a54565..0f1488bc7c5 100644 --- a/pkg/cli/add_wizard_command_test.go +++ b/pkg/cli/add_wizard_command_test.go @@ -39,6 +39,15 @@ func TestAddWizardCommand_FlagUsageMatchesAddCommand(t *testing.T) { } } +func TestAddWizardCommand_DeprecatesDisableSecurityScannerFlag(t *testing.T) { + cmd := NewAddWizardCommand(validateEngineStub) + require.NotNil(t, cmd) + + flag := cmd.Flags().Lookup("disable-security-scanner") + require.NotNil(t, flag, "add-wizard command should keep --disable-security-scanner as a deprecated alias") + assert.Equal(t, "use --no-security-scanner instead", flag.Deprecated) +} + func TestAddWizardCommand_ExamplesMentionNewFlags(t *testing.T) { cmd := NewAddWizardCommand(func(string) error { return nil }) require.NotNil(t, cmd) From e855bd9b206dd8677571da328a22adfccee82373 Mon Sep 17 00:00:00 2001 From: Mara Nikola Kiefer <8320933+mnkiefer@users.noreply.github.com> Date: Thu, 23 Jul 2026 08:13:59 +0200 Subject: [PATCH 5/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- pkg/cli/add_interactive_orchestrator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cli/add_interactive_orchestrator.go b/pkg/cli/add_interactive_orchestrator.go index 2b35994f8f5..fd99209cae2 100644 --- a/pkg/cli/add_interactive_orchestrator.go +++ b/pkg/cli/add_interactive_orchestrator.go @@ -136,7 +136,7 @@ func RunAddInteractive(ctx context.Context, config *AddInteractiveConfig) error } remainingBootstrapProfile := bootstrapProfile if config.hasWriteAccess { - if err := executeBootstrapConfigForAdd(ctx, config.RepoOverride, config.WorkflowSpecs, bootstrapProfileAddWizardPreInstall(bootstrapProfile), false, config.Verbose); err != nil { + if err := executeBootstrapConfigForAdd(ctx, config.RepoOverride, nil, bootstrapProfileAddWizardPreInstall(bootstrapProfile), false, config.Verbose); err != nil { return err } remainingBootstrapProfile = bootstrapProfileAddWizardPostInstall(bootstrapProfile) From cf0313be5b15822da658cf386a23017922427bfb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 06:25:09 +0000 Subject: [PATCH 6/6] fix: use neutral "setup steps" label in executeBootstrapConfigForAdd Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- pkg/cli/bootstrap_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cli/bootstrap_config.go b/pkg/cli/bootstrap_config.go index c22d95cc3d2..6b29c8b7678 100644 --- a/pkg/cli/bootstrap_config.go +++ b/pkg/cli/bootstrap_config.go @@ -130,7 +130,7 @@ func executeBootstrapConfigForAdd(ctx context.Context, repo string, sources []st bootstrapLog.Printf("Applying bootstrap config for add: repo=%s, package=%s, actions=%d, useCopilotRequests=%t", repo, profile.PackageID, len(profile.Profile.Config), useCopilotRequests) fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Applying post-installation steps from "+profile.PackageID+"...")) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Applying setup steps from "+profile.PackageID+"...")) repoDir, err := gitutil.FindGitRoot() if err != nil { bootstrapLog.Printf("Could not determine git root for add bootstrap config: %v", err)