Skip to content

Commit 7b610e5

Browse files
authored
Merge branch 'main' into ale-feat-app-naming
2 parents 5ed7e88 + 113aa42 commit 7b610e5

11 files changed

Lines changed: 254 additions & 36 deletions

cmd/platform/deploy.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -316,16 +316,13 @@ func printDeployHostingCompletion(clients *shared.ClientFactory, cmd *cobra.Comm
316316
func errorMissingDeployHook(clients *shared.ClientFactory) error {
317317
if !clients.SDKConfig.Hooks.Deploy.IsAvailable() {
318318
return slackerror.New(slackerror.ErrSDKHookNotFound).
319-
WithMessage("Missing the `deploy` hook from the `%s` file", config.GetProjectHooksJSONFilePath()).
319+
WithMessage("No deploy script found").
320320
WithRemediation("%s", strings.Join([]string{
321-
"Provide a command or script to run with the deploy command by adding a new hook.",
321+
"For deployment options, see:",
322+
" https://docs.slack.dev/tools/slack-cli/reference/hooks/#deploy",
322323
"",
323-
fmt.Sprintf("Example `%s` `deploy` hook:", config.GetProjectHooksJSONFilePath()),
324-
"{",
325-
` "hooks": {`,
326-
` "deploy": "./deploy.sh"`,
327-
" }",
328-
"}",
324+
"To start a local development server, use:",
325+
fmt.Sprintf(" %s", style.Commandf("run", false)),
329326
}, "\n"))
330327
}
331328
return nil

cmd/platform/deploy_test.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,14 @@ func TestDeployCommand(t *testing.T) {
111111

112112
func TestDeployCommand_HasValidDeploymentMethod(t *testing.T) {
113113
tests := map[string]struct {
114-
app types.App
115-
manifest types.SlackYaml
116-
manifestError error
117-
manifestSource config.ManifestSource
118-
deployScript string
119-
expectedError error
114+
app types.App
115+
manifest types.SlackYaml
116+
manifestError error
117+
manifestSource config.ManifestSource
118+
deployScript string
119+
expectedError error
120+
expectedMessage string
121+
expectedRemediation []string
120122
}{
121123
"fails when no manifest exists": {
122124
manifestError: slackerror.New(slackerror.ErrInvalidManifest),
@@ -142,8 +144,10 @@ func TestDeployCommand_HasValidDeploymentMethod(t *testing.T) {
142144
deployScript: "sleep 4",
143145
},
144146
"fails if no deploy hook is provided": {
145-
manifestSource: config.ManifestSourceLocal,
146-
expectedError: slackerror.New(slackerror.ErrSDKHookNotFound),
147+
manifestSource: config.ManifestSourceLocal,
148+
expectedError: slackerror.New(slackerror.ErrSDKHookNotFound),
149+
expectedMessage: "No deploy script found",
150+
expectedRemediation: []string{"https://docs.slack.dev/tools/slack-cli/reference/hooks/#deploy", "run"},
147151
},
148152
"succeeds if the app exists and the manifest source is remote": {
149153
app: types.App{
@@ -183,11 +187,14 @@ func TestDeployCommand_HasValidDeploymentMethod(t *testing.T) {
183187
err := hasValidDeploymentMethod(ctx, clients, app, types.SlackAuth{})
184188
if tc.expectedError != nil {
185189
require.Error(t, err)
186-
assert.Equal(
187-
t,
188-
slackerror.ToSlackError(tc.expectedError).Code,
189-
slackerror.ToSlackError(err).Code,
190-
)
190+
slackErr := slackerror.ToSlackError(err)
191+
assert.Equal(t, slackerror.ToSlackError(tc.expectedError).Code, slackErr.Code)
192+
if tc.expectedMessage != "" {
193+
assert.Contains(t, slackErr.Message, tc.expectedMessage)
194+
}
195+
for _, r := range tc.expectedRemediation {
196+
assert.Contains(t, slackErr.Remediation, r)
197+
}
191198
} else {
192199
require.NoError(t, err)
193200
}

cmd/project/create.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
var createTemplateURLFlag string
3636
var createGitBranchFlag string
3737
var createAppNameFlag string
38+
var createListFlag bool
3839

3940
// Handle to client's create function used for testing
4041
// TODO - Find best practice, such as using an Interface and Struct to create a client
@@ -80,6 +81,7 @@ name your app 'agent' (not create an AI Agent), use the --name flag instead.`,
8081
cmd.Flags().StringVarP(&createTemplateURLFlag, "template", "t", "", "template URL for your app")
8182
cmd.Flags().StringVarP(&createGitBranchFlag, "branch", "b", "", "name of git branch to checkout")
8283
cmd.Flags().StringVarP(&createAppNameFlag, "name", "n", "", "name for your app (overrides the name argument)")
84+
cmd.Flags().BoolVar(&createListFlag, "list", false, "list available app templates")
8385

8486
return cmd
8587
}
@@ -124,6 +126,11 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args []
124126
appNameArg = createAppNameFlag
125127
}
126128

129+
// List templates and exit early if the --list flag is set
130+
if createListFlag {
131+
return listTemplates(ctx, clients, categoryShortcut)
132+
}
133+
127134
// Collect the template URL or select a starting template
128135
template, err := promptTemplateSelection(cmd, clients, categoryShortcut)
129136
if err != nil {

cmd/project/create_template.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package project
1616

1717
import (
18+
"context"
1819
"fmt"
1920
"strings"
2021
"time"
@@ -240,6 +241,42 @@ func confirmExternalTemplateSelection(cmd *cobra.Command, clients *shared.Client
240241
return true, nil
241242
}
242243

244+
// listTemplates prints available templates for the create command
245+
func listTemplates(ctx context.Context, clients *shared.ClientFactory, categoryShortcut string) error {
246+
type categoryInfo struct {
247+
id string
248+
name string
249+
}
250+
251+
var categories []categoryInfo
252+
if categoryShortcut == "agent" {
253+
categories = []categoryInfo{
254+
{id: "slack-cli#ai-apps", name: "AI Agent apps"},
255+
}
256+
} else {
257+
categories = []categoryInfo{
258+
{id: "slack-cli#getting-started", name: "Getting started"},
259+
{id: "slack-cli#ai-apps", name: "AI Agent apps"},
260+
{id: "slack-cli#automation-apps", name: "Automation apps"},
261+
}
262+
}
263+
264+
for _, category := range categories {
265+
templates := getSelectionOptions(clients, category.id)
266+
secondary := make([]string, len(templates))
267+
for i, tmpl := range templates {
268+
secondary[i] = tmpl.Repository
269+
}
270+
clients.IO.PrintInfo(ctx, false, style.Sectionf(style.TextSection{
271+
Emoji: "house_buildings",
272+
Text: style.Bold(category.name),
273+
Secondary: secondary,
274+
}))
275+
}
276+
277+
return nil
278+
}
279+
243280
// getSelectionTemplate returns a custom formatted template used for selecting a
244281
// project template during creation
245282
func getSelectionTemplate(clients *shared.ClientFactory) string {

cmd/project/create_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,46 @@ func TestCreateCommand(t *testing.T) {
414414
cm.IO.AssertNotCalled(t, "InputPrompt", mock.Anything, "Name your app:", mock.Anything)
415415
},
416416
},
417+
"lists all templates with --list flag": {
418+
CmdArgs: []string{"--list"},
419+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
420+
createClientMock = new(CreateClientMock)
421+
CreateFunc = createClientMock.Create
422+
},
423+
ExpectedOutputs: []string{
424+
"Getting started",
425+
"AI Agent apps",
426+
"Automation apps",
427+
"slack-samples/bolt-js-starter-template",
428+
"slack-samples/bolt-python-starter-template",
429+
"slack-samples/bolt-js-assistant-template",
430+
"slack-samples/bolt-python-assistant-template",
431+
"slack-samples/bolt-js-custom-function-template",
432+
"slack-samples/bolt-python-custom-function-template",
433+
"slack-samples/deno-starter-template",
434+
},
435+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
436+
createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
437+
},
438+
},
439+
"lists agent templates with agent --list flag": {
440+
CmdArgs: []string{"agent", "--list"},
441+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
442+
createClientMock = new(CreateClientMock)
443+
CreateFunc = createClientMock.Create
444+
},
445+
ExpectedOutputs: []string{
446+
"AI Agent apps",
447+
"slack-samples/bolt-js-assistant-template",
448+
"slack-samples/bolt-python-assistant-template",
449+
},
450+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
451+
createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
452+
output := cm.GetCombinedOutput()
453+
assert.NotContains(t, output, "Getting started")
454+
assert.NotContains(t, output, "Automation apps")
455+
},
456+
},
417457
}, func(cf *shared.ClientFactory) *cobra.Command {
418458
return NewCreateCommand(cf)
419459
})

docs/guides/installing-the-slack-cli-for-mac-and-linux.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ Manual installation allows you to customize certain paths used when installing t
9999

100100
**2\. Download the** `slack` **CLI installer for your environment.**
101101

102-
🍎 ⚡️ [**Download for macOS Apple Silicon (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.12.0_macOS_arm64.tar.gz)
102+
🍎 ⚡️ [**Download for macOS Apple Silicon (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.13.0_macOS_arm64.tar.gz)
103103

104-
🍏 🪨 [**Download for macOS Intel (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.12.0_macOS_amd64.tar.gz)
104+
🍏 🪨 [**Download for macOS Intel (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.13.0_macOS_amd64.tar.gz)
105105

106-
🐧 💾 [**Download for Linux (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.12.0_linux_64-bit.tar.gz)
106+
🐧 💾 [**Download for Linux (.tar.gz)**](https://downloads.slack-edge.com/slack-cli/slack_cli_3.13.0_linux_64-bit.tar.gz)
107107

108108
**3\. Add the** `slack` **CLI to your path.**
109109

@@ -121,7 +121,7 @@ We recommend using an alias if another `slack` binary exists. To do this, change
121121

122122
```sh
123123
$ slack version
124-
Using slack v3.12.0
124+
Using slack v3.13.0
125125
```
126126

127127
</TabItem>

docs/guides/installing-the-slack-cli-for-windows.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Manual installation allows you to customize certain paths used when installing t
8888

8989
**2\. Download the** `slack` **CLI installer for your environment.**
9090

91-
<ts-icon class="ts_icon_windows"></ts-icon> &nbsp; <a href="https://downloads.slack-edge.com/slack-cli/slack_cli_3.12.0_windows_64-bit.zip"><strong>Windows (.zip)</strong></a>
91+
<ts-icon class="ts_icon_windows"></ts-icon> &nbsp; <a href="https://downloads.slack-edge.com/slack-cli/slack_cli_3.13.0_windows_64-bit.zip"><strong>Windows (.zip)</strong></a>
9292

9393
**3\. Add the** `slack` **CLI to your path.**
9494

@@ -104,7 +104,7 @@ We recommend using an alias if another `slack` binary exists. To do this, change
104104

105105
```pwsh
106106
$ slack version
107-
Using slack v3.12.0
107+
Using slack v3.13.0
108108
```
109109

110110
</TabItem>

docs/reference/commands/slack_create.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ Create a new Slack project
44

55
## Description
66

7-
Create a new Slack project on your local machine from an optional template
7+
Create a new Slack project on your local machine from an optional template.
8+
9+
The 'agent' argument is a shortcut to create an AI Agent app. If you want to
10+
name your app 'agent' (not create an AI Agent), use the --name flag instead.
811

912
```
10-
slack create [name] [flags]
13+
slack create [name | agent <name>] [flags]
1114
```
1215

1316
## Flags
1417

1518
```
1619
-b, --branch string name of git branch to checkout
1720
-h, --help help for create
21+
--list list available app templates
22+
-n, --name string name for your app (overrides the name argument)
1823
-t, --template string template URL for your app
1924
```
2025

@@ -38,8 +43,14 @@ slack create [name] [flags]
3843
# Create a new project from a template
3944
$ slack create my-project
4045
46+
# Create a new AI Agent app
47+
$ slack create agent my-agent-app
48+
4149
# Start a new project from a specific template
4250
$ slack create my-project -t slack-samples/deno-hello-world
51+
52+
# Create a project named 'my-project'
53+
$ slack create --name my-project
4354
```
4455

4556
## See also

docs/reference/commands/slack_project_create.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ Create a new Slack project
44

55
## Description
66

7-
Create a new Slack project on your local machine from an optional template
7+
Create a new Slack project on your local machine from an optional template.
8+
9+
The 'agent' argument is a shortcut to create an AI Agent app. If you want to
10+
name your app 'agent' (not create an AI Agent), use the --name flag instead.
811

912
```
10-
slack project create [name] [flags]
13+
slack project create [name | agent <name>] [flags]
1114
```
1215

1316
## Flags
1417

1518
```
1619
-b, --branch string name of git branch to checkout
1720
-h, --help help for create
21+
--list list available app templates
22+
-n, --name string name for your app (overrides the name argument)
1823
-t, --template string template URL for your app
1924
```
2025

@@ -38,8 +43,14 @@ slack project create [name] [flags]
3843
# Create a new project from a template
3944
$ slack create my-project
4045
46+
# Create a new AI Agent app
47+
$ slack create agent my-agent-app
48+
4149
# Start a new project from a specific template
4250
$ slack create my-project -t slack-samples/deno-hello-world
51+
52+
# Create a project named 'my-project'
53+
$ slack create --name my-project
4354
```
4455

4556
## See also

internal/prompts/app_select.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,21 @@ func getAuths(ctx context.Context, clients *shared.ClientFactory) ([]types.Slack
245245
}
246246
}
247247
if len(allAuths) == 0 {
248-
auth := types.SlackAuth{}
249-
err := validateAuth(ctx, clients, &auth)
248+
// No workspaces connected - prompt user to login if interactive
249+
if !clients.IO.IsTTY() {
250+
return nil, slackerror.New(slackerror.ErrNotAuthed).
251+
WithMessage("No workspaces connected").
252+
WithRemediation("Run %s to sign in to a workspace", style.Commandf("login", false))
253+
}
254+
clients.IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{
255+
Emoji: "wave",
256+
Text: "No workspaces connected. Sign in to get started.",
257+
}))
258+
newAuth, _, err := authpkg.LoginWithClients(ctx, clients, "", false)
250259
if err != nil {
251-
return nil, slackerror.New(slackerror.ErrNotAuthed)
260+
return nil, slackerror.New(slackerror.ErrNotAuthed).WithRootCause(err)
252261
}
253-
allAuths = append(allAuths, auth)
262+
allAuths = append(allAuths, newAuth)
254263
}
255264
return allAuths, nil
256265
}

0 commit comments

Comments
 (0)