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
|
@@ -5,20 +5,26 @@
|
|
|
5
5
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
|
-
import { dirname, join
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
apiKeySecretsFromProcess,
|
|
12
|
+
commonProbeFlags,
|
|
13
|
+
defaultApiKeyFromEnv,
|
|
14
|
+
defaultSettingSourcesFromEnv,
|
|
15
|
+
parseArgv,
|
|
16
|
+
requireApiKey,
|
|
17
|
+
} from "./lib/cursor-cli-args.mjs";
|
|
18
|
+
import { parseJsonLines, terminateChild, waitForChildClose } from "./lib/cursor-child-process.mjs";
|
|
19
|
+
import { scrubSensitiveText } from "../shared/cursor-sensitive-text.mjs";
|
|
20
|
+
import { createScriptFail } from "./lib/cursor-script-fail.mjs";
|
|
21
|
+
import { serializeCursorSettingSources } from "../shared/cursor-setting-sources.mjs";
|
|
15
22
|
|
|
16
23
|
const require = createRequire(import.meta.url);
|
|
17
24
|
const root = fileURLToPath(new URL("..", import.meta.url));
|
|
18
25
|
const packageJson = require("../package.json");
|
|
19
26
|
const DEFAULT_MODEL = "cursor/composer-2.5";
|
|
20
27
|
const DEFAULT_OUT_BASE = ".debug/cursor-sdk-events";
|
|
21
|
-
const CHILD_SHUTDOWN_GRACE_MS = 2_000;
|
|
22
28
|
const SDK_EVENT_DEBUG_LOG_PREFIX = "[pi-cursor-sdk:sdk-events]";
|
|
23
29
|
const PI_SESSION_SNAPSHOT_ARTIFACT = "pi-session-snapshot.jsonl";
|
|
24
30
|
const SESSION_PI_SESSION_SNAPSHOT = "pi-session.jsonl";
|
|
@@ -72,113 +78,32 @@ Safety:
|
|
|
72
78
|
- Raw artifact files may contain local paths, tool args/results, or secrets. Do not commit or share them.`);
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
|
|
76
|
-
const scrubbed = scrubSensitiveText(message, secrets[0]);
|
|
77
|
-
console.error(`debug-provider-events: ${scrubbed}`);
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
81
|
+
const fail = createScriptFail("debug-provider-events");
|
|
80
82
|
|
|
81
83
|
export function parseDebugProviderEventsArgs(argv, env = process.env) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (arg.startsWith("--cwd=")) {
|
|
106
|
-
args.cwd = resolve(arg.slice("--cwd=".length));
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (arg === "--model") {
|
|
110
|
-
const value = argv[++index];
|
|
111
|
-
if (!value || value.startsWith("--")) fail("--model requires a value");
|
|
112
|
-
args.model = value.trim();
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
if (arg.startsWith("--model=")) {
|
|
116
|
-
args.model = arg.slice("--model=".length).trim();
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
if (arg === "--prompt") {
|
|
120
|
-
const value = argv[++index];
|
|
121
|
-
if (!value || value.startsWith("--")) fail("--prompt requires a value");
|
|
122
|
-
args.prompt = value;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (arg.startsWith("--prompt=")) {
|
|
126
|
-
args.prompt = arg.slice("--prompt=".length);
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (arg === "--prompt-file") {
|
|
130
|
-
const value = argv[++index];
|
|
131
|
-
if (!value || value.startsWith("--")) fail("--prompt-file requires a path");
|
|
132
|
-
args.promptFile = resolve(value);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
if (arg.startsWith("--prompt-file=")) {
|
|
136
|
-
args.promptFile = resolve(arg.slice("--prompt-file=".length));
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (arg === "--out") {
|
|
140
|
-
const value = argv[++index];
|
|
141
|
-
if (!value || value.startsWith("--")) fail("--out requires a directory path");
|
|
142
|
-
args.out = resolve(value);
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (arg.startsWith("--out=")) {
|
|
146
|
-
args.out = resolve(arg.slice("--out=".length));
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
if (arg === "--session-dir") {
|
|
150
|
-
const value = argv[++index];
|
|
151
|
-
if (!value || value.startsWith("--")) fail("--session-dir requires a path");
|
|
152
|
-
args.sessionDir = resolve(value);
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (arg.startsWith("--session-dir=")) {
|
|
156
|
-
args.sessionDir = resolve(arg.slice("--session-dir=".length));
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (arg === "--setting-sources") {
|
|
160
|
-
const value = argv[++index];
|
|
161
|
-
if (!value || value.startsWith("--")) fail("--setting-sources requires a value");
|
|
162
|
-
args.settingSources = resolveCursorSettingSources(value);
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (arg.startsWith("--setting-sources=")) {
|
|
166
|
-
args.settingSources = resolveCursorSettingSources(arg.slice("--setting-sources=".length));
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
if (arg === "--api-key") {
|
|
170
|
-
const value = argv[++index];
|
|
171
|
-
if (!value || value.startsWith("--")) fail("--api-key requires a value");
|
|
172
|
-
args.apiKey = value.trim();
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
if (arg.startsWith("--api-key=")) {
|
|
176
|
-
args.apiKey = arg.slice("--api-key=".length).trim();
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
fail(`unknown argument: ${arg}`);
|
|
180
|
-
}
|
|
181
|
-
return args;
|
|
84
|
+
return parseArgv(argv, {
|
|
85
|
+
defaults: {
|
|
86
|
+
cwd: root,
|
|
87
|
+
model: DEFAULT_MODEL,
|
|
88
|
+
prompt: undefined,
|
|
89
|
+
promptFile: undefined,
|
|
90
|
+
out: undefined,
|
|
91
|
+
settingSources: defaultSettingSourcesFromEnv(env),
|
|
92
|
+
sessionDir: undefined,
|
|
93
|
+
apiKey: defaultApiKeyFromEnv(env),
|
|
94
|
+
},
|
|
95
|
+
flags: {
|
|
96
|
+
cwd: commonProbeFlags.cwd,
|
|
97
|
+
model: commonProbeFlags.model,
|
|
98
|
+
prompt: commonProbeFlags.prompt,
|
|
99
|
+
promptFile: commonProbeFlags.promptFile,
|
|
100
|
+
out: commonProbeFlags.out,
|
|
101
|
+
sessionDir: commonProbeFlags.sessionDir,
|
|
102
|
+
apiKey: commonProbeFlags.apiKey,
|
|
103
|
+
settingSources: commonProbeFlags.settingSources,
|
|
104
|
+
},
|
|
105
|
+
fail,
|
|
106
|
+
});
|
|
182
107
|
}
|
|
183
108
|
|
|
184
109
|
function defaultOutDir(cwd) {
|
|
@@ -186,55 +111,6 @@ function defaultOutDir(cwd) {
|
|
|
186
111
|
return join(cwd, DEFAULT_OUT_BASE, stamp);
|
|
187
112
|
}
|
|
188
113
|
|
|
189
|
-
function parseEvents(stdout) {
|
|
190
|
-
const events = [];
|
|
191
|
-
for (const line of stdout.split("\n")) {
|
|
192
|
-
if (!line.trim()) continue;
|
|
193
|
-
try {
|
|
194
|
-
events.push(JSON.parse(line));
|
|
195
|
-
} catch {
|
|
196
|
-
// ignore partial lines
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return events;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function waitForChildClose(child) {
|
|
203
|
-
if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(child.exitCode ?? 1);
|
|
204
|
-
return new Promise((resolve) => {
|
|
205
|
-
child.once("close", (code) => resolve(code ?? 1));
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function signalChild(child, signal) {
|
|
210
|
-
if (!child.pid) return;
|
|
211
|
-
try {
|
|
212
|
-
if (process.platform === "win32") {
|
|
213
|
-
child.kill(signal);
|
|
214
|
-
} else {
|
|
215
|
-
process.kill(-child.pid, signal);
|
|
216
|
-
}
|
|
217
|
-
} catch {
|
|
218
|
-
try {
|
|
219
|
-
child.kill(signal);
|
|
220
|
-
} catch {
|
|
221
|
-
// child already exited
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async function terminateChild(child) {
|
|
227
|
-
child.stdin.destroy();
|
|
228
|
-
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
229
|
-
signalChild(child, "SIGTERM");
|
|
230
|
-
const killTimer = setTimeout(() => signalChild(child, "SIGKILL"), CHILD_SHUTDOWN_GRACE_MS);
|
|
231
|
-
try {
|
|
232
|
-
await waitForChildClose(child);
|
|
233
|
-
} finally {
|
|
234
|
-
clearTimeout(killTimer);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
114
|
function readCaptureSummary(artifactDir, stderr) {
|
|
239
115
|
const summaryPath = join(artifactDir, SUMMARY_ARTIFACT);
|
|
240
116
|
try {
|
|
@@ -254,6 +130,25 @@ function readCaptureSummary(artifactDir, stderr) {
|
|
|
254
130
|
return undefined;
|
|
255
131
|
}
|
|
256
132
|
|
|
133
|
+
function assertCompleteCaptureSummary(captureSummary, artifactDir, apiKey) {
|
|
134
|
+
if (!captureSummary?.artifactDir) {
|
|
135
|
+
fail(`missing summary.json in ${artifactDir}`, [apiKey]);
|
|
136
|
+
}
|
|
137
|
+
if (!captureSummary.artifacts || typeof captureSummary.artifacts !== "object") {
|
|
138
|
+
fail(`summary.json missing artifacts in ${artifactDir}`, [apiKey]);
|
|
139
|
+
}
|
|
140
|
+
if (!captureSummary.counts || typeof captureSummary.counts !== "object") {
|
|
141
|
+
fail(`summary.json missing counts in ${artifactDir}`, [apiKey]);
|
|
142
|
+
}
|
|
143
|
+
if (typeof captureSummary.elapsedMs !== "number") {
|
|
144
|
+
fail(`summary.json missing elapsedMs in ${artifactDir}`, [apiKey]);
|
|
145
|
+
}
|
|
146
|
+
if (typeof captureSummary.waitResultRecorded !== "boolean") {
|
|
147
|
+
fail(`summary.json missing waitResultRecorded in ${artifactDir}`, [apiKey]);
|
|
148
|
+
}
|
|
149
|
+
return captureSummary;
|
|
150
|
+
}
|
|
151
|
+
|
|
257
152
|
export function backfillPiSessionSnapshot(captureSummary, artifactDir, sessionDir) {
|
|
258
153
|
const sessionFile = captureSummary?.piSessionSnapshot?.sessionFile ?? captureSummary?.sessionFile;
|
|
259
154
|
if (!captureSummary || captureSummary.piSessionSnapshot?.copied || !sessionFile || !existsSync(sessionFile)) {
|
|
@@ -279,12 +174,12 @@ export function backfillPiSessionSnapshot(captureSummary, artifactDir, sessionDi
|
|
|
279
174
|
}
|
|
280
175
|
}
|
|
281
176
|
|
|
282
|
-
export async function runDebugProviderEvents(args) {
|
|
177
|
+
export async function runDebugProviderEvents(args, envInput = process.env) {
|
|
283
178
|
if (args.promptFile) {
|
|
284
179
|
args.prompt = readFileSync(args.promptFile, "utf8");
|
|
285
180
|
}
|
|
286
181
|
if (!args.prompt?.trim()) fail("--prompt or --prompt-file is required");
|
|
287
|
-
|
|
182
|
+
args.apiKey = requireApiKey(args, envInput, fail);
|
|
288
183
|
|
|
289
184
|
const artifactDir = args.out ?? defaultOutDir(args.cwd);
|
|
290
185
|
const sessionDir = args.sessionDir ?? join(artifactDir, "session");
|
|
@@ -303,13 +198,13 @@ export async function runDebugProviderEvents(args) {
|
|
|
303
198
|
sessionDir,
|
|
304
199
|
];
|
|
305
200
|
const env = {
|
|
306
|
-
...
|
|
201
|
+
...envInput,
|
|
307
202
|
CURSOR_API_KEY: args.apiKey,
|
|
308
203
|
PI_CURSOR_SDK_EVENT_DEBUG: "1",
|
|
309
204
|
PI_CURSOR_SDK_EVENT_DEBUG_RUN_DIR: artifactDir,
|
|
310
|
-
PI_CURSOR_SETTING_SOURCES: args.settingSources
|
|
311
|
-
PI_CURSOR_NATIVE_TOOL_DISPLAY: envFlag(
|
|
312
|
-
PI_CURSOR_PI_TOOL_BRIDGE: envFlag(
|
|
205
|
+
PI_CURSOR_SETTING_SOURCES: serializeCursorSettingSources(args.settingSources),
|
|
206
|
+
PI_CURSOR_NATIVE_TOOL_DISPLAY: envFlag(envInput.PI_CURSOR_NATIVE_TOOL_DISPLAY, "1"),
|
|
207
|
+
PI_CURSOR_PI_TOOL_BRIDGE: envFlag(envInput.PI_CURSOR_PI_TOOL_BRIDGE, "1"),
|
|
313
208
|
};
|
|
314
209
|
|
|
315
210
|
const child = spawn("pi", piArgs, {
|
|
@@ -336,10 +231,10 @@ export async function runDebugProviderEvents(args) {
|
|
|
336
231
|
try {
|
|
337
232
|
send({ type: "prompt", message: args.prompt });
|
|
338
233
|
await new Promise((resolve, reject) => {
|
|
339
|
-
const timeoutMs = Number(
|
|
234
|
+
const timeoutMs = Number(envInput.PI_PROVIDER_EVENT_DEBUG_TIMEOUT_MS ?? 600_000);
|
|
340
235
|
const start = Date.now();
|
|
341
236
|
const tick = () => {
|
|
342
|
-
const events =
|
|
237
|
+
const events = parseJsonLines(stdout);
|
|
343
238
|
if (events.some((event) => event.type === "agent_end")) {
|
|
344
239
|
resolve(events);
|
|
345
240
|
return;
|
|
@@ -359,10 +254,11 @@ export async function runDebugProviderEvents(args) {
|
|
|
359
254
|
fail(`pi exited ${exitCode}\nstderr=${scrubSensitiveText(stderr.slice(-2000), args.apiKey)}`, [args.apiKey]);
|
|
360
255
|
}
|
|
361
256
|
|
|
362
|
-
const captureSummary =
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
257
|
+
const captureSummary = assertCompleteCaptureSummary(
|
|
258
|
+
backfillPiSessionSnapshot(readCaptureSummary(artifactDir, stderr), artifactDir, sessionDir),
|
|
259
|
+
artifactDir,
|
|
260
|
+
args.apiKey,
|
|
261
|
+
);
|
|
366
262
|
|
|
367
263
|
return {
|
|
368
264
|
artifactDir: captureSummary.artifactDir,
|
|
@@ -392,12 +288,11 @@ async function main(argv = process.argv.slice(2), env = process.env) {
|
|
|
392
288
|
printHelp();
|
|
393
289
|
return;
|
|
394
290
|
}
|
|
395
|
-
console.log(JSON.stringify(await runDebugProviderEvents(args)));
|
|
291
|
+
console.log(JSON.stringify(await runDebugProviderEvents(args, env)));
|
|
396
292
|
}
|
|
397
293
|
|
|
398
294
|
if (import.meta.url === new URL(process.argv[1], "file:").href) {
|
|
399
295
|
main().catch((error) => {
|
|
400
|
-
|
|
401
|
-
process.exitCode = 1;
|
|
296
|
+
fail(error instanceof Error ? error.message : String(error), apiKeySecretsFromProcess());
|
|
402
297
|
});
|
|
403
298
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export interface CursorDebugSdkEventsArgs {
|
|
2
|
+
cwd: string;
|
|
3
|
+
model: string;
|
|
4
|
+
prompt?: string;
|
|
5
|
+
out?: string;
|
|
6
|
+
settingSources?: string[] | undefined;
|
|
7
|
+
includeConversation: boolean;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
help: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CursorSdkEventDebugSummary {
|
|
13
|
+
artifactDir: string;
|
|
14
|
+
files: {
|
|
15
|
+
metadata: string;
|
|
16
|
+
streamEvents: string;
|
|
17
|
+
onDelta: string;
|
|
18
|
+
onStep: string;
|
|
19
|
+
waitResult: string;
|
|
20
|
+
conversation?: string;
|
|
21
|
+
};
|
|
22
|
+
counts: {
|
|
23
|
+
stream: Record<string, number>;
|
|
24
|
+
onDelta: Record<string, number>;
|
|
25
|
+
onStep: Record<string, number>;
|
|
26
|
+
};
|
|
27
|
+
timing: {
|
|
28
|
+
stream: CursorSdkEventTimingSnapshot;
|
|
29
|
+
onDelta: CursorSdkEventTimingSnapshot;
|
|
30
|
+
onStep: CursorSdkEventTimingSnapshot;
|
|
31
|
+
};
|
|
32
|
+
wait?: {
|
|
33
|
+
status: string;
|
|
34
|
+
durationMs: number;
|
|
35
|
+
hasResultText: boolean;
|
|
36
|
+
};
|
|
37
|
+
conversation?: { turnCount: number } | Record<string, unknown>;
|
|
38
|
+
warnings: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface CursorSdkEventTimingSnapshot {
|
|
42
|
+
eventCount: number;
|
|
43
|
+
firstMs?: number;
|
|
44
|
+
lastMs?: number;
|
|
45
|
+
maxGapMs?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export declare function parseDebugSdkEventsArgs(
|
|
49
|
+
argv: string[],
|
|
50
|
+
env?: NodeJS.ProcessEnv,
|
|
51
|
+
): CursorDebugSdkEventsArgs;
|
|
52
|
+
|
|
53
|
+
export declare function createTimingTracker(): {
|
|
54
|
+
eventCount: number;
|
|
55
|
+
firstMs?: number;
|
|
56
|
+
lastMs?: number;
|
|
57
|
+
maxGapMs?: number;
|
|
58
|
+
record(elapsedMs: number): void;
|
|
59
|
+
snapshot(): CursorSdkEventTimingSnapshot;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export interface CursorSdkEventJsonlSink {
|
|
63
|
+
appendStream(event: unknown): void;
|
|
64
|
+
appendDelta(update: unknown): void;
|
|
65
|
+
appendStep(step: unknown): void;
|
|
66
|
+
getSummaryState(): {
|
|
67
|
+
counts: {
|
|
68
|
+
stream: Record<string, number>;
|
|
69
|
+
onDelta: Record<string, number>;
|
|
70
|
+
onStep: Record<string, number>;
|
|
71
|
+
};
|
|
72
|
+
timing: {
|
|
73
|
+
stream: CursorSdkEventTimingSnapshot;
|
|
74
|
+
onDelta: CursorSdkEventTimingSnapshot;
|
|
75
|
+
onStep: CursorSdkEventTimingSnapshot;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
close(): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export declare function createEventJsonlSink(artifactDir: string, startedAt: number): CursorSdkEventJsonlSink;
|
|
82
|
+
|
|
83
|
+
export declare function buildSummary(input: {
|
|
84
|
+
artifactDir: string;
|
|
85
|
+
counts: CursorSdkEventDebugSummary["counts"];
|
|
86
|
+
timing: CursorSdkEventDebugSummary["timing"];
|
|
87
|
+
waitResult?: { status: string; durationMs: number; result?: string };
|
|
88
|
+
conversation?: unknown;
|
|
89
|
+
includeConversation: boolean;
|
|
90
|
+
}): CursorSdkEventDebugSummary;
|
|
@@ -5,12 +5,17 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
|
-
import { dirname, join
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
apiKeySecretsFromProcess,
|
|
11
|
+
commonProbeFlags,
|
|
12
|
+
defaultApiKeyFromEnv,
|
|
13
|
+
defaultSettingSourcesFromEnv,
|
|
14
|
+
defaultTimestampedDir,
|
|
15
|
+
parseArgv,
|
|
16
|
+
requireApiKey,
|
|
17
|
+
} from "./lib/cursor-cli-args.mjs";
|
|
18
|
+
import { createScriptFail } from "./lib/cursor-script-fail.mjs";
|
|
14
19
|
import { installCursorSdkOutputFilter, suppressCursorSdkOutput } from "./lib/cursor-sdk-output-filter.mjs";
|
|
15
20
|
|
|
16
21
|
const require = createRequire(import.meta.url);
|
|
@@ -78,101 +83,36 @@ Safety:
|
|
|
78
83
|
https://cursor.com/docs/sdk/typescript before drawing integration conclusions.`);
|
|
79
84
|
}
|
|
80
85
|
|
|
81
|
-
|
|
82
|
-
const scrubbed = scrubSensitiveText(message, secrets[0]);
|
|
83
|
-
console.error(`debug-sdk-events: ${scrubbed}`);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
+
const fail = createScriptFail("debug-sdk-events");
|
|
86
87
|
|
|
87
88
|
export function parseDebugSdkEventsArgs(argv, env = process.env) {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (!value || value.startsWith("--")) fail("--cwd requires a path");
|
|
111
|
-
args.cwd = resolve(value);
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
if (arg.startsWith("--cwd=")) {
|
|
115
|
-
args.cwd = resolve(arg.slice("--cwd=".length));
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (arg === "--model") {
|
|
119
|
-
const value = argv[++index];
|
|
120
|
-
if (!value || value.startsWith("--")) fail("--model requires a value");
|
|
121
|
-
args.model = value.trim();
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if (arg.startsWith("--model=")) {
|
|
125
|
-
args.model = arg.slice("--model=".length).trim();
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (arg === "--prompt") {
|
|
129
|
-
const value = argv[++index];
|
|
130
|
-
if (!value || value.startsWith("--")) fail("--prompt requires a value");
|
|
131
|
-
args.prompt = value;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (arg.startsWith("--prompt=")) {
|
|
135
|
-
args.prompt = arg.slice("--prompt=".length);
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
if (arg === "--out") {
|
|
139
|
-
const value = argv[++index];
|
|
140
|
-
if (!value || value.startsWith("--")) fail("--out requires a directory path");
|
|
141
|
-
args.out = resolve(value);
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
if (arg.startsWith("--out=")) {
|
|
145
|
-
args.out = resolve(arg.slice("--out=".length));
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
if (arg === "--setting-sources") {
|
|
149
|
-
const value = argv[++index];
|
|
150
|
-
if (!value || value.startsWith("--")) fail("--setting-sources requires a value");
|
|
151
|
-
args.settingSources = resolveCursorSettingSources(value);
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (arg.startsWith("--setting-sources=")) {
|
|
155
|
-
args.settingSources = resolveCursorSettingSources(arg.slice("--setting-sources=".length));
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
if (arg === "--api-key") {
|
|
159
|
-
const value = argv[++index];
|
|
160
|
-
if (!value || value.startsWith("--")) fail("--api-key requires a value");
|
|
161
|
-
args.apiKey = value.trim();
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
if (arg.startsWith("--api-key=")) {
|
|
165
|
-
args.apiKey = arg.slice("--api-key=".length).trim();
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
fail(`unknown argument: ${arg}`);
|
|
169
|
-
}
|
|
89
|
+
const includeConversation = argv.includes("--include-conversation");
|
|
90
|
+
const filteredArgv = argv.filter((arg) => arg !== "--include-conversation");
|
|
91
|
+
const args = parseArgv(filteredArgv, {
|
|
92
|
+
defaults: {
|
|
93
|
+
cwd: process.cwd(),
|
|
94
|
+
model: DEFAULT_MODEL,
|
|
95
|
+
prompt: undefined,
|
|
96
|
+
out: undefined,
|
|
97
|
+
settingSources: defaultSettingSourcesFromEnv(env),
|
|
98
|
+
includeConversation,
|
|
99
|
+
apiKey: defaultApiKeyFromEnv(env),
|
|
100
|
+
},
|
|
101
|
+
flags: {
|
|
102
|
+
cwd: commonProbeFlags.cwd,
|
|
103
|
+
model: commonProbeFlags.model,
|
|
104
|
+
prompt: commonProbeFlags.prompt,
|
|
105
|
+
out: commonProbeFlags.out,
|
|
106
|
+
apiKey: commonProbeFlags.apiKey,
|
|
107
|
+
settingSources: commonProbeFlags.settingSources,
|
|
108
|
+
},
|
|
109
|
+
fail,
|
|
110
|
+
});
|
|
170
111
|
return args;
|
|
171
112
|
}
|
|
172
113
|
|
|
173
114
|
function defaultOutDir() {
|
|
174
|
-
|
|
175
|
-
return join("/tmp", `pi-cursor-sdk-sdk-events-${stamp}`);
|
|
115
|
+
return defaultTimestampedDir("pi-cursor-sdk-sdk-events");
|
|
176
116
|
}
|
|
177
117
|
|
|
178
118
|
function eventType(value) {
|
|
@@ -399,15 +339,13 @@ async function main(argv = process.argv.slice(2), env = process.env) {
|
|
|
399
339
|
if (!args.prompt?.trim()) {
|
|
400
340
|
fail("--prompt is required");
|
|
401
341
|
}
|
|
402
|
-
|
|
403
|
-
fail("Cursor API key is required. Set CURSOR_API_KEY or pass --api-key.");
|
|
404
|
-
}
|
|
342
|
+
args.apiKey = requireApiKey(args, env, fail);
|
|
405
343
|
await captureEvents(args);
|
|
406
344
|
}
|
|
407
345
|
|
|
408
346
|
if (import.meta.url === new URL(process.argv[1], "file:").href) {
|
|
409
347
|
main().catch((error) => {
|
|
410
348
|
const message = error instanceof Error ? error.message : String(error);
|
|
411
|
-
fail(message);
|
|
349
|
+
fail(message, apiKeySecretsFromProcess());
|
|
412
350
|
});
|
|
413
351
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simulates plan-mode execute: strips grep/find/cursor before turn_start resync.
|
|
3
|
+
*/
|
|
4
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
const NORMAL_MODE_TOOLS = ["read", "bash", "edit", "write"];
|
|
7
|
+
|
|
8
|
+
export default function planStripShim(pi: ExtensionAPI): void {
|
|
9
|
+
pi.on("turn_start", () => {
|
|
10
|
+
pi.setActiveTools(NORMAL_MODE_TOOLS);
|
|
11
|
+
});
|
|
12
|
+
}
|