Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
158 changes: 127 additions & 31 deletions cmd/project/create_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
{
Expand All @@ -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")),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: We originally brainstormed having the Secondary display the breadcrumb of the previous selection. I'm just curious why you decided to not include that?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mwbrooks Forgive this was omitted until comments below and e82f2a9! 🙏 ✨

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zimeg Thanks, this is much better! 🙇🏻

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 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: 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: 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: 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: fmt.Sprintf("OpenAI Agents SDK %s", style.Secondary("Bolt for Python")),
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: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python")),
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": {
"slack-cli#ai-apps/starter-agent/bolt-js": {
{
Title: fmt.Sprintf("Bolt for JavaScript %s", style.Secondary("Node.js")),
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: fmt.Sprintf("Bolt for Python %s", style.Secondary("Python")),
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: 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: 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: fmt.Sprintf("Pydantic AI %s", style.Secondary("Bolt for Python - Starter Agent Template")),
Repository: "slack-samples/bolt-python-starter-agent",
Subdir: "pydantic-ai",
},
},
}
return frameworkPromptObjects[template]
return adapterPromptObjects[framework]
}

// getSelectionOptionsForCategory returns the top-level category options for
Expand Down Expand Up @@ -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 Bolt 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 agent framework:", 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
}
Expand Down Expand Up @@ -300,27 +390,33 @@ 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"},
}
}

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) {
Expand Down
Loading
Loading