Skip to content

Commit f2a985b

Browse files
committed
feat: agent argument shortcut to slack create command
1 parent 3f37a5a commit f2a985b

3 files changed

Lines changed: 150 additions & 35 deletions

File tree

cmd/project/create.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,10 @@ func NewCreateCommand(clients *shared.ClientFactory) *cobra.Command {
5858
Long: `Create a new Slack project on your local machine from an optional template`,
5959
Example: style.ExampleCommandsf([]style.ExampleCommand{
6060
{Command: "create my-project", Meaning: "Create a new project from a template"},
61+
{Command: "create agent my-agent-app", Meaning: "Create a new AI agent app"},
6162
{Command: "create my-project -t slack-samples/deno-hello-world", Meaning: "Start a new project from a specific template"},
6263
}),
63-
Args: cobra.MaximumNArgs(1),
64+
Args: cobra.MaximumNArgs(2),
6465
RunE: func(cmd *cobra.Command, args []string) error {
6566
clients.Config.SetFlags(cmd)
6667
return runCreateCommand(clients, cmd, args)
@@ -80,14 +81,35 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args []
8081
// Set up event logger
8182
log := newCreateLogger(clients, cmd)
8283

83-
// Get optional app name passed as an arg
84+
// Get optional app name passed as an arg and check for category shortcuts
8485
appNameArg := ""
85-
if len(args) > 0 && args[0] != "samples" && args[0] != "create" {
86-
appNameArg = args[0]
86+
categoryShortcut := ""
87+
templateFlagProvided := cmd.Flags().Changed("template")
88+
89+
if len(args) > 0 {
90+
switch args[0] {
91+
case "samples", "create":
92+
// These are special commands, not app names
93+
case "agent":
94+
// Only treat as shortcut if --template flag is not provided
95+
if !templateFlagProvided {
96+
// Shortcut to AI apps category
97+
categoryShortcut = "agent"
98+
// Check if a second argument was provided as the app name
99+
if len(args) > 1 {
100+
appNameArg = args[1]
101+
}
102+
} else {
103+
// When --template is provided, "agent" is the app name
104+
appNameArg = args[0]
105+
}
106+
default:
107+
appNameArg = args[0]
108+
}
87109
}
88110

89111
// Collect the template URL or select a starting template
90-
template, err := promptTemplateSelection(cmd, clients)
112+
template, err := promptTemplateSelection(cmd, clients, categoryShortcut)
91113
if err != nil {
92114
return err
93115
}

cmd/project/create_template.go

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -97,43 +97,48 @@ func getSelectionOptionsForCategory(clients *shared.ClientFactory) []promptObjec
9797
}
9898

9999
// promptTemplateSelection prompts the user to select a project template
100-
func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory) (create.Template, error) {
100+
func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory, categoryShortcut string) (create.Template, error) {
101101
ctx := cmd.Context()
102102
var categoryID string
103103
var selectedTemplate string
104104

105-
// Prompt for the category
106-
promptForCategory := "Select an app:"
107-
optionsForCategory := getSelectionOptionsForCategory(clients)
108-
titlesForCategory := make([]string, len(optionsForCategory))
109-
for i, m := range optionsForCategory {
110-
titlesForCategory[i] = m.Title
111-
}
112-
templateForCategory := getSelectionTemplate(clients)
105+
// Check if a category shortcut was provided
106+
if categoryShortcut == "agent" {
107+
categoryID = "slack-cli#ai-apps"
108+
} else {
109+
// Prompt for the category
110+
promptForCategory := "Select an app:"
111+
optionsForCategory := getSelectionOptionsForCategory(clients)
112+
titlesForCategory := make([]string, len(optionsForCategory))
113+
for i, m := range optionsForCategory {
114+
titlesForCategory[i] = m.Title
115+
}
116+
templateForCategory := getSelectionTemplate(clients)
113117

114-
// Print a trace with info about the category title options provided by CLI
115-
clients.IO.PrintTrace(ctx, slacktrace.CreateCategoryOptions, strings.Join(titlesForCategory, ", "))
118+
// Print a trace with info about the category title options provided by CLI
119+
clients.IO.PrintTrace(ctx, slacktrace.CreateCategoryOptions, strings.Join(titlesForCategory, ", "))
116120

117-
// Prompt to choose a category
118-
selection, err := clients.IO.SelectPrompt(ctx, promptForCategory, titlesForCategory, iostreams.SelectPromptConfig{
119-
Description: func(value string, index int) string {
120-
return optionsForCategory[index].Description
121-
},
122-
Flag: clients.Config.Flags.Lookup("template"),
123-
Required: true,
124-
Template: templateForCategory,
125-
})
126-
if err != nil {
127-
return create.Template{}, slackerror.ToSlackError(err)
128-
} else if selection.Flag {
129-
selectedTemplate = selection.Option
130-
} else if selection.Prompt {
131-
categoryID = optionsForCategory[selection.Index].Repository
132-
}
121+
// Prompt to choose a category
122+
selection, err := clients.IO.SelectPrompt(ctx, promptForCategory, titlesForCategory, iostreams.SelectPromptConfig{
123+
Description: func(value string, index int) string {
124+
return optionsForCategory[index].Description
125+
},
126+
Flag: clients.Config.Flags.Lookup("template"),
127+
Required: true,
128+
Template: templateForCategory,
129+
})
130+
if err != nil {
131+
return create.Template{}, slackerror.ToSlackError(err)
132+
} else if selection.Flag {
133+
selectedTemplate = selection.Option
134+
} else if selection.Prompt {
135+
categoryID = optionsForCategory[selection.Index].Repository
136+
}
133137

134-
// Set template to view more samples, so the sample prompt is triggered
135-
if categoryID == viewMoreSamples {
136-
selectedTemplate = viewMoreSamples
138+
// Set template to view more samples, so the sample prompt is triggered
139+
if categoryID == viewMoreSamples {
140+
selectedTemplate = viewMoreSamples
141+
}
137142
}
138143

139144
// Prompt for the template

cmd/project/create_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,94 @@ func TestCreateCommand(t *testing.T) {
107107
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
108108
},
109109
},
110+
"creates an agent app using agent argument shortcut": {
111+
CmdArgs: []string{"agent"},
112+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
113+
// Should skip category prompt and go directly to language selection
114+
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
115+
Return(
116+
iostreams.SelectPromptResponse{
117+
Prompt: true,
118+
Index: 0, // Select Node.js template
119+
},
120+
nil,
121+
)
122+
createClientMock = new(CreateClientMock)
123+
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
124+
CreateFunc = createClientMock.Create
125+
},
126+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
127+
template, err := create.ResolveTemplateURL("slack-samples/bolt-js-assistant-template")
128+
require.NoError(t, err)
129+
expected := create.CreateArgs{
130+
Template: template,
131+
}
132+
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
133+
// Verify that category prompt was NOT called
134+
cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything)
135+
},
136+
},
137+
"creates an agent app with app name using agent argument": {
138+
CmdArgs: []string{"agent", "my-agent-app"},
139+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
140+
// Should skip category prompt and go directly to language selection
141+
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
142+
Return(
143+
iostreams.SelectPromptResponse{
144+
Prompt: true,
145+
Index: 1, // Select Python template
146+
},
147+
nil,
148+
)
149+
createClientMock = new(CreateClientMock)
150+
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
151+
CreateFunc = createClientMock.Create
152+
},
153+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
154+
template, err := create.ResolveTemplateURL("slack-samples/bolt-python-assistant-template")
155+
require.NoError(t, err)
156+
expected := create.CreateArgs{
157+
AppName: "my-agent-app",
158+
Template: template,
159+
}
160+
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
161+
// Verify that category prompt was NOT called
162+
cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything)
163+
},
164+
},
165+
"creates an app named agent when template flag is provided": {
166+
CmdArgs: []string{"agent", "--template", "slack-samples/bolt-js-starter-template"},
167+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
168+
cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything).
169+
Return(
170+
iostreams.SelectPromptResponse{
171+
Flag: true,
172+
Option: "slack-samples/bolt-js-starter-template",
173+
},
174+
nil,
175+
)
176+
cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything).
177+
Return(
178+
iostreams.SelectPromptResponse{
179+
Flag: true,
180+
Option: "slack-samples/bolt-js-starter-template",
181+
},
182+
nil,
183+
)
184+
createClientMock = new(CreateClientMock)
185+
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
186+
CreateFunc = createClientMock.Create
187+
},
188+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
189+
template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-template")
190+
require.NoError(t, err)
191+
expected := create.CreateArgs{
192+
AppName: "agent",
193+
Template: template,
194+
}
195+
createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected)
196+
},
197+
},
110198
}, func(cf *shared.ClientFactory) *cobra.Command {
111199
return NewCreateCommand(cf)
112200
})

0 commit comments

Comments
 (0)