Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 18 additions & 78 deletions cmd/project/create_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"time"

"github.com/slackapi/slack-cli/internal/api"
"github.com/slackapi/slack-cli/internal/experiment"
"github.com/slackapi/slack-cli/internal/iostreams"
"github.com/slackapi/slack-cli/internal/pkg/create"
"github.com/slackapi/slack-cli/internal/shared"
Expand All @@ -32,53 +31,9 @@ import (
)

// getSelectionOptions returns the app template options for a given category.
func getSelectionOptions(clients *shared.ClientFactory, categoryID string) []promptObject {
if clients.Config.WithExperimentOn(experiment.Templates) {
templatePromptObjects := map[string]([]promptObject){
"slack-cli#getting-started": {
{
Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")),
Repository: "slack-samples/bolt-js-starter-template",
},
{
Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")),
Repository: "slack-samples/bolt-python-starter-template",
},
},
"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("Custom Agent %s", style.Secondary("Start from scratch")),
Repository: "slack-cli#ai-apps/custom-agent",
},
},
"slack-cli#automation-apps": {
{
Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")),
Repository: "slack-samples/bolt-js-custom-function-template",
},
{
Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")),
Repository: "slack-samples/bolt-python-custom-function-template",
},
{
Title: fmt.Sprintf("Deno Slack SDK %s", style.Secondary("Deno")),
Repository: "slack-samples/deno-starter-template",
},
},
}
return templatePromptObjects[categoryID]
}

if strings.TrimSpace(categoryID) == "" {
categoryID = "slack-cli#getting-started"
}

func getSelectionOptions(categoryID string) []promptObject {
templatePromptObjects := map[string]([]promptObject){
"slack-cli#getting-started": []promptObject{
"slack-cli#getting-started": {
{
Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")),
Repository: "slack-samples/bolt-js-starter-template",
Expand All @@ -88,6 +43,16 @@ func getSelectionOptions(clients *shared.ClientFactory, categoryID string) []pro
Repository: "slack-samples/bolt-python-starter-template",
},
},
"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("Custom Agent %s", style.Secondary("Start from scratch")),
Repository: "slack-cli#ai-apps/custom-agent",
},
},
"slack-cli#automation-apps": {
{
Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")),
Expand All @@ -102,18 +67,7 @@ func getSelectionOptions(clients *shared.ClientFactory, categoryID string) []pro
Repository: "slack-samples/deno-starter-template",
},
},
"slack-cli#ai-apps": {
{
Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")),
Repository: "slack-samples/bolt-js-assistant-template",
},
{
Title: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")),
Repository: "slack-samples/bolt-python-assistant-template",
},
},
}

return templatePromptObjects[categoryID]
}

Expand Down Expand Up @@ -243,14 +197,10 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory,

// Prompt for the example template
prompt := "Select a language:"
if clients.Config.WithExperimentOn(experiment.Templates) {
if categoryID == "slack-cli#ai-apps" {
prompt = "Select a template:"
} else {
prompt = "Select a language:"
}
if categoryID == "slack-cli#ai-apps" {
prompt = "Select a template:"
}
options := getSelectionOptions(clients, categoryID)
options := getSelectionOptions(categoryID)
titles := make([]string, len(options))
for i, m := range options {
titles[i] = m.Title
Expand Down Expand Up @@ -348,28 +298,18 @@ func listTemplates(ctx context.Context, clients *shared.ClientFactory, categoryS
}

var categories []categoryInfo
if categoryShortcut == "agent" && clients.Config.WithExperimentOn(experiment.Templates) {
if categoryShortcut == "agent" {
categories = []categoryInfo{
{id: "slack-cli#ai-apps/support-agent", name: "Support agent"},
{id: "slack-cli#ai-apps/custom-agent", name: "Custom agent"},
}
} else if categoryShortcut == "agent" {
categories = []categoryInfo{
{id: "slack-cli#ai-apps", name: "AI Agent apps"},
}
} else if clients.Config.WithExperimentOn(experiment.Templates) {
} 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/custom-agent", name: "Custom agent"},
{id: "slack-cli#automation-apps", name: "Automation apps"},
}
} else {
categories = []categoryInfo{
{id: "slack-cli#getting-started", name: "Getting started"},
{id: "slack-cli#ai-apps", name: "AI Agent apps"},
{id: "slack-cli#automation-apps", name: "Automation apps"},
}
}

for _, category := range categories {
Expand All @@ -383,7 +323,7 @@ func listTemplates(ctx context.Context, clients *shared.ClientFactory, categoryS
secondary = append(secondary, repo)
}
} else {
for _, tmpl := range getSelectionOptions(clients, category.id) {
for _, tmpl := range getSelectionOptions(category.id) {
secondary = append(secondary, tmpl.Repository)
}
}
Expand Down
89 changes: 36 additions & 53 deletions cmd/project/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,15 @@ func TestCreateCommand(t *testing.T) {
CmdArgs: []string{"agent"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
cm.IO.On("IsTTY").Return(true)
// Should skip category prompt and go directly to language selection
// Should skip category prompt and go directly to template selection
cm.IO.On("SelectPrompt", mock.Anything, "Select a template:", mock.Anything, mock.Anything).
Return(
iostreams.SelectPromptResponse{
Prompt: true,
Index: 1, // Select Custom Agent
},
nil,
)
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
Return(
iostreams.SelectPromptResponse{
Expand Down Expand Up @@ -151,7 +159,15 @@ func TestCreateCommand(t *testing.T) {
"creates an agent app with app name using agent argument": {
CmdArgs: []string{"agent", "my-agent-app"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
// Should skip category prompt and go directly to language selection
// Should skip category prompt and go directly to template selection
cm.IO.On("SelectPrompt", mock.Anything, "Select a template:", mock.Anything, mock.Anything).
Return(
iostreams.SelectPromptResponse{
Prompt: true,
Index: 1, // Select Custom Agent
},
nil,
)
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
Return(
iostreams.SelectPromptResponse{
Expand All @@ -178,12 +194,9 @@ func TestCreateCommand(t *testing.T) {
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
},
},
"creates a pydantic ai agent app with templates experiment": {
"creates a pydantic ai agent app": {
CmdArgs: []string{"my-pydantic-app"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = append(cm.Config.ExperimentsFlag, "templates")
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", 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).
Expand Down Expand Up @@ -283,6 +296,14 @@ func TestCreateCommand(t *testing.T) {
CmdArgs: []string{"agent", "--name", "my-custom-name"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
// Should skip category prompt due to agent shortcut
cm.IO.On("SelectPrompt", mock.Anything, "Select a template:", mock.Anything, mock.Anything).
Return(
iostreams.SelectPromptResponse{
Prompt: true,
Index: 1, // Select Custom Agent
},
nil,
)
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
Return(
iostreams.SelectPromptResponse{
Expand Down Expand Up @@ -346,6 +367,14 @@ func TestCreateCommand(t *testing.T) {
CmdArgs: []string{"agent", "my-project", "--name", "my-name"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
// Should skip category prompt due to agent shortcut
cm.IO.On("SelectPrompt", mock.Anything, "Select a template:", mock.Anything, mock.Anything).
Return(
iostreams.SelectPromptResponse{
Prompt: true,
Index: 1, // Select Custom Agent
},
nil,
)
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
Return(
iostreams.SelectPromptResponse{
Expand Down Expand Up @@ -567,49 +596,6 @@ func TestCreateCommand(t *testing.T) {
createClientMock = new(CreateClientMock)
CreateFunc = createClientMock.Create
},
ExpectedOutputs: []string{
"Getting started",
"AI Agent apps",
"Automation apps",
"slack-samples/bolt-js-starter-template",
"slack-samples/bolt-python-starter-template",
"slack-samples/bolt-js-assistant-template",
"slack-samples/bolt-python-assistant-template",
"slack-samples/bolt-js-custom-function-template",
"slack-samples/bolt-python-custom-function-template",
"slack-samples/deno-starter-template",
},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything)
},
},
"lists agent templates with agent --list flag": {
CmdArgs: []string{"agent", "--list"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
createClientMock = new(CreateClientMock)
CreateFunc = createClientMock.Create
},
ExpectedOutputs: []string{
"AI Agent apps",
"slack-samples/bolt-js-assistant-template",
"slack-samples/bolt-python-assistant-template",
},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything)
output := cm.GetCombinedOutput()
assert.NotContains(t, output, "Getting started")
assert.NotContains(t, output, "Automation apps")
},
},
"lists all templates with --list flag and templates experiment": {
CmdArgs: []string{"--list"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = append(cm.Config.ExperimentsFlag, "templates")
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
createClientMock = new(CreateClientMock)
CreateFunc = createClientMock.Create
},
ExpectedOutputs: []string{
"Getting started",
"slack-samples/bolt-js-starter-template",
Expand All @@ -630,12 +616,9 @@ func TestCreateCommand(t *testing.T) {
createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything)
},
},
"lists agent templates with agent --list flag and templates experiment": {
"lists agent templates with agent --list flag": {
CmdArgs: []string{"agent", "--list"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
cm.AddDefaultMocks()
cm.Config.ExperimentsFlag = append(cm.Config.ExperimentsFlag, "templates")
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
createClientMock = new(CreateClientMock)
CreateFunc = createClientMock.Create
},
Expand Down
1 change: 0 additions & 1 deletion docs/reference/experiments.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ The following is a list of currently available experiments. We'll remove experim

- `lipgloss`: shows pretty styles.
- `sandboxes`: enables users who have joined the Slack Developer Program to manage their sandboxes ([PR#379](https://github.com/slackapi/slack-cli/pull/379)).
- `templates`: brings more agent templates to the `create` command.

## Experiments changelog

Expand Down
4 changes: 0 additions & 4 deletions internal/experiment/experiment.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ const (

// Sandboxes experiment lets users who have joined the Slack Developer Program use the CLI to manage their sandboxes.
Sandboxes Experiment = "sandboxes"

// Templates experiment brings more agent templates to the create command.
Templates Experiment = "templates"
)

// AllExperiments is a list of all available experiments that can be enabled
Expand All @@ -49,7 +46,6 @@ var AllExperiments = []Experiment{
Lipgloss,
Placeholder,
Sandboxes,
Templates,
}

// EnabledExperiments is a list of experiments that are permanently enabled
Expand Down
Loading