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.
Files changed (88) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +49 -9
  3. package/docs/cursor-dogfood-checklist.md +57 -0
  4. package/docs/cursor-live-smoke-checklist.md +115 -9
  5. package/docs/cursor-model-ux-spec.md +57 -17
  6. package/docs/cursor-native-tool-replay.md +15 -7
  7. package/docs/cursor-native-tool-visual-audit.md +104 -59
  8. package/docs/cursor-testing-lessons.md +8 -3
  9. package/docs/cursor-tool-surfaces.md +69 -0
  10. package/package.json +34 -10
  11. package/scripts/debug-provider-events.d.mts +59 -0
  12. package/scripts/debug-provider-events.mjs +70 -175
  13. package/scripts/debug-sdk-events.d.mts +90 -0
  14. package/scripts/debug-sdk-events.mjs +36 -98
  15. package/scripts/fixtures/plan-strip-shim/index.ts +12 -0
  16. package/scripts/isolated-cursor-smoke.sh +264 -102
  17. package/scripts/lib/cursor-child-process.d.mts +10 -0
  18. package/scripts/lib/cursor-child-process.mjs +50 -0
  19. package/scripts/lib/cursor-cli-args.d.mts +63 -0
  20. package/scripts/lib/cursor-cli-args.mjs +129 -0
  21. package/scripts/lib/cursor-script-fail.d.mts +1 -0
  22. package/scripts/lib/cursor-script-fail.mjs +13 -0
  23. package/scripts/lib/cursor-sdk-output-filter.d.mts +5 -0
  24. package/scripts/lib/cursor-smoke-env.d.mts +38 -0
  25. package/scripts/lib/cursor-smoke-env.mjs +81 -0
  26. package/scripts/lib/cursor-smoke-shell.sh +174 -0
  27. package/scripts/lib/cursor-visual-render.d.mts +15 -0
  28. package/scripts/lib/cursor-visual-render.mjs +131 -0
  29. package/scripts/probe-mcp-coldstart.mjs +20 -38
  30. package/scripts/refresh-cursor-model-snapshots.mjs +29 -65
  31. package/scripts/steering-rpc-smoke.mjs +170 -65
  32. package/scripts/tmux-live-smoke.sh +152 -98
  33. package/scripts/visual-tui-smoke.mjs +659 -0
  34. package/shared/cursor-sdk-event-debug-env.d.mts +12 -0
  35. package/shared/cursor-sdk-event-debug-env.mjs +13 -0
  36. package/shared/cursor-sensitive-text.d.mts +1 -0
  37. package/{scripts/lib/cursor-probe-utils.mjs → shared/cursor-sensitive-text.mjs} +1 -13
  38. package/shared/cursor-setting-sources.d.mts +5 -0
  39. package/shared/cursor-setting-sources.mjs +22 -0
  40. package/src/context.ts +21 -12
  41. package/src/cursor-bridge-contract.ts +1 -3
  42. package/src/cursor-incomplete-tool-visibility.ts +22 -5
  43. package/src/cursor-native-tool-display-registration.ts +63 -27
  44. package/src/cursor-native-tool-display-replay.ts +246 -144
  45. package/src/cursor-native-tool-display-state.ts +2 -0
  46. package/src/cursor-native-tool-display-tools.ts +149 -41
  47. package/src/cursor-provider-live-run-drain.ts +1 -52
  48. package/src/cursor-provider-run-finalizer.ts +235 -0
  49. package/src/cursor-provider-run-outcome.ts +149 -0
  50. package/src/cursor-provider-turn-api-key.ts +8 -0
  51. package/src/cursor-provider-turn-coordinator.ts +98 -446
  52. package/src/cursor-provider-turn-display-router.ts +216 -0
  53. package/src/cursor-provider-turn-emit.ts +59 -0
  54. package/src/cursor-provider-turn-finalize.ts +119 -0
  55. package/src/cursor-provider-turn-lifecycle-emitter.ts +97 -0
  56. package/src/cursor-provider-turn-message-offset.ts +15 -0
  57. package/src/cursor-provider-turn-prepare.ts +216 -0
  58. package/src/cursor-provider-turn-runner.ts +138 -0
  59. package/src/cursor-provider-turn-sdk-normalizer.ts +88 -0
  60. package/src/cursor-provider-turn-send.ts +103 -0
  61. package/src/cursor-provider-turn-shell-output.ts +107 -0
  62. package/src/cursor-provider-turn-tool-ledger.ts +126 -0
  63. package/src/cursor-provider-turn-types.ts +87 -0
  64. package/src/cursor-provider.ts +16 -504
  65. package/src/cursor-replay-activity-builders.ts +276 -0
  66. package/src/cursor-replay-source-names.ts +33 -0
  67. package/src/cursor-replay-summary-args.ts +191 -0
  68. package/src/cursor-replay-tool-details.ts +464 -0
  69. package/src/cursor-run-final-text.ts +56 -0
  70. package/src/cursor-sdk-abort-error-guard.ts +4 -0
  71. package/src/cursor-sdk-event-debug-constants.ts +14 -5
  72. package/src/cursor-sdk-event-debug.ts +2 -1
  73. package/src/cursor-sensitive-text.ts +3 -36
  74. package/src/cursor-session-agent.ts +3 -1
  75. package/src/cursor-setting-sources.ts +7 -10
  76. package/src/cursor-state.ts +232 -28
  77. package/src/cursor-tool-lifecycle.ts +9 -8
  78. package/src/cursor-tool-manifest.ts +41 -0
  79. package/src/cursor-tool-names.ts +18 -106
  80. package/src/cursor-tool-presentation-registry.ts +556 -0
  81. package/src/cursor-tool-transcript.ts +1 -1
  82. package/src/cursor-tool-visibility.ts +3 -27
  83. package/src/cursor-transcript-tool-formatters.ts +0 -59
  84. package/src/cursor-transcript-tool-specs.ts +158 -233
  85. package/src/cursor-transcript-utils.ts +0 -44
  86. package/src/cursor-web-tool-activity.ts +10 -60
  87. package/src/cursor-web-tool-args.ts +39 -0
  88. package/src/index.ts +4 -10
@@ -2,21 +2,32 @@
2
2
  set -euo pipefail
3
3
 
4
4
  ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
+ # shellcheck source=scripts/lib/cursor-smoke-shell.sh
6
+ . "$ROOT/scripts/lib/cursor-smoke-shell.sh"
7
+
5
8
  SMOKE_DIR="${SMOKE_DIR:-/tmp/pi-cursor-sdk-live-smoke-$(date +%Y%m%dT%H%M%S)}"
6
9
  SHELL_BIN="${SHELL:-/bin/bash}"
7
10
 
8
- PI_BASE=(
9
- pi -e "$ROOT"
10
- --cursor-no-fast
11
- --model cursor/composer-2.5
12
- )
11
+ PI_BIN=""
12
+ NODE_BIN=""
13
+ NPM_BIN=""
14
+ RG_BIN=""
15
+ TMUX_BIN=""
16
+ ENV_BIN=""
17
+ SEALED_PATH=""
18
+ PI_BASE=()
19
+ DEBUG_ENV_UNSETS=()
20
+ BASE_ENV=()
21
+ NONE_ENV=()
22
+ DEFAULT_ENV=()
13
23
 
14
24
  TMUX_SESSIONS=()
15
25
 
16
26
  cleanup() {
17
27
  local session
28
+ [[ -n "${TMUX_BIN:-}" ]] || return 0
18
29
  for session in "${TMUX_SESSIONS[@]:-}"; do
19
- tmux kill-session -t "$session" 2>/dev/null || true
30
+ "$TMUX_BIN" kill-session -t "$session" 2>/dev/null || true
20
31
  done
21
32
  }
22
33
  trap cleanup EXIT
@@ -31,11 +42,13 @@ Usage:
31
42
 
32
43
  Environment:
33
44
  SMOKE_DIR Artifact directory. Defaults to /tmp/pi-cursor-sdk-live-smoke-<timestamp>.
34
- CURSOR_API_KEY Required for live Cursor runs.
45
+ CURSOR_API_KEY Optional fallback auth. Stored pi auth in ~/.pi/agent/auth.json is also supported.
35
46
 
36
47
  Prerequisites:
37
- pi, node, rg, tmux on PATH
48
+ pi, node, npm, rg, tmux on PATH
49
+ Resolved pi/node/npm/rg/tmux paths from the parent shell are reused in tmux-launched checks; pi shims run with the resolved node directory first on PATH.
38
50
  timeout or gtimeout optional; bash process-group kill fallback is used when absent
51
+ Child pi runs clear Cursor SDK event-debug env; isolated cases force PI_CURSOR_SETTING_SOURCES=none and default-settings unsets it.
39
52
 
40
53
  Coverage:
41
54
  - prereq model listing
@@ -48,10 +61,15 @@ Coverage:
48
61
  - JSONL assistant usage validation
49
62
 
50
63
  Not covered here:
51
- bridge MCP, standalone native replay, abort/cancel, packaging, full checklist sections 4-9
64
+ - canonical rendered-PNG visual smoke; collect separately with docs/cursor-native-tool-visual-audit.md
65
+ - bridge MCP
66
+ - standalone native replay
67
+ - abort/cancel cleanup
68
+ - packaging and isolated smoke
52
69
 
53
70
  Options:
54
71
  -h, --help Show this help.
72
+ --self-test Run sealed PATH/env probes without live Cursor auth.
55
73
 
56
74
  Exit codes:
57
75
  0 all partial checks passed
@@ -59,61 +77,16 @@ Exit codes:
59
77
  EOF
60
78
  }
61
79
 
62
- log() {
63
- printf '[smoke] %s\n' "$*"
64
- }
65
-
66
- fail() {
67
- printf '[smoke] FAIL: %s\n' "$*" >&2
68
- exit 1
69
- }
80
+ log() { smoke_log "$@"; }
81
+ fail() { smoke_fail "$@"; }
82
+ run_with_timeout() { smoke_run_with_timeout "$@"; }
70
83
 
71
- require_cmd() {
72
- command -v "$1" >/dev/null 2>&1 || fail "missing required command: $1"
73
- }
74
-
75
- run_with_timeout() {
76
- local timeout_secs="$1"
77
- shift
78
- if command -v timeout >/dev/null 2>&1; then
79
- timeout "$timeout_secs" "$@"
80
- return $?
81
- fi
82
- if command -v gtimeout >/dev/null 2>&1; then
83
- gtimeout "$timeout_secs" "$@"
84
- return $?
85
- fi
86
-
87
- local restore_monitor=0
88
- case $- in
89
- *m*) ;;
90
- *)
91
- restore_monitor=1
92
- set -m
93
- ;;
94
- esac
95
-
96
- "$@" &
97
- local pid=$!
98
- (
99
- sleep "$timeout_secs"
100
- kill -TERM "-$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true
101
- sleep 2
102
- kill -KILL "-$pid" 2>/dev/null || kill -KILL "$pid" 2>/dev/null || true
103
- ) &
104
- local watcher=$!
105
- local code=0
106
- if wait "$pid"; then
107
- code=0
108
- else
109
- code=$?
110
- fi
111
- kill "$watcher" 2>/dev/null || true
112
- wait "$watcher" 2>/dev/null || true
113
- if (( restore_monitor )); then
114
- set +m
115
- fi
116
- return "$code"
84
+ build_smoke_env_arrays() {
85
+ smoke_build_cursor_sdk_event_debug_unsets
86
+ DEBUG_ENV_UNSETS=( "${SMOKE_CURSOR_SDK_EVENT_DEBUG_ENV_UNSETS[@]}" )
87
+ BASE_ENV=( "$ENV_BIN" "${DEBUG_ENV_UNSETS[@]}" "PATH=$SEALED_PATH" )
88
+ NONE_ENV=( "$ENV_BIN" "${DEBUG_ENV_UNSETS[@]}" "PATH=$SEALED_PATH" PI_CURSOR_SETTING_SOURCES=none )
89
+ DEFAULT_ENV=( "$ENV_BIN" "${DEBUG_ENV_UNSETS[@]}" -u PI_CURSOR_SETTING_SOURCES "PATH=$SEALED_PATH" )
117
90
  }
118
91
 
119
92
  tail_file() {
@@ -131,7 +104,7 @@ assert_file_contains() {
131
104
  local file="$2"
132
105
  local pattern="$3"
133
106
  local label="$4"
134
- if ! rg -q "$pattern" "$file"; then
107
+ if ! "$RG_BIN" -q "$pattern" "$file"; then
135
108
  printf '[smoke] %s missing %s in %s\n' "$name" "$label" "$file" >&2
136
109
  printf '[smoke] %s transcript tail:\n' "$name" >&2
137
110
  tail_file "$file" 120 >&2
@@ -153,7 +126,7 @@ run_direct_attempt() {
153
126
  shift 4
154
127
  rm -f "$stdout" "$stderr"
155
128
 
156
- if run_with_timeout "$timeout_secs" "$@" >"$stdout" 2>"$stderr"; then
129
+ if run_with_timeout "$timeout_secs" "$@" </dev/null >"$stdout" 2>"$stderr"; then
157
130
  return 0
158
131
  fi
159
132
  return $?
@@ -193,7 +166,7 @@ run_direct() {
193
166
  else
194
167
  code=$?
195
168
  fi
196
- if [[ "$code" == "0" ]] && rg -q "$expected_pattern" "$stdout"; then
169
+ if [[ "$code" == "0" ]] && "$RG_BIN" -q "$expected_pattern" "$stdout"; then
197
170
  log "$name PASS"
198
171
  return 0
199
172
  fi
@@ -213,7 +186,7 @@ run_direct() {
213
186
  log "$name retrying once after empty output with exit $code"
214
187
  if run_direct_attempt "$name" "$timeout_secs" "$stdout" "$stderr" "$@"; then
215
188
  local retry_code=0
216
- if rg -q "$expected_pattern" "$stdout"; then
189
+ if "$RG_BIN" -q "$expected_pattern" "$stdout"; then
217
190
  log "$name PASS after retry (first exit $code; first stderr: $first_stderr)"
218
191
  return 0
219
192
  fi
@@ -259,21 +232,22 @@ run_tui_math_footer_poll() {
259
232
  command="$(quote_command "$@")"
260
233
  rm -f "$capture"
261
234
 
262
- printf -v script 'cd %q || exit 97
235
+ printf -v script 'export PATH=%q
236
+ cd %q || exit 97
263
237
  exec %s
264
- ' "$ROOT" "$command"
265
- tmux new-session -d -s "$session" -x 120 -y 40 -- "$SHELL_BIN" -lc "$script"
238
+ ' "$SEALED_PATH" "$ROOT" "$command"
239
+ "$TMUX_BIN" new-session -d -s "$session" -x 120 -y 40 -- "$SHELL_BIN" -lc "$script"
266
240
  TMUX_SESSIONS+=("$session")
267
241
 
268
242
  local elapsed=0
269
243
  local missing=""
270
244
  while true; do
271
- tmux capture-pane -pt "$session" >"$capture" 2>/dev/null || true
245
+ "$TMUX_BIN" capture-pane -pt "$session" >"$capture" 2>/dev/null || true
272
246
  missing=""
273
- rg -q "SUM=42" "$capture" || missing="${missing} SUM=42"
274
- rg -q "\\(cursor\\) composer-2\\.5" "$capture" || missing="${missing} footer (cursor) composer-2.5"
247
+ "$RG_BIN" -q "SUM=42" "$capture" || missing="${missing} SUM=42"
248
+ "$RG_BIN" -q "\\(cursor\\) composer-2\\.5" "$capture" || missing="${missing} footer (cursor) composer-2.5"
275
249
  if [[ -z "$missing" ]]; then
276
- tmux kill-session -t "$session" 2>/dev/null || true
250
+ "$TMUX_BIN" kill-session -t "$session" 2>/dev/null || true
277
251
  log "$name PASS"
278
252
  return 0
279
253
  fi
@@ -281,7 +255,7 @@ exec %s
281
255
  sleep 2
282
256
  elapsed=$((elapsed + 2))
283
257
  if (( elapsed >= timeout_secs )); then
284
- tmux kill-session -t "$session" 2>/dev/null || true
258
+ "$TMUX_BIN" kill-session -t "$session" 2>/dev/null || true
285
259
  printf '[smoke] %s timed out after %ss; missing:%s\n' "$name" "$timeout_secs" "$missing" >&2
286
260
  printf '[smoke] %s capture tail:\n' "$name" >&2
287
261
  tail_file "$capture" 120 >&2
@@ -304,12 +278,13 @@ run_tmux() {
304
278
  command="$(quote_command "$@")"
305
279
  rm -f "$marker" "$stdout" "$stderr"
306
280
 
307
- printf -v script 'cd %q || exit 97
281
+ printf -v script 'export PATH=%q
282
+ cd %q || exit 97
308
283
  %s> %q 2> %q
309
284
  code=$?
310
285
  printf '\''%%s\n'\'' "$code" > %q
311
- ' "$ROOT" "$command" "$stdout" "$stderr" "$marker"
312
- tmux new-session -d -s "$session" -- "$SHELL_BIN" -lc "$script"
286
+ ' "$SEALED_PATH" "$ROOT" "$command" "$stdout" "$stderr" "$marker"
287
+ "$TMUX_BIN" new-session -d -s "$session" -- "$SHELL_BIN" -lc "$script"
313
288
  TMUX_SESSIONS+=("$session")
314
289
 
315
290
  local elapsed=0
@@ -317,15 +292,15 @@ printf '\''%%s\n'\'' "$code" > %q
317
292
  sleep 2
318
293
  elapsed=$((elapsed + 2))
319
294
  if (( elapsed >= timeout_secs )); then
320
- tmux capture-pane -pt "$session" >"$SMOKE_DIR/${name}.capture.txt" || true
321
- tmux kill-session -t "$session" 2>/dev/null || true
295
+ "$TMUX_BIN" capture-pane -pt "$session" >"$SMOKE_DIR/${name}.capture.txt" || true
296
+ "$TMUX_BIN" kill-session -t "$session" 2>/dev/null || true
322
297
  fail "$name timed out after ${timeout_secs}s (see ${name}.capture.txt)"
323
298
  fi
324
299
  done
325
300
 
326
301
  local code
327
302
  code="$(cat "$marker")"
328
- tmux kill-session -t "$session" 2>/dev/null || true
303
+ "$TMUX_BIN" kill-session -t "$session" 2>/dev/null || true
329
304
  if [[ "$code" != "0" ]]; then
330
305
  if [[ "$dump_stderr_on_fail" == "1" ]]; then
331
306
  cat "$stderr" >&2 || true
@@ -337,30 +312,109 @@ printf '\''%%s\n'\'' "$code" > %q
337
312
 
338
313
  model_listed() {
339
314
  local file="$1"
340
- rg -q "composer-2\\.5" "$file"
315
+ "$RG_BIN" -q "composer-2\\.5" "$file"
316
+ }
317
+
318
+ run_self_test() {
319
+ local temp_dir bin_dir fake_pi fake_node fake_node_marker env_capture hostile_path captured_path node_dir name
320
+ temp_dir="$(mktemp -d /tmp/pi-cursor-sdk-live-smoke-self-test.XXXXXX)"
321
+ trap 'rm -rf "$temp_dir"' RETURN
322
+ bin_dir="$temp_dir/bin"
323
+ mkdir -p "$bin_dir"
324
+ fake_pi="$bin_dir/pi"
325
+ fake_node="$bin_dir/node"
326
+ fake_node_marker="$temp_dir/fake-node-used"
327
+ env_capture="$temp_dir/fake-pi.env"
328
+ cat >"$fake_pi" <<EOF_SELFTEST_PI
329
+ #!/usr/bin/env node
330
+ const { writeFileSync } = require("node:fs");
331
+ writeFileSync("$env_capture", Object.entries(process.env).map(([key, value]) => key + "=" + (value ?? "")).join("\\n") + "\\n", "utf8");
332
+ EOF_SELFTEST_PI
333
+ cat >"$fake_node" <<EOF_SELFTEST_NODE
334
+ #!/usr/bin/env bash
335
+ echo fake-node-used > "$fake_node_marker"
336
+ exit 99
337
+ EOF_SELFTEST_NODE
338
+ chmod +x "$fake_pi" "$fake_node"
339
+
340
+ ENV_BIN="$(smoke_resolve_cmd env)"
341
+ NODE_BIN="$(smoke_resolve_cmd node)"
342
+ smoke_load_cursor_sdk_event_debug_env_names "$NODE_BIN" "$ROOT/shared/cursor-sdk-event-debug-env.mjs"
343
+ hostile_path="$bin_dir:$PATH"
344
+ [[ "$(smoke_build_sealed_node_path "$NODE_BIN" "")" != *: ]] || fail "self-test failed: empty inherited PATH left a trailing PATH separator"
345
+ SEALED_PATH="$(smoke_build_sealed_node_path "$NODE_BIN" "$hostile_path")"
346
+ build_smoke_env_arrays
347
+ node_dir="$(dirname "$NODE_BIN")"
348
+
349
+ PI_CURSOR_SETTING_SOURCES=all \
350
+ PI_CURSOR_SDK_EVENT_DEBUG=1 \
351
+ PI_CURSOR_SDK_EVENT_DEBUG_DIR="$temp_dir/debug-dir" \
352
+ PI_CURSOR_SDK_EVENT_DEBUG_RUN_DIR="$temp_dir/debug-run-dir" \
353
+ PI_CURSOR_SDK_EVENT_DEBUG_SESSION_DIR="$temp_dir/debug-session-dir" \
354
+ PI_CURSOR_SDK_EVENT_DEBUG_STDERR=1 \
355
+ "${NONE_ENV[@]}" "$fake_pi" --version
356
+ [[ ! -e "$fake_node_marker" ]] || fail "self-test failed: sealed PATH still used hostile fake node"
357
+ captured_path="$(awk -F= '$1 == "PATH" { print substr($0, 6); exit }' "$env_capture")"
358
+ [[ "${captured_path%%:*}" == "$node_dir" ]] || fail "self-test failed: PATH did not start with resolved node dir"
359
+ grep -qx 'PI_CURSOR_SETTING_SOURCES=none' "$env_capture" || fail "self-test failed: isolated env did not force PI_CURSOR_SETTING_SOURCES=none"
360
+ for name in "${SMOKE_CURSOR_SDK_EVENT_DEBUG_ENV_NAMES[@]}"; do
361
+ if grep -q "^${name}=" "$env_capture"; then
362
+ fail "self-test failed: $name was not cleared"
363
+ fi
364
+ done
365
+
366
+ PI_CURSOR_SETTING_SOURCES=all "${DEFAULT_ENV[@]}" "$fake_pi" --version
367
+ if grep -q '^PI_CURSOR_SETTING_SOURCES=' "$env_capture"; then
368
+ fail "self-test failed: default-settings env did not unset PI_CURSOR_SETTING_SOURCES"
369
+ fi
370
+ printf '[smoke] self-test PASS\n'
341
371
  }
342
372
 
343
373
  if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
344
374
  print_help
345
375
  exit 0
346
376
  fi
377
+ if [[ "${1:-}" == "--self-test" ]]; then
378
+ run_self_test
379
+ exit 0
380
+ fi
347
381
 
348
- require_cmd pi
349
- require_cmd node
350
- require_cmd rg
351
- require_cmd tmux
382
+ PI_BIN="$(smoke_resolve_cmd pi)"
383
+ NODE_BIN="$(smoke_resolve_cmd node)"
384
+ NPM_BIN="$(smoke_resolve_cmd npm)"
385
+ RG_BIN="$(smoke_resolve_cmd rg)"
386
+ TMUX_BIN="$(smoke_resolve_cmd tmux)"
387
+ ENV_BIN="$(smoke_resolve_cmd env)"
388
+ smoke_load_cursor_sdk_event_debug_env_names "$NODE_BIN" "$ROOT/shared/cursor-sdk-event-debug-env.mjs"
389
+ SEALED_PATH="$(smoke_build_sealed_node_path "$NODE_BIN" "$PATH")"
390
+ build_smoke_env_arrays
391
+ if [[ "$SHELL_BIN" != /* ]]; then
392
+ SHELL_BIN="$(smoke_resolve_cmd "$SHELL_BIN")"
393
+ fi
394
+ PI_BASE=(
395
+ "$PI_BIN" -e "$ROOT"
396
+ --cursor-no-fast
397
+ --model cursor/composer-2.5
398
+ )
352
399
 
353
400
  if [[ -z "${CURSOR_API_KEY:-}" ]]; then
354
- fail "CURSOR_API_KEY is required"
401
+ log "CURSOR_API_KEY is unset; relying on stored pi auth or other supported Cursor auth"
355
402
  fi
356
403
 
357
404
  mkdir -p "$SMOKE_DIR"
358
405
  printf '%s\n' "$SMOKE_DIR" >"$SMOKE_DIR/smoke-dir.txt"
359
406
 
360
407
  log "SMOKE_DIR=$SMOKE_DIR"
408
+ log "pi=$PI_BIN"
409
+ log "node=$NODE_BIN"
410
+ log "npm=$NPM_BIN"
411
+ log "tmux=$TMUX_BIN"
361
412
  log "partial live smoke: prereq, basic, default-settings, noninteractive-math, tui, steering, diagnostics, jsonl"
362
413
 
363
- if ! PI_CURSOR_SETTING_SOURCES=none "${PI_BASE[@]}" --list-models cursor 2>"$SMOKE_DIR/prereq.stderr.txt" | tee "$SMOKE_DIR/prereq.models.txt" | rg -q "composer-2\\.5"; then
414
+ "${BASE_ENV[@]}" "$PI_BIN" --version | tee "$SMOKE_DIR/prereq.pi-version.txt"
415
+ "${BASE_ENV[@]}" "$NPM_BIN" --prefix "$ROOT" ls @cursor/sdk @earendil-works/pi-coding-agent @earendil-works/pi-ai @earendil-works/pi-tui | tee "$SMOKE_DIR/prereq.npm-ls.txt"
416
+
417
+ if ! "${NONE_ENV[@]}" "${PI_BASE[@]}" --list-models cursor 2>"$SMOKE_DIR/prereq.stderr.txt" | tee "$SMOKE_DIR/prereq.models.txt" | "$RG_BIN" -q "composer-2\\.5"; then
364
418
  if ! model_listed "$SMOKE_DIR/prereq.stderr.txt"; then
365
419
  fail "cursor/composer-2.5 not listed"
366
420
  fi
@@ -368,34 +422,34 @@ fi
368
422
  log "prereq PASS"
369
423
 
370
424
  run_direct basic 600 retry-empty-output "PI_CURSOR_SMOKE_OK" "PI_CURSOR_SMOKE_OK" \
371
- env PI_CURSOR_SETTING_SOURCES=none "${PI_BASE[@]}" \
425
+ "${NONE_ENV[@]}" "${PI_BASE[@]}" \
372
426
  --session-dir "$SMOKE_DIR/basic" \
373
427
  --no-tools \
374
428
  -p 'Live smoke. Reply exactly: PI_CURSOR_SMOKE_OK'
375
429
 
376
430
  run_direct default-settings 300 strict "PRODUCT=42" "PRODUCT=42" \
377
- "${PI_BASE[@]}" \
431
+ "${DEFAULT_ENV[@]}" "${PI_BASE[@]}" \
378
432
  --session-dir "$SMOKE_DIR/default-settings" \
379
433
  --no-tools \
380
434
  -p 'Default settings smoke. Include PRODUCT=42 in the final answer.'
381
435
 
382
436
  run_direct noninteractive-math 300 strict "SUM=42" "SUM=42" \
383
- env PI_CURSOR_SETTING_SOURCES=none "${PI_BASE[@]}" \
437
+ "${NONE_ENV[@]}" "${PI_BASE[@]}" \
384
438
  --session-dir "$SMOKE_DIR/noninteractive-math" \
385
439
  --no-tools \
386
440
  -p 'Noninteractive math smoke. Compute 19 + 23. Reply only with SUM=42.'
387
441
 
388
442
  run_tui_math_footer_poll tui 420 \
389
- env PI_CURSOR_SETTING_SOURCES=none "${PI_BASE[@]}" \
443
+ "${NONE_ENV[@]}" "${PI_BASE[@]}" \
390
444
  --session-dir "$SMOKE_DIR/tui" \
391
445
  --no-tools \
392
446
  'TUI smoke. Compute 19 + 23. Reply only with SUM=<number>.'
393
447
 
394
448
  run_tmux steering 420 1 \
395
- env "SMOKE_SESSION_DIR=$SMOKE_DIR/steering" node "$ROOT/scripts/steering-rpc-smoke.mjs"
396
- rg -q '"steerOk":true' "$SMOKE_DIR/steering.stdout.txt" || fail "steering missing steerOk"
397
- rg -q '"steerChain":true' "$SMOKE_DIR/steering.stdout.txt" || fail "steering missing steerChain"
398
- rg -q "already has active run|AgentBusyError" "$SMOKE_DIR/steering.stdout.txt" "$SMOKE_DIR/steering.stderr.txt" && fail "steering hit AgentBusyError" || true
449
+ "${NONE_ENV[@]}" "SMOKE_SESSION_DIR=$SMOKE_DIR/steering" "PI_BIN=$PI_BIN" "$NODE_BIN" "$ROOT/scripts/steering-rpc-smoke.mjs"
450
+ "$RG_BIN" -q '"steerOk":true' "$SMOKE_DIR/steering.stdout.txt" || fail "steering missing steerOk"
451
+ "$RG_BIN" -q '"steerChain":true' "$SMOKE_DIR/steering.stdout.txt" || fail "steering missing steerChain"
452
+ "$RG_BIN" -q "already has active run|AgentBusyError" "$SMOKE_DIR/steering.stdout.txt" "$SMOKE_DIR/steering.stderr.txt" && fail "steering hit AgentBusyError" || true
399
453
 
400
454
  forbidden_files="$(find "$SMOKE_DIR" -type f \( -name '*stderr.txt' -o -name '*capture*.txt' \) -print0 |
401
455
  xargs -0 grep -IlE 'CURSOR_API_KEY|Bearer [A-Za-z0-9._-]+|/cursor-pi-tool-bridge/[^ ]+/mcp|127\.0\.0\.1:[0-9]+/cursor-pi-tool-bridge|apiKey|cookie|session-cookie|secret-token' || true)"
@@ -413,6 +467,6 @@ if [[ -n "$forbidden_files" ]]; then
413
467
  fi
414
468
  log "diagnostics safety PASS"
415
469
 
416
- node "$ROOT/scripts/validate-smoke-jsonl.mjs" "$SMOKE_DIR"
470
+ "$NODE_BIN" "$ROOT/scripts/validate-smoke-jsonl.mjs" "$SMOKE_DIR"
417
471
  log "jsonl structural scan PASS"
418
- log "partial live smoke checks passed (see --help for uncovered checklist sections)"
472
+ log "partial live smoke checks passed (see --help for uncovered named release checks)"