@cursor/july 0.1.39 → 0.1.41
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.
- package/AGENTS.md +4 -3
- package/dist/bin/agent-serve.js +28 -9
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +18 -8
- package/dist/channels/slack/attachments.d.ts +82 -0
- package/dist/channels/slack/attachments.d.ts.map +1 -0
- package/dist/channels/slack/attachments.js +520 -0
- package/dist/channels/slack/cursor-account.d.ts +7 -1
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +2 -2
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +26 -17
- package/dist/channels/slack/dispatch.d.ts +4 -3
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +84 -21
- package/dist/channels/slack/inbound.d.ts +6 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +48 -2
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +16 -4
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +1 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +7 -4
- package/dist/channels/slack/types.d.ts +49 -1
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +6 -6
- package/dist/docs/assets/{ab.md.6cLOW7--.js → ab.md.hdxsvX5M.js} +2 -2
- package/dist/docs/assets/{app.BQ8Hihdf.js → app.mIw24pb1.js} +1 -1
- package/dist/docs/assets/{building-with-agents.md.txrcGU2B.js → building-with-agents.md.PeZaZA1P.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BA7V8dEJ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BBCr8Yuy.js → VPLocalSearchBox.hOzpPwrG.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CK_NiGC-.js → theme.BtFhUYmh.js} +2 -2
- package/dist/docs/assets/{deployment.md.CuK5SNjN.js → deployment.md.BHVjVqRa.js} +4 -4
- package/dist/docs/assets/{deployment.md.CuK5SNjN.lean.js → deployment.md.BHVjVqRa.lean.js} +1 -1
- package/dist/docs/assets/{evals.md.BQXI3rXy.js → evals.md.qeoGgPw0.js} +3 -3
- package/dist/docs/assets/example-agents_index.md.xmJ87d_3.js +2 -0
- package/dist/docs/assets/{guides_webhooks.md.BERuBSJW.js → guides_webhooks.md.DB-r_er9.js} +47 -4
- package/dist/docs/assets/{guides_webhooks.md.BERuBSJW.lean.js → guides_webhooks.md.DB-r_er9.lean.js} +1 -1
- package/dist/docs/assets/index.md.BKpbkCa6.js +24 -0
- package/dist/docs/assets/{index.md.CmhptOmN.lean.js → index.md.BKpbkCa6.lean.js} +1 -1
- package/dist/docs/assets/{quickstart.md.C_b6ESpD.js → quickstart.md.rIbfahJM.js} +4 -4
- package/dist/docs/assets/{reference_cli.md.Byvrg8eu.js → reference_cli.md.DqEqQpGn.js} +5 -5
- package/dist/docs/assets/{reference_cli.md.Byvrg8eu.lean.js → reference_cli.md.DqEqQpGn.lean.js} +1 -1
- package/dist/docs/assets/{reference_hooks.md.BGDw4VLm.js → reference_hooks.md.BHeG-vjK.js} +2 -2
- package/dist/docs/assets/{reference_hooks.md.BGDw4VLm.lean.js → reference_hooks.md.BHeG-vjK.lean.js} +1 -1
- package/dist/docs/assets/{storage.md.mQDtIULc.js → storage.md.DcGSnIxX.js} +10 -4
- package/dist/docs/assets/{storage.md.mQDtIULc.lean.js → storage.md.DcGSnIxX.lean.js} +1 -1
- package/dist/docs/assets/troubleshooting.md.CkgwmVI0.js +1 -0
- package/dist/docs/assets/{troubleshooting.md.B5RVX_tL.lean.js → troubleshooting.md.CkgwmVI0.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +5 -5
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +6 -6
- package/dist/docs/evals.html +7 -7
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +5 -5
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +49 -6
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +7 -7
- package/dist/docs/quickstart.html +8 -8
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +8 -8
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +5 -5
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +14 -8
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +22 -2
- package/dist/internal/cli-deploy.d.ts +6 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +14 -4
- package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
- package/dist/internal/cli-mcp-oauth.js +3 -1
- package/dist/internal/cli-mcp.js +4 -4
- package/dist/internal/cursor-event-relay.d.ts +45 -0
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +96 -2
- package/dist/internal/deploy-client.js +1 -1
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +35 -7
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +3 -1
- package/dist/internal/durable-session-store.d.ts +48 -0
- package/dist/internal/durable-session-store.d.ts.map +1 -0
- package/dist/internal/durable-session-store.js +86 -0
- package/dist/internal/hosting.d.ts +1 -1
- package/dist/internal/hosting.js +2 -2
- package/dist/internal/init-project.d.ts +34 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +121 -2
- package/dist/internal/ledger-serving.d.ts +101 -0
- package/dist/internal/ledger-serving.d.ts.map +1 -0
- package/dist/internal/ledger-serving.js +174 -0
- package/dist/internal/listen-port.d.ts +22 -0
- package/dist/internal/listen-port.d.ts.map +1 -0
- package/dist/internal/listen-port.js +77 -0
- package/dist/internal/mcp-oauth.d.ts +12 -1
- package/dist/internal/mcp-oauth.d.ts.map +1 -1
- package/dist/internal/mcp-oauth.js +25 -3
- package/dist/internal/playground/static.d.ts +21 -4
- package/dist/internal/playground/static.d.ts.map +1 -1
- package/dist/internal/playground/static.js +82 -18
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +10 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +67 -49
- package/dist/internal/session-engine.d.ts +57 -1
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +117 -28
- package/dist/internal/slack-provision-client.js +1 -1
- package/dist/internal/workspace.d.ts +6 -2
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +10 -2
- package/dist/kv/memory-kv.d.ts +23 -0
- package/dist/kv/memory-kv.d.ts.map +1 -0
- package/dist/kv/memory-kv.js +103 -0
- package/dist/kv.d.ts +22 -0
- package/dist/kv.d.ts.map +1 -0
- package/dist/kv.js +20 -0
- package/dist/ledger/session-ledger.d.ts +204 -0
- package/dist/ledger/session-ledger.d.ts.map +1 -0
- package/dist/ledger/session-ledger.js +382 -0
- package/dist/ledger.d.ts +11 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +9 -0
- package/dist/playground/assets/index-B3HBneZZ.js +85 -0
- package/dist/playground/assets/index-Bqk9nplh.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +25 -3
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +50 -2
- package/dist/storage-backends/file-kv.d.ts +5 -2
- package/dist/storage-backends/file-kv.d.ts.map +1 -1
- package/dist/storage-backends/file-kv.js +107 -7
- package/dist/storage.d.ts +60 -5
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +28 -4
- package/dist/types.d.ts +123 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -0
- package/docs/README.md +10 -7
- package/docs/ab.md +2 -2
- package/docs/building-with-agents.md +1 -1
- package/docs/deployment.md +34 -11
- package/docs/evals.md +3 -3
- package/docs/example-agents/index.md +1 -1
- package/docs/guides/webhooks.md +87 -0
- package/docs/quickstart.md +7 -8
- package/docs/reference/cli.md +15 -6
- package/docs/reference/hooks.md +8 -2
- package/docs/storage.md +67 -7
- package/docs/troubleshooting.md +10 -2
- package/package.json +20 -7
- package/skills/create-agent/SKILL.md +1 -1
- package/src/bin/agent-serve.ts +27 -11
- package/src/channels/slack/api.ts +19 -14
- package/src/channels/slack/attachments.ts +635 -0
- package/src/channels/slack/cursor-account.ts +8 -0
- package/src/channels/slack/defaults.ts +34 -18
- package/src/channels/slack/dispatch.ts +121 -6
- package/src/channels/slack/inbound.ts +60 -0
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/live-delivery.ts +17 -3
- package/src/channels/slack/manifest.ts +1 -0
- package/src/channels/slack/slack-channel.ts +7 -0
- package/src/channels/slack/types.ts +47 -1
- package/src/index.ts +9 -0
- package/src/internal/cli-ax.ts +25 -1
- package/src/internal/cli-deploy.ts +22 -4
- package/src/internal/cli-mcp-oauth.ts +3 -1
- package/src/internal/cli-mcp.ts +4 -4
- package/src/internal/cursor-event-relay.ts +168 -0
- package/src/internal/deploy-client.ts +1 -1
- package/src/internal/discovery.ts +42 -5
- package/src/internal/distribution.ts +3 -1
- package/src/internal/durable-session-store.ts +95 -0
- package/src/internal/hosting.ts +2 -2
- package/src/internal/init-project.ts +151 -2
- package/src/internal/ledger-serving.ts +240 -0
- package/src/internal/listen-port.ts +85 -0
- package/src/internal/mcp-oauth.ts +29 -4
- package/src/internal/playground/static.ts +111 -20
- package/src/internal/sdk-runner.ts +11 -1
- package/src/internal/server.ts +76 -62
- package/src/internal/session-engine.ts +192 -16
- package/src/internal/slack-provision-client.ts +1 -1
- package/src/internal/workspace.ts +14 -3
- package/src/kv/memory-kv.ts +99 -0
- package/src/kv.ts +27 -0
- package/src/ledger/session-ledger.ts +535 -0
- package/src/ledger.ts +29 -0
- package/src/storage-backends/cursor-hosted.ts +108 -3
- package/src/storage-backends/file-kv.ts +128 -12
- package/src/storage.ts +90 -6
- package/src/types.ts +130 -4
- package/dist/channels/slack/post-update-delivery.d.ts +0 -85
- package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.B-VTH4As.js +0 -1
- package/dist/docs/assets/example-agents_index.md.DgGBwckv.js +0 -2
- package/dist/docs/assets/index.md.CmhptOmN.js +0 -24
- package/dist/docs/assets/troubleshooting.md.B5RVX_tL.js +0 -1
- package/dist/internal/json-dir-store.d.ts +0 -32
- package/dist/internal/json-dir-store.d.ts.map +0 -1
- package/dist/internal/persistence-coordinator.d.ts +0 -127
- package/dist/internal/persistence-coordinator.d.ts.map +0 -1
- package/dist/multi-tenant.d.ts +0 -80
- package/dist/multi-tenant.d.ts.map +0 -1
- package/dist/multi-tenant.js +0 -69
- package/dist/persistence.d.ts +0 -184
- package/dist/persistence.d.ts.map +0 -1
- package/dist/playground/assets/index-BRhBbnd4.css +0 -1
- package/dist/playground/assets/index-_2WKw9a-.js +0 -85
- package/dist/storage-backends/postgres-kv.d.ts +0 -82
- package/dist/storage-backends/postgres-kv.d.ts.map +0 -1
- package/dist/storage-backends/postgres-kv.js +0 -185
- package/src/storage-backends/postgres-kv.ts +0 -253
- /package/dist/docs/assets/{ab.md.6cLOW7--.lean.js → ab.md.hdxsvX5M.lean.js} +0 -0
- /package/dist/docs/assets/{building-with-agents.md.txrcGU2B.lean.js → building-with-agents.md.PeZaZA1P.lean.js} +0 -0
- /package/dist/docs/assets/{evals.md.BQXI3rXy.lean.js → evals.md.qeoGgPw0.lean.js} +0 -0
- /package/dist/docs/assets/{example-agents_index.md.DgGBwckv.lean.js → example-agents_index.md.xmJ87d_3.lean.js} +0 -0
- /package/dist/docs/assets/{quickstart.md.C_b6ESpD.lean.js → quickstart.md.rIbfahJM.lean.js} +0 -0
|
@@ -155,6 +155,25 @@ export function buildDefaultEvents(
|
|
|
155
155
|
const getLog = options.getLog ?? (() => defaultSlackLog);
|
|
156
156
|
const strategy = createStreamDeliveryStrategy({ botToken, getLog });
|
|
157
157
|
|
|
158
|
+
const clearWorkingStatus = async (
|
|
159
|
+
channelId: string,
|
|
160
|
+
threadTs: string
|
|
161
|
+
): Promise<void> => {
|
|
162
|
+
const result = await setThreadStatus({
|
|
163
|
+
botToken,
|
|
164
|
+
channelId,
|
|
165
|
+
threadTs,
|
|
166
|
+
status: "",
|
|
167
|
+
});
|
|
168
|
+
if (!result.ok) {
|
|
169
|
+
getLog().warn("assistant.threads.setStatus not-ok", {
|
|
170
|
+
error: result.error,
|
|
171
|
+
channel: channelId,
|
|
172
|
+
thread_ts: threadTs,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
158
177
|
return {
|
|
159
178
|
async "turn.started"(event, channel) {
|
|
160
179
|
const log = getLog();
|
|
@@ -366,15 +385,6 @@ export function buildDefaultEvents(
|
|
|
366
385
|
return;
|
|
367
386
|
}
|
|
368
387
|
|
|
369
|
-
const clearStatus = async (): Promise<void> => {
|
|
370
|
-
await setThreadStatus({
|
|
371
|
-
botToken,
|
|
372
|
-
channelId: state.channelId!,
|
|
373
|
-
threadTs: state.threadTs!,
|
|
374
|
-
status: "",
|
|
375
|
-
});
|
|
376
|
-
};
|
|
377
|
-
|
|
378
388
|
// Empty final text: still close any open stream / clear the skeleton
|
|
379
389
|
// and clear typing.
|
|
380
390
|
if (event.data.text === "") {
|
|
@@ -386,7 +396,7 @@ export function buildDefaultEvents(
|
|
|
386
396
|
});
|
|
387
397
|
channel.state.streamTs = null;
|
|
388
398
|
}
|
|
389
|
-
await
|
|
399
|
+
await clearWorkingStatus(state.channelId, state.threadTs);
|
|
390
400
|
return;
|
|
391
401
|
}
|
|
392
402
|
|
|
@@ -405,7 +415,7 @@ export function buildDefaultEvents(
|
|
|
405
415
|
sessionId: event.sessionId,
|
|
406
416
|
});
|
|
407
417
|
if (delivered) {
|
|
408
|
-
await
|
|
418
|
+
await clearWorkingStatus(state.channelId, state.threadTs);
|
|
409
419
|
return;
|
|
410
420
|
}
|
|
411
421
|
}
|
|
@@ -439,7 +449,7 @@ export function buildDefaultEvents(
|
|
|
439
449
|
);
|
|
440
450
|
}
|
|
441
451
|
|
|
442
|
-
await
|
|
452
|
+
await clearWorkingStatus(state.channelId, state.threadTs);
|
|
443
453
|
},
|
|
444
454
|
|
|
445
455
|
async "turn.completed"(event, channel) {
|
|
@@ -453,6 +463,17 @@ export function buildDefaultEvents(
|
|
|
453
463
|
|
|
454
464
|
await strategy.cleanupOrphan({ channel });
|
|
455
465
|
|
|
466
|
+
// Always drop the assistant chip. message.completed already clears
|
|
467
|
+
// after a normal reply, but a tool-call-only finish (or a missed
|
|
468
|
+
// final assistant message) never hits that path — leaving
|
|
469
|
+
// "Working…" stuck until Slack expires it.
|
|
470
|
+
if (channel.state.channelId !== null && channel.state.threadTs !== null) {
|
|
471
|
+
await clearWorkingStatus(
|
|
472
|
+
channel.state.channelId,
|
|
473
|
+
channel.state.threadTs
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
456
477
|
if (
|
|
457
478
|
suggestedPrompts.length === 0 ||
|
|
458
479
|
channel.state.channelId === null ||
|
|
@@ -518,12 +539,7 @@ export function buildDefaultEvents(
|
|
|
518
539
|
);
|
|
519
540
|
}
|
|
520
541
|
}
|
|
521
|
-
await
|
|
522
|
-
botToken,
|
|
523
|
-
channelId: state.channelId,
|
|
524
|
-
threadTs: state.threadTs,
|
|
525
|
-
status: "",
|
|
526
|
-
});
|
|
542
|
+
await clearWorkingStatus(state.channelId, state.threadTs);
|
|
527
543
|
},
|
|
528
544
|
};
|
|
529
545
|
}
|
|
@@ -10,6 +10,11 @@ import type {
|
|
|
10
10
|
} from "../../types.js";
|
|
11
11
|
import { buildSlackBinding, slackContinuationKey } from "./api.js";
|
|
12
12
|
import { firstLineTitle, maybeSetThreadTitle } from "./assistant.js";
|
|
13
|
+
import {
|
|
14
|
+
appendSlackAttachmentAnnotations,
|
|
15
|
+
prepareSlackAttachments,
|
|
16
|
+
SLACK_ATTACHMENT_ONLY_PROMPT,
|
|
17
|
+
} from "./attachments.js";
|
|
13
18
|
import type { SlackBotMentionGate } from "./bot-mentions.js";
|
|
14
19
|
import type { ChannelPostWatcher } from "./channel-watch.js";
|
|
15
20
|
import {
|
|
@@ -23,6 +28,7 @@ import {
|
|
|
23
28
|
parseAppMentionEvent,
|
|
24
29
|
parseChannelMessageEvent,
|
|
25
30
|
parseDirectMessageEvent,
|
|
31
|
+
parseReactionAddedEvent,
|
|
26
32
|
} from "./inbound.js";
|
|
27
33
|
import { defaultSlackLog, type SlackLog } from "./log.js";
|
|
28
34
|
import { isWholeMessageStop } from "./stop.js";
|
|
@@ -31,6 +37,7 @@ import {
|
|
|
31
37
|
loadSlackThreadDeltaBlock,
|
|
32
38
|
} from "./thread-context.js";
|
|
33
39
|
import type {
|
|
40
|
+
SlackBotToken,
|
|
34
41
|
SlackChannelState,
|
|
35
42
|
SlackContinuationProbes,
|
|
36
43
|
SlackCredentials,
|
|
@@ -41,6 +48,55 @@ import type {
|
|
|
41
48
|
|
|
42
49
|
const FOLLOWUP_TYPING = "Saw your follow-up…";
|
|
43
50
|
|
|
51
|
+
function slackUserText(message: SlackMessage, override?: string): string {
|
|
52
|
+
if (override !== undefined) {
|
|
53
|
+
return override;
|
|
54
|
+
}
|
|
55
|
+
if (message.markdown.trim().length > 0) {
|
|
56
|
+
return message.markdown;
|
|
57
|
+
}
|
|
58
|
+
if (message.files !== undefined && message.files.length > 0) {
|
|
59
|
+
return SLACK_ATTACHMENT_ONLY_PROMPT;
|
|
60
|
+
}
|
|
61
|
+
return message.markdown;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function slackTurnMedia(args: {
|
|
65
|
+
message: SlackMessage;
|
|
66
|
+
decision: SlackDispatchDecision;
|
|
67
|
+
botToken: SlackBotToken | undefined;
|
|
68
|
+
log: SlackLog;
|
|
69
|
+
}): Promise<{
|
|
70
|
+
userText: string;
|
|
71
|
+
workspaceFiles?: SlackDispatchDecision["workspaceFiles"];
|
|
72
|
+
images?: SlackDispatchDecision["images"];
|
|
73
|
+
}> {
|
|
74
|
+
const prepared =
|
|
75
|
+
args.message.files !== undefined && args.message.files.length > 0
|
|
76
|
+
? await prepareSlackAttachments({
|
|
77
|
+
botToken: args.botToken,
|
|
78
|
+
files: args.message.files,
|
|
79
|
+
log: args.log,
|
|
80
|
+
})
|
|
81
|
+
: undefined;
|
|
82
|
+
|
|
83
|
+
const workspaceFiles = {
|
|
84
|
+
...args.decision.workspaceFiles,
|
|
85
|
+
...prepared?.workspaceFiles,
|
|
86
|
+
};
|
|
87
|
+
const images = [...(args.decision.images ?? []), ...(prepared?.images ?? [])];
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
userText: appendSlackAttachmentAnnotations(
|
|
91
|
+
slackUserText(args.message, args.decision.message),
|
|
92
|
+
prepared?.annotations ?? []
|
|
93
|
+
),
|
|
94
|
+
workspaceFiles:
|
|
95
|
+
Object.keys(workspaceFiles).length > 0 ? workspaceFiles : undefined,
|
|
96
|
+
images: images.length > 0 ? images : undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
44
100
|
/** Blank per-session adapter state. */
|
|
45
101
|
export function emptySlackChannelState(
|
|
46
102
|
patch: Partial<SlackChannelState> = {}
|
|
@@ -68,6 +124,7 @@ export interface SlackInboundDispatchDeps extends SlackContinuationProbes {
|
|
|
68
124
|
waitUntil: (promise: Promise<unknown>) => void;
|
|
69
125
|
onAppMention: SlackMessageHandler;
|
|
70
126
|
onDirectMessage: SlackMessageHandler;
|
|
127
|
+
onReactionAdded?: SlackMessageHandler;
|
|
71
128
|
credentials: SlackCredentials | undefined;
|
|
72
129
|
eventIds: EventIdDedupe;
|
|
73
130
|
host: HostContext;
|
|
@@ -117,7 +174,12 @@ function triggeringMergeMessage(
|
|
|
117
174
|
}
|
|
118
175
|
|
|
119
176
|
export async function dispatchInboundMessage(args: {
|
|
120
|
-
kind:
|
|
177
|
+
kind:
|
|
178
|
+
| "app_mention"
|
|
179
|
+
| "direct_message"
|
|
180
|
+
| "channel_post"
|
|
181
|
+
| "reaction_added"
|
|
182
|
+
| "block_action";
|
|
121
183
|
message: SlackMessage;
|
|
122
184
|
handler: SlackMessageHandler;
|
|
123
185
|
send: SendMessageFn;
|
|
@@ -302,7 +364,22 @@ export async function dispatchInboundMessage(args: {
|
|
|
302
364
|
return;
|
|
303
365
|
}
|
|
304
366
|
|
|
305
|
-
const
|
|
367
|
+
const media = await slackTurnMedia({
|
|
368
|
+
message,
|
|
369
|
+
decision,
|
|
370
|
+
botToken: args.credentials?.botToken,
|
|
371
|
+
log,
|
|
372
|
+
});
|
|
373
|
+
if (message.files !== undefined && message.files.length > 0) {
|
|
374
|
+
log.info("slack attachments prepared", {
|
|
375
|
+
kind,
|
|
376
|
+
channel: message.channelId,
|
|
377
|
+
thread_ts: message.threadTs,
|
|
378
|
+
images: media.images?.length ?? 0,
|
|
379
|
+
workspace_files: Object.keys(media.workspaceFiles ?? {}).length,
|
|
380
|
+
event_id: args.eventId,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
306
383
|
const excludeThreadTimestamps = [
|
|
307
384
|
message.ts,
|
|
308
385
|
...(decision.excludeThreadTimestamps ?? []),
|
|
@@ -347,6 +424,7 @@ export async function dispatchInboundMessage(args: {
|
|
|
347
424
|
...(afterTs === undefined ? {} : { after_ts: afterTs }),
|
|
348
425
|
});
|
|
349
426
|
}
|
|
427
|
+
|
|
350
428
|
const prompt = [
|
|
351
429
|
formatSlackContextBlock({
|
|
352
430
|
userId: message.author?.userId ?? "",
|
|
@@ -360,7 +438,7 @@ export async function dispatchInboundMessage(args: {
|
|
|
360
438
|
...(threadContext === undefined ? [] : [threadContext]),
|
|
361
439
|
...(decision.context ?? []),
|
|
362
440
|
"",
|
|
363
|
-
userText,
|
|
441
|
+
media.userText,
|
|
364
442
|
]
|
|
365
443
|
.filter((part) => part.length > 0)
|
|
366
444
|
.join("\n");
|
|
@@ -378,7 +456,8 @@ export async function dispatchInboundMessage(args: {
|
|
|
378
456
|
}) as unknown as JsonValue,
|
|
379
457
|
title: decision.title ?? `slack:${message.channelId}:${message.threadTs}`,
|
|
380
458
|
workspaceDir: decision.workspaceDir,
|
|
381
|
-
workspaceFiles:
|
|
459
|
+
workspaceFiles: media.workspaceFiles,
|
|
460
|
+
images: media.images,
|
|
382
461
|
cloud: decision.cloud,
|
|
383
462
|
sdkAgentId: decision.sdkAgentId,
|
|
384
463
|
};
|
|
@@ -443,8 +522,8 @@ export async function dispatchInboundMessage(args: {
|
|
|
443
522
|
|
|
444
523
|
/**
|
|
445
524
|
* Parse an Events API body (or Socket Mode `events_api` payload) and dispatch
|
|
446
|
-
* app_mention / DM handlers. Deduplicates by `event_id` when
|
|
447
|
-
* true when a handler was scheduled.
|
|
525
|
+
* app_mention / DM / reaction handlers. Deduplicates by `event_id` when
|
|
526
|
+
* present. Returns true when a handler was scheduled.
|
|
448
527
|
*/
|
|
449
528
|
export function dispatchInboundFromEventBody(
|
|
450
529
|
body: unknown,
|
|
@@ -566,6 +645,42 @@ export function dispatchInboundFromEventBody(
|
|
|
566
645
|
return true;
|
|
567
646
|
}
|
|
568
647
|
|
|
648
|
+
const reaction = parseReactionAddedEvent(body);
|
|
649
|
+
if (reaction !== null && deps.onReactionAdded !== undefined) {
|
|
650
|
+
deps.waitUntil(
|
|
651
|
+
dispatchInboundMessage({
|
|
652
|
+
kind: "reaction_added",
|
|
653
|
+
message: reaction,
|
|
654
|
+
handler: deps.onReactionAdded,
|
|
655
|
+
send: deps.send,
|
|
656
|
+
credentials: deps.credentials,
|
|
657
|
+
host: deps.host,
|
|
658
|
+
artifacts: deps.artifacts,
|
|
659
|
+
log,
|
|
660
|
+
evalCommandsEnabled: false,
|
|
661
|
+
cursorAgentName: deps.cursorAgentName,
|
|
662
|
+
blockExternals: deps.blockExternals,
|
|
663
|
+
...(deps.hasContinuationSession === undefined
|
|
664
|
+
? {}
|
|
665
|
+
: { hasContinuationSession: deps.hasContinuationSession }),
|
|
666
|
+
...(deps.isContinuationBusy === undefined
|
|
667
|
+
? {}
|
|
668
|
+
: { isContinuationBusy: deps.isContinuationBusy }),
|
|
669
|
+
...(deps.getContinuationLastBotMessageTs === undefined
|
|
670
|
+
? {}
|
|
671
|
+
: {
|
|
672
|
+
getContinuationLastBotMessageTs:
|
|
673
|
+
deps.getContinuationLastBotMessageTs,
|
|
674
|
+
}),
|
|
675
|
+
...(deps.interruptContinuation === undefined
|
|
676
|
+
? {}
|
|
677
|
+
: { interruptContinuation: deps.interruptContinuation }),
|
|
678
|
+
...(eventId === undefined ? {} : { eventId }),
|
|
679
|
+
})
|
|
680
|
+
);
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
|
|
569
684
|
if (deps.channelWatch !== undefined) {
|
|
570
685
|
// Parse keeps bot-authored posts; the watcher enforces the per-channel
|
|
571
686
|
// policy (includeBotPosts, own-post loop safety) with skip logging.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Parse Slack Events API payloads into structured inbound messages.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { parseSlackFileRefs } from "./attachments.js";
|
|
5
6
|
import { parseDelegationMetadata } from "./bot-mentions.js";
|
|
6
7
|
import { slackApiMessageBody } from "./message-body.js";
|
|
7
8
|
import type { SlackAuthor, SlackMessage } from "./types.js";
|
|
@@ -69,6 +70,7 @@ function buildSlackMessage(
|
|
|
69
70
|
const body = slackApiMessageBody(event);
|
|
70
71
|
const threadTs = asString(event.thread_ts) ?? ts;
|
|
71
72
|
const author = parseAuthor(event);
|
|
73
|
+
const files = parseSlackFileRefs(event);
|
|
72
74
|
return {
|
|
73
75
|
text,
|
|
74
76
|
body,
|
|
@@ -78,6 +80,7 @@ function buildSlackMessage(
|
|
|
78
80
|
channelId,
|
|
79
81
|
...(teamId === undefined ? {} : { teamId }),
|
|
80
82
|
...(author === undefined ? {} : { author }),
|
|
83
|
+
files,
|
|
81
84
|
};
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -155,6 +158,63 @@ export function parseDirectMessageEvent(body: unknown): SlackMessage | null {
|
|
|
155
158
|
return buildSlackMessage(event, asString(root.team_id));
|
|
156
159
|
}
|
|
157
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Parse a message `reaction_added` event into a synthetic Slack message.
|
|
163
|
+
* The reacted-to timestamp becomes the thread root so normal hydration loads
|
|
164
|
+
* the complete thread before the dedicated handler dispatches a turn.
|
|
165
|
+
*/
|
|
166
|
+
export function parseReactionAddedEvent(body: unknown): SlackMessage | null {
|
|
167
|
+
const root = asRecord(body);
|
|
168
|
+
if (root === null || root.type !== "event_callback") {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const event = asRecord(root.event);
|
|
172
|
+
if (event === null || event.type !== "reaction_added") {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const item = asRecord(event.item);
|
|
176
|
+
if (item === null || item.type !== "message") {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const userId = asString(event.user);
|
|
180
|
+
const reaction = asString(event.reaction);
|
|
181
|
+
const channelId = asString(item.channel);
|
|
182
|
+
const threadTs = asString(item.ts);
|
|
183
|
+
if (
|
|
184
|
+
userId === undefined ||
|
|
185
|
+
userId === "" ||
|
|
186
|
+
reaction === undefined ||
|
|
187
|
+
reaction === "" ||
|
|
188
|
+
channelId === undefined ||
|
|
189
|
+
channelId === "" ||
|
|
190
|
+
threadTs === undefined ||
|
|
191
|
+
threadTs === ""
|
|
192
|
+
) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const ts = asString(event.event_ts) ?? threadTs;
|
|
196
|
+
const text = `Slack user <@${userId}> added :${reaction}: to this thread.`;
|
|
197
|
+
const itemUserId = asString(event.item_user);
|
|
198
|
+
const teamId = asString(root.team_id);
|
|
199
|
+
return {
|
|
200
|
+
text,
|
|
201
|
+
body: text,
|
|
202
|
+
markdown: slackTextToMarkdown(text),
|
|
203
|
+
ts,
|
|
204
|
+
threadTs,
|
|
205
|
+
channelId,
|
|
206
|
+
...(teamId === undefined ? {} : { teamId }),
|
|
207
|
+
author: {
|
|
208
|
+
userId,
|
|
209
|
+
isBot: false,
|
|
210
|
+
},
|
|
211
|
+
reaction: {
|
|
212
|
+
name: reaction,
|
|
213
|
+
...(itemUserId === undefined ? {} : { itemUserId }),
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
158
218
|
/** Parsed `message.channels` / `message.groups` event for channel watch. */
|
|
159
219
|
export type SlackChannelMessageEvent =
|
|
160
220
|
| { kind: "post"; message: SlackMessage }
|
|
@@ -183,9 +183,23 @@ export function createStreamDeliveryStrategy(
|
|
|
183
183
|
await stopChatStream({ botToken, channel: channelId, ts: streamTs });
|
|
184
184
|
},
|
|
185
185
|
|
|
186
|
-
async cleanupOrphan() {
|
|
187
|
-
//
|
|
188
|
-
//
|
|
186
|
+
async cleanupOrphan({ channel }) {
|
|
187
|
+
// message.completed / turn.failed already stop the stream. If the
|
|
188
|
+
// turn ended without a final assistant message, close it here so
|
|
189
|
+
// Slack does not leave the live reply in a streaming state.
|
|
190
|
+
const streamTs = channel.state.streamTs;
|
|
191
|
+
const channelId =
|
|
192
|
+
channel.state.streamChannelId ?? channel.state.channelId;
|
|
193
|
+
if (
|
|
194
|
+
streamTs === null ||
|
|
195
|
+
streamTs === "" ||
|
|
196
|
+
channelId === null ||
|
|
197
|
+
channelId === ""
|
|
198
|
+
) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
await stopChatStream({ botToken, channel: channelId, ts: streamTs });
|
|
202
|
+
channel.state.streamTs = null;
|
|
189
203
|
},
|
|
190
204
|
|
|
191
205
|
async fail({ channel, channelId, streamTs, errorText }) {
|
|
@@ -187,6 +187,11 @@ export function slackChannel<const Name extends string = string>(
|
|
|
187
187
|
"slackChannel({ cursorAccount: true }) does not support onBlockAction — interactive Block Kit clicks are not relayed over the Cursor Slack connection. Block actions are Socket Mode only."
|
|
188
188
|
);
|
|
189
189
|
}
|
|
190
|
+
if (options.onReactionAdded !== undefined) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
"slackChannel({ cursorAccount: true }) does not support onReactionAdded because the Cursor Slack relay does not forward reaction events. Reaction handling is Socket Mode only."
|
|
193
|
+
);
|
|
194
|
+
}
|
|
190
195
|
}
|
|
191
196
|
const accountTransport = cursorAccount
|
|
192
197
|
? createPendingSlackApiTransport()
|
|
@@ -202,6 +207,7 @@ export function slackChannel<const Name extends string = string>(
|
|
|
202
207
|
const defaultInbound = createDefaultOnInbound(loadingMessages, policy);
|
|
203
208
|
const onAppMention = options.onAppMention ?? defaultInbound;
|
|
204
209
|
const onDirectMessage = options.onDirectMessage ?? defaultInbound;
|
|
210
|
+
const onReactionAdded = options.onReactionAdded;
|
|
205
211
|
const onChannelPost =
|
|
206
212
|
options.onChannelPost ??
|
|
207
213
|
createDefaultOnChannelPost(loadingMessages, policy);
|
|
@@ -277,6 +283,7 @@ export function slackChannel<const Name extends string = string>(
|
|
|
277
283
|
waitUntil: args.waitUntil,
|
|
278
284
|
onAppMention,
|
|
279
285
|
onDirectMessage,
|
|
286
|
+
...(onReactionAdded === undefined ? {} : { onReactionAdded }),
|
|
280
287
|
credentials,
|
|
281
288
|
eventIds,
|
|
282
289
|
host: args.host,
|
|
@@ -89,6 +89,13 @@ export interface SlackAuthor {
|
|
|
89
89
|
botId?: string;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
export interface SlackReactionAddedInfo {
|
|
93
|
+
/** Slack reaction name without surrounding colons. */
|
|
94
|
+
name: string;
|
|
95
|
+
/** User id of the message author, when Slack includes it. */
|
|
96
|
+
itemUserId?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
92
99
|
/**
|
|
93
100
|
* Delegation metadata carried on bot-to-bot mention posts (Slack message
|
|
94
101
|
* `metadata`, event type {@link SLACK_DELEGATION_METADATA_EVENT_TYPE}).
|
|
@@ -104,6 +111,22 @@ export interface SlackDelegationInfo {
|
|
|
104
111
|
originSessionId?: string;
|
|
105
112
|
}
|
|
106
113
|
|
|
114
|
+
/** File upload on an inbound Slack message (see {@link SlackMessage.files}). */
|
|
115
|
+
export interface SlackMessageFile {
|
|
116
|
+
id: string;
|
|
117
|
+
name?: string;
|
|
118
|
+
title?: string;
|
|
119
|
+
mimetype?: string;
|
|
120
|
+
filetype?: string;
|
|
121
|
+
mode?: string;
|
|
122
|
+
size?: number;
|
|
123
|
+
/**
|
|
124
|
+
* When set (cursorAccount relay), bytes were already downloaded by the
|
|
125
|
+
* Cursor backend — skip a second `files.info` / private URL fetch.
|
|
126
|
+
*/
|
|
127
|
+
dataBase64?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
107
130
|
export interface SlackMessage {
|
|
108
131
|
/** Raw top-level `text` exactly as Slack sent it. Often empty on bot posts. */
|
|
109
132
|
text: string;
|
|
@@ -121,8 +144,15 @@ export interface SlackMessage {
|
|
|
121
144
|
channelId: string;
|
|
122
145
|
teamId?: string;
|
|
123
146
|
author?: SlackAuthor;
|
|
147
|
+
/** Present only for a synthetic message dispatched from `reaction_added`. */
|
|
148
|
+
reaction?: SlackReactionAddedInfo;
|
|
124
149
|
/** Parsed delegation metadata when the post carried it (bot mentions). */
|
|
125
150
|
delegation?: SlackDelegationInfo;
|
|
151
|
+
/**
|
|
152
|
+
* Slack file uploads on this message (top-level + forwarded shares).
|
|
153
|
+
* Downloaded during dispatch into vision images / workspace files.
|
|
154
|
+
*/
|
|
155
|
+
files?: SlackMessageFile[];
|
|
126
156
|
}
|
|
127
157
|
|
|
128
158
|
export interface SlackDispatchDecision {
|
|
@@ -144,8 +174,17 @@ export interface SlackDispatchDecision {
|
|
|
144
174
|
* Relative path → contents written into the session workspace before the
|
|
145
175
|
* first turn (new sessions only). Prefer this for host-prepared files like
|
|
146
176
|
* `pr.txt` so default skill materialization stays on the session cwd.
|
|
177
|
+
* Values may be utf8 strings or `{ encoding: "base64", data }` blobs.
|
|
147
178
|
*/
|
|
148
|
-
workspaceFiles?: Record<
|
|
179
|
+
workspaceFiles?: Record<
|
|
180
|
+
string,
|
|
181
|
+
string | { encoding: "base64"; data: string; mimeType?: string }
|
|
182
|
+
>;
|
|
183
|
+
/**
|
|
184
|
+
* Base64 images attached to this turn for vision (Cursor SDK `images`).
|
|
185
|
+
* Populated automatically from Slack file uploads during inbound dispatch.
|
|
186
|
+
*/
|
|
187
|
+
images?: Array<{ data: string; mimeType: string }>;
|
|
149
188
|
/** Session display title (new sessions only). */
|
|
150
189
|
title?: string;
|
|
151
190
|
/**
|
|
@@ -497,6 +536,13 @@ export interface SlackChannelOptions {
|
|
|
497
536
|
engagement?: SlackEngagementOptions;
|
|
498
537
|
onAppMention?: SlackMessageHandler;
|
|
499
538
|
onDirectMessage?: SlackMessageHandler;
|
|
539
|
+
/**
|
|
540
|
+
* Handler for Socket Mode `reaction_added` events on messages. The Slack
|
|
541
|
+
* app must subscribe to `reaction_added` and have `reactions:read`.
|
|
542
|
+
* The handler receives a synthetic message whose thread timestamp is the
|
|
543
|
+
* reacted-to message and whose {@link SlackMessage.reaction} names the emoji.
|
|
544
|
+
*/
|
|
545
|
+
onReactionAdded?: SlackMessageHandler;
|
|
500
546
|
/**
|
|
501
547
|
* Handler for watched channel posts (same contract as `onAppMention`:
|
|
502
548
|
* return `null` to skip, or a {@link SlackDispatchDecision} to dispatch).
|
package/src/index.ts
CHANGED
|
@@ -91,6 +91,13 @@ export {
|
|
|
91
91
|
UnknownSessionError,
|
|
92
92
|
UnknownToolError,
|
|
93
93
|
} from "./internal/session-engine.js";
|
|
94
|
+
export { createMemoryKv, memoryCasTable } from "./kv/memory-kv.js";
|
|
95
|
+
export {
|
|
96
|
+
LedgerCasExhaustedError,
|
|
97
|
+
LedgerFencedError,
|
|
98
|
+
SessionLedger,
|
|
99
|
+
supportsSessionLedgerKv,
|
|
100
|
+
} from "./ledger.js";
|
|
94
101
|
export type { MemoryBackend, TurnMemoryRecord } from "./memory.js";
|
|
95
102
|
export {
|
|
96
103
|
agentStoreMemoryBackend,
|
|
@@ -116,6 +123,7 @@ export { defineSchedule } from "./schedules.js";
|
|
|
116
123
|
export { defineSkill } from "./skills.js";
|
|
117
124
|
export type {
|
|
118
125
|
StorageABTable,
|
|
126
|
+
StorageCasTable,
|
|
119
127
|
StorageConfig,
|
|
120
128
|
StorageContext,
|
|
121
129
|
StorageDefinition,
|
|
@@ -140,6 +148,7 @@ export {
|
|
|
140
148
|
isSymbolicConnectionTransport,
|
|
141
149
|
MAX_RUNNING_TURNS_LIMIT,
|
|
142
150
|
normalizeCursorAccountServers,
|
|
151
|
+
supportsKvCas,
|
|
143
152
|
} from "./types.js";
|
|
144
153
|
|
|
145
154
|
/**
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
initNextCommands,
|
|
47
47
|
initProject,
|
|
48
48
|
installScaffoldDependencies,
|
|
49
|
+
linkCliOntoPath,
|
|
49
50
|
printInitGuide,
|
|
50
51
|
} from "./init-project.js";
|
|
51
52
|
import { offerInstallCursorSkills } from "./install-cursor-skills.js";
|
|
@@ -297,6 +298,10 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
297
298
|
const install = await installScaffoldDependencies(result.rootDir, {
|
|
298
299
|
stdio: "pipe",
|
|
299
300
|
});
|
|
301
|
+
const linked = install.ok
|
|
302
|
+
? await linkCliOntoPath(result.rootDir, { stdio: "pipe" })
|
|
303
|
+
: undefined;
|
|
304
|
+
const cliOnPath = linked?.ok === true;
|
|
300
305
|
process.stdout.write(
|
|
301
306
|
`${JSON.stringify(
|
|
302
307
|
{
|
|
@@ -308,10 +313,17 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
308
313
|
installError: install.ok
|
|
309
314
|
? null
|
|
310
315
|
: install.stderr.trim() || `npm install exited ${install.exitCode}`,
|
|
316
|
+
cliOnPath,
|
|
317
|
+
cliLinkError:
|
|
318
|
+
install.ok && linked !== undefined && !linked.ok
|
|
319
|
+
? linked.stderr.trim() ||
|
|
320
|
+
`npm install -g exited ${linked.exitCode}`
|
|
321
|
+
: null,
|
|
311
322
|
next: initNextCommands({
|
|
312
323
|
rootDir: result.rootDir,
|
|
313
324
|
cwd,
|
|
314
325
|
signedIn,
|
|
326
|
+
cliOnPath,
|
|
315
327
|
}),
|
|
316
328
|
},
|
|
317
329
|
null,
|
|
@@ -333,12 +345,23 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
333
345
|
});
|
|
334
346
|
if (!install.ok) {
|
|
335
347
|
process.stderr.write(
|
|
336
|
-
`\nnpm install failed (exit ${install.exitCode}). Fix the install, then
|
|
348
|
+
`\nnpm install failed (exit ${install.exitCode}). Fix the install, then retry init.\n`
|
|
337
349
|
);
|
|
338
350
|
return install.exitCode || 1;
|
|
339
351
|
}
|
|
340
352
|
process.stdout.write("\n");
|
|
341
353
|
|
|
354
|
+
process.stdout.write(`Installing ${CLI_COMMAND_NAME} on your PATH\n\n`);
|
|
355
|
+
const linked = await linkCliOntoPath(result.rootDir, { stdio: "inherit" });
|
|
356
|
+
const cliOnPath = linked.ok;
|
|
357
|
+
if (!cliOnPath) {
|
|
358
|
+
process.stderr.write(
|
|
359
|
+
`\nCould not put ${CLI_COMMAND_NAME} on PATH (${linked.stderr.trim() || `exit ${linked.exitCode}`}). Next steps include a PATH export for node_modules/.bin.\n\n`
|
|
360
|
+
);
|
|
361
|
+
} else {
|
|
362
|
+
process.stdout.write("\n");
|
|
363
|
+
}
|
|
364
|
+
|
|
342
365
|
// Personal Cursor skills so coding agents can pick up create-agent / debug / …
|
|
343
366
|
try {
|
|
344
367
|
await offerInstallCursorSkills();
|
|
@@ -362,6 +385,7 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
362
385
|
rootDir: result.rootDir,
|
|
363
386
|
cwd,
|
|
364
387
|
signedIn,
|
|
388
|
+
cliOnPath,
|
|
365
389
|
})
|
|
366
390
|
);
|
|
367
391
|
return loginExit;
|