@cursor/july 0.1.49 → 0.1.51

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 (119) hide show
  1. package/AGENTS.md +3 -3
  2. package/README.md +7 -14
  3. package/dist/bin/agent-serve.js +22 -8
  4. package/dist/channels/slack/init.d.ts +2 -3
  5. package/dist/channels/slack/init.d.ts.map +1 -1
  6. package/dist/channels/slack/init.js +8 -43
  7. package/dist/channels/slack/manifest.d.ts +3 -3
  8. package/dist/channels/slack/manifest.js +3 -3
  9. package/dist/channels/slack/setup.d.ts +2 -11
  10. package/dist/channels/slack/setup.d.ts.map +1 -1
  11. package/dist/channels/slack/setup.js +26 -94
  12. package/dist/channels/slack/types.d.ts +3 -4
  13. package/dist/channels/slack/types.d.ts.map +1 -1
  14. package/dist/docs/404.html +2 -2
  15. package/dist/docs/ab.html +3 -3
  16. package/dist/docs/assets/{app.BpWAgjaR.js → app.C51TX1Bw.js} +1 -1
  17. package/dist/docs/assets/chunks/@localSearchIndexroot.D2DpXdZN.js +1 -0
  18. package/dist/docs/assets/chunks/{VPLocalSearchBox.CnAktZtG.js → VPLocalSearchBox.BB9HCJPq.js} +1 -1
  19. package/dist/docs/assets/chunks/{theme.B5IvUxOB.js → theme.Dp-0SZcu.js} +2 -2
  20. package/dist/docs/assets/{deployment.md.Cdp4rTE4.js → deployment.md.B8kW-h7P.js} +1 -1
  21. package/dist/docs/assets/{example-agents_benny.md.l7JTmm8X.js → example-agents_benny.md.B-LIDGja.js} +1 -1
  22. package/dist/docs/assets/{example-agents_oncall.md.ZE0n6ZFN.js → example-agents_oncall.md.CBmyuAKc.js} +2 -2
  23. package/dist/docs/assets/{guides_human-in-the-loop.md.BWvT7UqY.js → guides_human-in-the-loop.md.Cf8kIIqC.js} +2 -2
  24. package/dist/docs/assets/{guides_human-in-the-loop.md.BWvT7UqY.lean.js → guides_human-in-the-loop.md.Cf8kIIqC.lean.js} +1 -1
  25. package/dist/docs/assets/{guides_slack.md.zriQpU_9.js → guides_slack.md.D2nTRu9M.js} +7 -12
  26. package/dist/docs/assets/guides_slack.md.D2nTRu9M.lean.js +1 -0
  27. package/dist/docs/assets/{reference_cli.md.BDmjPPAm.js → reference_cli.md.CGKfz_Yj.js} +3 -7
  28. package/dist/docs/assets/{reference_cli.md.BDmjPPAm.lean.js → reference_cli.md.CGKfz_Yj.lean.js} +1 -1
  29. package/dist/docs/assets/{troubleshooting.md.CkgwmVI0.js → troubleshooting.md.C6n3wCr6.js} +1 -1
  30. package/dist/docs/building-with-agents.html +3 -3
  31. package/dist/docs/concepts.html +3 -3
  32. package/dist/docs/deployment.html +5 -5
  33. package/dist/docs/evals.html +3 -3
  34. package/dist/docs/example-agents/approval-buddy.html +3 -3
  35. package/dist/docs/example-agents/benny.html +5 -5
  36. package/dist/docs/example-agents/bugbot.html +3 -3
  37. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  38. package/dist/docs/example-agents/codeowners-review.html +3 -3
  39. package/dist/docs/example-agents/concierge.html +3 -3
  40. package/dist/docs/example-agents/fsd.html +3 -3
  41. package/dist/docs/example-agents/index.html +3 -3
  42. package/dist/docs/example-agents/knowledge-base.html +3 -3
  43. package/dist/docs/example-agents/oncall.html +6 -6
  44. package/dist/docs/example-agents/security-reviewer.html +3 -3
  45. package/dist/docs/example-agents/slack-agent.html +3 -3
  46. package/dist/docs/example-agents/weather-agent.html +3 -3
  47. package/dist/docs/guides/agent-to-agent.html +3 -3
  48. package/dist/docs/guides/cloud-runtime.html +3 -3
  49. package/dist/docs/guides/github.html +3 -3
  50. package/dist/docs/guides/human-in-the-loop.html +5 -5
  51. package/dist/docs/guides/mcp-oauth.html +3 -3
  52. package/dist/docs/guides/opentelemetry.html +3 -3
  53. package/dist/docs/guides/slack.html +11 -16
  54. package/dist/docs/guides/webhooks.html +3 -3
  55. package/dist/docs/hashmap.json +1 -1
  56. package/dist/docs/hillclimbing.html +3 -3
  57. package/dist/docs/index.html +3 -3
  58. package/dist/docs/quickstart.html +3 -3
  59. package/dist/docs/reference/agent-config.html +3 -3
  60. package/dist/docs/reference/artifacts.html +3 -3
  61. package/dist/docs/reference/channels.html +3 -3
  62. package/dist/docs/reference/cli.html +6 -10
  63. package/dist/docs/reference/connections.html +3 -3
  64. package/dist/docs/reference/hooks.html +3 -3
  65. package/dist/docs/reference/http-api.html +3 -3
  66. package/dist/docs/reference/instructions.html +3 -3
  67. package/dist/docs/reference/playground.html +3 -3
  68. package/dist/docs/reference/project-layout.html +3 -3
  69. package/dist/docs/reference/prompt.html +3 -3
  70. package/dist/docs/reference/schedules.html +3 -3
  71. package/dist/docs/reference/sessions.html +3 -3
  72. package/dist/docs/reference/skills.html +3 -3
  73. package/dist/docs/reference/subagents.html +3 -3
  74. package/dist/docs/reference/tools.html +3 -3
  75. package/dist/docs/scaffolding-agents.html +3 -3
  76. package/dist/docs/storage.html +3 -3
  77. package/dist/docs/templates/triage.html +3 -3
  78. package/dist/docs/troubleshooting.html +5 -5
  79. package/dist/internal/cli-ax.js +3 -3
  80. package/dist/internal/cli-cursor.d.ts +18 -0
  81. package/dist/internal/cli-cursor.d.ts.map +1 -1
  82. package/dist/internal/cli-cursor.js +25 -0
  83. package/dist/internal/cli-slack.d.ts +3 -4
  84. package/dist/internal/cli-slack.d.ts.map +1 -1
  85. package/dist/internal/cli-slack.js +126 -322
  86. package/dist/internal/convert-automation/convert-workflow.js +2 -2
  87. package/dist/internal/slack-provision-client.d.ts +37 -2
  88. package/dist/internal/slack-provision-client.d.ts.map +1 -1
  89. package/dist/internal/slack-provision-client.js +46 -0
  90. package/dist/playground/assets/{index-Dq_HqyU-.js → index-BKMz70wx.js} +39 -39
  91. package/dist/playground/assets/index-BmMrulSc.css +1 -0
  92. package/dist/playground/index.html +2 -2
  93. package/docs/deployment.md +1 -1
  94. package/docs/example-agents/benny.md +2 -3
  95. package/docs/example-agents/oncall.md +4 -5
  96. package/docs/guides/human-in-the-loop.md +1 -1
  97. package/docs/guides/slack.md +51 -112
  98. package/docs/reference/cli.md +14 -37
  99. package/docs/troubleshooting.md +2 -2
  100. package/package.json +1 -1
  101. package/skills/create-agent/SKILL.md +4 -5
  102. package/skills/setup-slack/SKILL.md +73 -131
  103. package/src/bin/agent-serve.ts +26 -9
  104. package/src/channels/slack/init.ts +10 -71
  105. package/src/channels/slack/manifest.ts +3 -3
  106. package/src/channels/slack/setup.ts +25 -103
  107. package/src/channels/slack/types.ts +3 -4
  108. package/src/internal/cli-ax.ts +3 -3
  109. package/src/internal/cli-cursor.ts +34 -0
  110. package/src/internal/cli-slack.ts +136 -389
  111. package/src/internal/convert-automation/convert-workflow.ts +2 -2
  112. package/src/internal/slack-provision-client.ts +83 -2
  113. package/dist/docs/assets/chunks/@localSearchIndexroot.D00d772K.js +0 -1
  114. package/dist/docs/assets/guides_slack.md.zriQpU_9.lean.js +0 -1
  115. package/dist/playground/assets/index-Dox74yWJ.css +0 -1
  116. /package/dist/docs/assets/{deployment.md.Cdp4rTE4.lean.js → deployment.md.B8kW-h7P.lean.js} +0 -0
  117. /package/dist/docs/assets/{example-agents_benny.md.l7JTmm8X.lean.js → example-agents_benny.md.B-LIDGja.lean.js} +0 -0
  118. /package/dist/docs/assets/{example-agents_oncall.md.ZE0n6ZFN.lean.js → example-agents_oncall.md.CBmyuAKc.lean.js} +0 -0
  119. /package/dist/docs/assets/{troubleshooting.md.CkgwmVI0.lean.js → troubleshooting.md.C6n3wCr6.lean.js} +0 -0
@@ -1,7 +1,6 @@
1
1
  /**
2
- * `agent-serve slack …` CLI — manifests, setup, doctor (Socket Mode), and
3
- * managed provisioning (`create` / `destroy`) through Cursor's
4
- * "Add to Slack" API (`slack-provision-client.ts`).
2
+ * `agent-serve slack …` CLI — doctor, destroy/icon, and `create` as a
3
+ * dashboard-wizard launcher (`slack-provision-client.ts`).
5
4
  */
6
5
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
6
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -14,10 +13,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
14
13
  };
15
14
  import { access, chmod, mkdir, readFile, writeFile } from "node:fs/promises";
16
15
  import { basename, extname, join, resolve as resolvePath } from "node:path";
17
- import { buildSlackManifest, buildSlackManifestPair, envPrefixFromName, formatSlackSetupGuide, initSlackChannel, runSlackDoctor, slackEnvKeys, } from "../channels/slack/index.js";
16
+ import { buildSlackManifestPair, envPrefixFromName, formatSlackSetupGuide, initSlackChannel, runSlackDoctor, slackEnvKeys, } from "../channels/slack/index.js";
18
17
  import { resolveDeployAuth } from "./cli-deploy.js";
19
18
  import { cursorExternalApiUrl } from "./cursor/credentials.js";
20
19
  import { slugFromDirectoryName } from "./deploy-client.js";
20
+ import { inferDeployGitSource } from "./deploy-source.js";
21
21
  import { describeError } from "./describe-error.js";
22
22
  import { CLI_COMMAND_NAME as CLI } from "./distribution.js";
23
23
  import { loadAgentLocalEnv } from "./local-env.js";
@@ -34,33 +34,26 @@ Usage:
34
34
  [--team <id>] [--json]
35
35
  ${CLI} slack icon <URL-or-FILE> [--dir .] [--slack-team T0123ABCD]
36
36
  [--prod] [--team <id>] [--json]
37
- ${CLI} slack init [--dir .] [--name NAME] [--prefix PREFIX | --no-prefix]
38
- [--channel-posts]
39
- ${CLI} slack manifest --env dev|prod|both [--dir .] [--name NAME]
40
- [--print]
41
37
  ${CLI} slack doctor [--dir .] [--prefix PREFIX | --no-prefix] [--json]
42
38
 
43
- create/destroy/icon — Cursor "Add to Slack" managed provisioning:
44
- create One-command Path B setup: connects the Slack workspace (browser
45
- consent) when needed, creates + installs this agent's dedicated
46
- Socket Mode app from the generated manifest, writes the token
47
- pair into <dir>/.env.local, then runs doctor (prose mode; --json
48
- reports without the doctor pass). Requires
49
- \`${CLI} login\` (or CURSOR_API_KEY) and the feature gate.
50
- --prod provisions the prod app (default dev); --slack-team picks
51
- the workspace when several are connected. --icon sets the app's
52
- icon (best-effort: a bad icon warns, never fails the create).
53
- A re-run refuses to overwrite manifest edits made by hand at
54
- api.slack.com (manifest_drift); --force overwrites them.
39
+ create/destroy/icon — dedicated Socket Mode app via the Cursor dashboard:
40
+ create Opens the signed-in dashboard wizard. Finish name, workspace,
41
+ and Slack consent there. The CLI writes the token pair into
42
+ <dir>/.env.local and runs doctor. Requires \`${CLI} login\`
43
+ (or CURSOR_API_KEY). --prod provisions the prod app (default
44
+ dev). --name / --icon / --channel-posts prefill the wizard.
55
45
  destroy Delete the provisioned Slack app for this agent (+env). Tokens
56
46
  already in .env.local are left in place (they just stop working).
57
47
  icon Set the provisioned app's icon: an https image URL or a local
58
48
  png/jpg/gif file (at most 512KB).
59
49
 
50
+ \`slack init\` is removed. Use \`slack create\`. Do not paste a manifest
51
+ at api.slack.com.
52
+
60
53
  Env tokens:
61
54
  single agent: SLACK_BOT_TOKEN + SLACK_APP_TOKEN
62
55
  multi-agent: <PREFIX>_SLACK_BOT_TOKEN + <PREFIX>_SLACK_APP_TOKEN
63
- (init/create default PREFIX from the directory name;
56
+ (create default PREFIX from the directory name;
64
57
  doctor defaults to it too when those keys are set)
65
58
 
66
59
  See also: skills/setup-slack/SKILL.md
@@ -98,37 +91,13 @@ export function cmdSlack(subcommand, options) {
98
91
  }
99
92
  function cmdSlackInit(options) {
100
93
  return __awaiter(this, void 0, void 0, function* () {
101
- var _a;
102
- const dir = resolvePath(options.dir);
103
- try {
104
- const result = yield initSlackChannel(Object.assign(Object.assign(Object.assign({ dir }, (options.name === undefined ? {} : { name: options.name })), (options.noPrefix === true
105
- ? { envPrefix: "" }
106
- : options.prefix === undefined
107
- ? {}
108
- : { envPrefix: options.prefix })), (options.channelPosts === true ? { channelPosts: true } : {})));
109
- process.stdout.write(`${JSON.stringify({
110
- ok: true,
111
- directory: result.rootDir,
112
- created: result.files,
113
- envPrefix: (_a = result.envPrefix) !== null && _a !== void 0 ? _a : null,
114
- envKeys: result.envKeys,
115
- next: [
116
- "Open https://api.slack.com/apps → Create from manifest",
117
- "Import .agent-serve/slack/manifest.dev.json (then prod)",
118
- `Install apps; set ${result.envKeys.bot} + ${result.envKeys.app}`,
119
- result.envPrefix === undefined
120
- ? "agent-serve slack doctor"
121
- : `agent-serve slack doctor --prefix ${result.envPrefix}`,
122
- "agent-serve serve --dir . --dev",
123
- "agent-serve slack setup",
124
- ],
125
- }, null, 2)}\n`);
126
- return 0;
127
- }
128
- catch (error) {
129
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
130
- return 1;
131
- }
94
+ errStream(options)(`\`slack init\` is removed. Socket Mode apps are created with:
95
+
96
+ ${CLI} slack create --dir .
97
+
98
+ That opens the Cursor dashboard wizard. Do not paste a manifest at api.slack.com.
99
+ `);
100
+ return 1;
132
101
  });
133
102
  }
134
103
  function runManifest(options) {
@@ -137,10 +106,11 @@ function runManifest(options) {
137
106
  const env = (_a = options.env) !== null && _a !== void 0 ? _a : "both";
138
107
  const dir = resolvePath(options.dir);
139
108
  const name = (_b = options.name) !== null && _b !== void 0 ? _b : basename(dir);
140
- const pair = buildSlackManifestPair(Object.assign({ name,
141
- // These files are pasted into api.slack.com by hand, so they must not
142
- // carry the manager-token-only managed_app_settings block.
143
- managedByCursor: false }, (options.channelPosts === true ? { channelPosts: true } : {})));
109
+ const pair = buildSlackManifestPair({
110
+ name,
111
+ managedByCursor: false,
112
+ channelPosts: options.channelPosts === true ? true : undefined,
113
+ });
144
114
  if (options.print) {
145
115
  if (env === "dev" || env === "both") {
146
116
  process.stdout.write(`${JSON.stringify(pair.dev, null, 2)}\n`);
@@ -210,8 +180,8 @@ function resolveDoctorEnvPrefix(options, dir) {
210
180
  // ============================================================================
211
181
  // create / destroy — Cursor "Add to Slack" managed provisioning
212
182
  // ============================================================================
213
- const DEFAULT_CONNECT_POLL_INTERVAL_MS = 3000;
214
- const DEFAULT_CONNECT_POLL_TIMEOUT_MS = 5 * 60 * 1000;
183
+ const DEFAULT_WIZARD_POLL_INTERVAL_MS = 3000;
184
+ const DEFAULT_WIZARD_POLL_TIMEOUT_MS = 60 * 60 * 1000;
215
185
  function resolveSlackAppTarget(options, err) {
216
186
  var _a, _b;
217
187
  if (options.env === "both" && options.prod !== true) {
@@ -285,38 +255,26 @@ function toSlackAppIconInput(source) {
285
255
  : { base64: source.base64, contentType: source.contentType };
286
256
  }
287
257
  /**
288
- * URL icons ride the provision request; file icons post to the icon
289
- * endpoint afterwards. Best-effort: failures warn, never fail the create.
258
+ * File icons post after the wizard finishes. URL icons ride the mint
259
+ * payload. Best-effort: failures warn, never fail the create.
290
260
  */
291
- function applySlackCreateIcon(ctx, source, result) {
261
+ function applySlackCreateFileIcon(ctx, source, appId) {
292
262
  return __awaiter(this, void 0, void 0, function* () {
293
- if (source === undefined) {
263
+ if (source === undefined || source.kind === "url") {
294
264
  return undefined;
295
265
  }
296
- let failure;
297
- if (source.kind === "url") {
298
- if (result.iconStatus !== "set") {
299
- failure = "Slack did not accept the icon URL";
300
- }
301
- }
302
- else {
303
- try {
304
- yield ctx.client.setAppIcon({
305
- teamId: ctx.teamId,
306
- slackAppId: result.appId,
307
- icon: toSlackAppIconInput(source),
308
- });
309
- }
310
- catch (error) {
311
- failure = describeError(error);
312
- }
313
- }
314
- if (failure === undefined) {
266
+ try {
267
+ yield ctx.client.setAppIcon({
268
+ teamId: ctx.teamId,
269
+ slackAppId: appId,
270
+ icon: toSlackAppIconInput(source),
271
+ });
315
272
  return "set";
316
273
  }
317
- const retryWith = source.kind === "url" ? source.url : source.path;
318
- ctx.err(`warning: setting the Slack app icon failed: ${failure}\nRetry with \`${CLI} slack icon ${retryWith}\`.\n`);
319
- return "failed";
274
+ catch (error) {
275
+ ctx.err(`warning: setting the Slack app icon failed: ${describeError(error)}\nRetry with \`${CLI} slack icon ${source.path}\`.\n`);
276
+ return "failed";
277
+ }
320
278
  });
321
279
  }
322
280
  /**
@@ -351,102 +309,18 @@ function prepareProvision(options) {
351
309
  out,
352
310
  err,
353
311
  openBrowserImpl: (_b = options.openBrowserImpl) !== null && _b !== void 0 ? _b : openBrowser,
354
- pollIntervalMs: (_c = options.connectPollIntervalMs) !== null && _c !== void 0 ? _c : DEFAULT_CONNECT_POLL_INTERVAL_MS,
355
- pollTimeoutMs: (_d = options.connectPollTimeoutMs) !== null && _d !== void 0 ? _d : DEFAULT_CONNECT_POLL_TIMEOUT_MS,
312
+ pollIntervalMs: (_c = options.connectPollIntervalMs) !== null && _c !== void 0 ? _c : DEFAULT_WIZARD_POLL_INTERVAL_MS,
313
+ pollTimeoutMs: (_d = options.connectPollTimeoutMs) !== null && _d !== void 0 ? _d : DEFAULT_WIZARD_POLL_TIMEOUT_MS,
356
314
  };
357
315
  });
358
316
  }
359
- /**
360
- * Slack's team-scoped admin page listing pending app-install requests.
361
- * Provisioned apps have no OAuth flow (no redirect URLs, install-from-Slack
362
- * disabled), so the oauth_authorize_url Slack returns from
363
- * apps.manifest.create dead-ends; approval happens here instead.
364
- */
365
- function slackAppRequestsUrl(slackTeamId) {
366
- return `https://app.slack.com/apps-manage/${encodeURIComponent(slackTeamId)}/integrations/requests`;
367
- }
368
- /** "Name (T0123ABCD)", or the bare id when the backend has no name. */
369
- function formatWorkspace(workspace) {
370
- var _a;
371
- const name = (_a = workspace.slackTeamName) !== null && _a !== void 0 ? _a : "";
372
- return name === ""
373
- ? workspace.slackTeamId
374
- : `${name} (${workspace.slackTeamId})`;
375
- }
376
- function matchConnectedWorkspace(workspaces, wait) {
377
- const candidates = wait.slackTeamId === undefined
378
- ? workspaces
379
- : workspaces.filter((workspace) => workspace.slackTeamId === wait.slackTeamId);
380
- if (wait.notConnectedAt === undefined) {
381
- return candidates[0];
382
- }
383
- return candidates.find((workspace) => workspace.connectedAt !== wait.notConnectedAt);
384
- }
385
- /**
386
- * Mint the browser consent URL, open it, and poll `GET /status` until the
387
- * awaited workspace shows up (or the poll times out). Returns the
388
- * connected workspace, or an exit code with the error already rendered.
389
- */
390
- function runConnectFlow(ctx, wait) {
391
- return __awaiter(this, void 0, void 0, function* () {
392
- let url;
393
- try {
394
- url = yield ctx.client.connect(ctx.teamId);
395
- }
396
- catch (error) {
397
- return renderSlackApiError(error, ctx.err);
398
- }
399
- ctx.err(`\nConnect your Slack workspace to Cursor — approve in the browser:\n\n ${url}\n\n` +
400
- `After approving in Slack you land on cursor.com, which links the workspace to your Cursor account — sign in there if the browser is not already signed in as you.\n\n` +
401
- `(waiting for the Slack consent to complete, up to ${Math.round(ctx.pollTimeoutMs / 60000)} minutes)\n`);
402
- ctx.openBrowserImpl(url);
403
- const deadline = Date.now() + ctx.pollTimeoutMs;
404
- let lastPollFailure;
405
- for (;;) {
406
- let workspace;
407
- let sleepMs = ctx.pollIntervalMs;
408
- try {
409
- const status = yield ctx.client.status(ctx.teamId);
410
- workspace = matchConnectedWorkspace(status.workspaces, wait);
411
- lastPollFailure = undefined;
412
- }
413
- catch (error) {
414
- // Transient poll failures must not abort a minted consent flow; only
415
- // a still-failing poll at the deadline surfaces the error.
416
- if (Date.now() >= deadline) {
417
- return renderSlackApiError(error, ctx.err);
418
- }
419
- const failure = describeError(error);
420
- if (failure !== lastPollFailure) {
421
- lastPollFailure = failure;
422
- ctx.err(`(status poll failed, retrying: ${failure})\n`);
423
- }
424
- if (error instanceof SlackProvisionApiError &&
425
- error.retryAfterSeconds !== undefined) {
426
- // Respect backend backpressure instead of hammering at the poll rate.
427
- sleepMs = Math.max(sleepMs, error.retryAfterSeconds * 1000);
428
- }
429
- }
430
- if (workspace !== undefined) {
431
- ctx.err(`Connected Slack workspace ${workspace.slackTeamId}.\n`);
432
- return workspace;
433
- }
434
- if (Date.now() >= deadline) {
435
- ctx.err(`Timed out waiting for the Slack workspace connection. The consent link stays valid for ~10 minutes — approve it, finish the sign-in on cursor.com, then re-run \`${CLI} slack create\`.\n`);
436
- return 1;
437
- }
438
- yield sleep(sleepMs);
439
- }
440
- });
441
- }
442
317
  export function cmdSlackCreate(options) {
443
318
  return __awaiter(this, void 0, void 0, function* () {
444
- var _a;
319
+ var _a, _b, _c;
445
320
  const target = resolveSlackAppTarget(options, errStream(options));
446
321
  if (typeof target === "number") {
447
322
  return target;
448
323
  }
449
- // Resolved before any network I/O so a bad path/type/size fails fast.
450
324
  let iconSource;
451
325
  if (options.icon !== undefined) {
452
326
  const resolved = yield resolveSlackIconSource(options.icon, errStream(options));
@@ -459,38 +333,6 @@ export function cmdSlackCreate(options) {
459
333
  if (typeof ctx === "number") {
460
334
  return ctx;
461
335
  }
462
- // Workspace: --slack-team wins; one → use it; none → connect; several → ask.
463
- let status;
464
- try {
465
- status = yield ctx.client.status(ctx.teamId);
466
- }
467
- catch (error) {
468
- return renderSlackApiError(error, ctx.err);
469
- }
470
- let workspace;
471
- if (options.slackTeam !== undefined) {
472
- workspace = status.workspaces.find((candidate) => candidate.slackTeamId === options.slackTeam);
473
- }
474
- else if (status.workspaces.length === 1) {
475
- workspace = status.workspaces[0];
476
- }
477
- else if (status.workspaces.length > 1) {
478
- ctx.err(`Multiple Slack workspaces are connected:\n` +
479
- status.workspaces
480
- .map((candidate) => ` - ${formatWorkspace(candidate)} (Slack user ${candidate.slackUserId}, connected ${candidate.connectedAt})\n`)
481
- .join("") +
482
- `Pass --slack-team <id> to pick one.\n`);
483
- return 1;
484
- }
485
- if (workspace === undefined) {
486
- const connected = yield runConnectFlow(ctx, {
487
- slackTeamId: options.slackTeam,
488
- });
489
- if (typeof connected === "number") {
490
- return connected;
491
- }
492
- workspace = connected;
493
- }
494
336
  let wrote;
495
337
  try {
496
338
  wrote = yield scaffoldSlackChannelFiles(options, target);
@@ -502,90 +344,80 @@ export function cmdSlackCreate(options) {
502
344
  if (wrote.length > 0 && !ctx.json) {
503
345
  ctx.err(`Scaffolded the Slack channel: ${wrote.join(", ")}\n`);
504
346
  }
505
- // A re-run rebuilds this manifest from flags, so backend-side
506
- // customizations reset to it (documented in the skill).
507
- const manifest = buildSlackManifest({
508
- name: target.name,
509
- env: target.env,
510
- channelPosts: options.channelPosts === true,
511
- });
512
- const manifestJson = JSON.stringify(manifest);
513
- // One connect-and-retry for not_connected / insufficient_scopes /
514
- // manager_reauth_required (all fixed by a fresh consent grant).
515
- let result;
516
- let connectRetried = false;
347
+ const source = yield inferDeployGitSource(target.dir);
348
+ let minted;
349
+ try {
350
+ minted = yield ctx.client.mintWizard({
351
+ teamId: ctx.teamId,
352
+ slug: target.slug,
353
+ env: target.env,
354
+ envPrefix: target.envPrefix,
355
+ name: (_a = options.name) !== null && _a !== void 0 ? _a : target.name,
356
+ gitRepoUrl: source.gitRepoUrl,
357
+ gitRef: source.gitRef,
358
+ agentPath: source.agentPath,
359
+ channelPosts: options.channelPosts === true ? true : undefined,
360
+ slackTeamId: options.slackTeam,
361
+ iconUrl: (iconSource === null || iconSource === void 0 ? void 0 : iconSource.kind) === "url" ? iconSource.url : undefined,
362
+ force: options.force === true ? true : undefined,
363
+ });
364
+ }
365
+ catch (error) {
366
+ return renderSlackApiError(error, ctx.err);
367
+ }
368
+ ctx.err(`Finish Slack setup in the browser:\n\n ${minted.url}\n\n` +
369
+ `Sign in as the same Cursor account that ran this command. ` +
370
+ `The CLI waits here (up to ${Math.round(ctx.pollTimeoutMs / 60000)} minutes).\n`);
371
+ ctx.openBrowserImpl(minted.url);
372
+ const deadline = Date.now() + ctx.pollTimeoutMs;
373
+ let sawPendingAdmin = false;
374
+ let poll;
517
375
  for (;;) {
518
376
  try {
519
- result = yield ctx.client.provisionApp({
520
- teamId: ctx.teamId,
521
- slackTeamId: workspace.slackTeamId,
522
- slug: target.slug,
523
- env: target.env,
524
- manifest: manifestJson,
525
- icon: (iconSource === null || iconSource === void 0 ? void 0 : iconSource.kind) === "url" ? { url: iconSource.url } : undefined,
526
- force: options.force === true ? true : undefined,
527
- });
528
- break;
377
+ poll = yield ctx.client.pollWizard(minted.wizardId);
529
378
  }
530
379
  catch (error) {
531
- if (error instanceof SlackProvisionApiError &&
532
- (error.code === "not_connected" ||
533
- error.code === "insufficient_scopes" ||
534
- error.code === "manager_reauth_required") &&
535
- !connectRetried) {
536
- connectRetried = true;
537
- // All three codes arrive holding a row that GET /status already
538
- // lists, so the poll waits for a fresh grant instead of re-matching
539
- // that row.
540
- const wait = {
541
- slackTeamId: workspace.slackTeamId,
542
- notConnectedAt: workspace.connectedAt,
543
- };
544
- if (error.code === "insufficient_scopes") {
545
- ctx.err(`The Slack connection is missing scopes provisioning needs; approving the consent link again grants them.\n`);
546
- }
547
- else if (error.code === "manager_reauth_required") {
548
- ctx.err(`Slack revoked Cursor's authorization for workspace ${workspace.slackTeamId}; re-approving the consent link restores it.\n`);
549
- }
550
- else {
551
- ctx.err(`The Slack workspace is not connected to Cursor; starting the connect flow.\n`);
552
- }
553
- const connected = yield runConnectFlow(ctx, wait);
554
- if (typeof connected === "number") {
555
- return connected;
556
- }
557
- workspace = connected;
558
- continue;
380
+ if (Date.now() >= deadline) {
381
+ return renderSlackApiError(error, ctx.err);
559
382
  }
560
- return renderCreateProvisionError(error, ctx.err);
383
+ yield sleep(ctx.pollIntervalMs);
384
+ continue;
561
385
  }
562
- }
563
- const iconStatus = yield applySlackCreateIcon(ctx, iconSource, result);
564
- if (result.status === "pending_admin_approval") {
565
- const approveUrl = slackAppRequestsUrl(workspace.slackTeamId);
566
- if (ctx.json) {
567
- ctx.out(`${JSON.stringify({
568
- ok: false,
569
- appId: result.appId,
570
- status: result.status,
571
- slackTeamId: workspace.slackTeamId,
572
- envKeys: target.envKeys,
573
- wrote,
574
- approveUrl,
575
- iconStatus,
576
- }, null, 2)}\n`);
386
+ if (poll.status === "complete") {
387
+ break;
388
+ }
389
+ if (poll.status === "failed") {
390
+ ctx.err(`${(_b = poll.error) !== null && _b !== void 0 ? _b : "Slack setup failed"}\n`);
391
+ return 1;
577
392
  }
578
- else {
579
- ctx.err(`The Slack app was created (app ${result.appId}), but installing it needs a Slack workspace-admin approval.\n` +
580
- `\n A workspace admin can approve it here:\n\n ${approveUrl}\n\n` +
581
- `Once an admin approves the app, re-run \`${CLI} slack create\` to finish the install and mint the tokens.\n`);
393
+ if (poll.status === "pending_admin_approval" && !sawPendingAdmin) {
394
+ sawPendingAdmin = true;
395
+ const appSuffix = poll.appId === undefined ? "" : ` (${poll.appId})`;
396
+ ctx.err(`Waiting for a Slack admin to approve the app${appSuffix}. Keep this command running.\n` +
397
+ `Managed install does not file the request. Open Slack to submit it; that same page lets you send a reminder to workspace owners.\n`);
398
+ if (poll.oauthAuthorizeUrl !== undefined &&
399
+ poll.oauthAuthorizeUrl.length > 0) {
400
+ ctx.err(` ${poll.oauthAuthorizeUrl}\n`);
401
+ }
402
+ ctx.err("After an admin approves, click Retry in the dashboard.\n");
403
+ }
404
+ if (Date.now() >= deadline) {
405
+ ctx.err(`Timed out waiting for Slack setup. Finish it in the browser, then re-run \`${CLI} slack create\`.\n ${minted.url}\n`);
406
+ return 1;
582
407
  }
408
+ yield sleep(ctx.pollIntervalMs);
409
+ }
410
+ if (poll === undefined || poll.localEnv === undefined) {
411
+ ctx.err(poll.localEnvClaimed === true
412
+ ? "Tokens were already claimed by another poll; re-run create to rotate them.\n"
413
+ : "Slack setup finished but no tokens were returned; re-run create.\n");
583
414
  return 1;
584
415
  }
585
- // Tokens come back exactly once and are never printed.
586
- const envEntries = [{ key: target.envKeys.bot, value: result.botToken }];
587
- if (result.appToken !== undefined) {
588
- envEntries.push({ key: target.envKeys.app, value: result.appToken });
416
+ const envEntries = [
417
+ { key: target.envKeys.bot, value: poll.localEnv.botToken },
418
+ ];
419
+ if (poll.localEnv.appToken !== undefined) {
420
+ envEntries.push({ key: target.envKeys.app, value: poll.localEnv.appToken });
589
421
  }
590
422
  const envFile = join(target.dir, ".env.local");
591
423
  try {
@@ -602,29 +434,33 @@ export function cmdSlackCreate(options) {
602
434
  for (const { key, value } of envEntries) {
603
435
  process.env[key] = value;
604
436
  }
605
- if (result.appToken === undefined) {
606
- ctx.err(`warning: Slack did not return an app-level token, so ${target.envKeys.app} was not written (an existing value was left as-is and may belong to an older app); re-run \`${CLI} slack create\` to recover it.\n`);
437
+ if (poll.localEnv.appToken === undefined) {
438
+ ctx.err(`warning: Slack did not return an app-level token, so ${target.envKeys.app} was not written; re-run \`${CLI} slack create\` to recover it.\n`);
439
+ }
440
+ let iconStatus;
441
+ if (poll.appId !== undefined) {
442
+ iconStatus = yield applySlackCreateFileIcon(ctx, iconSource, poll.appId);
607
443
  }
608
444
  if (ctx.json) {
609
445
  ctx.out(`${JSON.stringify({
610
446
  ok: true,
611
- appId: result.appId,
612
- status: result.status,
613
- slackTeamId: workspace.slackTeamId,
447
+ appId: poll.appId,
448
+ status: poll.status,
614
449
  envKeys: target.envKeys,
615
450
  wrote,
616
451
  iconStatus,
617
452
  }, null, 2)}\n`);
618
453
  return 0;
619
454
  }
620
- ctx.out(`Provisioned the ${target.env} Slack app for ${target.slug} (app ${result.appId}) in workspace ${workspace.slackTeamId}.\n` +
621
- ` wrote ${envEntries.map((entry) => entry.key).join(" + ")} to ${envFile}\n\n`);
622
- const doctor = (_a = options.doctorImpl) !== null && _a !== void 0 ? _a : runSlackDoctor;
455
+ ctx.out(`Slack is connected for ${target.slug}` +
456
+ (poll.appId === undefined ? "" : ` (app ${poll.appId})`) +
457
+ `.\n wrote ${envEntries.map((entry) => entry.key).join(" + ")} to ${envFile}\n\n`);
458
+ const doctor = (_c = options.doctorImpl) !== null && _c !== void 0 ? _c : runSlackDoctor;
623
459
  const doctorResult = yield doctor(target.envPrefix === undefined ? {} : { envPrefix: target.envPrefix });
624
460
  ctx.out(renderDoctorReport(doctorResult));
625
461
  ctx.out(`\nNext steps:\n` +
626
- ` 1. ${CLI} dev ${options.dir}\n` +
627
- ` 2. In Slack, invite @${manifest.features.bot_user.display_name} to a channel and @mention it (or DM it).\n`);
462
+ ` 1. ${CLI} serve --dir ${options.dir} --dev\n` +
463
+ ` 2. Invite the bot in Slack and @mention it.\n`);
628
464
  return 0;
629
465
  });
630
466
  }
@@ -758,10 +594,8 @@ function formatAppList(apps) {
758
594
  .join("");
759
595
  }
760
596
  /**
761
- * Scaffold the channel files when the channel file is absent. Existing
762
- * files are never overwritten: initSlackChannel writes with `wx`, and
763
- * EEXIST means "already scaffolded" (provisioning builds its manifest in
764
- * memory and does not need the on-disk copies).
597
+ * Scaffold `agent/channels/slack.ts` when absent. Existing files are
598
+ * never overwritten: initSlackChannel writes with `wx`.
765
599
  */
766
600
  function scaffoldSlackChannelFiles(options, target) {
767
601
  return __awaiter(this, void 0, void 0, function* () {
@@ -877,42 +711,12 @@ function renderDoctorReport(result) {
877
711
  text += result.ok ? "doctor: pass\n" : "doctor: fail\n";
878
712
  return text;
879
713
  }
880
- /**
881
- * Actionable rendering for `POST /apps` failures the create flow does not
882
- * retry itself (the connect-and-retry codes are handled by the caller).
883
- */
884
- function renderCreateProvisionError(error, err) {
885
- var _a;
886
- if (!(error instanceof SlackProvisionApiError)) {
887
- throw error;
888
- }
889
- switch (error.code) {
890
- case "app_conflict":
891
- err(`${error.message}\nA concurrent \`${CLI} slack create\` for this agent won the race; re-run create to pick up that app.\n`);
892
- return 1;
893
- case "ratelimited":
894
- err(`${error.message}\nRe-run \`${CLI} slack create\` after the wait.\n`);
895
- return 1;
896
- case "invalid_app_id":
897
- err(`${error.message}\nThe provisioned Slack app seems to have been deleted outside Cursor. Run \`${CLI} slack destroy\` to drop the stale record, then re-run \`${CLI} slack create\`.\n`);
898
- return 1;
899
- case "manifest_drift":
900
- err(`${error.message}\n` +
901
- ((_a = error.changedPaths) !== null && _a !== void 0 ? _a : []).map((path) => ` - ${path}\n`).join("") +
902
- `Re-run \`${CLI} slack create --force\` to overwrite those edits.\n`);
903
- return 1;
904
- default:
905
- return renderSlackApiError(error, err);
906
- }
907
- }
908
714
  function renderSlackApiError(error, err) {
909
715
  if (!(error instanceof SlackProvisionApiError)) {
910
716
  throw error;
911
717
  }
912
718
  err(error.code === "manager_reauth_required"
913
- ? // There is no standalone connect command: create runs the consent
914
- // flow before provisioning.
915
- `${error.message}\nRe-approve the Slack consent by re-running \`${CLI} slack create\`, which reconnects the workspace first.\n`
719
+ ? `${error.message}\nRe-run \`${CLI} slack create\` and finish Slack consent in the dashboard.\n`
916
720
  : `${error.message}\n`);
917
721
  return 1;
918
722
  }
@@ -681,7 +681,7 @@ function emitSlackChannel(automation, triggers, slugs, out) {
681
681
  out.warnings.push("The Slack trigger marked triggering messages with a completion reaction; agentkit has no completion-reaction option, so that signal is lost unless agent/instructions.md tells the agent to add the reaction itself via Slack tools.");
682
682
  }
683
683
  if (socketMode) {
684
- out.setupSteps.push(`Slack channel-watch / reaction / channel-created triggers need a dedicated Socket Mode Slack app (not the Cursor connection): run \`${CLI_COMMAND_NAME} slack setup\`, then \`${CLI_COMMAND_NAME} slack init --channel-posts\`, and set the generated tokens.`);
684
+ out.setupSteps.push(`Slack channel-watch / reaction / channel-created triggers need a dedicated Socket Mode Slack app (not the Cursor connection): run \`${CLI_COMMAND_NAME} slack create --channel-posts\`, then finish in the dashboard wizard.`);
685
685
  }
686
686
  const header = [
687
687
  `import { slackChannel } from "${PACKAGE_NAME}/channels/slack";`,
@@ -727,7 +727,7 @@ function emitSlackChannel(automation, triggers, slugs, out) {
727
727
  body = [
728
728
  "//",
729
729
  "// This channel needs a dedicated Socket Mode Slack app (not the Cursor",
730
- `// connection): run \`${CLI_COMMAND_NAME} slack setup\` and \`${CLI_COMMAND_NAME} slack init\`.`,
730
+ `// connection): run \`${CLI_COMMAND_NAME} slack create --channel-posts\`.`,
731
731
  "export default slackChannel({",
732
732
  ...engagement,
733
733
  "});",