From 2a4b50279931926e6bd54e0c34397ff57ce903b8 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Wed, 4 Feb 2026 16:53:27 -0500 Subject: [PATCH 1/4] feat(cmd): add --name flag to disambiguate 'agent' shortcut --- cmd/project/create.go | 17 ++++++++-- cmd/project/create_test.go | 64 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/cmd/project/create.go b/cmd/project/create.go index 48d0763b..adf943ff 100644 --- a/cmd/project/create.go +++ b/cmd/project/create.go @@ -31,6 +31,7 @@ import ( // Flags var createTemplateURLFlag string var createGitBranchFlag string +var createAppNameFlag string // Handle to client's create function used for testing // TODO - Find best practice, such as using an Interface and Struct to create a client @@ -53,13 +54,17 @@ const viewMoreSamples = "slack-cli#view-more-samples" func NewCreateCommand(clients *shared.ClientFactory) *cobra.Command { cmd := &cobra.Command{ SuggestFor: []string{"new"}, - Use: "create [name] [flags]", + Use: "create [name | agent ] [flags]", Short: "Create a new Slack project", - Long: `Create a new Slack project on your local machine from an optional template`, + Long: `Create a new Slack project on your local machine from an optional template. + +The 'agent' argument is a shortcut to create an AI Agent app. If you want to +name your app 'agent' (not create an AI Agent), use the --name flag instead.`, Example: style.ExampleCommandsf([]style.ExampleCommand{ {Command: "create my-project", Meaning: "Create a new project from a template"}, {Command: "create agent my-agent-app", Meaning: "Create a new AI Agent app"}, {Command: "create my-project -t slack-samples/deno-hello-world", Meaning: "Start a new project from a specific template"}, + {Command: "create --name agent", Meaning: "Create a project named 'agent' (not an AI Agent)"}, }), Args: cobra.MaximumNArgs(2), RunE: func(cmd *cobra.Command, args []string) error { @@ -71,6 +76,7 @@ func NewCreateCommand(clients *shared.ClientFactory) *cobra.Command { // Add flags cmd.Flags().StringVarP(&createTemplateURLFlag, "template", "t", "", "template URL for your app") cmd.Flags().StringVarP(&createGitBranchFlag, "branch", "b", "", "name of git branch to checkout") + cmd.Flags().StringVarP(&createAppNameFlag, "name", "n", "", "name for your app (use this to name your app 'agent')") return cmd } @@ -85,6 +91,7 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args [] appNameArg := "" categoryShortcut := "" templateFlagProvided := cmd.Flags().Changed("template") + nameFlagProvided := cmd.Flags().Changed("name") if len(args) > 0 { switch args[0] { @@ -108,6 +115,12 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args [] } } + // --name flag overrides any positional app name argument + // This allows users to name their app "agent" without triggering the AI Agent shortcut + if nameFlagProvided { + appNameArg = createAppNameFlag + } + // Collect the template URL or select a starting template template, err := promptTemplateSelection(cmd, clients, categoryShortcut) if err != nil { diff --git a/cmd/project/create_test.go b/cmd/project/create_test.go index 202fd863..dfaa94d1 100644 --- a/cmd/project/create_test.go +++ b/cmd/project/create_test.go @@ -195,6 +195,70 @@ func TestCreateCommand(t *testing.T) { createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected) }, }, + "creates an app named agent using name flag without triggering shortcut": { + CmdArgs: []string{"--name", "agent"}, + Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) { + // Should prompt for category since agent shortcut is NOT triggered + cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select starter app + }, + nil, + ) + cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select Node.js + }, + nil, + ) + createClientMock = new(CreateClientMock) + createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil) + CreateFunc = createClientMock.Create + }, + ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { + template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-template") + require.NoError(t, err) + expected := create.CreateArgs{ + AppName: "agent", + Template: template, + } + createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected) + // Verify that category prompt WAS called (shortcut was not triggered) + cm.IO.AssertCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything) + }, + }, + "creates an agent app with name flag overriding positional arg": { + 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 language:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select Node.js + }, + nil, + ) + createClientMock = new(CreateClientMock) + createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil) + CreateFunc = createClientMock.Create + }, + ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { + template, err := create.ResolveTemplateURL("slack-samples/bolt-js-assistant-template") + require.NoError(t, err) + expected := create.CreateArgs{ + AppName: "my-custom-name", // --name flag overrides + Template: template, + } + createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected) + // Verify that category prompt was NOT called (shortcut was triggered) + cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything) + }, + }, }, func(cf *shared.ClientFactory) *cobra.Command { return NewCreateCommand(cf) }) From b6e5970c596b0320ffa16e8b1e637109763f91c6 Mon Sep 17 00:00:00 2001 From: Maria Alejandra <104795114+srtaalej@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:42:44 -0500 Subject: [PATCH 2/4] Update cmd/project/create.go Co-authored-by: Michael Brooks --- cmd/project/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/project/create.go b/cmd/project/create.go index adf943ff..e6ddf6f5 100644 --- a/cmd/project/create.go +++ b/cmd/project/create.go @@ -64,7 +64,7 @@ name your app 'agent' (not create an AI Agent), use the --name flag instead.`, {Command: "create my-project", Meaning: "Create a new project from a template"}, {Command: "create agent my-agent-app", Meaning: "Create a new AI Agent app"}, {Command: "create my-project -t slack-samples/deno-hello-world", Meaning: "Start a new project from a specific template"}, - {Command: "create --name agent", Meaning: "Create a project named 'agent' (not an AI Agent)"}, + {Command: "create --name my-project", Meaning: "Create a project named 'my-project'"}, }), Args: cobra.MaximumNArgs(2), RunE: func(cmd *cobra.Command, args []string) error { From f786eee97e4efc602a30288708fda997c0e24446 Mon Sep 17 00:00:00 2001 From: Maria Alejandra <104795114+srtaalej@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:43:08 -0500 Subject: [PATCH 3/4] Update cmd/project/create.go Co-authored-by: Michael Brooks --- cmd/project/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/project/create.go b/cmd/project/create.go index e6ddf6f5..03117528 100644 --- a/cmd/project/create.go +++ b/cmd/project/create.go @@ -76,7 +76,7 @@ name your app 'agent' (not create an AI Agent), use the --name flag instead.`, // Add flags cmd.Flags().StringVarP(&createTemplateURLFlag, "template", "t", "", "template URL for your app") cmd.Flags().StringVarP(&createGitBranchFlag, "branch", "b", "", "name of git branch to checkout") - cmd.Flags().StringVarP(&createAppNameFlag, "name", "n", "", "name for your app (use this to name your app 'agent')") + cmd.Flags().StringVarP(&createAppNameFlag, "name", "n", "", "name for your app (overrides the name argument)") return cmd } From 956e7d8ee7bf3b02b1ce882a996f120aae770307 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Thu, 5 Feb 2026 10:51:45 -0500 Subject: [PATCH 4/4] tests: add test cases for --name flag --- cmd/project/create_test.go | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/cmd/project/create_test.go b/cmd/project/create_test.go index dfaa94d1..a0f501a0 100644 --- a/cmd/project/create_test.go +++ b/cmd/project/create_test.go @@ -259,6 +259,67 @@ func TestCreateCommand(t *testing.T) { cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything) }, }, + "name flag overrides positional app name argument": { + CmdArgs: []string{"my-project", "--name", "my-name"}, + Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) { + cm.IO.On("SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select starter app + }, + nil, + ) + cm.IO.On("SelectPrompt", mock.Anything, "Select a language:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select Node.js + }, + nil, + ) + createClientMock = new(CreateClientMock) + createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil) + CreateFunc = createClientMock.Create + }, + ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { + template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-template") + require.NoError(t, err) + expected := create.CreateArgs{ + AppName: "my-name", // --name flag overrides "my-project" positional arg + Template: template, + } + createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected) + }, + }, + "name flag overrides positional app name argument with agent shortcut": { + 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 language:", mock.Anything, mock.Anything). + Return( + iostreams.SelectPromptResponse{ + Prompt: true, + Index: 0, // Select Node.js + }, + nil, + ) + createClientMock = new(CreateClientMock) + createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil) + CreateFunc = createClientMock.Create + }, + ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) { + template, err := create.ResolveTemplateURL("slack-samples/bolt-js-assistant-template") + require.NoError(t, err) + expected := create.CreateArgs{ + AppName: "my-name", // --name flag overrides "my-project" positional arg + Template: template, + } + createClientMock.AssertCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, expected) + // Verify that category prompt was NOT called (agent shortcut was triggered) + cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything) + }, + }, }, func(cf *shared.ClientFactory) *cobra.Command { return NewCreateCommand(cf) })