@cursor/july 0.1.52 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/AGENTS.md +1 -0
  2. package/README.md +4 -2
  3. package/dist/bin/agent-serve.js +2 -0
  4. package/dist/channels/slack/init.d.ts +16 -3
  5. package/dist/channels/slack/init.d.ts.map +1 -1
  6. package/dist/channels/slack/init.js +92 -18
  7. package/dist/channels/slack/manifest.d.ts +8 -0
  8. package/dist/channels/slack/manifest.d.ts.map +1 -1
  9. package/dist/channels/slack/manifest.js +17 -0
  10. package/dist/channels/slack/setup.d.ts +26 -1
  11. package/dist/channels/slack/setup.d.ts.map +1 -1
  12. package/dist/channels/slack/setup.js +107 -25
  13. package/dist/docs/404.html +3 -3
  14. package/dist/docs/ab.html +6 -6
  15. package/dist/docs/assets/{app.Cbeq6FOy.js → app.DAKcDclS.js} +1 -1
  16. package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +1 -0
  17. package/dist/docs/assets/chunks/{VPLocalSearchBox.Bmb8QjCI.js → VPLocalSearchBox.BNIaCSfG.js} +2 -2
  18. package/dist/docs/assets/chunks/{theme.BLNMeCRL.js → theme.B47GzJjc.js} +2 -2
  19. package/dist/docs/assets/{guides_slack.md.D2nTRu9M.js → guides_slack.md.DP4H75WP.js} +8 -6
  20. package/dist/docs/assets/guides_slack.md.DP4H75WP.lean.js +1 -0
  21. package/dist/docs/assets/{reference_cli.md.CGKfz_Yj.js → reference_cli.md.-V6J7Cl8.js} +7 -3
  22. package/dist/docs/assets/{reference_cli.md.CGKfz_Yj.lean.js → reference_cli.md.-V6J7Cl8.lean.js} +1 -1
  23. package/dist/docs/assets/{style.CK3hwt6q.css → style.kTsvp4pE.css} +1 -1
  24. package/dist/docs/assets/{troubleshooting.md.C6n3wCr6.js → troubleshooting.md.CuVztCKs.js} +1 -1
  25. package/dist/docs/building-with-agents.html +6 -6
  26. package/dist/docs/concepts.html +6 -6
  27. package/dist/docs/deployment.html +6 -6
  28. package/dist/docs/evals.html +6 -6
  29. package/dist/docs/example-agents/approval-buddy.html +6 -6
  30. package/dist/docs/example-agents/benny.html +6 -6
  31. package/dist/docs/example-agents/bugbot.html +6 -6
  32. package/dist/docs/example-agents/codebase-wiki.html +6 -6
  33. package/dist/docs/example-agents/codeowners-review.html +6 -6
  34. package/dist/docs/example-agents/concierge.html +6 -6
  35. package/dist/docs/example-agents/fsd.html +6 -6
  36. package/dist/docs/example-agents/index.html +6 -6
  37. package/dist/docs/example-agents/knowledge-base.html +6 -6
  38. package/dist/docs/example-agents/oncall.html +6 -6
  39. package/dist/docs/example-agents/security-reviewer.html +6 -6
  40. package/dist/docs/example-agents/slack-agent.html +6 -6
  41. package/dist/docs/example-agents/weather-agent.html +6 -6
  42. package/dist/docs/guides/agent-to-agent.html +6 -6
  43. package/dist/docs/guides/cloud-runtime.html +6 -6
  44. package/dist/docs/guides/github.html +6 -6
  45. package/dist/docs/guides/human-in-the-loop.html +6 -6
  46. package/dist/docs/guides/mcp-oauth.html +6 -6
  47. package/dist/docs/guides/opentelemetry.html +6 -6
  48. package/dist/docs/guides/slack.html +14 -12
  49. package/dist/docs/guides/webhooks.html +6 -6
  50. package/dist/docs/hashmap.json +1 -1
  51. package/dist/docs/hillclimbing.html +6 -6
  52. package/dist/docs/index.html +6 -6
  53. package/dist/docs/quickstart.html +6 -6
  54. package/dist/docs/reference/agent-config.html +6 -6
  55. package/dist/docs/reference/artifacts.html +6 -6
  56. package/dist/docs/reference/channels.html +6 -6
  57. package/dist/docs/reference/cli.html +13 -9
  58. package/dist/docs/reference/connections.html +6 -6
  59. package/dist/docs/reference/hooks.html +6 -6
  60. package/dist/docs/reference/http-api.html +6 -6
  61. package/dist/docs/reference/instructions.html +6 -6
  62. package/dist/docs/reference/playground.html +5 -5
  63. package/dist/docs/reference/project-layout.html +6 -6
  64. package/dist/docs/reference/prompt.html +6 -6
  65. package/dist/docs/reference/schedules.html +6 -6
  66. package/dist/docs/reference/sessions.html +6 -6
  67. package/dist/docs/reference/skills.html +6 -6
  68. package/dist/docs/reference/subagents.html +6 -6
  69. package/dist/docs/reference/tools.html +6 -6
  70. package/dist/docs/scaffolding-agents.html +5 -5
  71. package/dist/docs/storage.html +6 -6
  72. package/dist/docs/templates/triage.html +6 -6
  73. package/dist/docs/troubleshooting.html +6 -6
  74. package/dist/internal/cli-deploy.d.ts.map +1 -1
  75. package/dist/internal/cli-deploy.js +8 -18
  76. package/dist/internal/cli-slack-manual.d.ts +20 -0
  77. package/dist/internal/cli-slack-manual.d.ts.map +1 -0
  78. package/dist/internal/cli-slack-manual.js +106 -0
  79. package/dist/internal/cli-slack.d.ts +5 -2
  80. package/dist/internal/cli-slack.d.ts.map +1 -1
  81. package/dist/internal/cli-slack.js +18 -69
  82. package/dist/internal/deploy-client.d.ts +7 -10
  83. package/dist/internal/deploy-client.d.ts.map +1 -1
  84. package/dist/internal/deploy-client.js +1 -3
  85. package/dist/internal/deploy-source.d.ts +1 -1
  86. package/dist/internal/deploy-source.js +1 -1
  87. package/dist/internal/process-memory-telemetry.d.ts +3 -4
  88. package/dist/internal/process-memory-telemetry.d.ts.map +1 -1
  89. package/dist/internal/process-memory-telemetry.js +4 -17
  90. package/dist/playground/assets/{index-BKMz70wx.js → index-BHAPd9KT.js} +31 -31
  91. package/dist/playground/assets/index-DpKH0HcT.css +1 -0
  92. package/dist/playground/index.html +2 -2
  93. package/docs/guides/slack.md +52 -7
  94. package/docs/reference/cli.md +29 -6
  95. package/docs/troubleshooting.md +1 -1
  96. package/package.json +1 -1
  97. package/skills/create-agent/SKILL.md +1 -0
  98. package/skills/setup-slack/SKILL.md +99 -7
  99. package/src/bin/agent-serve.ts +2 -0
  100. package/src/channels/slack/init.ts +130 -20
  101. package/src/channels/slack/manifest.ts +27 -0
  102. package/src/channels/slack/setup.ts +124 -23
  103. package/src/internal/cli-deploy.ts +8 -22
  104. package/src/internal/cli-slack-manual.ts +127 -0
  105. package/src/internal/cli-slack.ts +18 -70
  106. package/src/internal/deploy-client.ts +8 -13
  107. package/src/internal/deploy-source.ts +1 -1
  108. package/src/internal/process-memory-telemetry.ts +5 -24
  109. package/dist/docs/assets/chunks/@localSearchIndexroot.TzAgoWLl.js +0 -1
  110. package/dist/docs/assets/guides_slack.md.D2nTRu9M.lean.js +0 -1
  111. package/dist/playground/assets/index-BmMrulSc.css +0 -1
  112. /package/dist/docs/assets/{troubleshooting.md.C6n3wCr6.lean.js → troubleshooting.md.CuVztCKs.lean.js} +0 -0
@@ -8,8 +8,8 @@
8
8
  />
9
9
  <meta name="viewport" content="width=device-width, initial-scale=1" />
10
10
  <title>agent-serve playground</title>
11
- <script type="module" crossorigin src="./assets/index-BKMz70wx.js"></script>
12
- <link rel="stylesheet" crossorigin href="./assets/index-BmMrulSc.css">
11
+ <script type="module" crossorigin src="./assets/index-BHAPd9KT.js"></script>
12
+ <link rel="stylesheet" crossorigin href="./assets/index-DpKH0HcT.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -1,13 +1,15 @@
1
1
  ---
2
2
  title: "Slack"
3
- description: "Put the agent in Slack: Cursor-hosted connection or a dedicated Socket Mode app via the dashboard wizard, plus doctor, engagement rules, access policy, and approval buttons."
3
+ description: "Put the agent in Slack: Cursor-hosted connection, a dedicated Socket Mode app via the dashboard wizard, or a manual manifest import."
4
4
  ---
5
5
 
6
6
  # Slack agents
7
7
 
8
8
  The Slack channel puts your agent in Slack. Two products: the Cursor-hosted
9
9
  connection (`cursorAccount: true`), or a dedicated Socket Mode app created
10
- in the dashboard wizard (`agent-sdk slack create`). Socket Mode has no
10
+ in the dashboard wizard (`agent-sdk slack create`). To own the Slack app
11
+ yourself, run `agent-sdk slack init --manual` and paste the manifests at
12
+ [api.slack.com](https://api.slack.com/apps). Socket Mode has no
11
13
  public Request URL. Replies stream in threads, with tool "thinking" steps,
12
14
  suggested prompts, and opt-in approval buttons.
13
15
 
@@ -103,8 +105,8 @@ For a dedicated Socket Mode bot, run `agent-sdk slack create`. The CLI
103
105
  opens the signed-in Cursor dashboard wizard. Slack consent, bot name,
104
106
  and admin-approval wait happen there. Tokens are stored as deployment
105
107
  secrets and written to `.env.local`. They are never shown. Do not paste
106
- a manifest at [api.slack.com](https://api.slack.com/apps). `slack init`
107
- is removed.
108
+ a manifest at [api.slack.com](https://api.slack.com/apps) unless you
109
+ are on the [manual setup](#manual-setup) path.
108
110
 
109
111
  Bots that already have tokens in `.env.local` keep working. Skip the
110
112
  wizard unless you want a Cursor-managed app.
@@ -177,6 +179,46 @@ Thinking… status, then a threaded streaming reply, with correlated
177
179
  server logs (`inbound kind=app_mention`, `session start`,
178
180
  `reply delivered via stream|postMessage`).
179
181
 
182
+ ### Manual setup
183
+
184
+ `slack init --manual` is for a Slack app you own at api.slack.com. You
185
+ paste the generated manifests and mint tokens yourself. Use this when
186
+ you want an unmanaged app, or when the dashboard wizard is not an
187
+ option.
188
+
189
+ ```bash
190
+ agent-sdk slack init --manual --dir . --name "My Agent"
191
+ ```
192
+
193
+ That writes `agent/channels/slack.ts`, importable manifests at
194
+ `.agent-serve/slack/manifest.{dev,prod}.json`, `env.example`, and
195
+ `setup-status.json`. `--no-prefix` uses shared `SLACK_*` variables on
196
+ a single-agent host. `--prefix CUSTOM` overrides the directory-derived
197
+ prefix. `--channel-posts` subscribes the manifests to channel-post
198
+ events.
199
+
200
+ `slack manifest --env both` regenerates the JSON files without
201
+ touching the channel file.
202
+
203
+ #### Create the apps
204
+
205
+ This phase needs someone with permission to create Slack apps.
206
+
207
+ 1. Open [api.slack.com/apps](https://api.slack.com/apps) → **Create New
208
+ App** → **From a manifest**.
209
+ 2. Paste `manifest.dev.json` and create the dev app.
210
+ 3. Repeat with `manifest.prod.json`. Dev and prod are separate Slack
211
+ apps with separate tokens.
212
+
213
+ #### Install and mint tokens
214
+
215
+ For each app: **Install to Workspace** and copy the Bot User OAuth Token
216
+ (`xoxb-…`). Then under **Basic Information → App-Level Tokens** create a
217
+ token with scope `connections:write` and copy it (`xapp-…`).
218
+
219
+ Put the pair in `.env.local` using the names from `env.example`, then
220
+ run `slack doctor` and serve as [above](#wire-the-env-and-verify).
221
+
180
222
  ## Choose when the agent engages
181
223
 
182
224
  Mentions and DMs are on by default. Watching channels means dispatching
@@ -203,8 +245,9 @@ export default slackChannel({
203
245
 
204
246
  Channel watching needs the `message.channels` / `message.groups` events
205
247
  on the Slack app (Socket Mode only; not available with
206
- `cursorAccount: true`). Pass `--channel-posts` on `slack create`. The
207
- bot must also be a member of each watched channel.
248
+ `cursorAccount: true`). Pass `--channel-posts` on `slack create` or
249
+ `slack init --manual`. The bot must also be a member of each watched
250
+ channel.
208
251
 
209
252
  Set `includeBotPosts: true` when the posts worth watching come from bots:
210
253
  alert feeds, webhook integrations, or other agents posting notes. The
@@ -274,11 +317,13 @@ Two habits matter most.
274
317
  The `slack` subcommands cover setup end to end.
275
318
 
276
319
  ```bash
277
- agent-sdk slack setup # two-product chooser
320
+ agent-sdk slack setup # two-product chooser plus manual phases
278
321
  agent-sdk slack create --dir . # dashboard wizard (dev app)
279
322
  agent-sdk slack create --dir . --prod # prod app
280
323
  agent-sdk slack destroy --dir . # delete the provisioned app
281
324
  agent-sdk slack icon ./icon.png --dir . # set the provisioned app's icon
325
+ agent-sdk slack init --manual --dir . # manifests to paste at api.slack.com
326
+ agent-sdk slack manifest --env both # regenerate those JSON files
282
327
  agent-sdk slack doctor --prefix MY_AGENT # token / connectivity checks
283
328
  ```
284
329
 
@@ -574,9 +574,10 @@ directory name. Deployment slugs contain lowercase letters, digits, `_`,
574
574
  or `-`, with a maximum of 64 characters. For a directory with several
575
575
  agents, select one with `--slug`, deploy all with `--all`, or choose from
576
576
  the TTY prompt. Non-interactive callers must pass `--slug` or `--all`.
577
- If `--dir` contains no agent project or child agents, `deploy` creates a
578
- static placeholder using `--slug` or a slug derived from the directory
579
- name. In this case, `--all` fails.
577
+ If `--dir` contains no agent project or child agents, `deploy` requires
578
+ `--slug` (or a slug derived from the directory name) and an https git
579
+ repository URL (`--repo`, or inferred from `origin` when `--dir` is an
580
+ agent project). `--all` fails when there is no agent project.
580
581
 
581
582
  The command infers `--repo`, `--ref`, and `--path` from the current Git
582
583
  checkout when possible. Explicit flags take precedence. `--repo` must
@@ -777,10 +778,15 @@ agent-sdk slack destroy [--dir <path>] [--prod] [--slack-team <T…>]
777
778
  [--team <id>] [--json]
778
779
  agent-sdk slack icon <https-url-or-file> [--dir <path>] [--prod]
779
780
  [--slack-team <T…>] [--team <id>] [--json]
781
+ agent-sdk slack init --manual [--dir <path>] [--name <name>]
782
+ [--prefix <prefix> | --no-prefix] [--channel-posts]
783
+ agent-sdk slack manifest [--dir <path>] [--name <name>]
784
+ [--env dev|prod|both] [--channel-posts] [--print]
780
785
  agent-sdk slack doctor [--dir <path>] [--prefix <prefix> | --no-prefix] [--json]
781
786
  ```
782
787
 
783
- `slack setup` prints the two-product chooser and doesn't change files.
788
+ `slack setup` prints the two-product chooser plus the `--manual` setup
789
+ checklist. It doesn't change files.
784
790
 
785
791
  `slack create` opens the signed-in Cursor dashboard wizard. Finish Slack
786
792
  consent and the bot name there. The CLI writes the token pair into
@@ -800,8 +806,25 @@ stop working.
800
806
  `slack icon` sets the provisioned app's icon from an https image URL or
801
807
  a local png, jpg, or gif file of at most 512KB.
802
808
 
803
- `slack init` is removed. It exits non-zero and writes no files. Use
804
- `slack create`.
809
+ `slack init` without `--manual` exits non-zero and writes no files. Use
810
+ `slack create` for the dashboard wizard. `slack init --manual` writes
811
+ the channel file, development and production manifests, `env.example`,
812
+ and setup status under the project. You paste those manifests at
813
+ api.slack.com. The command refuses to overwrite a target file. If a
814
+ collision occurs, it exits non-zero; files created earlier in the run
815
+ remain. The token prefix defaults to the directory basename normalized
816
+ to uppercase snake case. Explicit `--prefix` values use the same
817
+ normalization. For example, `pr-approver` becomes
818
+ `PR_APPROVER_SLACK_BOT_TOKEN`. `--no-prefix` uses shared
819
+ `SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN`. `--channel-posts` subscribes
820
+ the manifests to channel-post events. The command always prints a JSON
821
+ summary.
822
+
823
+ `slack manifest` regenerates selected manifest files. `--env` defaults
824
+ to `both`, and `--name` defaults to the directory name. `--print` writes
825
+ the manifest JSON to stdout instead of changing files. With the default
826
+ `--env both`, it prints development JSON, a `--- prod ---` separator,
827
+ then production JSON.
805
828
 
806
829
  `slack doctor` checks both tokens, Socket Mode connectivity, and
807
830
  Slack's `auth.test`. It exits non-zero when any check fails.
@@ -59,7 +59,7 @@ Match your symptom below. Keep the commands as `agent-sdk`; see
59
59
 
60
60
  | What you see | What to do |
61
61
  | --- | --- |
62
- | Logs show `channel idle … missing credentials` | Expected when tokens are missing. Run `agent-sdk slack create --dir <agent>` to provision the app and write the tokens, or set `<PREFIX>_SLACK_BOT_TOKEN` and `<PREFIX>_SLACK_APP_TOKEN` per agent yourself, then run `agent-sdk slack doctor --prefix <PREFIX>`. |
62
+ | Logs show `channel idle … missing credentials` | Expected when tokens are missing. Run `agent-sdk slack create --dir <agent>` to provision the app and write the tokens, or `agent-sdk slack init --manual --dir <agent>` and paste the manifests at api.slack.com. Then set `<PREFIX>_SLACK_BOT_TOKEN` and `<PREFIX>_SLACK_APP_TOKEN` per agent and run `agent-sdk slack doctor --prefix <PREFIX>`. |
63
63
  | `slack create` reports the app needs admin approval | Open Slack's **Request approval** page (the CLI prints the link; the same URL is **Send a reminder** after you submit). Managed install does not file the request. Keep the CLI running, then click **Retry** in the dashboard after an admin approves. |
64
64
  | The bot ignores ordinary channel posts | Default engagement is mentions and DMs only. Enable `engagement.channelPosts` with an allowlist, and subscribe the app to `message.channels` / `message.groups`. |
65
65
  | Approve / Deny buttons do nothing | Channels that post approval cards need `toolApprovals: true`. Recreate the app with `slack create` if interactivity is off. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cursor/july",
3
- "version": "0.1.52",
3
+ "version": "0.1.53",
4
4
  "description": "(early alpha) Filesystem-first framework for defining Cursor agents as markdown and TypeScript and serving them over channels with the Cursor SDK.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "repository": {
@@ -161,6 +161,7 @@ plan:
161
161
  tokens, no manifests); for a dedicated Socket Mode app do not hand-write
162
162
  the channel — `agent-sdk slack create --dir ./<slug> --name "<Name>"`
163
163
  opens the dashboard wizard (env prefix from the directory name);
164
+ for a Slack app they own at api.slack.com, `agent-sdk slack init --manual --dir ./<slug> --name "<Name>"`;
164
165
  customize `agent/channels/slack.ts` afterwards (e.g. `suggestedPrompts`).
165
166
  5. `evals/evals.config.ts` — `defineEvalConfig({ maxConcurrency: 20 })`
166
167
  (playground runs are memory-only unless you add `persistRuns`)
@@ -23,10 +23,11 @@ There are **two ways to connect**:
23
23
  `.env.local` and as deployment secrets. Required for channel watching,
24
24
  approval buttons, or a distinct bot handle.
25
25
 
26
- Do not generate manifests or ask anyone to paste tokens. Defaulting into
26
+ Do not generate manifests or ask anyone to paste tokens unless they
27
+ asked for a custom Slack app (`slack init --manual`). Defaulting into
27
28
  Path B buries the user in Slack admin work they may not need.
28
29
 
29
- Existing DIY bots that already have `<PREFIX>_SLACK_BOT_TOKEN` and
30
+ Existing bots that already have `<PREFIX>_SLACK_BOT_TOKEN` and
30
31
  `_SLACK_APP_TOKEN` in `.env.local` keep working. Do not force them onto
31
32
  the wizard.
32
33
 
@@ -51,6 +52,8 @@ Decision shortcuts; skip the question when the request already decides:
51
52
  own bot user / handle → **Path B**. Say which requirement forced it.
52
53
  - Workspace has no Cursor Slack integration and won't install it →
53
54
  **Path B**.
55
+ - Asked for a custom Slack app they own at api.slack.com, or to paste a
56
+ manifest → **Path C** (`slack init --manual`).
54
57
  - "Just get it into Slack", a first bring-up, or the user already said
55
58
  to use the Cursor Slack integration, with none of the above → **Path A**.
56
59
 
@@ -160,7 +163,8 @@ install does not file the request. After an admin approves, click
160
163
  **Retry** in the wizard.
161
164
 
162
165
  `create` scaffolds `agent/channels/slack.ts` when missing. Do not paste
163
- a manifest at api.slack.com. `slack init` is removed.
166
+ a manifest at api.slack.com for this path. `slack init` without
167
+ `--manual` hard-errors; see Path C for a hand-imported app.
164
168
 
165
169
  Existing tokens in `.env.local` keep working. Skip the wizard for those
166
170
  agents unless you want a Cursor-managed app.
@@ -196,6 +200,88 @@ app. It keeps the Slack app id and replaces the manifest and tokens.
196
200
  `agent-sdk slack destroy` deletes the provisioned app. Tokens left in
197
201
  `.env.local` simply stop working.
198
202
 
203
+ ## Path C — Manual Slack app (`slack init --manual`)
204
+
205
+ Use this when the user wants a Slack app they own at api.slack.com, not
206
+ a Cursor-managed app. Not the default. Do not offer it unless they
207
+ asked for a custom/unmanaged app or already have one. Update
208
+ `.agent-serve/slack/setup-status.json` as you complete phases.
209
+
210
+ ### Phase 0 — Generate (agent)
211
+
212
+ ```bash
213
+ agent-sdk slack init --manual --dir . --name "My Agent"
214
+ ```
215
+
216
+ Creates:
217
+
218
+ - `agent/channels/slack.ts` with `envPrefix` from the directory name
219
+ - `.agent-serve/slack/manifest.{dev,prod}.json`
220
+ - `.agent-serve/slack/env.example` (`<PREFIX>_SLACK_BOT_TOKEN` / `_APP_TOKEN`)
221
+ - `.agent-serve/slack/setup-status.json`
222
+
223
+ Use `--no-prefix` for the shared `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN`
224
+ globals (single-agent only). Use `--prefix CUSTOM` to override the
225
+ default prefix. `--channel-posts` subscribes the manifests to
226
+ `message.channels` / `message.groups`.
227
+
228
+ These files omit `settings.managed_app_settings`. Slack accepts that
229
+ block only from a manager app's user token, so it belongs to
230
+ `slack create`, not to a manifest a human pastes in.
231
+
232
+ ### Phase 1 — Create apps (human)
233
+
234
+ 1. Open https://api.slack.com/apps → **Create New App** → **From a manifest**
235
+ 2. Paste `manifest.dev.json` → create the *dev* app
236
+ 3. Repeat with `manifest.prod.json` for prod
237
+
238
+ Stop and ask the human to complete this gate.
239
+
240
+ ### Phase 2 — Install + tokens (human)
241
+
242
+ For each app:
243
+
244
+ 1. **Install to Workspace** — copy Bot User OAuth Token (`xoxb-…`)
245
+ 2. **Basic Information → App-Level Tokens** — Create Token with scope
246
+ `connections:write` → copy (`xapp-…`)
247
+
248
+ ### Phase 3 — Env (agent)
249
+
250
+ Write into `.env.local` (dev app), using the keys from `env.example`:
251
+
252
+ ```bash
253
+ MY_AGENT_SLACK_BOT_TOKEN=xoxb-…
254
+ MY_AGENT_SLACK_APP_TOKEN=xapp-…
255
+ ```
256
+
257
+ ```bash
258
+ agent-sdk slack doctor --prefix MY_AGENT
259
+ ```
260
+
261
+ Stop when `app_token`, `connections_open`, `bot_token`, and `auth_test`
262
+ are green.
263
+
264
+ ### Phase 4 — Serve + smoke (agent + human)
265
+
266
+ ```bash
267
+ agent-sdk dev .
268
+ ```
269
+
270
+ Confirm log: `[agent-sdk/slack] Socket Mode connected`.
271
+
272
+ When tokens are missing the channel logs
273
+ `channel idle reason=missing credentials need=…` and `serve` continues.
274
+
275
+ Human:
276
+
277
+ 1. Invite the bot to a channel
278
+ 2. `@mention` it (or DM)
279
+ 3. Confirm Thinking… / Working… typing and a threaded reply
280
+ 4. Server logs should show correlated lines (`event_id`, `session`, `channel`, `thread_ts`):
281
+ - `inbound kind=app_mention …`
282
+ - `session start …`
283
+ - `reply delivered via postMessage|stream …`
284
+
199
285
  ## Features (defaults ON)
200
286
 
201
287
  - Chat streaming (`chat.startStream` / `appendStream` / `stopStream`) with postMessage fallback (Path B; Path A posts one final reply)
@@ -226,8 +312,8 @@ export default slackChannel({
226
312
  });
227
313
  ```
228
314
 
229
- Pass `--channel-posts` on `slack create` so the wizard-built app
230
- subscribes to `message.channels` / `message.groups`. Invite the bot to
315
+ Pass `--channel-posts` on `slack create` or `slack init --manual` so the
316
+ app subscribes to `message.channels` / `message.groups`. Invite the bot to
231
317
  each watched channel. Posts mentioning the bot stay on the
232
318
  `app_mention` path; watch sessions reuse the thread-scoped principal with an
233
319
  `engagement: "channel_post"` attribute, so a later mention continues the
@@ -278,8 +364,10 @@ Requirements and caveats:
278
364
 
279
365
  ## CLI reference
280
366
 
281
- Path B tooling is `create` / `destroy` / `icon` / `doctor`. Path A needs
282
- only `login`. `slack setup` prints this chooser. `slack init` hard-errors.
367
+ Path B tooling is `create` / `destroy` / `icon` / `doctor`. Path C is
368
+ `init --manual` / `manifest` / `doctor`. Path A needs only `login`.
369
+ `slack setup` prints this chooser. `slack init` hard-errors unless
370
+ `--manual`.
283
371
 
284
372
  ```bash
285
373
  agent-sdk slack setup
@@ -287,5 +375,9 @@ agent-sdk slack create --dir . # dashboard wizard (dev app)
287
375
  agent-sdk slack create --dir . --prod # prod app
288
376
  agent-sdk slack create --dir . --slack-team T0123ABCD
289
377
  agent-sdk slack destroy --dir . [--prod] [--slack-team T0123ABCD]
378
+ agent-sdk slack init --manual --dir . --name "My Agent"
379
+ agent-sdk slack init --manual --dir . --no-prefix
380
+ agent-sdk slack init --manual --dir . --channel-posts
381
+ agent-sdk slack manifest --env both
290
382
  agent-sdk slack doctor --prefix MY_AGENT
291
383
  ```
@@ -535,6 +535,7 @@ async function main(argv: string[]): Promise<number> {
535
535
  "slack-team": { type: "string" },
536
536
  icon: { type: "string" },
537
537
  force: { type: "boolean", default: false },
538
+ manual: { type: "boolean", default: false },
538
539
  repo: { type: "string", multiple: true },
539
540
  org: { type: "string" },
540
541
  "cursor-events": { type: "boolean", default: false },
@@ -599,6 +600,7 @@ async function main(argv: string[]): Promise<number> {
599
600
  apiKey: values["api-key"],
600
601
  icon: values.icon,
601
602
  force: values.force,
603
+ manual: values.manual,
602
604
  iconSource: positionals[0] === "icon" ? positionals[1] : undefined,
603
605
  });
604
606
  }
@@ -1,26 +1,39 @@
1
1
  /**
2
2
  * Scaffold a Slack channel into an agent-serve project.
3
+ *
4
+ * `initSlackChannel` is the create-safe writer: `agent/channels/slack.ts`
5
+ * only. DIY manifests for paste at api.slack.com live in
6
+ * {@link initSlackManual} so `slack create` cannot reach them.
3
7
  */
4
8
 
5
9
  import { mkdir, writeFile } from "node:fs/promises";
6
10
  import { basename, join, resolve } from "node:path";
7
11
  import { PACKAGE_NAME } from "../../internal/distribution.js";
8
12
  import { envPrefixFromName, slackEnvKeys } from "./credentials.js";
13
+ import {
14
+ buildSlackManifestPair,
15
+ slackManifestFileEntries,
16
+ } from "./manifest.js";
17
+ import { initialSlackSetupStatus } from "./setup.js";
9
18
 
10
19
  export interface InitSlackOptions {
11
20
  dir: string;
12
21
  /** Display name for the Slack app. Defaults to directory basename. */
13
22
  name?: string;
14
- description?: string;
15
23
  /**
16
24
  * Env prefix for tokens (`MY_AGENT` → `MY_AGENT_SLACK_BOT_TOKEN`). Defaults to
17
25
  * the directory basename so multi-agent serve can mount several Slack bots.
18
26
  * Pass `""` to use the shared `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN` globals.
19
27
  */
20
28
  envPrefix?: string;
29
+ }
30
+
31
+ export interface InitSlackManualOptions extends InitSlackOptions {
32
+ description?: string;
21
33
  /**
22
- * Subscribe the generated app to `message.channels` + `message.groups`
23
- * for the opt-in channel watch (`engagement.channelPosts`).
34
+ * Subscribe the generated manifests to `message.channels` +
35
+ * `message.groups` for the opt-in channel watch
36
+ * (`engagement.channelPosts` on `slackChannel`).
24
37
  */
25
38
  channelPosts?: boolean;
26
39
  }
@@ -32,33 +45,61 @@ export interface InitSlackResult {
32
45
  envKeys: { bot: string; app: string };
33
46
  }
34
47
 
35
- export async function initSlackChannel(
36
- options: InitSlackOptions
37
- ): Promise<InitSlackResult> {
48
+ interface SlackInitTarget {
49
+ rootDir: string;
50
+ name: string;
51
+ envPrefix: string | undefined;
52
+ envKeys: { bot: string; app: string };
53
+ }
54
+
55
+ function resolveSlackInitTarget(options: InitSlackOptions): SlackInitTarget {
38
56
  const rootDir = resolve(options.dir);
39
57
  const envPrefix =
40
58
  options.envPrefix === ""
41
59
  ? undefined
42
60
  : (options.envPrefix ?? envPrefixFromName(basename(rootDir)));
43
- const envKeys = slackEnvKeys(envPrefix);
44
- const written: string[] = [];
61
+ return {
62
+ rootDir,
63
+ name: options.name ?? basename(rootDir),
64
+ envPrefix,
65
+ envKeys: slackEnvKeys(envPrefix),
66
+ };
67
+ }
45
68
 
46
- const abs = join(rootDir, "agent/channels/slack.ts");
69
+ async function writeExclusive(
70
+ rootDir: string,
71
+ rel: string,
72
+ contents: string
73
+ ): Promise<void> {
74
+ const abs = join(rootDir, rel);
47
75
  await mkdir(join(abs, ".."), { recursive: true });
76
+ await writeFile(abs, contents, { flag: "wx" });
77
+ }
78
+
79
+ function slackChannelSource(
80
+ target: SlackInitTarget,
81
+ kind: "create" | "manual"
82
+ ): string {
48
83
  const envPrefixLine =
49
- envPrefix === undefined
84
+ target.envPrefix === undefined
50
85
  ? ""
51
- : ` envPrefix: ${JSON.stringify(envPrefix)},\n`;
52
- await writeFile(
53
- abs,
54
- `import { slackChannel } from "${PACKAGE_NAME}/channels/slack";
86
+ : ` envPrefix: ${JSON.stringify(target.envPrefix)},\n`;
87
+ const tokenLines =
88
+ kind === "manual"
89
+ ? ` * Tokens: ${target.envKeys.bot} + ${target.envKeys.app}\n * (see .agent-serve/slack/env.example).`
90
+ : ` * Tokens: ${target.envKeys.bot} + ${target.envKeys.app}`;
91
+ const setupLine =
92
+ kind === "manual"
93
+ ? " * Full setup: agent-serve slack setup"
94
+ : " * Full setup: agent-serve slack create --dir .";
95
+ return `import { slackChannel } from "${PACKAGE_NAME}/channels/slack";
55
96
 
56
97
  /**
57
98
  * Slack channel (Socket Mode).
58
99
  *
59
- * Tokens: ${envKeys.bot} + ${envKeys.app}
100
+ ${tokenLines}
60
101
  *
61
- * Full setup: agent-serve slack create --dir .
102
+ ${setupLine}
62
103
  */
63
104
  export default slackChannel({
64
105
  ${envPrefixLine} // Streaming and thinking steps default to true.
@@ -67,10 +108,79 @@ ${envPrefixLine} // Streaming and thinking steps default to true.
67
108
  { title: "Status", message: "What are you working on?" },
68
109
  ],
69
110
  });
70
- `,
71
- { flag: "wx" }
111
+ `;
112
+ }
113
+
114
+ /** Create-safe: write `agent/channels/slack.ts` only. */
115
+ export async function initSlackChannel(
116
+ options: InitSlackOptions
117
+ ): Promise<InitSlackResult> {
118
+ const target = resolveSlackInitTarget(options);
119
+ await writeExclusive(
120
+ target.rootDir,
121
+ "agent/channels/slack.ts",
122
+ slackChannelSource(target, "create")
123
+ );
124
+ return {
125
+ rootDir: target.rootDir,
126
+ files: ["agent/channels/slack.ts"],
127
+ envPrefix: target.envPrefix,
128
+ envKeys: target.envKeys,
129
+ };
130
+ }
131
+
132
+ /**
133
+ * `slack init --manual`: channel file plus DIY manifests
134
+ * (`managedByCursor: false`), env.example, and setup-status.json.
135
+ */
136
+ export async function initSlackManual(
137
+ options: InitSlackManualOptions
138
+ ): Promise<InitSlackResult> {
139
+ const target = resolveSlackInitTarget(options);
140
+ const files: string[] = [];
141
+ const write = async (rel: string, contents: string): Promise<void> => {
142
+ await writeExclusive(target.rootDir, rel, contents);
143
+ files.push(rel);
144
+ };
145
+
146
+ await write("agent/channels/slack.ts", slackChannelSource(target, "manual"));
147
+
148
+ const pair = buildSlackManifestPair({
149
+ name: target.name,
150
+ managedByCursor: false,
151
+ description: options.description,
152
+ channelPosts: options.channelPosts,
153
+ });
154
+ for (const file of slackManifestFileEntries(pair)) {
155
+ await write(`.agent-serve/slack/${file.name}`, file.contents);
156
+ }
157
+ await write(
158
+ ".agent-serve/slack/env.example",
159
+ `# Copy to .env.local (or export in your shell). Use the *dev* Slack app
160
+ # for local development; use the Prod app tokens in deployment secrets.
161
+ #
162
+ # Multi-agent serve needs a distinct Slack app (and token pair) per agent.
163
+
164
+ ${target.envKeys.bot}=xoxb-...
165
+ ${target.envKeys.app}=xapp-...
166
+ `
167
+ );
168
+ await write(
169
+ ".agent-serve/slack/setup-status.json",
170
+ `${JSON.stringify(
171
+ initialSlackSetupStatus({
172
+ envPrefix: target.envPrefix,
173
+ envKeys: target.envKeys,
174
+ }),
175
+ null,
176
+ 2
177
+ )}\n`
72
178
  );
73
- written.push("agent/channels/slack.ts");
74
179
 
75
- return { rootDir, files: written, envPrefix, envKeys };
180
+ return {
181
+ rootDir: target.rootDir,
182
+ files,
183
+ envPrefix: target.envPrefix,
184
+ envKeys: target.envKeys,
185
+ };
76
186
  }
@@ -212,3 +212,30 @@ export function buildSlackManifestPair(
212
212
  prod: buildSlackManifest({ ...options, env: "prod" }),
213
213
  };
214
214
  }
215
+
216
+ /** Relative names under `.agent-serve/slack/`. */
217
+ export function slackManifestFileEntries(
218
+ pair: { dev: SlackAppManifest; prod: SlackAppManifest },
219
+ env: "dev" | "prod" | "both" = "both"
220
+ ): ReadonlyArray<{
221
+ name: "manifest.dev.json" | "manifest.prod.json";
222
+ contents: string;
223
+ }> {
224
+ const files: Array<{
225
+ name: "manifest.dev.json" | "manifest.prod.json";
226
+ contents: string;
227
+ }> = [];
228
+ if (env === "dev" || env === "both") {
229
+ files.push({
230
+ name: "manifest.dev.json",
231
+ contents: `${JSON.stringify(pair.dev, null, 2)}\n`,
232
+ });
233
+ }
234
+ if (env === "prod" || env === "both") {
235
+ files.push({
236
+ name: "manifest.prod.json",
237
+ contents: `${JSON.stringify(pair.prod, null, 2)}\n`,
238
+ });
239
+ }
240
+ return files;
241
+ }