pi-cursor-sdk 0.1.20 → 0.1.21
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/CHANGELOG.md +32 -0
- package/README.md +49 -9
- package/docs/cursor-dogfood-checklist.md +57 -0
- package/docs/cursor-live-smoke-checklist.md +115 -9
- package/docs/cursor-model-ux-spec.md +57 -17
- package/docs/cursor-native-tool-replay.md +15 -7
- package/docs/cursor-native-tool-visual-audit.md +104 -59
- package/docs/cursor-testing-lessons.md +8 -3
- package/docs/cursor-tool-surfaces.md +69 -0
- package/package.json +34 -10
- package/scripts/debug-provider-events.d.mts +59 -0
- package/scripts/debug-provider-events.mjs +70 -175
- package/scripts/debug-sdk-events.d.mts +90 -0
- package/scripts/debug-sdk-events.mjs +36 -98
- package/scripts/fixtures/plan-strip-shim/index.ts +12 -0
- package/scripts/isolated-cursor-smoke.sh +264 -102
- package/scripts/lib/cursor-child-process.d.mts +10 -0
- package/scripts/lib/cursor-child-process.mjs +50 -0
- package/scripts/lib/cursor-cli-args.d.mts +63 -0
- package/scripts/lib/cursor-cli-args.mjs +129 -0
- package/scripts/lib/cursor-script-fail.d.mts +1 -0
- package/scripts/lib/cursor-script-fail.mjs +13 -0
- package/scripts/lib/cursor-sdk-output-filter.d.mts +5 -0
- package/scripts/lib/cursor-smoke-env.d.mts +38 -0
- package/scripts/lib/cursor-smoke-env.mjs +81 -0
- package/scripts/lib/cursor-smoke-shell.sh +174 -0
- package/scripts/lib/cursor-visual-render.d.mts +15 -0
- package/scripts/lib/cursor-visual-render.mjs +131 -0
- package/scripts/probe-mcp-coldstart.mjs +20 -38
- package/scripts/refresh-cursor-model-snapshots.mjs +29 -65
- package/scripts/steering-rpc-smoke.mjs +170 -65
- package/scripts/tmux-live-smoke.sh +152 -98
- package/scripts/visual-tui-smoke.mjs +659 -0
- package/shared/cursor-sdk-event-debug-env.d.mts +12 -0
- package/shared/cursor-sdk-event-debug-env.mjs +13 -0
- package/shared/cursor-sensitive-text.d.mts +1 -0
- package/{scripts/lib/cursor-probe-utils.mjs → shared/cursor-sensitive-text.mjs} +1 -13
- package/shared/cursor-setting-sources.d.mts +5 -0
- package/shared/cursor-setting-sources.mjs +22 -0
- package/src/context.ts +21 -12
- package/src/cursor-bridge-contract.ts +1 -3
- package/src/cursor-incomplete-tool-visibility.ts +22 -5
- package/src/cursor-native-tool-display-registration.ts +63 -27
- package/src/cursor-native-tool-display-replay.ts +246 -144
- package/src/cursor-native-tool-display-state.ts +2 -0
- package/src/cursor-native-tool-display-tools.ts +149 -41
- package/src/cursor-provider-live-run-drain.ts +1 -52
- package/src/cursor-provider-run-finalizer.ts +235 -0
- package/src/cursor-provider-run-outcome.ts +149 -0
- package/src/cursor-provider-turn-api-key.ts +8 -0
- package/src/cursor-provider-turn-coordinator.ts +98 -446
- package/src/cursor-provider-turn-display-router.ts +216 -0
- package/src/cursor-provider-turn-emit.ts +59 -0
- package/src/cursor-provider-turn-finalize.ts +119 -0
- package/src/cursor-provider-turn-lifecycle-emitter.ts +97 -0
- package/src/cursor-provider-turn-message-offset.ts +15 -0
- package/src/cursor-provider-turn-prepare.ts +216 -0
- package/src/cursor-provider-turn-runner.ts +138 -0
- package/src/cursor-provider-turn-sdk-normalizer.ts +88 -0
- package/src/cursor-provider-turn-send.ts +103 -0
- package/src/cursor-provider-turn-shell-output.ts +107 -0
- package/src/cursor-provider-turn-tool-ledger.ts +126 -0
- package/src/cursor-provider-turn-types.ts +87 -0
- package/src/cursor-provider.ts +16 -504
- package/src/cursor-replay-activity-builders.ts +276 -0
- package/src/cursor-replay-source-names.ts +33 -0
- package/src/cursor-replay-summary-args.ts +191 -0
- package/src/cursor-replay-tool-details.ts +464 -0
- package/src/cursor-run-final-text.ts +56 -0
- package/src/cursor-sdk-abort-error-guard.ts +4 -0
- package/src/cursor-sdk-event-debug-constants.ts +14 -5
- package/src/cursor-sdk-event-debug.ts +2 -1
- package/src/cursor-sensitive-text.ts +3 -36
- package/src/cursor-session-agent.ts +3 -1
- package/src/cursor-setting-sources.ts +7 -10
- package/src/cursor-state.ts +232 -28
- package/src/cursor-tool-lifecycle.ts +9 -8
- package/src/cursor-tool-manifest.ts +41 -0
- package/src/cursor-tool-names.ts +18 -106
- package/src/cursor-tool-presentation-registry.ts +556 -0
- package/src/cursor-tool-transcript.ts +1 -1
- package/src/cursor-tool-visibility.ts +3 -27
- package/src/cursor-transcript-tool-formatters.ts +0 -59
- package/src/cursor-transcript-tool-specs.ts +158 -233
- package/src/cursor-transcript-utils.ts +0 -44
- package/src/cursor-web-tool-activity.ts +10 -60
- package/src/cursor-web-tool-args.ts +39 -0
- package/src/index.ts +4 -10
|
@@ -505,18 +505,6 @@ export function getTodoTotalCount(args: Record<string, unknown>, result: Normali
|
|
|
505
505
|
return getNumber(asRecord(result.value), "totalCount") ?? getNumber(args, "totalCount") ?? todos.length;
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
-
export function summarizeTodos(args: Record<string, unknown>, result: NormalizedResult): string {
|
|
509
|
-
const todos = getTodoItems(args, result);
|
|
510
|
-
const total = getTodoTotalCount(args, result, todos);
|
|
511
|
-
const completed = todos.filter((todo) => todo.status === "completed").length;
|
|
512
|
-
const inProgress = todos.filter((todo) => todo.status === "inProgress").length;
|
|
513
|
-
const pending = todos.filter((todo) => todo.status === "pending").length;
|
|
514
|
-
const parts = [`${completed}/${total} completed`];
|
|
515
|
-
if (inProgress > 0) parts.push(`${inProgress} in progress`);
|
|
516
|
-
if (pending > 0) parts.push(`${pending} pending`);
|
|
517
|
-
return parts.join(", ");
|
|
518
|
-
}
|
|
519
|
-
|
|
520
508
|
function formatTodoStatus(status: string | undefined): string {
|
|
521
509
|
if (status === "completed") return "✓";
|
|
522
510
|
if (status === "inProgress") return "…";
|
|
@@ -532,12 +520,6 @@ export function formatTodos(args: Record<string, unknown>, result: NormalizedRes
|
|
|
532
520
|
return joinSections(header, limitText(lines.join("\n"), options));
|
|
533
521
|
}
|
|
534
522
|
|
|
535
|
-
export function summarizePlan(args: Record<string, unknown>, result: NormalizedResult): string {
|
|
536
|
-
const planText = getString(args, "plan") ?? getString(asRecord(result.value), "plan");
|
|
537
|
-
const firstLine = planText ? firstNonEmptyLine(planText) : undefined;
|
|
538
|
-
return firstLine ? truncateArg(firstLine, 160) : summarizeTodos(args, result);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
523
|
export function formatPlan(args: Record<string, unknown>, result: NormalizedResult, options: TranscriptOptions): string {
|
|
542
524
|
if (result.status === "error") return joinSections("createPlan", formatError(result.error));
|
|
543
525
|
const planText = getString(args, "plan") ?? getString(asRecord(result.value), "plan");
|
|
@@ -578,13 +560,6 @@ export function formatTask(args: Record<string, unknown>, result: NormalizedResu
|
|
|
578
560
|
return joinSections(`task ${description}`, limitText(taskText || stringifyUnknown(result.value), options));
|
|
579
561
|
}
|
|
580
562
|
|
|
581
|
-
export function summarizeTask(description: string, taskText: string): string {
|
|
582
|
-
const firstLine = firstNonEmptyLine(taskText);
|
|
583
|
-
if (!firstLine) return truncateArg(description);
|
|
584
|
-
if (description === "task" || description === firstLine) return truncateArg(firstLine);
|
|
585
|
-
return truncateArg(`${description}: ${firstLine}`, 160);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
563
|
function getGenerateImageValue(result: NormalizedResult): Record<string, unknown> | undefined {
|
|
589
564
|
return asRecord(result.value);
|
|
590
565
|
}
|
|
@@ -642,16 +617,6 @@ function describeNonTextMcpContent(entry: unknown): string {
|
|
|
642
617
|
return `[${type} omitted]`;
|
|
643
618
|
}
|
|
644
619
|
|
|
645
|
-
export function summarizeSemSearch(args: Record<string, unknown>): string {
|
|
646
|
-
const query = getString(args, "query") ?? "semantic search";
|
|
647
|
-
const targetDirectories = getArray(args, "targetDirectories");
|
|
648
|
-
const dirHint =
|
|
649
|
-
targetDirectories && targetDirectories.length > 0
|
|
650
|
-
? ` (${targetDirectories.length} dir${targetDirectories.length === 1 ? "" : "s"})`
|
|
651
|
-
: "";
|
|
652
|
-
return truncateArg(`${query}${dirHint}`);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
620
|
export function formatSemSearch(args: Record<string, unknown>, result: NormalizedResult, options: TranscriptOptions): string {
|
|
656
621
|
const query = getString(args, "query") ?? "semantic search";
|
|
657
622
|
const header = `semSearch ${truncateArg(query)}`;
|
|
@@ -694,24 +659,6 @@ function formatRecordingDurationMs(ms: number | undefined): string | undefined {
|
|
|
694
659
|
return seconds < 60 ? `${seconds.toFixed(1)}s` : `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
|
|
695
660
|
}
|
|
696
661
|
|
|
697
|
-
export function summarizeRecordScreen(
|
|
698
|
-
args: Record<string, unknown>,
|
|
699
|
-
result: NormalizedResult,
|
|
700
|
-
options: TranscriptOptions,
|
|
701
|
-
): string {
|
|
702
|
-
const mode = getString(args, "mode");
|
|
703
|
-
if (result.status === "error") return formatRecordScreenMode(mode);
|
|
704
|
-
const value = asRecord(result.value);
|
|
705
|
-
const path = getString(value, "path");
|
|
706
|
-
const displayPath = path ? formatDisplayPath(path, options.cwd) : undefined;
|
|
707
|
-
const duration = formatRecordingDurationMs(getNumber(value, "recordingDurationMs"));
|
|
708
|
-
const modeLabel = formatRecordScreenMode(mode);
|
|
709
|
-
if (displayPath && duration) return `${displayPath} · ${duration}`;
|
|
710
|
-
if (displayPath) return displayPath;
|
|
711
|
-
if (duration) return `${modeLabel} · ${duration}`;
|
|
712
|
-
return modeLabel;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
662
|
export function formatRecordScreen(args: Record<string, unknown>, result: NormalizedResult, options: TranscriptOptions): string {
|
|
716
663
|
const mode = getString(args, "mode");
|
|
717
664
|
const header = `recordScreen ${formatRecordScreenMode(mode)}`;
|
|
@@ -750,12 +697,6 @@ export function getMcpResultPreview(result: NormalizedResult): string | undefine
|
|
|
750
697
|
return undefined;
|
|
751
698
|
}
|
|
752
699
|
|
|
753
|
-
export function summarizeMcp(args: Record<string, unknown>, result: NormalizedResult): string {
|
|
754
|
-
const toolName = truncateArg(getString(args, "toolName") ?? "mcp");
|
|
755
|
-
const preview = getMcpResultPreview(result);
|
|
756
|
-
return preview && preview !== toolName ? `${toolName} · ${preview}` : toolName;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
700
|
function formatWebToolBody(
|
|
760
701
|
toolLabel: string,
|
|
761
702
|
args: Record<string, unknown>,
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
CURSOR_KNOWN_NORMALIZED_TOOL_NAMES,
|
|
3
3
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
4
|
+
getCursorReplayActivityLabelKey,
|
|
4
5
|
getCursorReplayActivityTitle,
|
|
6
|
+
getCursorReplayCallSummary,
|
|
5
7
|
getCursorReplayDisplayLabel,
|
|
8
|
+
getCursorToolActivityReplaySpec,
|
|
9
|
+
getCursorToolGenerateImageReplaySpec,
|
|
10
|
+
type CursorNormalizedToolName,
|
|
11
|
+
type CursorReplayActivityToolName,
|
|
6
12
|
type CursorReplayLegacyToolName,
|
|
7
|
-
} from "./cursor-tool-
|
|
13
|
+
} from "./cursor-tool-presentation-registry.js";
|
|
8
14
|
import { resolveCursorEditDiff } from "./cursor-edit-diff.js";
|
|
15
|
+
import {
|
|
16
|
+
assembleCursorReplayActivityDetails,
|
|
17
|
+
assembleCursorReplayGenerateImageDetails,
|
|
18
|
+
buildCursorReplayNativeEditDetails,
|
|
19
|
+
buildCursorReplayNativeWriteDetails,
|
|
20
|
+
CURSOR_REPLAY_UNREGISTERED_ACTIVITY_TOOL_NAME,
|
|
21
|
+
type CursorReplayToolDetails,
|
|
22
|
+
} from "./cursor-replay-tool-details.js";
|
|
9
23
|
import {
|
|
10
24
|
asRecord,
|
|
11
25
|
firstNonEmptyLine,
|
|
@@ -30,7 +44,6 @@ import {
|
|
|
30
44
|
buildReadDisplayArgs,
|
|
31
45
|
buildShellDisplayArgs,
|
|
32
46
|
buildWriteDisplayArgs,
|
|
33
|
-
collectTaskText,
|
|
34
47
|
formatDelete,
|
|
35
48
|
formatEdit,
|
|
36
49
|
formatFallback,
|
|
@@ -52,27 +65,13 @@ import {
|
|
|
52
65
|
formatWrite,
|
|
53
66
|
formatNativeReadDisplayContent,
|
|
54
67
|
getCursorWriteArgContent,
|
|
55
|
-
getGenerateImageDisplayPath,
|
|
56
|
-
getGenerateImagePath,
|
|
57
68
|
getGlobBody,
|
|
58
69
|
getGrepBody,
|
|
59
70
|
getLsBody,
|
|
60
|
-
getReadLintDiagnostics,
|
|
61
|
-
getReadLintPaths,
|
|
62
71
|
getShellOutput,
|
|
63
|
-
getTaskDescription,
|
|
64
|
-
getTodoItems,
|
|
65
|
-
getTodoTotalCount,
|
|
66
|
-
inferImageMimeType,
|
|
67
|
-
summarizePlan,
|
|
68
|
-
summarizeMcp,
|
|
69
|
-
summarizeRecordScreen,
|
|
70
|
-
summarizeSemSearch,
|
|
71
|
-
summarizeTask,
|
|
72
|
-
summarizeTodos,
|
|
73
72
|
usesLocalReadPreview,
|
|
74
73
|
} from "./cursor-transcript-tool-formatters.js";
|
|
75
|
-
import {
|
|
74
|
+
import type { CursorReplaySummaryArgs } from "./cursor-replay-summary-args.js";
|
|
76
75
|
|
|
77
76
|
export interface ToolDisplayContext {
|
|
78
77
|
rawName: string;
|
|
@@ -82,17 +81,17 @@ export interface ToolDisplayContext {
|
|
|
82
81
|
options: TranscriptOptions;
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
labelKey: CursorReplayLegacyToolName;
|
|
87
|
-
buildActivityArgs: (context: ToolDisplayContext) => Record<string, unknown>;
|
|
88
|
-
buildActivitySummary: (context: ToolDisplayContext) => string | undefined;
|
|
89
|
-
buildDetails: (context: ToolDisplayContext, contentText: string) => Record<string, unknown>;
|
|
90
|
-
}
|
|
84
|
+
type NeutralActivityReplayToolName = Exclude<CursorReplayActivityToolName, "edit" | "write" | "generateImage">;
|
|
91
85
|
|
|
92
86
|
interface ToolDisplaySpec {
|
|
93
87
|
formatTranscript: (context: ToolDisplayContext) => string;
|
|
94
88
|
buildPiToolDisplay: (context: ToolDisplayContext) => CursorPiToolDisplay;
|
|
95
|
-
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function requireReplayActivityLabelKey(normalizedName: CursorReplayActivityToolName): CursorReplayLegacyToolName {
|
|
92
|
+
const labelKey = getCursorReplayActivityLabelKey(normalizedName);
|
|
93
|
+
if (!labelKey) throw new Error(`Missing replay activity label for ${normalizedName}`);
|
|
94
|
+
return labelKey;
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
function textToolResult(text: string, details?: unknown): PiToolDisplayResult {
|
|
@@ -112,22 +111,30 @@ function buildCursorActivityDisplayArgs(
|
|
|
112
111
|
};
|
|
113
112
|
}
|
|
114
113
|
|
|
114
|
+
function buildRegistryReplaySummary(
|
|
115
|
+
labelKey: CursorReplayLegacyToolName,
|
|
116
|
+
args: CursorReplaySummaryArgs,
|
|
117
|
+
): string | undefined {
|
|
118
|
+
return getCursorReplayCallSummary(labelKey, args);
|
|
119
|
+
}
|
|
120
|
+
|
|
115
121
|
function buildReplaySummaryDisplay(
|
|
116
122
|
toolName: string,
|
|
117
123
|
args: Record<string, unknown>,
|
|
118
124
|
result: NormalizedResult,
|
|
119
125
|
contentText: string,
|
|
120
|
-
details:
|
|
126
|
+
details: CursorReplayToolDetails,
|
|
121
127
|
): CursorPiToolDisplay {
|
|
122
128
|
const isError = result.status === "error";
|
|
123
|
-
const
|
|
129
|
+
const expandedText = details.expandedText ?? contentText;
|
|
130
|
+
const summary = details.summary;
|
|
124
131
|
return {
|
|
125
132
|
toolName,
|
|
126
133
|
args,
|
|
127
134
|
result: textToolResult(contentText, {
|
|
128
135
|
...details,
|
|
129
136
|
summary,
|
|
130
|
-
expandedText
|
|
137
|
+
expandedText,
|
|
131
138
|
}),
|
|
132
139
|
isError,
|
|
133
140
|
};
|
|
@@ -137,30 +144,48 @@ function getCursorToolActivityTitle(toolName: string): string {
|
|
|
137
144
|
return getCursorReplayActivityTitle(toolName) ?? buildGenericUnknownToolActivityTitle(toolName);
|
|
138
145
|
}
|
|
139
146
|
|
|
140
|
-
function buildActivityReplayDisplay(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
+
function buildActivityReplayDisplay(
|
|
148
|
+
sourceToolName: NeutralActivityReplayToolName,
|
|
149
|
+
context: ToolDisplayContext,
|
|
150
|
+
): CursorPiToolDisplay {
|
|
151
|
+
const spec = TOOL_DISPLAY_SPECS[sourceToolName];
|
|
152
|
+
const activity = getCursorToolActivityReplaySpec(sourceToolName);
|
|
153
|
+
if (!activity) throw new Error(`Missing activity replay spec for ${sourceToolName}`);
|
|
154
|
+
const labelKey = requireReplayActivityLabelKey(sourceToolName);
|
|
155
|
+
const activityTitle = getCursorReplayDisplayLabel(labelKey);
|
|
156
|
+
const replayArgs = activity.buildActivityArgs(context);
|
|
157
|
+
const activitySummary = buildRegistryReplaySummary(labelKey, replayArgs);
|
|
158
|
+
const activityArgs = buildCursorActivityDisplayArgs({ ...replayArgs }, activityTitle, activitySummary);
|
|
159
|
+
const contentText = spec.formatTranscript(context).trimEnd();
|
|
160
|
+
const activityFields = activity.buildDetails(context, contentText);
|
|
161
|
+
const details = assembleCursorReplayActivityDetails(
|
|
162
|
+
sourceToolName,
|
|
147
163
|
activityTitle,
|
|
164
|
+
activityFields,
|
|
165
|
+
contentText,
|
|
166
|
+
context.result.status === "error",
|
|
148
167
|
activitySummary,
|
|
149
168
|
);
|
|
169
|
+
return buildReplaySummaryDisplay(CURSOR_REPLAY_ACTIVITY_TOOL_NAME, activityArgs, context.result, contentText, details);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function buildGenerateImageReplayDisplay(context: ToolDisplayContext): CursorPiToolDisplay {
|
|
173
|
+
const spec = TOOL_DISPLAY_SPECS.generateImage;
|
|
174
|
+
const replay = getCursorToolGenerateImageReplaySpec("generateImage");
|
|
175
|
+
if (!replay) throw new Error("Missing generate image replay spec");
|
|
176
|
+
const labelKey = requireReplayActivityLabelKey("generateImage");
|
|
177
|
+
const activityTitle = getCursorReplayDisplayLabel(labelKey);
|
|
178
|
+
const replayArgs = replay.buildActivityArgs(context);
|
|
179
|
+
const activitySummary = buildRegistryReplaySummary(labelKey, replayArgs);
|
|
180
|
+
const activityArgs = buildCursorActivityDisplayArgs({ ...replayArgs }, activityTitle, activitySummary);
|
|
150
181
|
const contentText = spec.formatTranscript(context).trimEnd();
|
|
151
|
-
const details =
|
|
152
|
-
|
|
153
|
-
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
154
|
-
activityArgs,
|
|
155
|
-
context.result,
|
|
182
|
+
const details = assembleCursorReplayGenerateImageDetails(
|
|
183
|
+
replay.buildDetails(context, contentText),
|
|
156
184
|
contentText,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
title: activityTitle,
|
|
160
|
-
summary: context.result.status === "error" ? undefined : details.summary ?? activitySummary,
|
|
161
|
-
...details,
|
|
162
|
-
},
|
|
185
|
+
context.result.status === "error",
|
|
186
|
+
activitySummary,
|
|
163
187
|
);
|
|
188
|
+
return buildReplaySummaryDisplay(CURSOR_REPLAY_ACTIVITY_TOOL_NAME, activityArgs, context.result, contentText, details);
|
|
164
189
|
}
|
|
165
190
|
|
|
166
191
|
function buildGenericUnknownToolActivityTitle(displayName: string): string {
|
|
@@ -185,12 +210,35 @@ function buildGenericPiToolDisplay(context: ToolDisplayContext): CursorPiToolDis
|
|
|
185
210
|
result.status === "error"
|
|
186
211
|
? undefined
|
|
187
212
|
: activitySummary ?? truncateArg(displayName === "unknown" ? "tool" : displayName);
|
|
188
|
-
return buildReplaySummaryDisplay(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
213
|
+
return buildReplaySummaryDisplay(
|
|
214
|
+
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
215
|
+
activityArgs,
|
|
216
|
+
result,
|
|
217
|
+
contentText,
|
|
218
|
+
assembleCursorReplayActivityDetails(
|
|
219
|
+
CURSOR_REPLAY_UNREGISTERED_ACTIVITY_TOOL_NAME,
|
|
220
|
+
activityTitle,
|
|
221
|
+
{ summary, expandedText: contentText },
|
|
222
|
+
contentText,
|
|
223
|
+
result.status === "error",
|
|
224
|
+
activitySummary,
|
|
225
|
+
),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function buildEditActivitySummary(
|
|
230
|
+
displayPath: string | undefined,
|
|
231
|
+
value: Record<string, unknown>,
|
|
232
|
+
): string | undefined {
|
|
233
|
+
const path = displayPath ?? "replayed";
|
|
234
|
+
const linesAdded = getNumber(value, "linesAdded");
|
|
235
|
+
const linesRemoved = getNumber(value, "linesRemoved");
|
|
236
|
+
const parts = [
|
|
237
|
+
linesAdded ? `added ${linesAdded} line${linesAdded === 1 ? "" : "s"}` : undefined,
|
|
238
|
+
linesRemoved ? `removed ${linesRemoved} line${linesRemoved === 1 ? "" : "s"}` : undefined,
|
|
239
|
+
].filter((part): part is string => Boolean(part));
|
|
240
|
+
if (parts.length > 0) return `${path} ${parts.join(", ")}`;
|
|
241
|
+
return path;
|
|
194
242
|
}
|
|
195
243
|
|
|
196
244
|
function buildEditPiToolDisplay(context: ToolDisplayContext): CursorPiToolDisplay {
|
|
@@ -204,15 +252,14 @@ function buildEditPiToolDisplay(context: ToolDisplayContext): CursorPiToolDispla
|
|
|
204
252
|
const activityTitle = getCursorToolActivityTitle("edit");
|
|
205
253
|
const activityArgs = buildCursorActivityDisplayArgs(baseActivityArgs, activityTitle, displayPath);
|
|
206
254
|
const contentText = formatEdit(activityArgs, result, options);
|
|
207
|
-
const details = {
|
|
208
|
-
cursorToolName: "edit",
|
|
255
|
+
const details = buildCursorReplayNativeEditDetails({
|
|
209
256
|
path: displayPath,
|
|
210
257
|
linesAdded: getNumber(value, "linesAdded"),
|
|
211
258
|
linesRemoved: getNumber(value, "linesRemoved"),
|
|
212
259
|
diffString: normalizedDiff,
|
|
213
260
|
diff: normalizedDiff,
|
|
214
261
|
firstChangedLine: getNumber(value, "firstChangedLine"),
|
|
215
|
-
};
|
|
262
|
+
});
|
|
216
263
|
if (nativeEditArgs) {
|
|
217
264
|
return {
|
|
218
265
|
toolName: "edit",
|
|
@@ -226,11 +273,22 @@ function buildEditPiToolDisplay(context: ToolDisplayContext): CursorPiToolDispla
|
|
|
226
273
|
activityArgs,
|
|
227
274
|
result,
|
|
228
275
|
contentText.trimEnd(),
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
276
|
+
assembleCursorReplayActivityDetails(
|
|
277
|
+
"edit",
|
|
278
|
+
activityTitle,
|
|
279
|
+
{
|
|
280
|
+
path: displayPath,
|
|
281
|
+
summary: result.status === "error" ? undefined : buildEditActivitySummary(displayPath, value ?? {}),
|
|
282
|
+
expandedText: contentText.trimEnd(),
|
|
283
|
+
diffString: normalizedDiff,
|
|
284
|
+
diff: normalizedDiff,
|
|
285
|
+
linesAdded: getNumber(value, "linesAdded"),
|
|
286
|
+
linesRemoved: getNumber(value, "linesRemoved"),
|
|
287
|
+
},
|
|
288
|
+
contentText.trimEnd(),
|
|
289
|
+
result.status === "error",
|
|
290
|
+
displayPath,
|
|
291
|
+
),
|
|
234
292
|
);
|
|
235
293
|
}
|
|
236
294
|
|
|
@@ -241,14 +299,13 @@ function buildWritePiToolDisplay(context: ToolDisplayContext): CursorPiToolDispl
|
|
|
241
299
|
const displayArgs = buildWriteDisplayArgs(args, options);
|
|
242
300
|
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
243
301
|
const contentText = formatWrite(args, result, options).trimEnd();
|
|
244
|
-
const details = {
|
|
245
|
-
cursorToolName: "write",
|
|
302
|
+
const details = buildCursorReplayNativeWriteDetails({
|
|
246
303
|
path: displayPath,
|
|
247
304
|
linesCreated: getNumber(value, "linesCreated"),
|
|
248
305
|
fileSize: getNumber(value, "fileSize"),
|
|
249
306
|
fileContentAfterWrite: getString(value, "fileContentAfterWrite"),
|
|
250
307
|
expandedText: contentText,
|
|
251
|
-
};
|
|
308
|
+
});
|
|
252
309
|
if (content === undefined) {
|
|
253
310
|
const activityTitle = getCursorToolActivityTitle("write");
|
|
254
311
|
return buildReplaySummaryDisplay(
|
|
@@ -256,11 +313,19 @@ function buildWritePiToolDisplay(context: ToolDisplayContext): CursorPiToolDispl
|
|
|
256
313
|
buildCursorActivityDisplayArgs(displayArgs, activityTitle, displayPath ?? "file"),
|
|
257
314
|
result,
|
|
258
315
|
contentText,
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
316
|
+
assembleCursorReplayActivityDetails(
|
|
317
|
+
"write",
|
|
318
|
+
activityTitle,
|
|
319
|
+
{
|
|
320
|
+
path: displayPath,
|
|
321
|
+
summary: result.status === "error" ? undefined : displayPath ?? "wrote file",
|
|
322
|
+
expandedText: contentText,
|
|
323
|
+
fileContentAfterWrite: getString(value, "fileContentAfterWrite"),
|
|
324
|
+
},
|
|
325
|
+
contentText,
|
|
326
|
+
result.status === "error",
|
|
327
|
+
displayPath ?? "file",
|
|
328
|
+
),
|
|
264
329
|
);
|
|
265
330
|
}
|
|
266
331
|
return {
|
|
@@ -271,7 +336,7 @@ function buildWritePiToolDisplay(context: ToolDisplayContext): CursorPiToolDispl
|
|
|
271
336
|
};
|
|
272
337
|
}
|
|
273
338
|
|
|
274
|
-
const
|
|
339
|
+
const TOOL_DISPLAY_IMPLEMENTATIONS: Record<CursorNormalizedToolName, ToolDisplaySpec> = {
|
|
275
340
|
read: {
|
|
276
341
|
formatTranscript: ({ args, result, options }) => formatRead(args, result, options),
|
|
277
342
|
buildPiToolDisplay: ({ args, result, options }) => {
|
|
@@ -344,209 +409,69 @@ const TOOL_DISPLAY_SPECS: Record<string, ToolDisplaySpec> = {
|
|
|
344
409
|
},
|
|
345
410
|
delete: {
|
|
346
411
|
formatTranscript: ({ args, result, options }) => formatDelete(args, result, options),
|
|
347
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("delete",
|
|
348
|
-
activityReplay: {
|
|
349
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.delete,
|
|
350
|
-
buildActivityArgs: ({ args, options }) => {
|
|
351
|
-
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
352
|
-
return displayPath ? { path: displayPath } : {};
|
|
353
|
-
},
|
|
354
|
-
buildActivitySummary: ({ args, options }) => {
|
|
355
|
-
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
356
|
-
return displayPath ?? "file";
|
|
357
|
-
},
|
|
358
|
-
buildDetails: ({ args, result, options }) => {
|
|
359
|
-
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
360
|
-
const value = asRecord(result.value);
|
|
361
|
-
return {
|
|
362
|
-
path: displayPath,
|
|
363
|
-
fileSize: getNumber(value, "fileSize"),
|
|
364
|
-
summary: result.status === "error" ? undefined : displayPath ? `deleted ${displayPath}` : "deleted file",
|
|
365
|
-
};
|
|
366
|
-
},
|
|
367
|
-
},
|
|
412
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("delete", context),
|
|
368
413
|
},
|
|
369
414
|
readLints: {
|
|
370
415
|
formatTranscript: ({ args, result, options }) => formatReadLints(args, result, options),
|
|
371
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("readLints",
|
|
372
|
-
activityReplay: {
|
|
373
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.readLints,
|
|
374
|
-
buildActivityArgs: ({ args, result, options }) => {
|
|
375
|
-
const paths = getReadLintPaths(args, result, options);
|
|
376
|
-
const diagnosticCount = getReadLintDiagnostics(result, options).length;
|
|
377
|
-
return { paths, diagnosticCount };
|
|
378
|
-
},
|
|
379
|
-
buildActivitySummary: ({ args, result, options }) => {
|
|
380
|
-
const paths = getReadLintPaths(args, result, options);
|
|
381
|
-
const diagnosticCount = getReadLintDiagnostics(result, options).length;
|
|
382
|
-
return `${diagnosticCount} diagnostic${diagnosticCount === 1 ? "" : "s"}${paths.length > 0 ? ` in ${paths.join(", ")}` : ""}`;
|
|
383
|
-
},
|
|
384
|
-
buildDetails: () => ({}),
|
|
385
|
-
},
|
|
416
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("readLints", context),
|
|
386
417
|
},
|
|
387
418
|
updateTodos: {
|
|
388
419
|
formatTranscript: ({ args, result, options }) => formatTodos(args, result, options, "updateTodos"),
|
|
389
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("updateTodos",
|
|
390
|
-
activityReplay: {
|
|
391
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.updateTodos,
|
|
392
|
-
buildActivityArgs: ({ args, result }) => {
|
|
393
|
-
const todos = getTodoItems(args, result);
|
|
394
|
-
return { totalCount: getTodoTotalCount(args, result, todos) };
|
|
395
|
-
},
|
|
396
|
-
buildActivitySummary: ({ args, result }) => summarizeTodos(args, result),
|
|
397
|
-
buildDetails: () => ({}),
|
|
398
|
-
},
|
|
420
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("updateTodos", context),
|
|
399
421
|
},
|
|
400
422
|
createPlan: {
|
|
401
423
|
formatTranscript: ({ args, result, options }) => formatPlan(args, result, options),
|
|
402
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("createPlan",
|
|
403
|
-
activityReplay: {
|
|
404
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.createPlan,
|
|
405
|
-
buildActivityArgs: ({ args, result }) => {
|
|
406
|
-
const todos = getTodoItems(args, result);
|
|
407
|
-
return { totalCount: getTodoTotalCount(args, result, todos) };
|
|
408
|
-
},
|
|
409
|
-
buildActivitySummary: ({ args, result }) => summarizePlan(args, result),
|
|
410
|
-
buildDetails: () => ({}),
|
|
411
|
-
},
|
|
424
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("createPlan", context),
|
|
412
425
|
},
|
|
413
426
|
task: {
|
|
414
427
|
formatTranscript: ({ args, result, options }) => formatTask(args, result, options),
|
|
415
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("task",
|
|
416
|
-
activityReplay: {
|
|
417
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.task,
|
|
418
|
-
buildActivityArgs: ({ args, result }) => {
|
|
419
|
-
const description = getTaskDescription(args, result);
|
|
420
|
-
return { description: truncateArg(description) };
|
|
421
|
-
},
|
|
422
|
-
buildActivitySummary: ({ args, result }) => {
|
|
423
|
-
const description = getTaskDescription(args, result);
|
|
424
|
-
return summarizeTask(description, collectTaskText(result));
|
|
425
|
-
},
|
|
426
|
-
buildDetails: () => ({}),
|
|
427
|
-
},
|
|
428
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("task", context),
|
|
428
429
|
},
|
|
429
430
|
generateImage: {
|
|
430
431
|
formatTranscript: ({ args, result, options }) => formatGenerateImage(args, result, options),
|
|
431
|
-
buildPiToolDisplay: (context) =>
|
|
432
|
-
activityReplay: {
|
|
433
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.generateImage,
|
|
434
|
-
buildActivityArgs: ({ args }) => {
|
|
435
|
-
const prompt = getString(args, "prompt") ?? getString(args, "description") ?? "image";
|
|
436
|
-
return { prompt: truncateArg(prompt) };
|
|
437
|
-
},
|
|
438
|
-
buildActivitySummary: ({ args, result, options }) => {
|
|
439
|
-
const prompt = getString(args, "prompt") ?? getString(args, "description") ?? "image";
|
|
440
|
-
const imageDisplayPath = getGenerateImageDisplayPath(args, result, options);
|
|
441
|
-
return imageDisplayPath ?? truncateArg(prompt);
|
|
442
|
-
},
|
|
443
|
-
buildDetails: ({ args, result, options }, contentText) => {
|
|
444
|
-
const imagePath = getGenerateImagePath(args, result);
|
|
445
|
-
const imageDisplayPath = getGenerateImageDisplayPath(args, result, options);
|
|
446
|
-
return {
|
|
447
|
-
imagePath,
|
|
448
|
-
imageDisplayPath,
|
|
449
|
-
imageMimeType: inferImageMimeType(imagePath),
|
|
450
|
-
summary: result.status === "error" ? undefined : imageDisplayPath ? `saved ${imageDisplayPath}` : "image generated",
|
|
451
|
-
expandedText: contentText,
|
|
452
|
-
};
|
|
453
|
-
},
|
|
454
|
-
},
|
|
432
|
+
buildPiToolDisplay: (context) => buildGenerateImageReplayDisplay(context),
|
|
455
433
|
},
|
|
456
434
|
mcp: {
|
|
457
435
|
formatTranscript: ({ args, result, options }) => formatMcp(args, result, options),
|
|
458
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("mcp",
|
|
459
|
-
activityReplay: {
|
|
460
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.mcp,
|
|
461
|
-
buildActivityArgs: ({ args }) => {
|
|
462
|
-
const toolName = getString(args, "toolName") ?? "mcp";
|
|
463
|
-
return { toolName: truncateArg(toolName) };
|
|
464
|
-
},
|
|
465
|
-
buildActivitySummary: ({ args, result }) => summarizeMcp(args, result),
|
|
466
|
-
buildDetails: ({ args, result }) => ({
|
|
467
|
-
summary: result.status === "error" ? undefined : summarizeMcp(args, result),
|
|
468
|
-
}),
|
|
469
|
-
},
|
|
436
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("mcp", context),
|
|
470
437
|
},
|
|
471
438
|
semSearch: {
|
|
472
439
|
formatTranscript: ({ args, result, options }) => formatSemSearch(args, result, options),
|
|
473
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("semSearch",
|
|
474
|
-
activityReplay: {
|
|
475
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.semSearch,
|
|
476
|
-
buildActivityArgs: ({ args }) => {
|
|
477
|
-
const query = getString(args, "query") ?? "semantic search";
|
|
478
|
-
return { query: truncateArg(query) };
|
|
479
|
-
},
|
|
480
|
-
buildActivitySummary: ({ args }) => summarizeSemSearch(args),
|
|
481
|
-
buildDetails: ({ result }, contentText) => ({
|
|
482
|
-
summary: result.status === "error" ? undefined : firstNonEmptyLine(contentText) ?? "semantic search results captured",
|
|
483
|
-
}),
|
|
484
|
-
},
|
|
440
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("semSearch", context),
|
|
485
441
|
},
|
|
486
442
|
recordScreen: {
|
|
487
443
|
formatTranscript: ({ args, result, options }) => formatRecordScreen(args, result, options),
|
|
488
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("recordScreen",
|
|
489
|
-
activityReplay: {
|
|
490
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.recordScreen,
|
|
491
|
-
buildActivityArgs: ({ args, result, options }) => {
|
|
492
|
-
const mode = getString(args, "mode");
|
|
493
|
-
const path = getString(asRecord(result.value), "path");
|
|
494
|
-
return {
|
|
495
|
-
...(mode ? { mode } : {}),
|
|
496
|
-
...(path ? { path: formatDisplayPath(path, options.cwd) } : {}),
|
|
497
|
-
};
|
|
498
|
-
},
|
|
499
|
-
buildActivitySummary: ({ args, result, options }) => summarizeRecordScreen(args, result, options),
|
|
500
|
-
buildDetails: ({ args, result, options }, contentText) => ({
|
|
501
|
-
summary:
|
|
502
|
-
result.status === "error"
|
|
503
|
-
? undefined
|
|
504
|
-
: summarizeRecordScreen(args, result, options) ?? firstNonEmptyLine(contentText) ?? "screen recording updated",
|
|
505
|
-
}),
|
|
506
|
-
},
|
|
444
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("recordScreen", context),
|
|
507
445
|
},
|
|
508
446
|
webSearch: {
|
|
509
447
|
formatTranscript: ({ args, result, options }) => formatWebSearch(args, result, options),
|
|
510
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webSearch",
|
|
511
|
-
activityReplay: {
|
|
512
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.webSearch,
|
|
513
|
-
buildActivityArgs: ({ args }) => {
|
|
514
|
-
const query = extractWebSearchQuery(args);
|
|
515
|
-
return query ? { query: truncateArg(query) } : {};
|
|
516
|
-
},
|
|
517
|
-
buildActivitySummary: ({ args }) => truncateArg(extractWebSearchQuery(args) ?? "web search"),
|
|
518
|
-
buildDetails: ({ result }, contentText) => ({
|
|
519
|
-
summary: result.status === "error" ? undefined : firstNonEmptyLine(contentText) ?? "web search result captured",
|
|
520
|
-
collapseDetailsByDefault: true,
|
|
521
|
-
}),
|
|
522
|
-
},
|
|
448
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webSearch", context),
|
|
523
449
|
},
|
|
524
450
|
webFetch: {
|
|
525
451
|
formatTranscript: ({ args, result, options }) => formatWebFetch(args, result, options),
|
|
526
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webFetch",
|
|
527
|
-
activityReplay: {
|
|
528
|
-
labelKey: CURSOR_REPLAY_ACTIVITY_LABEL_KEYS_BY_TOOL_NAME.webFetch,
|
|
529
|
-
buildActivityArgs: ({ args }) => {
|
|
530
|
-
const target = extractWebFetchTarget(args);
|
|
531
|
-
return target ? { url: truncateArg(target) } : {};
|
|
532
|
-
},
|
|
533
|
-
buildActivitySummary: ({ args }) => truncateArg(extractWebFetchTarget(args) ?? "web fetch"),
|
|
534
|
-
buildDetails: ({ result }, contentText) => ({
|
|
535
|
-
summary: result.status === "error" ? undefined : firstNonEmptyLine(contentText) ?? "web fetch result captured",
|
|
536
|
-
collapseDetailsByDefault: true,
|
|
537
|
-
}),
|
|
538
|
-
},
|
|
452
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webFetch", context),
|
|
539
453
|
},
|
|
540
454
|
};
|
|
541
455
|
|
|
456
|
+
export const CURSOR_TOOL_DISPLAY_SPEC_KEYS = CURSOR_KNOWN_NORMALIZED_TOOL_NAMES;
|
|
457
|
+
|
|
458
|
+
const TOOL_DISPLAY_SPECS = Object.fromEntries(
|
|
459
|
+
CURSOR_KNOWN_NORMALIZED_TOOL_NAMES.map((name) => [name, TOOL_DISPLAY_IMPLEMENTATIONS[name]]),
|
|
460
|
+
) as Record<CursorNormalizedToolName, ToolDisplaySpec>;
|
|
461
|
+
|
|
462
|
+
function getToolDisplaySpec(name: string): ToolDisplaySpec | undefined {
|
|
463
|
+
if (Object.hasOwn(TOOL_DISPLAY_SPECS, name)) return TOOL_DISPLAY_SPECS[name as CursorNormalizedToolName];
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
|
|
542
467
|
export function formatCursorToolTranscriptFromSpec(context: ToolDisplayContext): string {
|
|
543
|
-
const spec =
|
|
468
|
+
const spec = getToolDisplaySpec(context.name);
|
|
544
469
|
if (spec) return spec.formatTranscript(context);
|
|
545
470
|
return formatFallback(context.name, context.args, context.result, context.options);
|
|
546
471
|
}
|
|
547
472
|
|
|
548
473
|
export function buildCursorPiToolDisplayFromSpec(context: ToolDisplayContext): CursorPiToolDisplay {
|
|
549
|
-
const spec =
|
|
474
|
+
const spec = getToolDisplaySpec(context.name);
|
|
550
475
|
if (spec) return spec.buildPiToolDisplay(context);
|
|
551
476
|
return buildGenericPiToolDisplay(context);
|
|
552
477
|
}
|