From a523c56835d1f1b582047e888cb4470338581506 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 8 Apr 2026 18:57:20 -0700 Subject: [PATCH 1/6] fix: separate inputs for framework and adapter options --- cmd/project/create_template.go | 146 +++++++++++++++++++++++++++------ cmd/project/create_test.go | 66 +++++++++++++-- 2 files changed, 180 insertions(+), 32 deletions(-) diff --git a/cmd/project/create_template.go b/cmd/project/create_template.go index 98d9f9d0..e4e935dc 100644 --- a/cmd/project/create_template.go +++ b/cmd/project/create_template.go @@ -71,38 +71,96 @@ func getSelectionOptions(categoryID string) []promptObject { return templatePromptObjects[categoryID] } -// getFrameworkOptions returns the framework choices for a given template. +// getFrameworkOptions returns the framework choices for a given AI app template. func getFrameworkOptions(template string) []promptObject { frameworkPromptObjects := map[string][]promptObject{ "slack-cli#ai-apps/support-agent": { { - Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for Python")), + Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")), + Repository: "slack-cli#ai-apps/support-agent/bolt-js", + }, + { + Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")), + Repository: "slack-cli#ai-apps/support-agent/bolt-python", + }, + }, + "slack-cli#ai-apps/starter-agent": { + { + Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")), + Repository: "slack-cli#ai-apps/starter-agent/bolt-js", + }, + { + Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")), + Repository: "slack-cli#ai-apps/starter-agent/bolt-python", + }, + }, + } + return frameworkPromptObjects[template] +} + +// getAdapterOptions returns the AI adapter choices for a given template and framework. +func getAdapterOptions(framework string) []promptObject { + adapterPromptObjects := map[string][]promptObject{ + "slack-cli#ai-apps/support-agent/bolt-js": { + { + Title: "Claude Agent SDK", + Repository: "slack-samples/bolt-js-support-agent", + Subdir: "claude-agent-sdk", + }, + { + Title: "OpenAI Agents SDK", + Repository: "slack-samples/bolt-js-support-agent", + Subdir: "openai-agents-sdk", + }, + }, + "slack-cli#ai-apps/support-agent/bolt-python": { + { + Title: "Claude Agent SDK", Repository: "slack-samples/bolt-python-support-agent", Subdir: "claude-agent-sdk", }, { - Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for Python")), + Title: "OpenAI Agents SDK", Repository: "slack-samples/bolt-python-support-agent", Subdir: "openai-agents-sdk", }, { - Title: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python")), + Title: "Pydantic AI", Repository: "slack-samples/bolt-python-support-agent", Subdir: "pydantic-ai", }, }, - "slack-cli#ai-apps/starter-agent": { + "slack-cli#ai-apps/starter-agent/bolt-js": { { - Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")), + Title: "Claude Agent SDK", Repository: "slack-samples/bolt-js-starter-agent", + Subdir: "claude-agent-sdk", }, { - Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")), + Title: "OpenAI Agents SDK", + Repository: "slack-samples/bolt-js-starter-agent", + Subdir: "openai-agents-sdk", + }, + }, + "slack-cli#ai-apps/starter-agent/bolt-python": { + { + Title: "Claude Agent SDK", + Repository: "slack-samples/bolt-python-starter-agent", + Subdir: "claude-agent-sdk", + }, + { + Title: "OpenAI Agents SDK", + Repository: "slack-samples/bolt-python-starter-agent", + Subdir: "openai-agents-sdk", + }, + { + Title: "Pydantic AI", Repository: "slack-samples/bolt-python-starter-agent", + Subdir: "pydantic-ai", }, }, } - return frameworkPromptObjects[template] + return adapterPromptObjects[framework] } // getSelectionOptionsForCategory returns the top-level category options for @@ -223,31 +281,63 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory, } template := options[selection.Index].Repository - // Prompt for the example framework - examples := getFrameworkOptions(template) - choices := make([]string, len(examples)) - for i, opt := range examples { - choices[i] = opt.Title + // Prompt for the framework + frameworks := getFrameworkOptions(template) + frameworkChoices := make([]string, len(frameworks)) + for i, opt := range frameworks { + frameworkChoices[i] = opt.Title } - choice, err := clients.IO.SelectPrompt(ctx, "Select a framework:", choices, iostreams.SelectPromptConfig{ + frameworkSelection, err := clients.IO.SelectPrompt(ctx, "Select a framework:", frameworkChoices, iostreams.SelectPromptConfig{ Description: func(value string, index int) string { - return examples[index].Description + return frameworks[index].Description }, Required: true, Template: getSelectionTemplate(clients), }) if err != nil { return create.Template{}, err - } else if choice.Flag { + } else if frameworkSelection.Flag { return create.Template{}, slackerror.New(slackerror.ErrPrompt) } - example := examples[choice.Index] - resolved, err := create.ResolveTemplateURL(example.Repository) + framework := frameworks[frameworkSelection.Index] + + // Check if there are adapter options for this framework + adapters := getAdapterOptions(framework.Repository) + if len(adapters) > 0 { + adapterChoices := make([]string, len(adapters)) + for i, opt := range adapters { + adapterChoices[i] = opt.Title + } + adapterSelection, err := clients.IO.SelectPrompt(ctx, "Select an adapter:", adapterChoices, iostreams.SelectPromptConfig{ + Description: func(value string, index int) string { + return adapters[index].Description + }, + Required: true, + Template: getSelectionTemplate(clients), + }) + if err != nil { + return create.Template{}, err + } else if adapterSelection.Flag { + return create.Template{}, slackerror.New(slackerror.ErrPrompt) + } + adapter := adapters[adapterSelection.Index] + resolved, err := create.ResolveTemplateURL(adapter.Repository) + if err != nil { + return create.Template{}, err + } + if adapter.Subdir != "" { + resolved.SetSubdir(adapter.Subdir) + } + return resolved, nil + } + + // No adapter options - resolve the framework directly + resolved, err := create.ResolveTemplateURL(framework.Repository) if err != nil { return create.Template{}, err } - if example.Subdir != "" { - resolved.SetSubdir(example.Subdir) + if framework.Subdir != "" { + resolved.SetSubdir(framework.Subdir) } return resolved, nil } @@ -315,12 +405,18 @@ func listTemplates(ctx context.Context, clients *shared.ClientFactory, categoryS for _, category := range categories { var secondary []string if frameworks := getFrameworkOptions(category.id); len(frameworks) > 0 { - for _, tmpl := range frameworks { - repo := tmpl.Repository - if tmpl.Subdir != "" { - repo = fmt.Sprintf("%s --subdir %s", repo, tmpl.Subdir) + for _, fw := range frameworks { + if adapters := getAdapterOptions(fw.Repository); len(adapters) > 0 { + for _, adapter := range adapters { + repo := adapter.Repository + if adapter.Subdir != "" { + repo = fmt.Sprintf("%s --subdir %s", repo, adapter.Subdir) + } + secondary = append(secondary, repo) + } + } else { + secondary = append(secondary, fw.Repository) } - secondary = append(secondary, repo) } } else { for _, tmpl := range getSelectionOptions(category.id) { diff --git a/cmd/project/create_test.go b/cmd/project/create_test.go index dce88f48..5a331b35 100644 --- a/cmd/project/create_test.go +++ b/cmd/project/create_test.go @@ -133,7 +133,15 @@ func TestCreateCommand(t *testing.T) { Return( iostreams.SelectPromptResponse{ Prompt: true, - Index: 0, // Select Node.js template + Index: 0, // Select Node.js + }, + nil, + ) + cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select Claude Agent SDK }, nil, ) @@ -146,9 +154,11 @@ func TestCreateCommand(t *testing.T) { ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-agent") require.NoError(t, err) + template.SetSubdir("claude-agent-sdk") expected := create.CreateArgs{ AppName: "my-agent", Template: template, + Subdir: "claude-agent-sdk", } createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, expected) // Verify that category prompt was NOT called @@ -172,7 +182,15 @@ func TestCreateCommand(t *testing.T) { Return( iostreams.SelectPromptResponse{ Prompt: true, - Index: 1, // Select Python template + Index: 1, // Select Python + }, + nil, + ) + cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select Claude Agent SDK }, nil, ) @@ -183,9 +201,11 @@ func TestCreateCommand(t *testing.T) { ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { template, err := create.ResolveTemplateURL("slack-samples/bolt-python-starter-agent") require.NoError(t, err) + template.SetSubdir("claude-agent-sdk") expected := create.CreateArgs{ AppName: "my-agent-app", Template: template, + Subdir: "claude-agent-sdk", } createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, expected) // Verify that category prompt was NOT called @@ -202,7 +222,9 @@ func TestCreateCommand(t *testing.T) { cm.IO.On("SelectPrompt", mock.Anything, "Select a template:", mock.Anything, mock.Anything). Return(iostreams.SelectPromptResponse{Prompt: true, Index: 0}, nil) cm.IO.On("SelectPrompt", mock.Anything, "Select a framework:", mock.Anything, mock.Anything). - Return(iostreams.SelectPromptResponse{Prompt: true, Index: 2}, nil) + Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) // Select Bolt for Python + cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + Return(iostreams.SelectPromptResponse{Prompt: true, Index: 2}, nil) // Select Pydantic AI createClientMock = new(CreateClientMock) createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything).Return("", nil) CreateFunc = createClientMock.Create @@ -312,6 +334,14 @@ func TestCreateCommand(t *testing.T) { }, nil, ) + cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select Claude Agent SDK + }, + nil, + ) createClientMock = new(CreateClientMock) createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything).Return("", nil) CreateFunc = createClientMock.Create @@ -319,9 +349,11 @@ func TestCreateCommand(t *testing.T) { ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-agent") require.NoError(t, err) + template.SetSubdir("claude-agent-sdk") expected := create.CreateArgs{ AppName: "my-custom-name", // --name flag overrides Template: template, + Subdir: "claude-agent-sdk", } createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, expected) // Verify that category prompt was NOT called (shortcut was triggered) @@ -383,6 +415,14 @@ func TestCreateCommand(t *testing.T) { }, nil, ) + cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select Claude Agent SDK + }, + nil, + ) createClientMock = new(CreateClientMock) createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything).Return("", nil) CreateFunc = createClientMock.Create @@ -390,9 +430,11 @@ func TestCreateCommand(t *testing.T) { ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-agent") require.NoError(t, err) + template.SetSubdir("claude-agent-sdk") expected := create.CreateArgs{ AppName: "my-name", // --name flag overrides "my-project" positional arg Template: template, + Subdir: "claude-agent-sdk", } createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, expected) // Verify that category prompt was NOT called (agent shortcut was triggered) @@ -601,12 +643,17 @@ func TestCreateCommand(t *testing.T) { "slack-samples/bolt-js-starter-template", "slack-samples/bolt-python-starter-template", "Support agent", + "slack-samples/bolt-js-support-agent --subdir claude-agent-sdk", + "slack-samples/bolt-js-support-agent --subdir openai-agents-sdk", "slack-samples/bolt-python-support-agent --subdir claude-agent-sdk", "slack-samples/bolt-python-support-agent --subdir openai-agents-sdk", "slack-samples/bolt-python-support-agent --subdir pydantic-ai", "Starter agent", - "slack-samples/bolt-js-starter-agent", - "slack-samples/bolt-python-starter-agent", + "slack-samples/bolt-js-starter-agent --subdir claude-agent-sdk", + "slack-samples/bolt-js-starter-agent --subdir openai-agents-sdk", + "slack-samples/bolt-python-starter-agent --subdir claude-agent-sdk", + "slack-samples/bolt-python-starter-agent --subdir openai-agents-sdk", + "slack-samples/bolt-python-starter-agent --subdir pydantic-ai", "Automation apps", "slack-samples/bolt-js-custom-function-template", "slack-samples/bolt-python-custom-function-template", @@ -624,12 +671,17 @@ func TestCreateCommand(t *testing.T) { }, ExpectedOutputs: []string{ "Support agent", + "slack-samples/bolt-js-support-agent --subdir claude-agent-sdk", + "slack-samples/bolt-js-support-agent --subdir openai-agents-sdk", "slack-samples/bolt-python-support-agent --subdir claude-agent-sdk", "slack-samples/bolt-python-support-agent --subdir openai-agents-sdk", "slack-samples/bolt-python-support-agent --subdir pydantic-ai", "Starter agent", - "slack-samples/bolt-js-starter-agent", - "slack-samples/bolt-python-starter-agent", + "slack-samples/bolt-js-starter-agent --subdir claude-agent-sdk", + "slack-samples/bolt-js-starter-agent --subdir openai-agents-sdk", + "slack-samples/bolt-python-starter-agent --subdir claude-agent-sdk", + "slack-samples/bolt-python-starter-agent --subdir openai-agents-sdk", + "slack-samples/bolt-python-starter-agent --subdir pydantic-ai", }, ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything) From a7bb34a7417e9158469b5a7fda8f13a157078647 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 9 Apr 2026 13:19:10 -0700 Subject: [PATCH 2/6] fix: order starter agent before example agents --- cmd/project/create_template.go | 12 ++++++------ cmd/project/create_test.go | 34 +++++++++++++++++----------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cmd/project/create_template.go b/cmd/project/create_template.go index e4e935dc..ec54dace 100644 --- a/cmd/project/create_template.go +++ b/cmd/project/create_template.go @@ -44,14 +44,14 @@ func getSelectionOptions(categoryID string) []promptObject { }, }, "slack-cli#ai-apps": { - { - Title: fmt.Sprintf("Support Agent %s", style.Secondary("Resolve IT support cases")), - Repository: "slack-cli#ai-apps/support-agent", - }, { Title: fmt.Sprintf("Starter Agent %s", style.Secondary("Start from scratch")), Repository: "slack-cli#ai-apps/starter-agent", }, + { + Title: fmt.Sprintf("Support Agent %s", style.Secondary("Resolve IT support cases")), + Repository: "slack-cli#ai-apps/support-agent", + }, }, "slack-cli#automation-apps": { { @@ -390,14 +390,14 @@ func listTemplates(ctx context.Context, clients *shared.ClientFactory, categoryS var categories []categoryInfo if categoryShortcut == "agent" { categories = []categoryInfo{ - {id: "slack-cli#ai-apps/support-agent", name: "Support agent"}, {id: "slack-cli#ai-apps/starter-agent", name: "Starter agent"}, + {id: "slack-cli#ai-apps/support-agent", name: "Support agent"}, } } else { categories = []categoryInfo{ {id: "slack-cli#getting-started", name: "Getting started"}, - {id: "slack-cli#ai-apps/support-agent", name: "Support agent"}, {id: "slack-cli#ai-apps/starter-agent", name: "Starter agent"}, + {id: "slack-cli#ai-apps/support-agent", name: "Support agent"}, {id: "slack-cli#automation-apps", name: "Automation apps"}, } } diff --git a/cmd/project/create_test.go b/cmd/project/create_test.go index 5a331b35..2955a0c0 100644 --- a/cmd/project/create_test.go +++ b/cmd/project/create_test.go @@ -125,7 +125,7 @@ func TestCreateCommand(t *testing.T) { Return( iostreams.SelectPromptResponse{ Prompt: true, - Index: 1, // Select Starter Agent + Index: 0, // Select Starter Agent }, nil, ) @@ -174,7 +174,7 @@ func TestCreateCommand(t *testing.T) { Return( iostreams.SelectPromptResponse{ Prompt: true, - Index: 1, // Select Starter Agent + Index: 0, // Select Starter Agent }, nil, ) @@ -220,7 +220,7 @@ func TestCreateCommand(t *testing.T) { cm.IO.On("SelectPrompt", mock.Anything, "Select a category:", mock.Anything, mock.Anything). Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) cm.IO.On("SelectPrompt", mock.Anything, "Select a template:", mock.Anything, mock.Anything). - Return(iostreams.SelectPromptResponse{Prompt: true, Index: 0}, nil) + Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) // Select Support Agent cm.IO.On("SelectPrompt", mock.Anything, "Select a framework:", mock.Anything, mock.Anything). Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) // Select Bolt for Python cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). @@ -322,7 +322,7 @@ func TestCreateCommand(t *testing.T) { Return( iostreams.SelectPromptResponse{ Prompt: true, - Index: 1, // Select Starter Agent + Index: 0, // Select Starter Agent }, nil, ) @@ -403,7 +403,7 @@ func TestCreateCommand(t *testing.T) { Return( iostreams.SelectPromptResponse{ Prompt: true, - Index: 1, // Select Starter Agent + Index: 0, // Select Starter Agent }, nil, ) @@ -642,18 +642,18 @@ func TestCreateCommand(t *testing.T) { "Getting started", "slack-samples/bolt-js-starter-template", "slack-samples/bolt-python-starter-template", - "Support agent", - "slack-samples/bolt-js-support-agent --subdir claude-agent-sdk", - "slack-samples/bolt-js-support-agent --subdir openai-agents-sdk", - "slack-samples/bolt-python-support-agent --subdir claude-agent-sdk", - "slack-samples/bolt-python-support-agent --subdir openai-agents-sdk", - "slack-samples/bolt-python-support-agent --subdir pydantic-ai", "Starter agent", "slack-samples/bolt-js-starter-agent --subdir claude-agent-sdk", "slack-samples/bolt-js-starter-agent --subdir openai-agents-sdk", "slack-samples/bolt-python-starter-agent --subdir claude-agent-sdk", "slack-samples/bolt-python-starter-agent --subdir openai-agents-sdk", "slack-samples/bolt-python-starter-agent --subdir pydantic-ai", + "Support agent", + "slack-samples/bolt-js-support-agent --subdir claude-agent-sdk", + "slack-samples/bolt-js-support-agent --subdir openai-agents-sdk", + "slack-samples/bolt-python-support-agent --subdir claude-agent-sdk", + "slack-samples/bolt-python-support-agent --subdir openai-agents-sdk", + "slack-samples/bolt-python-support-agent --subdir pydantic-ai", "Automation apps", "slack-samples/bolt-js-custom-function-template", "slack-samples/bolt-python-custom-function-template", @@ -670,18 +670,18 @@ func TestCreateCommand(t *testing.T) { CreateFunc = createClientMock.Create }, ExpectedOutputs: []string{ - "Support agent", - "slack-samples/bolt-js-support-agent --subdir claude-agent-sdk", - "slack-samples/bolt-js-support-agent --subdir openai-agents-sdk", - "slack-samples/bolt-python-support-agent --subdir claude-agent-sdk", - "slack-samples/bolt-python-support-agent --subdir openai-agents-sdk", - "slack-samples/bolt-python-support-agent --subdir pydantic-ai", "Starter agent", "slack-samples/bolt-js-starter-agent --subdir claude-agent-sdk", "slack-samples/bolt-js-starter-agent --subdir openai-agents-sdk", "slack-samples/bolt-python-starter-agent --subdir claude-agent-sdk", "slack-samples/bolt-python-starter-agent --subdir openai-agents-sdk", "slack-samples/bolt-python-starter-agent --subdir pydantic-ai", + "Support agent", + "slack-samples/bolt-js-support-agent --subdir claude-agent-sdk", + "slack-samples/bolt-js-support-agent --subdir openai-agents-sdk", + "slack-samples/bolt-python-support-agent --subdir claude-agent-sdk", + "slack-samples/bolt-python-support-agent --subdir openai-agents-sdk", + "slack-samples/bolt-python-support-agent --subdir pydantic-ai", }, ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything) From 60a669c31ee03f92559303922913625bd130b788 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 9 Apr 2026 13:29:42 -0700 Subject: [PATCH 3/6] fix: select an agent framework no adapter --- cmd/project/create_template.go | 2 +- cmd/project/create_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/project/create_template.go b/cmd/project/create_template.go index ec54dace..eaddd807 100644 --- a/cmd/project/create_template.go +++ b/cmd/project/create_template.go @@ -308,7 +308,7 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory, for i, opt := range adapters { adapterChoices[i] = opt.Title } - adapterSelection, err := clients.IO.SelectPrompt(ctx, "Select an adapter:", adapterChoices, iostreams.SelectPromptConfig{ + adapterSelection, err := clients.IO.SelectPrompt(ctx, "Select an agent framework:", adapterChoices, iostreams.SelectPromptConfig{ Description: func(value string, index int) string { return adapters[index].Description }, diff --git a/cmd/project/create_test.go b/cmd/project/create_test.go index 2955a0c0..767fab02 100644 --- a/cmd/project/create_test.go +++ b/cmd/project/create_test.go @@ -137,7 +137,7 @@ func TestCreateCommand(t *testing.T) { }, nil, ) - cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select an agent framework:", mock.Anything, mock.Anything). Return( iostreams.SelectPromptResponse{ Prompt: true, @@ -186,7 +186,7 @@ func TestCreateCommand(t *testing.T) { }, nil, ) - cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select an agent framework:", mock.Anything, mock.Anything). Return( iostreams.SelectPromptResponse{ Prompt: true, @@ -223,7 +223,7 @@ func TestCreateCommand(t *testing.T) { Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) // Select Support Agent cm.IO.On("SelectPrompt", mock.Anything, "Select a framework:", mock.Anything, mock.Anything). Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) // Select Bolt for Python - cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select an agent framework:", mock.Anything, mock.Anything). Return(iostreams.SelectPromptResponse{Prompt: true, Index: 2}, nil) // Select Pydantic AI createClientMock = new(CreateClientMock) createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything).Return("", nil) @@ -334,7 +334,7 @@ func TestCreateCommand(t *testing.T) { }, nil, ) - cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select an agent framework:", mock.Anything, mock.Anything). Return( iostreams.SelectPromptResponse{ Prompt: true, @@ -415,7 +415,7 @@ func TestCreateCommand(t *testing.T) { }, nil, ) - cm.IO.On("SelectPrompt", mock.Anything, "Select an adapter:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select an agent framework:", mock.Anything, mock.Anything). Return( iostreams.SelectPromptResponse{ Prompt: true, From c8b280af641427b6de883851f38c739b3e05e74c Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 9 Apr 2026 13:54:28 -0700 Subject: [PATCH 4/6] feat: select a bolt framwork for agent apps --- cmd/project/create_template.go | 2 +- cmd/project/create_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/project/create_template.go b/cmd/project/create_template.go index eaddd807..d8077d4f 100644 --- a/cmd/project/create_template.go +++ b/cmd/project/create_template.go @@ -287,7 +287,7 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory, for i, opt := range frameworks { frameworkChoices[i] = opt.Title } - frameworkSelection, err := clients.IO.SelectPrompt(ctx, "Select a framework:", frameworkChoices, iostreams.SelectPromptConfig{ + frameworkSelection, err := clients.IO.SelectPrompt(ctx, "Select a Bolt framework:", frameworkChoices, iostreams.SelectPromptConfig{ Description: func(value string, index int) string { return frameworks[index].Description }, diff --git a/cmd/project/create_test.go b/cmd/project/create_test.go index 767fab02..c64266c2 100644 --- a/cmd/project/create_test.go +++ b/cmd/project/create_test.go @@ -129,7 +129,7 @@ func TestCreateCommand(t *testing.T) { }, nil, ) - cm.IO.On("SelectPrompt", mock.Anything, "Select a framework:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select a Bolt framework:", mock.Anything, mock.Anything). Return( iostreams.SelectPromptResponse{ Prompt: true, @@ -178,7 +178,7 @@ func TestCreateCommand(t *testing.T) { }, nil, ) - cm.IO.On("SelectPrompt", mock.Anything, "Select a framework:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select a Bolt framework:", mock.Anything, mock.Anything). Return( iostreams.SelectPromptResponse{ Prompt: true, @@ -221,7 +221,7 @@ func TestCreateCommand(t *testing.T) { Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) cm.IO.On("SelectPrompt", mock.Anything, "Select a template:", mock.Anything, mock.Anything). Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) // Select Support Agent - cm.IO.On("SelectPrompt", mock.Anything, "Select a framework:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select a Bolt framework:", mock.Anything, mock.Anything). Return(iostreams.SelectPromptResponse{Prompt: true, Index: 1}, nil) // Select Bolt for Python cm.IO.On("SelectPrompt", mock.Anything, "Select an agent framework:", mock.Anything, mock.Anything). Return(iostreams.SelectPromptResponse{Prompt: true, Index: 2}, nil) // Select Pydantic AI @@ -326,7 +326,7 @@ func TestCreateCommand(t *testing.T) { }, nil, ) - cm.IO.On("SelectPrompt", mock.Anything, "Select a framework:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select a Bolt framework:", mock.Anything, mock.Anything). Return( iostreams.SelectPromptResponse{ Prompt: true, @@ -407,7 +407,7 @@ func TestCreateCommand(t *testing.T) { }, nil, ) - cm.IO.On("SelectPrompt", mock.Anything, "Select a framework:", mock.Anything, mock.Anything). + cm.IO.On("SelectPrompt", mock.Anything, "Select a Bolt framework:", mock.Anything, mock.Anything). Return( iostreams.SelectPromptResponse{ Prompt: true, From e82f2a9c78ff61d1e4a1be458d2cd490998d8a0c Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 9 Apr 2026 13:54:49 -0700 Subject: [PATCH 5/6] feat: secondary descriptions of agentic templates --- cmd/project/create_template.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd/project/create_template.go b/cmd/project/create_template.go index d8077d4f..29e619e8 100644 --- a/cmd/project/create_template.go +++ b/cmd/project/create_template.go @@ -98,63 +98,63 @@ func getFrameworkOptions(template string) []promptObject { return frameworkPromptObjects[template] } -// getAdapterOptions returns the AI adapter choices for a given template and framework. +// getAdapterOptions returns the AI agent framework choices for a given template and framework. func getAdapterOptions(framework string) []promptObject { adapterPromptObjects := map[string][]promptObject{ "slack-cli#ai-apps/support-agent/bolt-js": { { - Title: "Claude Agent SDK", + Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for JavaScript - Support Agent Template")), Repository: "slack-samples/bolt-js-support-agent", Subdir: "claude-agent-sdk", }, { - Title: "OpenAI Agents SDK", + Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for JavaScript - Support Agent Template")), Repository: "slack-samples/bolt-js-support-agent", Subdir: "openai-agents-sdk", }, }, "slack-cli#ai-apps/support-agent/bolt-python": { { - Title: "Claude Agent SDK", + Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for Python - Support Agent Template")), Repository: "slack-samples/bolt-python-support-agent", Subdir: "claude-agent-sdk", }, { - Title: "OpenAI Agents SDK", + Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for Python - Support Agent Template")), Repository: "slack-samples/bolt-python-support-agent", Subdir: "openai-agents-sdk", }, { - Title: "Pydantic AI", + Title: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python - Support Agent Template")), Repository: "slack-samples/bolt-python-support-agent", Subdir: "pydantic-ai", }, }, "slack-cli#ai-apps/starter-agent/bolt-js": { { - Title: "Claude Agent SDK", + Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for JavaScript - Starter Agent Template")), Repository: "slack-samples/bolt-js-starter-agent", Subdir: "claude-agent-sdk", }, { - Title: "OpenAI Agents SDK", + Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for JavaScript - Starter Agent Template")), Repository: "slack-samples/bolt-js-starter-agent", Subdir: "openai-agents-sdk", }, }, "slack-cli#ai-apps/starter-agent/bolt-python": { { - Title: "Claude Agent SDK", + Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for Python - Starter Agent Template")), Repository: "slack-samples/bolt-python-starter-agent", Subdir: "claude-agent-sdk", }, { - Title: "OpenAI Agents SDK", + Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for Python - Starter Agent Template")), Repository: "slack-samples/bolt-python-starter-agent", Subdir: "openai-agents-sdk", }, { - Title: "Pydantic AI", + Title: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python - Starter Agent Template")), Repository: "slack-samples/bolt-python-starter-agent", Subdir: "pydantic-ai", }, From 897d8582bcef32358ae64b27d3f225a774d90873 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 9 Apr 2026 14:32:45 -0700 Subject: [PATCH 6/6] feat: breadcrumbs for the agent selection --- cmd/project/create_template.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cmd/project/create_template.go b/cmd/project/create_template.go index 29e619e8..622bcd39 100644 --- a/cmd/project/create_template.go +++ b/cmd/project/create_template.go @@ -76,21 +76,21 @@ func getFrameworkOptions(template string) []promptObject { frameworkPromptObjects := map[string][]promptObject{ "slack-cli#ai-apps/support-agent": { { - Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")), + Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Support Agent")), Repository: "slack-cli#ai-apps/support-agent/bolt-js", }, { - Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")), + Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Support Agent")), Repository: "slack-cli#ai-apps/support-agent/bolt-python", }, }, "slack-cli#ai-apps/starter-agent": { { - Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")), + Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Starter Agent")), Repository: "slack-cli#ai-apps/starter-agent/bolt-js", }, { - Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")), + Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Starter Agent")), Repository: "slack-cli#ai-apps/starter-agent/bolt-python", }, }, @@ -103,58 +103,58 @@ func getAdapterOptions(framework string) []promptObject { adapterPromptObjects := map[string][]promptObject{ "slack-cli#ai-apps/support-agent/bolt-js": { { - Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for JavaScript - Support Agent Template")), + Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for JavaScript - Support Agent")), Repository: "slack-samples/bolt-js-support-agent", Subdir: "claude-agent-sdk", }, { - Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for JavaScript - Support Agent Template")), + Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for JavaScript - Support Agent")), Repository: "slack-samples/bolt-js-support-agent", Subdir: "openai-agents-sdk", }, }, "slack-cli#ai-apps/support-agent/bolt-python": { { - Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for Python - Support Agent Template")), + Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for Python - Support Agent")), Repository: "slack-samples/bolt-python-support-agent", Subdir: "claude-agent-sdk", }, { - Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for Python - Support Agent Template")), + Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for Python - Support Agent")), Repository: "slack-samples/bolt-python-support-agent", Subdir: "openai-agents-sdk", }, { - Title: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python - Support Agent Template")), + Title: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python - Support Agent")), Repository: "slack-samples/bolt-python-support-agent", Subdir: "pydantic-ai", }, }, "slack-cli#ai-apps/starter-agent/bolt-js": { { - Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for JavaScript - Starter Agent Template")), + Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for JavaScript - Starter Agent")), Repository: "slack-samples/bolt-js-starter-agent", Subdir: "claude-agent-sdk", }, { - Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for JavaScript - Starter Agent Template")), + Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for JavaScript - Starter Agent")), Repository: "slack-samples/bolt-js-starter-agent", Subdir: "openai-agents-sdk", }, }, "slack-cli#ai-apps/starter-agent/bolt-python": { { - Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for Python - Starter Agent Template")), + Title: fmt.Sprintf("Claude Agent SDK %s", style.Secondary("Bolt for Python - Starter Agent")), Repository: "slack-samples/bolt-python-starter-agent", Subdir: "claude-agent-sdk", }, { - Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for Python - Starter Agent Template")), + Title: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for Python - Starter Agent")), Repository: "slack-samples/bolt-python-starter-agent", Subdir: "openai-agents-sdk", }, { - Title: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python - Starter Agent Template")), + Title: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python - Starter Agent")), Repository: "slack-samples/bolt-python-starter-agent", Subdir: "pydantic-ai", },