pi-cursor-sdk 0.1.19 → 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 +52 -0
- package/README.md +72 -11
- package/docs/cursor-dogfood-checklist.md +57 -0
- package/docs/cursor-live-smoke-checklist.md +116 -10
- package/docs/cursor-model-ux-spec.md +60 -19
- package/docs/cursor-native-tool-replay.md +21 -11
- package/docs/cursor-native-tool-visual-audit.md +104 -59
- package/docs/cursor-testing-lessons.md +10 -5
- package/docs/cursor-tool-surfaces.md +69 -0
- package/package.json +37 -11
- 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 +226 -0
- 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 +72 -49
- package/src/cursor-mcp-timeout-override.ts +66 -11
- package/src/cursor-native-tool-display-registration.ts +63 -27
- package/src/cursor-native-tool-display-replay.ts +246 -143
- 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 +113 -440
- 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 -482
- 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 +8 -2
- package/src/cursor-sensitive-text.ts +3 -36
- package/src/cursor-session-agent.ts +265 -88
- package/src/cursor-setting-sources.ts +7 -10
- package/src/cursor-state.ts +232 -28
- package/src/cursor-tool-lifecycle.ts +17 -42
- package/src/cursor-tool-manifest.ts +41 -0
- package/src/cursor-tool-names.ts +18 -79
- package/src/cursor-tool-presentation-registry.ts +556 -0
- package/src/cursor-tool-transcript.ts +1 -1
- package/src/cursor-tool-visibility.ts +39 -0
- package/src/cursor-transcript-tool-formatters.ts +0 -59
- package/src/cursor-transcript-tool-specs.ts +169 -232
- 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,5 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CURSOR_KNOWN_NORMALIZED_TOOL_NAMES,
|
|
3
|
+
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
4
|
+
getCursorReplayActivityLabelKey,
|
|
5
|
+
getCursorReplayActivityTitle,
|
|
6
|
+
getCursorReplayCallSummary,
|
|
7
|
+
getCursorReplayDisplayLabel,
|
|
8
|
+
getCursorToolActivityReplaySpec,
|
|
9
|
+
getCursorToolGenerateImageReplaySpec,
|
|
10
|
+
type CursorNormalizedToolName,
|
|
11
|
+
type CursorReplayActivityToolName,
|
|
12
|
+
type CursorReplayLegacyToolName,
|
|
13
|
+
} from "./cursor-tool-presentation-registry.js";
|
|
2
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";
|
|
3
23
|
import {
|
|
4
24
|
asRecord,
|
|
5
25
|
firstNonEmptyLine,
|
|
@@ -24,7 +44,6 @@ import {
|
|
|
24
44
|
buildReadDisplayArgs,
|
|
25
45
|
buildShellDisplayArgs,
|
|
26
46
|
buildWriteDisplayArgs,
|
|
27
|
-
collectTaskText,
|
|
28
47
|
formatDelete,
|
|
29
48
|
formatEdit,
|
|
30
49
|
formatFallback,
|
|
@@ -46,27 +65,13 @@ import {
|
|
|
46
65
|
formatWrite,
|
|
47
66
|
formatNativeReadDisplayContent,
|
|
48
67
|
getCursorWriteArgContent,
|
|
49
|
-
getGenerateImageDisplayPath,
|
|
50
|
-
getGenerateImagePath,
|
|
51
68
|
getGlobBody,
|
|
52
69
|
getGrepBody,
|
|
53
70
|
getLsBody,
|
|
54
|
-
getReadLintDiagnostics,
|
|
55
|
-
getReadLintPaths,
|
|
56
71
|
getShellOutput,
|
|
57
|
-
getTaskDescription,
|
|
58
|
-
getTodoItems,
|
|
59
|
-
getTodoTotalCount,
|
|
60
|
-
inferImageMimeType,
|
|
61
|
-
summarizePlan,
|
|
62
|
-
summarizeMcp,
|
|
63
|
-
summarizeRecordScreen,
|
|
64
|
-
summarizeSemSearch,
|
|
65
|
-
summarizeTask,
|
|
66
|
-
summarizeTodos,
|
|
67
72
|
usesLocalReadPreview,
|
|
68
73
|
} from "./cursor-transcript-tool-formatters.js";
|
|
69
|
-
import {
|
|
74
|
+
import type { CursorReplaySummaryArgs } from "./cursor-replay-summary-args.js";
|
|
70
75
|
|
|
71
76
|
export interface ToolDisplayContext {
|
|
72
77
|
rawName: string;
|
|
@@ -76,17 +81,17 @@ export interface ToolDisplayContext {
|
|
|
76
81
|
options: TranscriptOptions;
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
|
|
80
|
-
labelKey: CursorReplayLegacyToolName;
|
|
81
|
-
buildActivityArgs: (context: ToolDisplayContext) => Record<string, unknown>;
|
|
82
|
-
buildActivitySummary: (context: ToolDisplayContext) => string | undefined;
|
|
83
|
-
buildDetails: (context: ToolDisplayContext, contentText: string) => Record<string, unknown>;
|
|
84
|
-
}
|
|
84
|
+
type NeutralActivityReplayToolName = Exclude<CursorReplayActivityToolName, "edit" | "write" | "generateImage">;
|
|
85
85
|
|
|
86
86
|
interface ToolDisplaySpec {
|
|
87
87
|
formatTranscript: (context: ToolDisplayContext) => string;
|
|
88
88
|
buildPiToolDisplay: (context: ToolDisplayContext) => CursorPiToolDisplay;
|
|
89
|
-
|
|
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;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
function textToolResult(text: string, details?: unknown): PiToolDisplayResult {
|
|
@@ -106,51 +111,81 @@ function buildCursorActivityDisplayArgs(
|
|
|
106
111
|
};
|
|
107
112
|
}
|
|
108
113
|
|
|
114
|
+
function buildRegistryReplaySummary(
|
|
115
|
+
labelKey: CursorReplayLegacyToolName,
|
|
116
|
+
args: CursorReplaySummaryArgs,
|
|
117
|
+
): string | undefined {
|
|
118
|
+
return getCursorReplayCallSummary(labelKey, args);
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
function buildReplaySummaryDisplay(
|
|
110
122
|
toolName: string,
|
|
111
123
|
args: Record<string, unknown>,
|
|
112
124
|
result: NormalizedResult,
|
|
113
125
|
contentText: string,
|
|
114
|
-
details:
|
|
126
|
+
details: CursorReplayToolDetails,
|
|
115
127
|
): CursorPiToolDisplay {
|
|
116
128
|
const isError = result.status === "error";
|
|
117
|
-
const
|
|
129
|
+
const expandedText = details.expandedText ?? contentText;
|
|
130
|
+
const summary = details.summary;
|
|
118
131
|
return {
|
|
119
132
|
toolName,
|
|
120
133
|
args,
|
|
121
134
|
result: textToolResult(contentText, {
|
|
122
135
|
...details,
|
|
123
136
|
summary,
|
|
124
|
-
expandedText
|
|
137
|
+
expandedText,
|
|
125
138
|
}),
|
|
126
139
|
isError,
|
|
127
140
|
};
|
|
128
141
|
}
|
|
129
142
|
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
function getCursorToolActivityTitle(toolName: string): string {
|
|
144
|
+
return getCursorReplayActivityTitle(toolName) ?? buildGenericUnknownToolActivityTitle(toolName);
|
|
145
|
+
}
|
|
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,
|
|
137
163
|
activityTitle,
|
|
164
|
+
activityFields,
|
|
165
|
+
contentText,
|
|
166
|
+
context.result.status === "error",
|
|
138
167
|
activitySummary,
|
|
139
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);
|
|
140
181
|
const contentText = spec.formatTranscript(context).trimEnd();
|
|
141
|
-
const details =
|
|
142
|
-
|
|
143
|
-
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
144
|
-
activityArgs,
|
|
145
|
-
context.result,
|
|
182
|
+
const details = assembleCursorReplayGenerateImageDetails(
|
|
183
|
+
replay.buildDetails(context, contentText),
|
|
146
184
|
contentText,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
title: activityTitle,
|
|
150
|
-
summary: context.result.status === "error" ? undefined : details.summary ?? activitySummary,
|
|
151
|
-
...details,
|
|
152
|
-
},
|
|
185
|
+
context.result.status === "error",
|
|
186
|
+
activitySummary,
|
|
153
187
|
);
|
|
188
|
+
return buildReplaySummaryDisplay(CURSOR_REPLAY_ACTIVITY_TOOL_NAME, activityArgs, context.result, contentText, details);
|
|
154
189
|
}
|
|
155
190
|
|
|
156
191
|
function buildGenericUnknownToolActivityTitle(displayName: string): string {
|
|
@@ -175,12 +210,35 @@ function buildGenericPiToolDisplay(context: ToolDisplayContext): CursorPiToolDis
|
|
|
175
210
|
result.status === "error"
|
|
176
211
|
? undefined
|
|
177
212
|
: activitySummary ?? truncateArg(displayName === "unknown" ? "tool" : displayName);
|
|
178
|
-
return buildReplaySummaryDisplay(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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;
|
|
184
242
|
}
|
|
185
243
|
|
|
186
244
|
function buildEditPiToolDisplay(context: ToolDisplayContext): CursorPiToolDisplay {
|
|
@@ -191,18 +249,17 @@ function buildEditPiToolDisplay(context: ToolDisplayContext): CursorPiToolDispla
|
|
|
191
249
|
const nativeEditArgs = buildNativeEditDisplayArgs(rawName, args, options);
|
|
192
250
|
const baseActivityArgs = buildCursorEditActivityDisplayArgs(args, options);
|
|
193
251
|
const displayPath = typeof baseActivityArgs.path === "string" ? baseActivityArgs.path : undefined;
|
|
194
|
-
const activityTitle =
|
|
252
|
+
const activityTitle = getCursorToolActivityTitle("edit");
|
|
195
253
|
const activityArgs = buildCursorActivityDisplayArgs(baseActivityArgs, activityTitle, displayPath);
|
|
196
254
|
const contentText = formatEdit(activityArgs, result, options);
|
|
197
|
-
const details = {
|
|
198
|
-
cursorToolName: "edit",
|
|
255
|
+
const details = buildCursorReplayNativeEditDetails({
|
|
199
256
|
path: displayPath,
|
|
200
257
|
linesAdded: getNumber(value, "linesAdded"),
|
|
201
258
|
linesRemoved: getNumber(value, "linesRemoved"),
|
|
202
259
|
diffString: normalizedDiff,
|
|
203
260
|
diff: normalizedDiff,
|
|
204
261
|
firstChangedLine: getNumber(value, "firstChangedLine"),
|
|
205
|
-
};
|
|
262
|
+
});
|
|
206
263
|
if (nativeEditArgs) {
|
|
207
264
|
return {
|
|
208
265
|
toolName: "edit",
|
|
@@ -216,11 +273,22 @@ function buildEditPiToolDisplay(context: ToolDisplayContext): CursorPiToolDispla
|
|
|
216
273
|
activityArgs,
|
|
217
274
|
result,
|
|
218
275
|
contentText.trimEnd(),
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
+
),
|
|
224
292
|
);
|
|
225
293
|
}
|
|
226
294
|
|
|
@@ -231,26 +299,33 @@ function buildWritePiToolDisplay(context: ToolDisplayContext): CursorPiToolDispl
|
|
|
231
299
|
const displayArgs = buildWriteDisplayArgs(args, options);
|
|
232
300
|
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
233
301
|
const contentText = formatWrite(args, result, options).trimEnd();
|
|
234
|
-
const details = {
|
|
235
|
-
cursorToolName: "write",
|
|
302
|
+
const details = buildCursorReplayNativeWriteDetails({
|
|
236
303
|
path: displayPath,
|
|
237
304
|
linesCreated: getNumber(value, "linesCreated"),
|
|
238
305
|
fileSize: getNumber(value, "fileSize"),
|
|
239
306
|
fileContentAfterWrite: getString(value, "fileContentAfterWrite"),
|
|
240
307
|
expandedText: contentText,
|
|
241
|
-
};
|
|
308
|
+
});
|
|
242
309
|
if (content === undefined) {
|
|
243
|
-
const activityTitle =
|
|
310
|
+
const activityTitle = getCursorToolActivityTitle("write");
|
|
244
311
|
return buildReplaySummaryDisplay(
|
|
245
312
|
CURSOR_REPLAY_ACTIVITY_TOOL_NAME,
|
|
246
313
|
buildCursorActivityDisplayArgs(displayArgs, activityTitle, displayPath ?? "file"),
|
|
247
314
|
result,
|
|
248
315
|
contentText,
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
+
),
|
|
254
329
|
);
|
|
255
330
|
}
|
|
256
331
|
return {
|
|
@@ -261,7 +336,7 @@ function buildWritePiToolDisplay(context: ToolDisplayContext): CursorPiToolDispl
|
|
|
261
336
|
};
|
|
262
337
|
}
|
|
263
338
|
|
|
264
|
-
const
|
|
339
|
+
const TOOL_DISPLAY_IMPLEMENTATIONS: Record<CursorNormalizedToolName, ToolDisplaySpec> = {
|
|
265
340
|
read: {
|
|
266
341
|
formatTranscript: ({ args, result, options }) => formatRead(args, result, options),
|
|
267
342
|
buildPiToolDisplay: ({ args, result, options }) => {
|
|
@@ -334,207 +409,69 @@ const TOOL_DISPLAY_SPECS: Record<string, ToolDisplaySpec> = {
|
|
|
334
409
|
},
|
|
335
410
|
delete: {
|
|
336
411
|
formatTranscript: ({ args, result, options }) => formatDelete(args, result, options),
|
|
337
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("delete",
|
|
338
|
-
activityReplay: {
|
|
339
|
-
labelKey: "cursor_delete",
|
|
340
|
-
buildActivityArgs: ({ args, options }) => {
|
|
341
|
-
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
342
|
-
return displayPath ? { path: displayPath } : {};
|
|
343
|
-
},
|
|
344
|
-
buildActivitySummary: ({ args, options }) => {
|
|
345
|
-
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
346
|
-
return displayPath ?? "file";
|
|
347
|
-
},
|
|
348
|
-
buildDetails: ({ args, result, options }) => {
|
|
349
|
-
const displayPath = typeof args.path === "string" ? formatDisplayPath(args.path, options.cwd) : undefined;
|
|
350
|
-
const value = asRecord(result.value);
|
|
351
|
-
return {
|
|
352
|
-
path: displayPath,
|
|
353
|
-
fileSize: getNumber(value, "fileSize"),
|
|
354
|
-
summary: result.status === "error" ? undefined : displayPath ? `deleted ${displayPath}` : "deleted file",
|
|
355
|
-
};
|
|
356
|
-
},
|
|
357
|
-
},
|
|
412
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("delete", context),
|
|
358
413
|
},
|
|
359
414
|
readLints: {
|
|
360
415
|
formatTranscript: ({ args, result, options }) => formatReadLints(args, result, options),
|
|
361
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("readLints",
|
|
362
|
-
activityReplay: {
|
|
363
|
-
labelKey: "cursor_read_lints",
|
|
364
|
-
buildActivityArgs: ({ args, result, options }) => {
|
|
365
|
-
const paths = getReadLintPaths(args, result, options);
|
|
366
|
-
const diagnosticCount = getReadLintDiagnostics(result, options).length;
|
|
367
|
-
return { paths, diagnosticCount };
|
|
368
|
-
},
|
|
369
|
-
buildActivitySummary: ({ args, result, options }) => {
|
|
370
|
-
const paths = getReadLintPaths(args, result, options);
|
|
371
|
-
const diagnosticCount = getReadLintDiagnostics(result, options).length;
|
|
372
|
-
return `${diagnosticCount} diagnostic${diagnosticCount === 1 ? "" : "s"}${paths.length > 0 ? ` in ${paths.join(", ")}` : ""}`;
|
|
373
|
-
},
|
|
374
|
-
buildDetails: () => ({}),
|
|
375
|
-
},
|
|
416
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("readLints", context),
|
|
376
417
|
},
|
|
377
418
|
updateTodos: {
|
|
378
419
|
formatTranscript: ({ args, result, options }) => formatTodos(args, result, options, "updateTodos"),
|
|
379
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("updateTodos",
|
|
380
|
-
activityReplay: {
|
|
381
|
-
labelKey: "cursor_update_todos",
|
|
382
|
-
buildActivityArgs: ({ args, result }) => {
|
|
383
|
-
const todos = getTodoItems(args, result);
|
|
384
|
-
return { totalCount: getTodoTotalCount(args, result, todos) };
|
|
385
|
-
},
|
|
386
|
-
buildActivitySummary: ({ args, result }) => summarizeTodos(args, result),
|
|
387
|
-
buildDetails: () => ({}),
|
|
388
|
-
},
|
|
420
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("updateTodos", context),
|
|
389
421
|
},
|
|
390
422
|
createPlan: {
|
|
391
423
|
formatTranscript: ({ args, result, options }) => formatPlan(args, result, options),
|
|
392
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("createPlan",
|
|
393
|
-
activityReplay: {
|
|
394
|
-
labelKey: "cursor_create_plan",
|
|
395
|
-
buildActivityArgs: ({ args, result }) => {
|
|
396
|
-
const todos = getTodoItems(args, result);
|
|
397
|
-
return { totalCount: getTodoTotalCount(args, result, todos) };
|
|
398
|
-
},
|
|
399
|
-
buildActivitySummary: ({ args, result }) => summarizePlan(args, result),
|
|
400
|
-
buildDetails: () => ({}),
|
|
401
|
-
},
|
|
424
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("createPlan", context),
|
|
402
425
|
},
|
|
403
426
|
task: {
|
|
404
427
|
formatTranscript: ({ args, result, options }) => formatTask(args, result, options),
|
|
405
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("task",
|
|
406
|
-
activityReplay: {
|
|
407
|
-
labelKey: "cursor_task",
|
|
408
|
-
buildActivityArgs: ({ args, result }) => {
|
|
409
|
-
const description = getTaskDescription(args, result);
|
|
410
|
-
return { description: truncateArg(description) };
|
|
411
|
-
},
|
|
412
|
-
buildActivitySummary: ({ args, result }) => {
|
|
413
|
-
const description = getTaskDescription(args, result);
|
|
414
|
-
return summarizeTask(description, collectTaskText(result));
|
|
415
|
-
},
|
|
416
|
-
buildDetails: () => ({}),
|
|
417
|
-
},
|
|
428
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("task", context),
|
|
418
429
|
},
|
|
419
430
|
generateImage: {
|
|
420
431
|
formatTranscript: ({ args, result, options }) => formatGenerateImage(args, result, options),
|
|
421
|
-
buildPiToolDisplay: (context) =>
|
|
422
|
-
activityReplay: {
|
|
423
|
-
labelKey: "cursor_generate_image",
|
|
424
|
-
buildActivityArgs: ({ args }) => {
|
|
425
|
-
const prompt = getString(args, "prompt") ?? getString(args, "description") ?? "image";
|
|
426
|
-
return { prompt: truncateArg(prompt) };
|
|
427
|
-
},
|
|
428
|
-
buildActivitySummary: ({ args, result, options }) => {
|
|
429
|
-
const prompt = getString(args, "prompt") ?? getString(args, "description") ?? "image";
|
|
430
|
-
const imageDisplayPath = getGenerateImageDisplayPath(args, result, options);
|
|
431
|
-
return imageDisplayPath ?? truncateArg(prompt);
|
|
432
|
-
},
|
|
433
|
-
buildDetails: ({ args, result, options }, contentText) => {
|
|
434
|
-
const imagePath = getGenerateImagePath(args, result);
|
|
435
|
-
const imageDisplayPath = getGenerateImageDisplayPath(args, result, options);
|
|
436
|
-
return {
|
|
437
|
-
imagePath,
|
|
438
|
-
imageDisplayPath,
|
|
439
|
-
imageMimeType: inferImageMimeType(imagePath),
|
|
440
|
-
summary: result.status === "error" ? undefined : imageDisplayPath ? `saved ${imageDisplayPath}` : "image generated",
|
|
441
|
-
expandedText: contentText,
|
|
442
|
-
};
|
|
443
|
-
},
|
|
444
|
-
},
|
|
432
|
+
buildPiToolDisplay: (context) => buildGenerateImageReplayDisplay(context),
|
|
445
433
|
},
|
|
446
434
|
mcp: {
|
|
447
435
|
formatTranscript: ({ args, result, options }) => formatMcp(args, result, options),
|
|
448
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("mcp",
|
|
449
|
-
activityReplay: {
|
|
450
|
-
labelKey: "cursor_mcp",
|
|
451
|
-
buildActivityArgs: ({ args }) => {
|
|
452
|
-
const toolName = getString(args, "toolName") ?? "mcp";
|
|
453
|
-
return { toolName: truncateArg(toolName) };
|
|
454
|
-
},
|
|
455
|
-
buildActivitySummary: ({ args, result }) => summarizeMcp(args, result),
|
|
456
|
-
buildDetails: ({ args, result }) => ({
|
|
457
|
-
summary: result.status === "error" ? undefined : summarizeMcp(args, result),
|
|
458
|
-
}),
|
|
459
|
-
},
|
|
436
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("mcp", context),
|
|
460
437
|
},
|
|
461
438
|
semSearch: {
|
|
462
439
|
formatTranscript: ({ args, result, options }) => formatSemSearch(args, result, options),
|
|
463
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("semSearch",
|
|
464
|
-
activityReplay: {
|
|
465
|
-
labelKey: "cursor_sem_search",
|
|
466
|
-
buildActivityArgs: ({ args }) => {
|
|
467
|
-
const query = getString(args, "query") ?? "semantic search";
|
|
468
|
-
return { query: truncateArg(query) };
|
|
469
|
-
},
|
|
470
|
-
buildActivitySummary: ({ args }) => summarizeSemSearch(args),
|
|
471
|
-
buildDetails: ({ result }, contentText) => ({
|
|
472
|
-
summary: result.status === "error" ? undefined : firstNonEmptyLine(contentText) ?? "semantic search results captured",
|
|
473
|
-
}),
|
|
474
|
-
},
|
|
440
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("semSearch", context),
|
|
475
441
|
},
|
|
476
442
|
recordScreen: {
|
|
477
443
|
formatTranscript: ({ args, result, options }) => formatRecordScreen(args, result, options),
|
|
478
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("recordScreen",
|
|
479
|
-
activityReplay: {
|
|
480
|
-
labelKey: "cursor_record_screen",
|
|
481
|
-
buildActivityArgs: ({ args, result, options }) => {
|
|
482
|
-
const mode = getString(args, "mode");
|
|
483
|
-
const path = getString(asRecord(result.value), "path");
|
|
484
|
-
return {
|
|
485
|
-
...(mode ? { mode } : {}),
|
|
486
|
-
...(path ? { path: formatDisplayPath(path, options.cwd) } : {}),
|
|
487
|
-
};
|
|
488
|
-
},
|
|
489
|
-
buildActivitySummary: ({ args, result, options }) => summarizeRecordScreen(args, result, options),
|
|
490
|
-
buildDetails: ({ args, result, options }, contentText) => ({
|
|
491
|
-
summary:
|
|
492
|
-
result.status === "error"
|
|
493
|
-
? undefined
|
|
494
|
-
: summarizeRecordScreen(args, result, options) ?? firstNonEmptyLine(contentText) ?? "screen recording updated",
|
|
495
|
-
}),
|
|
496
|
-
},
|
|
444
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("recordScreen", context),
|
|
497
445
|
},
|
|
498
446
|
webSearch: {
|
|
499
447
|
formatTranscript: ({ args, result, options }) => formatWebSearch(args, result, options),
|
|
500
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webSearch",
|
|
501
|
-
activityReplay: {
|
|
502
|
-
labelKey: "cursor_web_search",
|
|
503
|
-
buildActivityArgs: ({ args }) => {
|
|
504
|
-
const query = extractWebSearchQuery(args);
|
|
505
|
-
return query ? { query: truncateArg(query) } : {};
|
|
506
|
-
},
|
|
507
|
-
buildActivitySummary: ({ args }) => truncateArg(extractWebSearchQuery(args) ?? "web search"),
|
|
508
|
-
buildDetails: ({ result }, contentText) => ({
|
|
509
|
-
summary: result.status === "error" ? undefined : firstNonEmptyLine(contentText) ?? "web search result captured",
|
|
510
|
-
}),
|
|
511
|
-
},
|
|
448
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webSearch", context),
|
|
512
449
|
},
|
|
513
450
|
webFetch: {
|
|
514
451
|
formatTranscript: ({ args, result, options }) => formatWebFetch(args, result, options),
|
|
515
|
-
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webFetch",
|
|
516
|
-
activityReplay: {
|
|
517
|
-
labelKey: "cursor_web_fetch",
|
|
518
|
-
buildActivityArgs: ({ args }) => {
|
|
519
|
-
const target = extractWebFetchTarget(args);
|
|
520
|
-
return target ? { url: truncateArg(target) } : {};
|
|
521
|
-
},
|
|
522
|
-
buildActivitySummary: ({ args }) => truncateArg(extractWebFetchTarget(args) ?? "web fetch"),
|
|
523
|
-
buildDetails: ({ result }, contentText) => ({
|
|
524
|
-
summary: result.status === "error" ? undefined : firstNonEmptyLine(contentText) ?? "web fetch result captured",
|
|
525
|
-
}),
|
|
526
|
-
},
|
|
452
|
+
buildPiToolDisplay: (context) => buildActivityReplayDisplay("webFetch", context),
|
|
527
453
|
},
|
|
528
454
|
};
|
|
529
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
|
+
|
|
530
467
|
export function formatCursorToolTranscriptFromSpec(context: ToolDisplayContext): string {
|
|
531
|
-
const spec =
|
|
468
|
+
const spec = getToolDisplaySpec(context.name);
|
|
532
469
|
if (spec) return spec.formatTranscript(context);
|
|
533
470
|
return formatFallback(context.name, context.args, context.result, context.options);
|
|
534
471
|
}
|
|
535
472
|
|
|
536
473
|
export function buildCursorPiToolDisplayFromSpec(context: ToolDisplayContext): CursorPiToolDisplay {
|
|
537
|
-
const spec =
|
|
474
|
+
const spec = getToolDisplaySpec(context.name);
|
|
538
475
|
if (spec) return spec.buildPiToolDisplay(context);
|
|
539
476
|
return buildGenericPiToolDisplay(context);
|
|
540
477
|
}
|