pi-cursor-sdk 0.1.62 → 0.3.6

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 (279) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +57 -53
  3. package/dist/bundled-context-windows.js +39 -0
  4. package/dist/context-window-cache.js +92 -0
  5. package/dist/context.js +338 -0
  6. package/dist/cursor-active-tools.js +3 -0
  7. package/dist/cursor-agent-message-web-tools.js +165 -0
  8. package/dist/cursor-agents-context-registration.js +17 -0
  9. package/dist/cursor-agents-context.js +113 -0
  10. package/dist/cursor-api-key.js +34 -0
  11. package/dist/cursor-bridge-contract.js +27 -0
  12. package/dist/cursor-cloud-lifecycle.js +650 -0
  13. package/dist/cursor-cloud-local-state.js +460 -0
  14. package/dist/cursor-cloud-options.js +145 -0
  15. package/dist/cursor-cloud-reporting.js +222 -0
  16. package/dist/cursor-compact-tool-summary.js +75 -0
  17. package/dist/cursor-config.js +613 -0
  18. package/dist/cursor-context-tools.js +4 -0
  19. package/dist/cursor-display-only-trace.js +11 -0
  20. package/dist/cursor-display-text.js +18 -0
  21. package/dist/cursor-durable-fs.js +53 -0
  22. package/dist/cursor-edit-diff.js +13 -0
  23. package/dist/cursor-env-boolean.js +19 -0
  24. package/dist/cursor-fallback-models.generated.js +7760 -0
  25. package/dist/cursor-fallback-warning.js +15 -0
  26. package/dist/cursor-http1.js +45 -0
  27. package/dist/cursor-incomplete-tool-visibility.js +83 -0
  28. package/dist/cursor-live-run-accounting.js +55 -0
  29. package/dist/cursor-live-run-coordinator.js +406 -0
  30. package/dist/cursor-mcp-timeout-override.js +116 -0
  31. package/dist/cursor-model-lifecycle.js +32 -0
  32. package/dist/cursor-model.js +5 -0
  33. package/dist/cursor-native-replay-routing.js +35 -0
  34. package/dist/cursor-native-replay-trace.js +28 -0
  35. package/dist/cursor-native-tool-display-registration.js +102 -0
  36. package/dist/cursor-native-tool-display-replay.js +428 -0
  37. package/dist/cursor-native-tool-display-state.js +70 -0
  38. package/dist/cursor-native-tool-display-tools.js +123 -0
  39. package/dist/cursor-native-tool-names.js +11 -0
  40. package/dist/cursor-partial-content-emitter.js +118 -0
  41. package/dist/cursor-pi-tool-bridge-abort.js +114 -0
  42. package/dist/cursor-pi-tool-bridge-constants.js +6 -0
  43. package/dist/cursor-pi-tool-bridge-diagnostics.js +125 -0
  44. package/dist/cursor-pi-tool-bridge-env.js +18 -0
  45. package/dist/cursor-pi-tool-bridge-mcp.js +100 -0
  46. package/dist/cursor-pi-tool-bridge-run.js +355 -0
  47. package/dist/cursor-pi-tool-bridge-server.js +163 -0
  48. package/dist/cursor-pi-tool-bridge-snapshot.js +63 -0
  49. package/dist/cursor-pi-tool-bridge-types.js +1 -0
  50. package/dist/cursor-pi-tool-bridge.js +123 -0
  51. package/dist/cursor-provider-errors.js +311 -0
  52. package/dist/cursor-provider-lazy.js +39 -0
  53. package/dist/cursor-provider-live-run-drain.js +412 -0
  54. package/dist/cursor-provider-overflow.js +90 -0
  55. package/dist/cursor-provider-run-finalizer.js +176 -0
  56. package/dist/cursor-provider-run-outcome.js +76 -0
  57. package/dist/cursor-provider-turn-coordinator.js +275 -0
  58. package/dist/cursor-provider-turn-display-router.js +158 -0
  59. package/dist/cursor-provider-turn-emit.js +35 -0
  60. package/dist/cursor-provider-turn-finalize.js +154 -0
  61. package/dist/cursor-provider-turn-lifecycle-emitter.js +126 -0
  62. package/dist/cursor-provider-turn-prepare.js +374 -0
  63. package/dist/cursor-provider-turn-runner.js +146 -0
  64. package/dist/cursor-provider-turn-sdk-normalizer.js +74 -0
  65. package/dist/cursor-provider-turn-send.js +138 -0
  66. package/dist/cursor-provider-turn-shell-output.js +117 -0
  67. package/dist/cursor-provider-turn-tool-ledger.js +99 -0
  68. package/dist/cursor-provider-turn-types.js +1 -0
  69. package/dist/cursor-provider.js +72 -0
  70. package/dist/cursor-question-tool.js +194 -0
  71. package/dist/cursor-record-utils.js +61 -0
  72. package/dist/cursor-replay-activity-builders.js +114 -0
  73. package/dist/cursor-replay-source-names.js +27 -0
  74. package/dist/cursor-replay-summary-args.js +105 -0
  75. package/dist/cursor-replay-tool-details.js +167 -0
  76. package/dist/cursor-ripgrep-path.js +27 -0
  77. package/dist/cursor-run-final-text.js +63 -0
  78. package/dist/cursor-runtime-state.js +380 -0
  79. package/dist/cursor-sdk-billed-usage.js +86 -0
  80. package/dist/cursor-sdk-event-debug-constants.js +33 -0
  81. package/dist/cursor-sdk-event-debug-session.js +107 -0
  82. package/dist/cursor-sdk-event-debug.js +461 -0
  83. package/dist/cursor-sdk-output-filter.js +1 -0
  84. package/dist/cursor-sdk-process-error-guard.js +221 -0
  85. package/dist/cursor-sdk-runtime.js +3 -0
  86. package/dist/cursor-sensitive-text.js +23 -0
  87. package/dist/cursor-session-agent-cleanup.js +311 -0
  88. package/dist/cursor-session-agent-lifecycle.js +32 -0
  89. package/dist/cursor-session-agent-lineage.js +103 -0
  90. package/dist/cursor-session-agent-resume.js +417 -0
  91. package/dist/cursor-session-agent.js +507 -0
  92. package/dist/cursor-session-compaction-prep.js +21 -0
  93. package/dist/cursor-session-scope.js +113 -0
  94. package/dist/cursor-session-send-policy.js +18 -0
  95. package/dist/cursor-session-store.js +105 -0
  96. package/dist/cursor-session-turn-queue.js +50 -0
  97. package/dist/cursor-setting-sources.js +15 -0
  98. package/dist/cursor-skill-tool.js +230 -0
  99. package/dist/cursor-state.js +519 -0
  100. package/dist/cursor-task-presentation.js +59 -0
  101. package/dist/cursor-tool-lifecycle.js +94 -0
  102. package/dist/cursor-tool-manifest.js +34 -0
  103. package/dist/cursor-tool-presentation-registry.js +305 -0
  104. package/dist/cursor-tool-result-display-readers.js +158 -0
  105. package/dist/cursor-tool-transcript.js +32 -0
  106. package/dist/cursor-tool-visibility.js +28 -0
  107. package/dist/cursor-transcript-tool-formatters.js +618 -0
  108. package/dist/cursor-transcript-tool-specs.js +284 -0
  109. package/dist/cursor-transcript-utils.js +170 -0
  110. package/dist/cursor-usage-accounting.js +166 -0
  111. package/dist/cursor-web-tool-activity.js +23 -0
  112. package/dist/cursor-web-tool-args.js +12 -0
  113. package/dist/index.js +82 -0
  114. package/dist/model-discovery.js +305 -0
  115. package/dist/model-list-cache.js +156 -0
  116. package/docs/cursor-dogfood-checklist.md +2 -1
  117. package/docs/cursor-live-smoke-checklist.md +15 -14
  118. package/docs/cursor-model-ux-spec.md +29 -8
  119. package/docs/cursor-native-tool-replay.md +13 -13
  120. package/docs/cursor-native-tool-visual-audit.md +6 -3
  121. package/docs/cursor-testing-lessons.md +7 -7
  122. package/docs/cursor-tool-surfaces.md +5 -5
  123. package/docs/platform-smoke.md +6 -6
  124. package/node_modules/fast-uri/index.js +37 -1
  125. package/node_modules/fast-uri/package.json +1 -1
  126. package/node_modules/fast-uri/test/security.test.js +136 -0
  127. package/node_modules/hono/dist/cjs/context.js +28 -13
  128. package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
  129. package/node_modules/hono/dist/cjs/helper/streaming/sse.js +1 -1
  130. package/node_modules/hono/dist/cjs/hono-base.js +3 -2
  131. package/node_modules/hono/dist/cjs/jsx/base.js +27 -23
  132. package/node_modules/hono/dist/cjs/jsx/hooks/index.js +16 -13
  133. package/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
  134. package/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
  135. package/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -2
  136. package/node_modules/hono/dist/cjs/middleware/etag/index.js +1 -1
  137. package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
  138. package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
  139. package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
  140. package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
  141. package/node_modules/hono/dist/cjs/middleware/secure-headers/secure-headers.js +10 -4
  142. package/node_modules/hono/dist/cjs/request.js +7 -9
  143. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +55 -56
  144. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +64 -85
  145. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +13 -5
  146. package/node_modules/hono/dist/cjs/router.js +1 -1
  147. package/node_modules/hono/dist/cjs/utils/accept.js +1 -1
  148. package/node_modules/hono/dist/cjs/utils/cookie.js +3 -2
  149. package/node_modules/hono/dist/cjs/utils/url.js +8 -8
  150. package/node_modules/hono/dist/context.js +28 -13
  151. package/node_modules/hono/dist/helper/proxy/index.js +4 -0
  152. package/node_modules/hono/dist/helper/streaming/sse.js +1 -1
  153. package/node_modules/hono/dist/hono-base.js +3 -2
  154. package/node_modules/hono/dist/jsx/base.js +27 -23
  155. package/node_modules/hono/dist/jsx/hooks/index.js +16 -13
  156. package/node_modules/hono/dist/middleware/cache/index.js +103 -8
  157. package/node_modules/hono/dist/middleware/compress/index.js +5 -0
  158. package/node_modules/hono/dist/middleware/cors/index.js +2 -2
  159. package/node_modules/hono/dist/middleware/etag/index.js +1 -1
  160. package/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
  161. package/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
  162. package/node_modules/hono/dist/middleware/language/language.js +10 -6
  163. package/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
  164. package/node_modules/hono/dist/middleware/secure-headers/secure-headers.js +10 -4
  165. package/node_modules/hono/dist/request.js +8 -10
  166. package/node_modules/hono/dist/router/reg-exp-router/node.js +55 -56
  167. package/node_modules/hono/dist/router/reg-exp-router/router.js +64 -85
  168. package/node_modules/hono/dist/router/reg-exp-router/trie.js +13 -5
  169. package/node_modules/hono/dist/router.js +1 -1
  170. package/node_modules/hono/dist/types/adapter/aws-lambda/types.d.ts +9 -0
  171. package/node_modules/hono/dist/types/client/types.d.ts +1 -1
  172. package/node_modules/hono/dist/types/hono-base.d.ts +4 -3
  173. package/node_modules/hono/dist/types/jsx/base.d.ts +4 -2
  174. package/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
  175. package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
  176. package/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
  177. package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
  178. package/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
  179. package/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
  180. package/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
  181. package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
  182. package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
  183. package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
  184. package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
  185. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +1 -1
  186. package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
  187. package/node_modules/hono/dist/types/router.d.ts +1 -1
  188. package/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
  189. package/node_modules/hono/dist/types/utils/url.d.ts +1 -0
  190. package/node_modules/hono/dist/utils/accept.js +1 -1
  191. package/node_modules/hono/dist/utils/cookie.js +4 -3
  192. package/node_modules/hono/dist/utils/url.js +6 -7
  193. package/node_modules/hono/package.json +10 -3
  194. package/node_modules/ip-address/README.md +146 -126
  195. package/node_modules/ip-address/dist/common.d.ts +29 -0
  196. package/node_modules/ip-address/dist/common.js +44 -5
  197. package/node_modules/ip-address/dist/common.js.map +1 -1
  198. package/node_modules/ip-address/dist/ipv4.d.ts +8 -1
  199. package/node_modules/ip-address/dist/ipv4.js +24 -20
  200. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  201. package/node_modules/ip-address/dist/ipv6.d.ts +68 -3
  202. package/node_modules/ip-address/dist/ipv6.js +180 -53
  203. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  204. package/node_modules/ip-address/dist/v4/constants.js +5 -1
  205. package/node_modules/ip-address/dist/v4/constants.js.map +1 -1
  206. package/node_modules/ip-address/dist/v6/constants.js +3 -2
  207. package/node_modules/ip-address/dist/v6/constants.js.map +1 -1
  208. package/node_modules/ip-address/package.json +6 -4
  209. package/package.json +23 -11
  210. package/platform-smoke.config.mjs +1 -1
  211. package/scripts/build.mjs +126 -0
  212. package/scripts/cloud-runtime-smoke.mjs +3 -1
  213. package/scripts/debug-provider-events.mjs +3 -1
  214. package/scripts/debug-sdk-events.mjs +1 -1
  215. package/scripts/isolated-cursor-smoke.sh +5 -4
  216. package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +1 -1
  217. package/scripts/lib/cursor-visual-render.mjs +1 -1
  218. package/scripts/lib/ensure-built.mjs +16 -0
  219. package/scripts/lib/local-resume-smoke-harness.mjs +1 -1
  220. package/scripts/local-resume-smoke.mjs +3 -1
  221. package/scripts/platform-smoke/card-detect.mjs +1 -1
  222. package/scripts/platform-smoke/live-suite-runner.mjs +16 -40
  223. package/scripts/platform-smoke/platform-build-windows.ps1 +1 -1
  224. package/scripts/platform-smoke/render-ansi.mjs +1 -1
  225. package/scripts/platform-smoke/scenarios.mjs +1 -1
  226. package/scripts/platform-smoke/targets.mjs +2 -2
  227. package/scripts/platform-smoke.mjs +5 -5
  228. package/scripts/prepare.mjs +102 -0
  229. package/scripts/refresh-cursor-model-snapshots.mjs +16 -7
  230. package/scripts/steering-rpc-smoke.mjs +4 -2
  231. package/scripts/tmux-live-smoke.sh +11 -11
  232. package/scripts/visual-tui-smoke-self-test.mjs +4 -1
  233. package/scripts/visual-tui-smoke.mjs +51 -7
  234. package/shared/cursor-model-selection-identities.d.mts +21 -0
  235. package/shared/cursor-model-selection-identities.mjs +95 -0
  236. package/src/bundled-context-windows.ts +21 -12
  237. package/src/context.ts +1 -1
  238. package/src/cursor-agents-context-registration.ts +2 -2
  239. package/src/cursor-agents-context.ts +0 -2
  240. package/src/cursor-api-key.ts +5 -4
  241. package/src/cursor-cloud-reporting.ts +23 -2
  242. package/src/cursor-context-tools.ts +1 -1
  243. package/src/cursor-display-only-trace.ts +1 -1
  244. package/src/cursor-fallback-models.generated.ts +1726 -824
  245. package/src/cursor-live-run-accounting.ts +1 -1
  246. package/src/cursor-live-run-coordinator.ts +2 -1
  247. package/src/cursor-native-replay-routing.ts +1 -1
  248. package/src/cursor-native-tool-display-registration.ts +10 -11
  249. package/src/cursor-partial-content-emitter.ts +1 -1
  250. package/src/cursor-pi-tool-bridge-mcp.ts +1 -1
  251. package/src/cursor-pi-tool-bridge-run.ts +1 -1
  252. package/src/cursor-pi-tool-bridge-types.ts +1 -1
  253. package/src/cursor-provider-errors.ts +1 -1
  254. package/src/cursor-provider-lazy.ts +6 -5
  255. package/src/cursor-provider-live-run-drain.ts +2 -1
  256. package/src/cursor-provider-overflow.ts +1 -1
  257. package/src/cursor-provider-run-finalizer.ts +2 -1
  258. package/src/cursor-provider-turn-coordinator.ts +1 -1
  259. package/src/cursor-provider-turn-finalize.ts +12 -0
  260. package/src/cursor-provider-turn-lifecycle-emitter.ts +1 -1
  261. package/src/cursor-provider-turn-prepare.ts +1 -1
  262. package/src/cursor-provider-turn-types.ts +3 -1
  263. package/src/cursor-provider.ts +1 -1
  264. package/src/cursor-run-final-text.ts +1 -1
  265. package/src/cursor-sdk-billed-usage.ts +107 -0
  266. package/src/cursor-sdk-event-debug.ts +1 -1
  267. package/src/cursor-sdk-process-error-guard.ts +6 -4
  268. package/src/cursor-session-agent-lifecycle.ts +8 -9
  269. package/src/cursor-session-agent-resume.ts +23 -0
  270. package/src/cursor-session-agent.ts +1 -1
  271. package/src/cursor-session-compaction-prep.ts +4 -0
  272. package/src/cursor-session-send-policy.ts +1 -1
  273. package/src/cursor-tool-manifest.ts +1 -1
  274. package/src/cursor-usage-accounting.ts +71 -10
  275. package/src/index.ts +1 -1
  276. package/src/model-discovery.ts +32 -89
  277. package/tsconfig.build.json +8 -0
  278. package/tsconfig.json +16 -0
  279. package/node_modules/cross-spawn/node_modules/which/CHANGELOG.md +0 -166
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Purpose: Produce the compiled runtime files that the Pi extension manifest loads.
4
+ * Responsibilities: Run TypeScript emit into a staging directory, then atomically swap it
5
+ * into dist/ so a failed TypeScript emit never destroys a previously working dist.
6
+ * Usage: `npm run build`; also invoked by scripts/prepare.mjs during install lifecycles.
7
+ * Invariants/Assumptions: `node_modules` provides `typescript`; deleting `dist/` is safe generated output.
8
+ */
9
+
10
+ import { execFile as execFileCallback } from "node:child_process";
11
+ import { existsSync } from "node:fs";
12
+ import { readdir, rename, rm } from "node:fs/promises";
13
+ import { join } from "node:path";
14
+ import process from "node:process";
15
+ import { setTimeout as delay } from "node:timers/promises";
16
+ import { promisify } from "node:util";
17
+
18
+ const execFile = promisify(execFileCallback);
19
+ const RM_OPTIONS = { force: true, maxRetries: 5, recursive: true, retryDelay: 100 };
20
+ const RENAME_RETRY_LIMIT = 50;
21
+ const RENAME_RETRY_MS = 50;
22
+ // Run tsc's JS entrypoint directly through the current node binary: no .cmd shim,
23
+ // no shell, safe for install paths containing spaces on every platform.
24
+ const tscPath = join(process.cwd(), "node_modules", "typescript", "bin", "tsc");
25
+
26
+ async function discardStaging(path) {
27
+ try {
28
+ await rm(path, RM_OPTIONS);
29
+ } catch (error) {
30
+ // Cleanup is always best-effort: it must not turn a successful race loss
31
+ // into failure or replace the compiler/publish error that matters.
32
+ console.warn(`could not remove staging ${path}: ${error?.message ?? error}`);
33
+ }
34
+ }
35
+
36
+ function isOwnerGone(pid) {
37
+ try {
38
+ process.kill(pid, 0);
39
+ return false;
40
+ } catch (error) {
41
+ return error?.code === "ESRCH";
42
+ }
43
+ }
44
+
45
+ async function hasPublishedDist(path) {
46
+ try {
47
+ return (await readdir(path)).length > 0;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ async function reapStrandedStaging(cwd) {
54
+ // Signal 0 proves only that the owner was gone at probe time. Pid reuse
55
+ // between this probe and removal is inherent to pid-based reaping.
56
+ for (const entry of await readdir(cwd, { withFileTypes: true })) {
57
+ const match = /^dist\.staging\.(\d+)$/.exec(entry.name);
58
+ if (!match || !entry.isDirectory()) continue;
59
+ const ownerPid = Number(match[1]);
60
+ if (ownerPid === process.pid || !isOwnerGone(ownerPid)) continue;
61
+ await discardStaging(join(cwd, entry.name));
62
+ }
63
+ }
64
+
65
+ async function compileToStaging(cwd, stagingDir) {
66
+ try {
67
+ const { stderr, stdout } = await execFile(
68
+ process.execPath,
69
+ [tscPath, "-p", "tsconfig.build.json", "--outDir", stagingDir],
70
+ { cwd, maxBuffer: 10 * 1024 * 1024 },
71
+ );
72
+ if (stdout) process.stdout.write(stdout);
73
+ if (stderr) process.stderr.write(stderr);
74
+ } catch (error) {
75
+ if (error?.stdout) process.stdout.write(error.stdout);
76
+ if (error?.stderr) process.stderr.write(error.stderr);
77
+ await discardStaging(stagingDir);
78
+ throw error;
79
+ }
80
+ }
81
+
82
+ async function publishStaging(stagingDir, distDir) {
83
+ try {
84
+ // A failed dist removal must fail loudly; keep it outside the retry loop.
85
+ // Remove dist exactly once so retries never delete a concurrent winner.
86
+ await rm(distDir, RM_OPTIONS);
87
+ for (let retry = 0; ; retry++) {
88
+ try {
89
+ await rename(stagingDir, distDir);
90
+ return;
91
+ } catch (error) {
92
+ // A completed staged build is non-empty; a bare directory is not proof
93
+ // that a concurrent publisher won the race.
94
+ if (await hasPublishedDist(distDir)) {
95
+ console.warn("dist/ was published by a concurrent build; discarding this build's staging tree.");
96
+ return;
97
+ }
98
+ // At most 50 retries (~2.5s) keep persistent filesystem errors bounded.
99
+ if (existsSync(stagingDir) && retry < RENAME_RETRY_LIMIT) {
100
+ await delay(RENAME_RETRY_MS);
101
+ continue;
102
+ }
103
+ throw error;
104
+ }
105
+ }
106
+ } finally {
107
+ // Safe after success too: rename moved stagingDir, so force makes this a no-op.
108
+ await discardStaging(stagingDir);
109
+ }
110
+ }
111
+
112
+ async function main() {
113
+ const cwd = process.cwd();
114
+ if (!existsSync(tscPath)) throw new Error(`typescript is not installed at ${tscPath}; run npm install first.`);
115
+ await reapStrandedStaging(cwd);
116
+ // Pid-scoped staging isolates concurrent emits; only a complete tree publishes.
117
+ const stagingDir = join(cwd, `dist.staging.${process.pid}`);
118
+ await rm(stagingDir, RM_OPTIONS);
119
+ await compileToStaging(cwd, stagingDir);
120
+ await publishStaging(stagingDir, join(cwd, "dist"));
121
+ }
122
+
123
+ main().catch((error) => {
124
+ console.error(error instanceof Error ? error.message : String(error));
125
+ process.exitCode = 1;
126
+ });
@@ -10,6 +10,7 @@ import { tmpdir } from "node:os";
10
10
  import { dirname, join, resolve } from "node:path";
11
11
  import { fileURLToPath, pathToFileURL } from "node:url";
12
12
  import { terminateChild } from "./lib/cursor-child-process.mjs";
13
+ import { ensureBuilt } from "./lib/ensure-built.mjs";
13
14
  import { buildCursorSmokeEnv } from "./lib/cursor-smoke-env.mjs";
14
15
  import {
15
16
  checkpointCloudSmokeShutdown,
@@ -50,7 +51,7 @@ import { scrubSensitiveText } from "../shared/cursor-sensitive-text.mjs";
50
51
 
51
52
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
52
53
  const EVIDENCE_PATH = join(root, "docs", "evidence", "cursor-cloud-smoke-matrix-latest.json");
53
- const MODEL = "cursor/composer-2-5";
54
+ const MODEL = "cursor/grok-4.6";
54
55
  const CLOUD_RUN_ID_PATTERN = /^run-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
55
56
  const cloudSmokeShutdown = createCloudSmokeShutdownController((child) => terminateChild(child, { graceMs: 15_000 }));
56
57
  const argv = process.argv.slice(2);
@@ -614,6 +615,7 @@ async function main() {
614
615
  }
615
616
 
616
617
  if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
618
+ ensureBuilt();
617
619
  main().catch((error) => {
618
620
  reportFailure(error);
619
621
  process.exit(1);
@@ -23,6 +23,7 @@ import {
23
23
  } from "./lib/cursor-child-process.mjs";
24
24
  import { scrubSensitiveText } from "../shared/cursor-sensitive-text.mjs";
25
25
  import { createScriptFail } from "./lib/cursor-script-fail.mjs";
26
+ import { ensureBuilt } from "./lib/ensure-built.mjs";
26
27
  import { serializeCursorSettingSources } from "../shared/cursor-setting-sources.mjs";
27
28
 
28
29
  function isMainModule() {
@@ -35,7 +36,7 @@ function isMainModule() {
35
36
  const require = createRequire(import.meta.url);
36
37
  const root = fileURLToPath(new URL("..", import.meta.url));
37
38
  const packageJson = require("../package.json");
38
- const DEFAULT_MODEL = "cursor/composer-2-5";
39
+ const DEFAULT_MODEL = "cursor/grok-4.6";
39
40
  const DEFAULT_OUT_BASE = ".debug/cursor-sdk-events";
40
41
  const SDK_EVENT_DEBUG_LOG_PREFIX = "[pi-cursor-sdk:sdk-events]";
41
42
  const PI_SESSION_SNAPSHOT_ARTIFACT = "pi-session-snapshot.jsonl";
@@ -301,6 +302,7 @@ async function main(argv = process.argv.slice(2), env = process.env) {
301
302
  printHelp();
302
303
  return;
303
304
  }
305
+ ensureBuilt();
304
306
  console.log(JSON.stringify(await runDebugProviderEvents(args, env)));
305
307
  }
306
308
 
@@ -39,7 +39,7 @@ const ARTIFACTS = {
39
39
  summary: "summary.json",
40
40
  };
41
41
 
42
- const DEFAULT_MODEL = "composer-2.5";
42
+ const DEFAULT_MODEL = "grok-4.6";
43
43
  const RAW_ARTIFACT_WARNING =
44
44
  "Raw artifact files may contain local paths, project text, tool args/results, or secrets from the workspace. Do not commit or share them.";
45
45
 
@@ -134,7 +134,7 @@ run_self_test() {
134
134
  env_capture="$temp_dir/fake-pi.env"
135
135
  cat >"$fake_pi" <<EOF_SELFTEST_PI
136
136
  #!/usr/bin/env node
137
- const { writeFileSync } = require("node:fs");
137
+ import { writeFileSync } from "node:fs";
138
138
  writeFileSync("$env_capture", Object.entries(process.env).map(([key, value]) => key + "=" + (value ?? "")).join("\\n") + "\\n", "utf8");
139
139
  EOF_SELFTEST_PI
140
140
  cat >"$fake_node" <<EOF_SELFTEST_NODE
@@ -359,13 +359,14 @@ log "check: list-models"
359
359
  LIST_OUT="$ISOLATED/list-models.txt"
360
360
  run_in_dir_capture_combined "list-models" 30 "$PROJECT_DIR" "$LIST_OUT" "${PI_CURSOR_ENV[@]}" \
361
361
  "$PI_BIN" --approve --cursor-no-fast --list-models cursor
362
+ "$RG_BIN" -q "grok-4\\.6" "$LIST_OUT" || fail "grok-4.6 not listed (see $LIST_OUT)"
362
363
  "$RG_BIN" -q "composer-2\\.5|composer-2-5" "$LIST_OUT" || fail "composer-2-5 not listed (see $LIST_OUT)"
363
364
 
364
365
  log "check: basic provider prompt"
365
366
  BASIC_DIR="$SESSION_ROOT/basic"
366
367
  mkdir -p "$BASIC_DIR"
367
368
  run_in_dir_capture_split "basic prompt" "$PI_LIVE_TIMEOUT" "$PROJECT_DIR" "$ISOLATED/basic.stdout.txt" "$ISOLATED/basic.stderr.txt" "${PI_CURSOR_ENV[@]}" \
368
- "$PI_BIN" --approve --cursor-no-fast --model cursor/composer-2-5 --session-dir "$BASIC_DIR" --no-tools -p 'Reply exactly: PI_CURSOR_ISOLATED_OK'
369
+ "$PI_BIN" --approve --cursor-no-fast --model cursor/grok-4.6 --session-dir "$BASIC_DIR" --no-tools -p 'Reply exactly: PI_CURSOR_ISOLATED_OK'
369
370
  "$RG_BIN" -q "PI_CURSOR_ISOLATED_OK" "$ISOLATED/basic.stdout.txt" || fail "basic prompt missing PI_CURSOR_ISOLATED_OK"
370
371
  validate_replay_jsonl "$BASIC_DIR"
371
372
 
@@ -373,14 +374,14 @@ log "check: native replay"
373
374
  REPLAY_DIR="$SESSION_ROOT/native-replay"
374
375
  mkdir -p "$REPLAY_DIR"
375
376
  run_in_dir_capture_split "native replay" "$PI_LIVE_TIMEOUT" "$PROJECT_DIR" "$ISOLATED/replay.stdout.txt" "$ISOLATED/replay.stderr.txt" "${PI_CURSOR_ENV[@]}" PI_CURSOR_NATIVE_TOOL_DISPLAY=1 \
376
- "$PI_BIN" --approve --cursor-no-fast --model cursor/composer-2-5 --session-dir "$REPLAY_DIR" -p 'Read ./README.md briefly, then answer README_SEEN=yes if it mentions pi-cursor-sdk.'
377
+ "$PI_BIN" --approve --cursor-no-fast --model cursor/grok-4.6 --session-dir "$REPLAY_DIR" -p 'Read ./README.md briefly, then answer README_SEEN=yes if it mentions pi-cursor-sdk.'
377
378
  validate_replay_jsonl "$REPLAY_DIR"
378
379
 
379
380
  log "check: plan-strip shim (plan-mode execute reset)"
380
381
  PLAN_DIR="$SESSION_ROOT/plan-strip"
381
382
  mkdir -p "$PLAN_DIR"
382
383
  run_in_dir_capture_split "plan-strip replay" "$PI_LIVE_TIMEOUT" "$PROJECT_DIR" "$ISOLATED/plan.stdout.txt" "$ISOLATED/plan.stderr.txt" "${PI_CURSOR_ENV[@]}" PI_CURSOR_NATIVE_TOOL_DISPLAY=1 \
383
- "$PI_BIN" --approve -e "$SHIM_DIR" --cursor-no-fast --model cursor/composer-2-5 --session-dir "$PLAN_DIR" -p 'After reset, read README.md and answer PLAN_STRIP_OK=yes.'
384
+ "$PI_BIN" --approve -e "$SHIM_DIR" --cursor-no-fast --model cursor/grok-4.6 --session-dir "$PLAN_DIR" -p 'After reset, read README.md and answer PLAN_STRIP_OK=yes.'
384
385
  validate_replay_jsonl "$PLAN_DIR"
385
386
 
386
387
  log "PASS isolated install smoke: $ISOLATED"
@@ -360,7 +360,7 @@ function buildCloudSmokeMatrixEvidence(input, laneProjector, allowDefaultTimesta
360
360
  } = data;
361
361
  const timestamp = data.timestamp ?? (allowDefaultTimestamp ? new Date().toISOString() : undefined);
362
362
  if (schemaVersion !== 1) fail("cloud smoke evidence schemaVersion must be 1");
363
- if (model !== "cursor/composer-2-5") fail("cloud smoke evidence model must be cursor/composer-2-5");
363
+ if (model !== "cursor/grok-4.6") fail("cloud smoke evidence model must be cursor/grok-4.6");
364
364
  if (!Array.isArray(lanes) || lanes.length !== LANE_NAMES.length) fail("cloud smoke evidence lanes must contain the complete required matrix");
365
365
  if (!Array.isArray(cleanup) || cleanup.length === 0) fail("cloud smoke evidence cleanup must be a non-empty array");
366
366
  const projectedLanes = lanes.map((lane, index) => laneProjector(lane, index));
@@ -66,7 +66,7 @@ header code { color: #d8dee9; }
66
66
  <header>
67
67
  <div><strong>pi-cursor-sdk visual smoke</strong> <code>${escapeHtml(options.label)}</code></div>
68
68
  <div>model <code>${escapeHtml(options.model)}</code> · mode <code>${escapeHtml(options.mode)}</code> · cwd <code>${escapeHtml(options.cwd)}</code></div>
69
- <div>session <code>${escapeHtml(options.sessionId)}</code> · captured ${new Date().toISOString()}</div>
69
+ <div>session <code>${escapeHtml(options.sessionId ?? "pi-assigned")}</code> · captured ${new Date().toISOString()}</div>
70
70
  </header>
71
71
  <div id="terminal"></div>
72
72
  <noscript><pre class="fallback">${escapeHtml(plain)}</pre></noscript>
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Rebuild dist/ before a launcher loads the repo-root extension, so direct
3
+ * `node scripts/<launcher>.mjs` invocations can never exercise stale code.
4
+ */
5
+
6
+ import { execFileSync } from "node:child_process";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ export function ensureBuilt() {
10
+ execFileSync(process.execPath, [fileURLToPath(new URL("../build.mjs", import.meta.url))], {
11
+ // build.mjs anchors tsc/dist/staging to its cwd; pin it to the repo root so
12
+ // direct launcher invocations work from any directory.
13
+ cwd: fileURLToPath(new URL("../..", import.meta.url)),
14
+ stdio: "inherit",
15
+ });
16
+ }
@@ -152,7 +152,7 @@ export function startRpc({
152
152
  baseEnv = process.env,
153
153
  }) {
154
154
  const model =
155
- process.env.CURSOR_LOCAL_RESUME_SMOKE_MODEL || "cursor/composer-2-5:slow";
155
+ process.env.CURSOR_LOCAL_RESUME_SMOKE_MODEL || "cursor/grok-4.6:slow";
156
156
  const workspaceDir = join(artifactDir, "workspace");
157
157
  mkdirSync(workspaceDir, { recursive: true });
158
158
  const pi = findPiCommand();
@@ -26,6 +26,7 @@ import {
26
26
  withRpc,
27
27
  writeTreeCommandExtension,
28
28
  } from "./lib/local-resume-smoke-harness.mjs";
29
+ import { ensureBuilt } from "./lib/ensure-built.mjs";
29
30
  import { runCleanupSmoke } from "./local-resume-cleanup-smoke.mjs";
30
31
  import { writePlatformArtifactBundle } from "./platform-smoke/artifacts.mjs";
31
32
  import { LOCAL_RESUME_SUITES } from "./platform-smoke/local-resume-suites.mjs";
@@ -45,7 +46,7 @@ ${npmUsage}
45
46
  ${nodeUsage}
46
47
 
47
48
  Environment:
48
- CURSOR_LOCAL_RESUME_SMOKE_MODEL Cursor model id (default: cursor/composer-2-5:slow).
49
+ CURSOR_LOCAL_RESUME_SMOKE_MODEL Cursor model id (default: cursor/grok-4.6:slow).
49
50
  CURSOR_LOCAL_RESUME_SMOKE_TIMEOUT_MS Timeout in ms per model turn (default: 300000).
50
51
  CURSOR_LOCAL_RESUME_SMOKE_KEEP_ARTIFACTS Keep temp artifacts when set to 1.
51
52
  CURSOR_LOCAL_RESUME_SMOKE_EXTENSION_PATH Packed extension path override (platform runner only).
@@ -627,6 +628,7 @@ function selectedRun() {
627
628
  }
628
629
 
629
630
  if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
631
+ ensureBuilt();
630
632
  const run = selectedRun();
631
633
  run()
632
634
  .catch((error) => {
@@ -31,7 +31,7 @@ const CARD_PATTERNS = [
31
31
  { id: "bridge-read-failure", pattern: /^\s*(?:read \.\/definitely-missing-platform-smoke-file\.txt|ENOENT: no such file)\s*/i },
32
32
  { id: "bridge-shell-success", pattern: /^\s*bridge visual smoke\s*$/i },
33
33
  { id: "http1-status", pattern: /\bcursor:local\b.*\bhttp1\b/i },
34
- { id: "footer-status", pattern: /\bcomposer-2-5\b|\bcomposer-2\.5\b/i },
34
+ { id: "footer-status", pattern: /\bgrok-4\.6\b|\bcomposer-2-5\b|\bcomposer-2\.5\b/i },
35
35
  ];
36
36
 
37
37
  function cleanLine(line) {
@@ -17,9 +17,8 @@ import { redactSecrets, writePlatformArtifactBundle } from "./artifacts.mjs";
17
17
  import { extractContentText, jsonlHasAssistantFinalTextMarker } from "./jsonl-text.mjs";
18
18
  import { getScenario, renderPrompt } from "./scenarios.mjs";
19
19
 
20
- const DEFAULT_MODEL = "cursor/composer-2-5";
20
+ const DEFAULT_MODEL = "cursor/grok-4.6";
21
21
  const DEFAULT_WAIT_MS = 240_000;
22
- const READY_WAIT_MS = 45_000;
23
22
  const SESSION_JSONL_WAIT_MS = 60_000;
24
23
  const COLS = 150;
25
24
  const ROWS = 45;
@@ -242,25 +241,10 @@ function stripANSI(text) {
242
241
  return text.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "").replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "");
243
242
  }
244
243
 
245
- function psQuote(value) {
246
- return `'${String(value).replace(/'/g, "''")}'`;
247
- }
248
-
249
- function shellQuote(value) {
250
- return `'${String(value).replace(/'/g, `'\\''`)}'`;
251
- }
252
-
253
- function ptySpawnCommand(piCli, args) {
254
- if (process.platform !== "win32") {
255
- return {
256
- file: "/bin/bash",
257
- args: ["-lc", ["exec", shellQuote(piCli), ...args.map(shellQuote)].join(" ")],
258
- };
259
- }
260
- return {
261
- file: "powershell.exe",
262
- args: ["-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", ["&", psQuote(piCli), ...args.map(psQuote)].join(" ")],
263
- };
244
+ function ptySpawnCommand(args) {
245
+ const cliEntry = resolve(process.cwd(), "node_modules", "@earendil-works", "pi-coding-agent", "dist", "cli.js");
246
+ if (!existsSync(cliEntry)) throw new Error(`Pi CLI entry not found: ${cliEntry}`);
247
+ return { file: process.execPath, args: [cliEntry, ...args] };
264
248
  }
265
249
 
266
250
  async function waitForSessionJsonl(sessionDir, finalMarker, startedAt, events) {
@@ -285,7 +269,7 @@ async function waitForSessionJsonl(sessionDir, finalMarker, startedAt, events) {
285
269
  return files;
286
270
  }
287
271
 
288
- async function runPtyPi({ artifactDir, piCli, piArgs, env, cwd, sessionDir, prompt, finalMarker, waitMs, abortMode, scenario }) {
272
+ async function runPtyPi({ artifactDir, ptyCommand, env, cwd, sessionDir, finalMarker, waitMs, abortMode, scenario }) {
289
273
  let pty;
290
274
  try {
291
275
  pty = await import("node-pty");
@@ -297,8 +281,7 @@ async function runPtyPi({ artifactDir, piCli, piArgs, env, cwd, sessionDir, prom
297
281
  let plain = "";
298
282
  const events = [];
299
283
  const startedAt = Date.now();
300
- const { file, args } = ptySpawnCommand(piCli, piArgs);
301
- writeFileSync(join(artifactDir, "pty-spawn-command.json"), JSON.stringify({ file, args, cwd, fileExists: existsSync(file), cwdExists: existsSync(cwd) }, null, 2));
284
+ const { file, args } = ptyCommand;
302
285
  let child;
303
286
  try {
304
287
  child = pty.spawn(file, args, {
@@ -322,15 +305,7 @@ async function runPtyPi({ artifactDir, piCli, piArgs, env, cwd, sessionDir, prom
322
305
  events.push({ type: "exit", elapsedMs: Date.now() - startedAt, code: event.exitCode, signal: event.signal });
323
306
  });
324
307
 
325
- const readyStartedAt = Date.now();
326
- while (!/(?:composer-2-5|escape interrupt|ctrl\+c\/ctrl\+d)/i.test(plain) && Date.now() - readyStartedAt < READY_WAIT_MS) {
327
- await delay(500);
328
- }
329
- events.push({ type: "ready_wait_finished", elapsedMs: Date.now() - startedAt, ready: /(?:composer-2-5|escape interrupt|ctrl\+c\/ctrl\+d)/i.test(plain) });
330
- child.write(`\x1b[200~${prompt}\x1b[201~\r`);
331
- events.push({ type: "prompt_sent", elapsedMs: Date.now() - startedAt, bytes: prompt.length });
332
- await delay(1_000);
333
- const responseStartOffset = plain.length;
308
+ const responseStartOffset = 0;
334
309
  events.push({ type: "response_watch_started", elapsedMs: Date.now() - startedAt, offset: responseStartOffset });
335
310
 
336
311
  let observed = false;
@@ -572,6 +547,7 @@ async function main() {
572
547
  const suiteEnv = {
573
548
  ...process.env,
574
549
  ...scenario.env,
550
+ PI_OFFLINE: "1",
575
551
  ...(args.suite === "cursor-abort-cleanup" ? { PLATFORM_ABORT_MARKER: "SHOULD_NOT_PRINT" } : {}),
576
552
  PI_CODING_AGENT_DIR: agentDir,
577
553
  PI_CURSOR_SDK_EVENT_DEBUG_DIR: debugDir,
@@ -581,21 +557,21 @@ async function main() {
581
557
  if (args.suite === "cursor-abort-cleanup") writeProcessSnapshot(logDir, "process-before", platform);
582
558
  const prompt = renderPrompt(scenario, platform);
583
559
  writeFileSync(join(artifactDir, "prompt.txt"), prompt);
584
- const piArgs = ["--approve", "--cursor-no-fast", "--cursor-mode", "agent", "--model", args.model, "--session-dir", sessionDir, "--session-id", `platform-${args.suite}-${Date.now()}`];
560
+ const piArgs = ["--approve", "--cursor-no-fast", "--cursor-mode", "agent", "--model", args.model, "--session-dir", sessionDir, "--session-id", `platform-${args.suite}-${Date.now()}`, prompt];
561
+ const ptyCommand = ptySpawnCommand(piArgs);
585
562
  writeFileSync(join(artifactDir, "pi-command.json"), JSON.stringify({
586
- piCli,
587
- args: piArgs,
563
+ ...ptyCommand,
588
564
  cwd: workspaceDir,
589
- env: Object.fromEntries(Object.entries(suiteEnv).filter(([key]) => key.startsWith("PI_CURSOR_") || key === "PI_CODING_AGENT_DIR" || key === "TERM")),
565
+ fileExists: existsSync(ptyCommand.file),
566
+ cwdExists: existsSync(workspaceDir),
567
+ env: Object.fromEntries(Object.entries(suiteEnv).filter(([key]) => key.startsWith("PI_CURSOR_") || key === "PI_CODING_AGENT_DIR" || key === "PI_OFFLINE" || key === "TERM")),
590
568
  }, null, 2));
591
569
  const ptyResult = await runPtyPi({
592
570
  artifactDir,
593
- piCli,
594
- piArgs,
571
+ ptyCommand,
595
572
  env: suiteEnv,
596
573
  cwd: workspaceDir,
597
574
  sessionDir,
598
- prompt,
599
575
  finalMarker: scenario.finalMarker,
600
576
  waitMs: args.waitMs,
601
577
  abortMode: args.suite === "cursor-abort-cleanup",
@@ -62,7 +62,7 @@ Write-SectionFile "NPM_CI_STDOUT" $NpmCiOut
62
62
  Write-SectionFile "NPM_CI_STDERR" $NpmCiErr
63
63
 
64
64
  Write-Output "=== check:platform-smoke ==="
65
- & npm.cmd run check:platform-smoke 1> $CheckPlatformSmokeOut 2> $CheckPlatformSmokeErr
65
+ & npm.cmd run check:platform-smoke -- --testTimeout=15000 1> $CheckPlatformSmokeOut 2> $CheckPlatformSmokeErr
66
66
  $CHECK_PLATFORM_SMOKE_EXIT = Exit-CodeFromLastCommand
67
67
  Write-Output "PLATFORM_CHECK_PLATFORM_SMOKE_EXIT=$CHECK_PLATFORM_SMOKE_EXIT"
68
68
  Write-SectionFile "CHECK_PLATFORM_SMOKE_STDOUT" $CheckPlatformSmokeOut
@@ -27,7 +27,7 @@ export async function renderHTML(ansiPath, htmlPath, options = {}) {
27
27
  plain,
28
28
  options: {
29
29
  label: options.label ?? "platform-smoke",
30
- model: options.model ?? "cursor/composer-2-5",
30
+ model: options.model ?? "cursor/grok-4.6",
31
31
  mode: options.mode ?? "agent",
32
32
  cwd: options.cwd ?? process.cwd(),
33
33
  sessionId: options.sessionId ?? "platform-smoke",
@@ -108,7 +108,7 @@ NATIVE_MATRIX_OK package=<name> grep=<yes/no> find=<yes/no> list=<yes/no> shell=
108
108
  PI_CURSOR_EXPOSE_BUILTIN_TOOLS: "0",
109
109
  PI_CURSOR_SDK_EVENT_DEBUG: "1",
110
110
  },
111
- promptTemplate: "Reply exactly HTTP1_LIVE_OK.",
111
+ promptTemplate: "Join HTTP1, LIVE, and OK with underscores and reply with only the result.",
112
112
  finalMarker: "HTTP1_LIVE_OK",
113
113
  requiredCards: ["http1-status"],
114
114
  },
@@ -413,7 +413,7 @@ export function buildPlatformBuildCommand(targetName, packageName = "pi-cursor-s
413
413
  lines.push(...posixSection("CHECK_PLATFORM_SMOKE_STDERR", 'cat "$PACK_DIR/check-platform-smoke.stderr.txt" 2>/dev/null || true'));
414
414
  lines.push("");
415
415
  lines.push('echo "=== npm test ==="');
416
- lines.push('npm test >"$PACK_DIR/npm-test.stdout.txt" 2>"$PACK_DIR/npm-test.stderr.txt"');
416
+ lines.push('npm test -- --testTimeout=15000 >"$PACK_DIR/npm-test.stdout.txt" 2>"$PACK_DIR/npm-test.stderr.txt"');
417
417
  lines.push("TEST_EXIT=$?");
418
418
  lines.push('echo "PLATFORM_NPM_TEST_EXIT=$TEST_EXIT"');
419
419
  lines.push(...posixSection("NPM_TEST_STDOUT", 'cat "$PACK_DIR/npm-test.stdout.txt" 2>/dev/null || true'));
@@ -662,7 +662,7 @@ async function executeLiveSuite(config, targetName, suiteName, suiteDir, slug, l
662
662
  }
663
663
 
664
664
  function buildLiveSuiteCommand(config, targetName, suiteName, prepDir) {
665
- const model = config.cursorModel ?? "cursor/composer-2-5";
665
+ const model = config.cursorModel ?? "cursor/grok-4.6";
666
666
  const packageName = config.packageName ?? "pi-cursor-sdk";
667
667
  const prepArgs = prepDir ? ` --prep-dir ${platformFor(targetName) === "powershell" ? prepDir : shellQuote(prepDir)}` : "";
668
668
  if (platformFor(targetName) === "powershell") {
@@ -28,7 +28,7 @@ function printHelp() {
28
28
 
29
29
  Commands:
30
30
  doctor Run all preflight checks (no Cursor tokens)
31
- run --target <names> Run one or more comma-separated targets concurrently
31
+ run --target <names> Run one or more comma-separated targets sequentially
32
32
  run --suite <name> Run one suite on all or specified targets
33
33
  run --target <n> --suite <n>
34
34
 
@@ -242,14 +242,14 @@ async function main() {
242
242
  }
243
243
 
244
244
  const startedAt = new Date().toISOString();
245
- const targetRuns = targets.map(async (targetName) => {
245
+ const results = [];
246
+ for (const targetName of targets) {
246
247
  console.log(`\n=== Target: ${targetName} ===`);
247
248
  const result = args.suite
248
249
  ? await runSuite(targetName, suites[0])
249
250
  : await runTarget(targetName, suites);
250
- return { targetName, result };
251
- });
252
- const results = await Promise.all(targetRuns);
251
+ results.push({ targetName, result });
252
+ }
253
253
  const finishedAt = new Date().toISOString();
254
254
  const latest = writeLatestPlatformSmokeIndex(config, results, {
255
255
  startedAt,
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Purpose: Build generated dist output for GitHub/source installs even when Pi invokes npm install --omit=dev.
4
+ * Responsibilities: Detect missing source-build dependencies via the local node_modules tree, install dev
5
+ * dependencies with lifecycle scripts disabled, run the canonical build, then prune the dev tree so installs
6
+ * keep a runtime-only footprint.
7
+ * Scope: Package install lifecycle only; runtime behavior remains owned by scripts/build.mjs.
8
+ * Usage: package.json prepare script.
9
+ */
10
+
11
+ import { execFile as execFileCallback } from "node:child_process";
12
+ import { existsSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import process from "node:process";
15
+ import { promisify } from "node:util";
16
+
17
+ const execFile = promisify(execFileCallback);
18
+
19
+ // Checked as literal node_modules paths: require.resolve() is unusable here because
20
+ // several @earendil-works packages expose import-only "exports" maps and throw
21
+ // ERR_PACKAGE_PATH_NOT_EXPORTED even when installed.
22
+ const REQUIRED_SOURCE_BUILD_MODULES = [
23
+ "typescript",
24
+ "typebox",
25
+ "@earendil-works/pi-ai",
26
+ "@earendil-works/pi-coding-agent",
27
+ "@earendil-works/pi-tui",
28
+ ];
29
+
30
+ function hasBuildDependencies() {
31
+ return REQUIRED_SOURCE_BUILD_MODULES.every((moduleName) =>
32
+ existsSync(join(process.cwd(), "node_modules", ...moduleName.split("/"), "package.json")),
33
+ );
34
+ }
35
+
36
+ async function runNpm(args) {
37
+ const npmExecPath = process.env.npm_execpath;
38
+ if (!npmExecPath) {
39
+ throw new Error(
40
+ `npm_execpath is not set; run "npm ${args.join(" ")}" manually in ${process.cwd()} and retry.`,
41
+ );
42
+ }
43
+ // No shell: process.execPath runs npm's cli.js directly, which is safe for paths
44
+ // containing spaces on every platform (shell:true concatenates args unescaped).
45
+ await execFile(process.execPath, [npmExecPath, ...args], {
46
+ cwd: process.cwd(),
47
+ maxBuffer: 20 * 1024 * 1024,
48
+ });
49
+ }
50
+
51
+ async function runBuild() {
52
+ const { stderr, stdout } = await execFile(process.execPath, [join(process.cwd(), "scripts", "build.mjs")], {
53
+ cwd: process.cwd(),
54
+ maxBuffer: 20 * 1024 * 1024,
55
+ });
56
+ // Forward build output on success too: install-time diagnostics such as the
57
+ // concurrent-swap race-loss warning are otherwise swallowed.
58
+ if (stdout) process.stdout.write(stdout);
59
+ if (stderr) process.stderr.write(stderr);
60
+ }
61
+
62
+ async function pruneDevDependencies() {
63
+ await runNpm(["prune", "--omit=dev", "--ignore-scripts"]);
64
+ }
65
+
66
+ async function pruneQuietly() {
67
+ try {
68
+ await pruneDevDependencies();
69
+ } catch (error) {
70
+ console.warn(`npm prune failed after a failed build: ${error?.message ?? error}`);
71
+ }
72
+ }
73
+
74
+ function reportError(error) {
75
+ if (error?.stdout) process.stdout.write(error.stdout);
76
+ if (error?.stderr) process.stderr.write(error.stderr);
77
+ console.error(error instanceof Error ? error.message : String(error));
78
+ }
79
+
80
+ async function main() {
81
+ if (hasBuildDependencies()) {
82
+ await runBuild();
83
+ return;
84
+ }
85
+ await runNpm(["install", "--include=dev", "--ignore-scripts"]);
86
+ try {
87
+ await runBuild();
88
+ } catch (buildError) {
89
+ // Report the build first; cleanup can warn afterward but never mask it.
90
+ reportError(buildError);
91
+ await pruneQuietly();
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+ // A prune failure after a successful build still fails the install.
96
+ await pruneDevDependencies();
97
+ }
98
+
99
+ main().catch((error) => {
100
+ reportError(error);
101
+ process.exitCode = 1;
102
+ });