@primitive.ai/prim 0.1.0-alpha.90 → 0.1.0-alpha.92

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 (45) hide show
  1. package/README.md +20 -12
  2. package/SKILL.md +6 -6
  3. package/dist/chunk-26P57LAR.js +1491 -0
  4. package/dist/{chunk-LMRYZL3D.js → chunk-5AFRMOPX.js} +1 -1
  5. package/dist/{chunk-RO3F6JYV.js → chunk-5GYQ4J3D.js} +2 -2
  6. package/dist/chunk-5HRR4AOR.js +264 -0
  7. package/dist/{chunk-3EEFL6NJ.js → chunk-67VNLLBN.js} +261 -1
  8. package/dist/{chunk-UZPBB7V4.js → chunk-ADUTW66W.js} +60 -48
  9. package/dist/{chunk-LEMX3IOG.js → chunk-BTNNEH2W.js} +1 -1
  10. package/dist/{chunk-YWO7O6QG.js → chunk-FBAYCS3X.js} +2 -2
  11. package/dist/{chunk-R26A7TWY.js → chunk-FKXFUUWJ.js} +1 -1
  12. package/dist/chunk-H4UNYFUC.js +196 -0
  13. package/dist/{chunk-CUE2LKLR.js → chunk-KFXF2V5G.js} +1 -1
  14. package/dist/{chunk-6IWNH776.js → chunk-LFFLHSQO.js} +4 -3
  15. package/dist/{chunk-C7T7B45R.js → chunk-LGGMJYY4.js} +150 -21
  16. package/dist/{chunk-MAZWKJGL.js → chunk-M6RIJVRE.js} +42 -232
  17. package/dist/{chunk-2JRX7ZR4.js → chunk-NAHHFSLH.js} +16 -5
  18. package/dist/{chunk-FI3LIRCB.js → chunk-OAAP2MY6.js} +2 -2
  19. package/dist/{chunk-Z7YPFWAZ.js → chunk-PCVGBE5B.js} +10 -268
  20. package/dist/chunk-WX52U7Z2.js +112 -0
  21. package/dist/{chunk-PPQYQEIR.js → chunk-ZB4F75SZ.js} +28 -0
  22. package/dist/daemon/server.js +30 -21
  23. package/dist/hook-runtime/daemon/server.js +30 -21
  24. package/dist/hook-runtime/hooks/post-commit.js +20 -20
  25. package/dist/hook-runtime/hooks/post-rewrite.js +20 -20
  26. package/dist/hook-runtime/hooks/post-tool-use.js +666 -147
  27. package/dist/hook-runtime/hooks/pre-tool-use.js +659 -111
  28. package/dist/hook-runtime/hooks/prim-hook.js +627 -111
  29. package/dist/hook-runtime/hooks/session-end.js +610 -27
  30. package/dist/hook-runtime/hooks/session-start.js +1132 -533
  31. package/dist/hook-runtime/index.js +764 -267
  32. package/dist/hook-runtime/statusline-main.js +48 -4
  33. package/dist/hooks/post-commit.js +11 -9
  34. package/dist/hooks/post-rewrite.js +11 -9
  35. package/dist/hooks/post-tool-use.js +73 -48
  36. package/dist/hooks/pre-commit.js +4 -4
  37. package/dist/hooks/pre-tool-use.js +46 -44
  38. package/dist/hooks/prim-hook.js +47 -25
  39. package/dist/hooks/session-end.js +15 -26
  40. package/dist/hooks/session-start.js +59 -32
  41. package/dist/index.js +561 -1430
  42. package/dist/statusline-main.js +48 -4
  43. package/package.json +1 -1
  44. package/dist/chunk-PMRDE2DA.js +0 -310
  45. package/dist/chunk-WB7AUZ4C.js +0 -52
@@ -179,6 +179,7 @@ function compareSemver(left, right) {
179
179
  // src/lib/bin-path.ts
180
180
  var PKG_NAME = "@primitive.ai/prim";
181
181
  var ROOT_WALK_LIMIT = 6;
182
+ var STABLE_HOOK_LAUNCHER_NAME = "prim-hook-launcher-v1";
182
183
  var resolvedRoot;
183
184
  function locateRoot() {
184
185
  if (resolvedRoot !== void 0) {
@@ -239,6 +240,31 @@ function pinnedNpxArgs(bin, args = [], options = {}) {
239
240
  function pinnedNpxCommand(bin, args = [], options = {}) {
240
241
  return ["npx", ...pinnedNpxArgs(bin, args, options)].map(shellWord).join(" ");
241
242
  }
243
+ var STABLE_HOOK_ARGUMENTS_RE = /^[-A-Za-z0-9_ ]*$/u;
244
+ var STABLE_HOOK_BIN_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/u;
245
+ function stableHookCommand(bin, args = "") {
246
+ if (!STABLE_HOOK_BIN_RE.test(bin) || !STABLE_HOOK_ARGUMENTS_RE.test(args)) {
247
+ throw new Error("invalid stable hook command");
248
+ }
249
+ const resolver = [
250
+ // #242 and earlier identify managed commands by this package marker. Keep
251
+ // it as inert data during the rolling window so an older uninstall or
252
+ // reinstall can remove the stable entry without invoking npm or PATH.
253
+ `prim_legacy_reader='-p ${PKG_NAME}@stable prim-shim.sh ${bin} '; `,
254
+ 'prim_absolute() { case "$1" in [/]* ) ;; *) return 1 ;; esac; case "$1" in [[:space:]]*|*[[:space:]]|*[/][/]*|*[/].[/]*|*[/]..[/]*|*[/].|*[/]..|?*[/]) return 1 ;; esac; }; ',
255
+ 'prim_config=${PRIM_CONFIG_DIR:-}; if ! prim_absolute "$prim_config"; then prim_config=${XDG_CONFIG_HOME:-}; if prim_absolute "$prim_config"; then ',
256
+ 'case "$prim_config" in /) prim_config=/prim ;; *) prim_config="$prim_config/prim" ;; esac; ',
257
+ 'else prim_home=${HOME:-}; prim_absolute "$prim_home" || exit 78; case "$prim_home" in /) prim_config=/.config/prim ;; *) prim_config="$prim_home/.config/prim" ;; esac; ',
258
+ "fi; fi; ",
259
+ `case "$prim_config" in /) prim_launcher=/${STABLE_HOOK_LAUNCHER_NAME} ;; *) prim_launcher="$prim_config/${STABLE_HOOK_LAUNCHER_NAME}" ;; esac; `,
260
+ 'exec "$prim_launcher" "$@"'
261
+ ].join("");
262
+ const suffix = args ? ` ${args}` : "";
263
+ return `/bin/sh -c ${shellQuote(resolver)} ${STABLE_HOOK_LAUNCHER_NAME} ${bin}${suffix}`;
264
+ }
265
+ function detachedHookShimCommand(bin, args = "") {
266
+ return `payload=$(cat); { trap '' HUP; printf '%s' "$payload" | { ${stableHookCommand(bin, args)}; }; } </dev/null >/dev/null 2>&1 &`;
267
+ }
242
268
 
243
269
  // src/lib/bin-cache.ts
244
270
  var GIT_HOOK_CACHE_SHELL_DIR = "${XDG_CACHE_HOME:-$HOME/.cache}/prim/bin";
@@ -274,12 +300,574 @@ function warmBinCache() {
274
300
  function parseAgent(argv) {
275
301
  const i = argv.indexOf("--agent");
276
302
  const value = i !== -1 ? argv[i + 1] : void 0;
277
- if (value === "codex" || value === "hermes") {
303
+ if (value === "codex" || value === "cursor" || value === "hermes") {
278
304
  return value;
279
305
  }
280
306
  return "claude_code";
281
307
  }
282
308
 
309
+ // src/commands/cursor-install.ts
310
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
311
+ import { homedir as homedir4 } from "os";
312
+ import { isAbsolute as isAbsolute5, join as join7 } from "path";
313
+
314
+ // src/lib/hook-runtime.ts
315
+ import { createHash, randomBytes as randomBytes2 } from "crypto";
316
+ import {
317
+ constants as constants2,
318
+ accessSync,
319
+ chmodSync as chmodSync2,
320
+ copyFileSync,
321
+ existsSync as existsSync3,
322
+ lstatSync,
323
+ mkdirSync as mkdirSync4,
324
+ mkdtempSync,
325
+ readFileSync as readFileSync3,
326
+ readdirSync,
327
+ realpathSync,
328
+ renameSync as renameSync2,
329
+ rmSync as rmSync3,
330
+ statSync as statSync2,
331
+ utimesSync,
332
+ writeFileSync as writeFileSync3
333
+ } from "fs";
334
+ import { delimiter, dirname as dirname4, isAbsolute as isAbsolute3, join as join5, resolve } from "path";
335
+
336
+ // src/lib/file-lock.ts
337
+ import { randomBytes } from "crypto";
338
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
339
+ import { dirname as dirname3, join as join3 } from "path";
340
+
341
+ // src/lib/process-liveness.ts
342
+ function processIsAlive(pid, probe = process.kill) {
343
+ if (!Number.isSafeInteger(pid) || pid <= 0) return false;
344
+ try {
345
+ probe(pid, 0);
346
+ return true;
347
+ } catch (error) {
348
+ return error.code === "EPERM";
349
+ }
350
+ }
351
+
352
+ // src/lib/file-lock.ts
353
+ var DEFAULT_TIMEOUT_MS = 3e4;
354
+ var DEFAULT_POLL_MS = 50;
355
+ var DEFAULT_INIT_GRACE_MS = 2e3;
356
+ var DIRECTORY_MODE = 448;
357
+ var FILE_MODE = 384;
358
+ function ownerPath(lockDir) {
359
+ return join3(lockDir, "owner.json");
360
+ }
361
+ function readOwner(lockDir) {
362
+ try {
363
+ const owner = JSON.parse(readFileSync2(ownerPath(lockDir), "utf8"));
364
+ if (!Number.isInteger(owner.pid) || owner.pid <= 0 || typeof owner.nonce !== "string" || typeof owner.createdAt !== "number") {
365
+ return null;
366
+ }
367
+ return owner;
368
+ } catch {
369
+ return null;
370
+ }
371
+ }
372
+ function sameOwner(a, b) {
373
+ return a?.pid === b.pid && a.nonce === b.nonce && a.createdAt === b.createdAt;
374
+ }
375
+ function tryTakeLock(lockDir, now) {
376
+ try {
377
+ mkdirSync3(lockDir, { mode: DIRECTORY_MODE });
378
+ } catch (error) {
379
+ if (error.code === "EEXIST") return null;
380
+ throw error;
381
+ }
382
+ const owner = {
383
+ pid: process.pid,
384
+ nonce: randomBytes(16).toString("hex"),
385
+ createdAt: now
386
+ };
387
+ try {
388
+ writeFileSync2(ownerPath(lockDir), `${JSON.stringify(owner)}
389
+ `, {
390
+ encoding: "utf8",
391
+ mode: FILE_MODE,
392
+ flag: "wx"
393
+ });
394
+ return owner;
395
+ } catch (error) {
396
+ rmSync2(lockDir, { recursive: true, force: true });
397
+ throw error;
398
+ }
399
+ }
400
+ function recoverStaleLock(lockDir, now, alive, initGraceMs) {
401
+ const owner = readOwner(lockDir);
402
+ if (owner) {
403
+ if (alive(owner.pid)) return false;
404
+ if (!sameOwner(readOwner(lockDir), owner)) return false;
405
+ rmSync2(lockDir, { recursive: true, force: true });
406
+ return true;
407
+ }
408
+ try {
409
+ const before = statSync(lockDir).mtimeMs;
410
+ if (now - before < initGraceMs) return false;
411
+ if (readOwner(lockDir)) return false;
412
+ if (statSync(lockDir).mtimeMs !== before) return false;
413
+ rmSync2(lockDir, { recursive: true, force: true });
414
+ return true;
415
+ } catch {
416
+ return true;
417
+ }
418
+ }
419
+ function abortError(signal) {
420
+ if (signal.reason instanceof Error) return signal.reason;
421
+ const error = new Error("The operation was aborted");
422
+ error.name = "AbortError";
423
+ return error;
424
+ }
425
+ function throwIfAborted(signal) {
426
+ if (signal?.aborted) throw abortError(signal);
427
+ }
428
+ async function defaultSleep(ms, signal) {
429
+ if (!signal) {
430
+ await new Promise((resolve9) => setTimeout(resolve9, ms));
431
+ return;
432
+ }
433
+ throwIfAborted(signal);
434
+ await new Promise((resolve9, reject) => {
435
+ const timer = setTimeout(() => {
436
+ signal.removeEventListener("abort", onAbort);
437
+ resolve9();
438
+ }, ms);
439
+ const onAbort = () => {
440
+ clearTimeout(timer);
441
+ reject(abortError(signal));
442
+ };
443
+ signal.addEventListener("abort", onAbort, { once: true });
444
+ });
445
+ }
446
+ var syncSleeper = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
447
+ async function withFileLock(lockDir, operation, options = {}) {
448
+ mkdirSync3(dirname3(lockDir), { recursive: true, mode: DIRECTORY_MODE });
449
+ const nowMs = options.nowMs ?? Date.now;
450
+ const alive = options.processAlive ?? processIsAlive;
451
+ const deadline = nowMs() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
452
+ let owner = null;
453
+ while (!owner) {
454
+ throwIfAborted(options.signal);
455
+ const now = nowMs();
456
+ owner = tryTakeLock(lockDir, now);
457
+ if (owner) break;
458
+ recoverStaleLock(lockDir, now, alive, options.initGraceMs ?? DEFAULT_INIT_GRACE_MS);
459
+ if (now >= deadline) {
460
+ throw new Error(`timed out waiting for file lock ${lockDir}`);
461
+ }
462
+ if (options.sleep) {
463
+ await options.sleep(options.pollMs ?? DEFAULT_POLL_MS);
464
+ throwIfAborted(options.signal);
465
+ } else {
466
+ await defaultSleep(options.pollMs ?? DEFAULT_POLL_MS, options.signal);
467
+ }
468
+ }
469
+ try {
470
+ throwIfAborted(options.signal);
471
+ return await operation();
472
+ } finally {
473
+ if (sameOwner(readOwner(lockDir), owner)) {
474
+ rmSync2(lockDir, { recursive: true, force: true });
475
+ }
476
+ }
477
+ }
478
+
479
+ // src/lib/paths.ts
480
+ import { homedir as homedir2 } from "os";
481
+ import { isAbsolute as isAbsolute2, join as join4, normalize } from "path";
482
+ var UNSAFE_CONFIG_PATH_CHARACTERS = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
483
+ function absolutePath(value, variable) {
484
+ if (value !== void 0 && UNSAFE_CONFIG_PATH_CHARACTERS.test(value)) {
485
+ throw new Error(`${variable} contains unsafe characters`);
486
+ }
487
+ if (!value || value.trim() !== value || !isAbsolute2(value)) return;
488
+ const normalized = normalize(value);
489
+ return normalized === value ? value : void 0;
490
+ }
491
+ function resolvePrimConfigDirectory(options = {}) {
492
+ const env = options.env ?? process.env;
493
+ const explicit = absolutePath(env.PRIM_CONFIG_DIR, "PRIM_CONFIG_DIR");
494
+ if (explicit) return { path: explicit, source: "explicit" };
495
+ const xdgConfigHome = absolutePath(env.XDG_CONFIG_HOME, "XDG_CONFIG_HOME");
496
+ if (xdgConfigHome) return { path: join4(xdgConfigHome, "prim"), source: "xdg" };
497
+ const home = absolutePath(options.homeDir ?? homedir2(), "HOME");
498
+ if (!home) {
499
+ throw new Error("cannot resolve Primitive config directory: HOME is not an absolute path");
500
+ }
501
+ return { path: join4(home, ".config", "prim"), source: "default" };
502
+ }
503
+ function primConfigDirectory(options = {}) {
504
+ return resolvePrimConfigDirectory(options).path;
505
+ }
506
+
507
+ // src/lib/hook-runtime.ts
508
+ var RELEASE_PREFIX = "release-";
509
+ var MAX_MANIFEST_BYTES = 64 * 1024;
510
+ var RELEASE_GRACE_MS = 10 * 60 * 1e3;
511
+ var HOOK_RUNTIME_ENTRIES = {
512
+ // SessionStart self-healing resolves this exact CLI entry to run
513
+ // `daemon ensure`; it must travel with the immutable hook release.
514
+ prim: "dist/index.js",
515
+ "prim-hook": "dist/hooks/prim-hook.js",
516
+ "prim-pre-commit": "dist/hooks/pre-commit.js",
517
+ "prim-post-commit": "dist/hooks/post-commit.js",
518
+ "prim-post-rewrite": "dist/hooks/post-rewrite.js",
519
+ "prim-pre-tool-use": "dist/hooks/pre-tool-use.js",
520
+ "prim-post-tool-use": "dist/hooks/post-tool-use.js",
521
+ "prim-session-start": "dist/hooks/session-start.js",
522
+ "prim-session-end": "dist/hooks/session-end.js",
523
+ "prim-daemon-server": "dist/daemon/server.js",
524
+ "prim-statusline": "dist/statusline-main.js"
525
+ };
526
+ var SOURCE_ENTRIES = Object.fromEntries(
527
+ Object.entries(HOOK_RUNTIME_ENTRIES).map(([bin, target]) => [
528
+ bin,
529
+ target.replace(/^dist\//u, "")
530
+ ])
531
+ );
532
+ var STABLE_HOOK_LAUNCHER_CONTENT = `#!/bin/sh
533
+ set -eu
534
+ case "$0" in */*) prim_launcher_dir=\${0%/*} ;; *) exit 78 ;; esac
535
+ IFS= read -r prim_release_name < "$prim_launcher_dir/hook-runtime/current" || exit 69
536
+ case "$prim_release_name" in ${RELEASE_PREFIX}*) ;; *) exit 69 ;; esac
537
+ prim_release_id=\${prim_release_name#${RELEASE_PREFIX}}
538
+ case "$prim_release_id" in *[!0-9a-f]*|"") exit 69 ;; esac
539
+ [ "\${#prim_release_id}" -eq 64 ] || exit 69
540
+ prim_release="$prim_launcher_dir/hook-runtime/releases/$prim_release_name"
541
+ case "\${1-}" in
542
+ ${Object.entries(HOOK_RUNTIME_ENTRIES).map(([bin, entry]) => ` ${bin}) prim_entry="$prim_release/${entry}" ;;`).join("\n")}
543
+ *) exit 64 ;;
544
+ esac
545
+ shift
546
+ IFS= read -r prim_node < "$prim_release/node" || exit 69
547
+ case "$prim_node" in /*) ;; *) exit 69 ;; esac
548
+ [ -x "$prim_node" ] && [ -f "$prim_entry" ] || exit 69
549
+ exec "$prim_node" "$prim_entry" "$@"
550
+ `;
551
+
552
+ // src/commands/claude-install.ts
553
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
554
+ import { homedir as homedir3 } from "os";
555
+ import { join as join6 } from "path";
556
+
557
+ // src/lib/git.ts
558
+ import { execFileSync } from "child_process";
559
+ import { createHash as createHash2 } from "crypto";
560
+ import { lstatSync as lstatSync2, realpathSync as realpathSync2 } from "fs";
561
+ import { basename, dirname as dirname5, isAbsolute as isAbsolute4, relative, resolve as resolve2, sep } from "path";
562
+ var GIT_TIMEOUT_MS = 1e3;
563
+ var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
564
+ var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
565
+ function gitToplevel(cwd) {
566
+ try {
567
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
568
+ cwd,
569
+ encoding: "utf-8",
570
+ stdio: ["ignore", "pipe", "ignore"],
571
+ timeout: GIT_TIMEOUT_MS
572
+ }).trim();
573
+ } catch {
574
+ return null;
575
+ }
576
+ }
577
+ function gitValue(cwd, args) {
578
+ try {
579
+ const value = execFileSync("git", args, {
580
+ cwd,
581
+ encoding: "utf-8",
582
+ stdio: ["ignore", "pipe", "ignore"],
583
+ timeout: GIT_TIMEOUT_MS
584
+ }).trim();
585
+ return value || void 0;
586
+ } catch {
587
+ return void 0;
588
+ }
589
+ }
590
+ function githubRepositoryFullName(cwd) {
591
+ const remote = gitValue(cwd, ["config", "--get", "remote.origin.url"]);
592
+ if (!remote || [...remote].some((char) => char < " " || char === "\x7F")) return null;
593
+ const match = /^(?:git@github\.com:([^?#]+)|(?:https|ssh):\/\/(?:[^/@]+@)?github\.com(?::\d+)?\/([^?#]+))$/i.exec(
594
+ remote
595
+ );
596
+ const path = match?.[1] ?? match?.[2];
597
+ if (!path) return null;
598
+ const parts = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "").split("/");
599
+ if (parts.length !== 2 || !GITHUB_OWNER_PATTERN.test(parts[0] ?? "") || !GITHUB_REPOSITORY_PATTERN.test(parts[1] ?? "")) {
600
+ return null;
601
+ }
602
+ return `${parts[0]}/${parts[1]}`;
603
+ }
604
+ function normalizeOriginRemote(remote) {
605
+ const value = remote.trim();
606
+ if (!value) return void 0;
607
+ const scp = !value.includes("://") ? /^(?:[^@/\s]+@)?(?<host>[^:/\s]+):(?<path>.+)$/.exec(value)?.groups : void 0;
608
+ if (scp?.host && scp.path) {
609
+ const path = scp.path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
610
+ return path ? `${scp.host.toLowerCase()}/${path}` : void 0;
611
+ }
612
+ try {
613
+ const url = new URL(value);
614
+ url.username = "";
615
+ url.password = "";
616
+ url.search = "";
617
+ url.hash = "";
618
+ const path = url.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
619
+ if (url.host && path) return `${url.host.toLowerCase()}/${path}`;
620
+ return void 0;
621
+ } catch {
622
+ return void 0;
623
+ }
624
+ }
625
+ function opaqueRepoKey(source, value) {
626
+ return `repo_v1_${createHash2("sha256").update(`${source}\0${value}`).digest("hex")}`;
627
+ }
628
+ function resolveRepositoryContext(cwd = process.cwd()) {
629
+ const top = gitToplevel(cwd);
630
+ if (!top) return null;
631
+ let repoRoot;
632
+ try {
633
+ repoRoot = realpathSync2.native(top);
634
+ } catch {
635
+ repoRoot = resolve2(top);
636
+ }
637
+ const origin = gitValue(repoRoot, ["config", "--get", "remote.origin.url"]);
638
+ const repoFullName = githubRepositoryFullName(repoRoot) ?? void 0;
639
+ const normalizedOrigin = origin ? normalizeOriginRemote(origin) : void 0;
640
+ if (normalizedOrigin) {
641
+ return {
642
+ repoRoot,
643
+ repoKey: opaqueRepoKey("origin", normalizedOrigin),
644
+ identitySource: "origin",
645
+ repoFullName
646
+ };
647
+ }
648
+ const roots = gitValue(repoRoot, ["rev-list", "--max-parents=0", "HEAD"])?.split(/\s+/).filter(Boolean).sort();
649
+ if (roots && roots.length > 0) {
650
+ return {
651
+ repoRoot,
652
+ repoKey: opaqueRepoKey("root_commit", roots.join("\n")),
653
+ identitySource: "root_commit",
654
+ repoFullName
655
+ };
656
+ }
657
+ return { repoRoot, repoFullName };
658
+ }
659
+
660
+ // src/commands/claude-install.ts
661
+ var CAPTURE_BIN = "prim-hook";
662
+ var GATE_BIN = "prim-pre-tool-use";
663
+ var POST_TOOL_USE_BIN = "prim-post-tool-use";
664
+ var SESSION_START_BIN = "prim-session-start";
665
+ var SESSION_END_BIN = "prim-session-end";
666
+ var STATUSLINE_BIN = "prim-statusline";
667
+ var STATUSLINE_ARGS = "";
668
+ var STATUSLINE_COMMAND = stableHookCommand(STATUSLINE_BIN, STATUSLINE_ARGS);
669
+ var USER_SCOPE_PATH = join6(homedir3(), ".claude", "settings.json");
670
+ function projectRoot() {
671
+ return gitToplevel() ?? process.cwd();
672
+ }
673
+ var CAPTURE_EVENTS = [
674
+ "SessionStart",
675
+ "UserPromptSubmit",
676
+ "PreToolUse",
677
+ "PostToolUse",
678
+ "PostToolUseFailure",
679
+ "Stop",
680
+ "SessionEnd",
681
+ "SubagentStop"
682
+ ];
683
+ function makeRegistration(event, matcher, bin, args = "") {
684
+ return {
685
+ event,
686
+ matcher,
687
+ bin,
688
+ command: stableHookCommand(bin, args)
689
+ };
690
+ }
691
+ function makeDetachedRegistration(event, matcher, bin, args = "") {
692
+ return { event, matcher, bin, command: detachedHookShimCommand(bin, args) };
693
+ }
694
+ var REGISTRATIONS = [
695
+ ...CAPTURE_EVENTS.map(
696
+ (event) => event === "SessionEnd" ? makeDetachedRegistration(event, "*", CAPTURE_BIN) : makeRegistration(event, "*", CAPTURE_BIN)
697
+ ),
698
+ makeRegistration("PreToolUse", "Edit|Write|MultiEdit|NotebookEdit|Bash", GATE_BIN),
699
+ makeRegistration("PostToolUse", "Edit|Write|MultiEdit|NotebookEdit|Bash", POST_TOOL_USE_BIN),
700
+ makeRegistration(
701
+ "PostToolUseFailure",
702
+ "Edit|Write|MultiEdit|NotebookEdit|Bash",
703
+ POST_TOOL_USE_BIN
704
+ ),
705
+ makeRegistration("SessionStart", "*", SESSION_START_BIN),
706
+ makeDetachedRegistration("SessionEnd", "*", SESSION_END_BIN)
707
+ ];
708
+ var PRIM_PERMISSION_RULE = "Bash(npx --yes @primitive.ai/prim*)";
709
+ var LEGACY_PRIM_PERMISSION_RULES = [
710
+ "Bash(npx --yes @primitive.ai/prim@latest:*)",
711
+ "Bash(npx --yes @primitive.ai/prim:*)"
712
+ ];
713
+ var ALL_PRIM_PERMISSION_RULES = /* @__PURE__ */ new Set([
714
+ PRIM_PERMISSION_RULE,
715
+ ...LEGACY_PRIM_PERMISSION_RULES
716
+ ]);
717
+
718
+ // src/commands/cursor-install.ts
719
+ var CAPTURE_BIN2 = "prim-hook";
720
+ var GATE_BIN2 = "prim-pre-tool-use";
721
+ var POST_TOOL_BIN = "prim-post-tool-use";
722
+ var SESSION_START_BIN2 = "prim-session-start";
723
+ var SESSION_END_BIN2 = "prim-session-end";
724
+ var STATUSLINE_BIN2 = "prim-statusline";
725
+ var CURSOR_EVENTS = [
726
+ "sessionStart",
727
+ "beforeSubmitPrompt",
728
+ "preToolUse",
729
+ "postToolUse",
730
+ "postToolUseFailure",
731
+ "afterAgentResponse",
732
+ "subagentStop",
733
+ "stop",
734
+ "sessionEnd"
735
+ ];
736
+ var REGISTRATIONS2 = [
737
+ ...CURSOR_EVENTS.map((event) => ({ event, bin: CAPTURE_BIN2 })),
738
+ { event: "preToolUse", bin: GATE_BIN2, matcher: "Write|Delete|Shell", timeout: 10 },
739
+ { event: "postToolUse", bin: POST_TOOL_BIN },
740
+ { event: "postToolUseFailure", bin: POST_TOOL_BIN },
741
+ { event: "sessionStart", bin: SESSION_START_BIN2 },
742
+ { event: "sessionEnd", bin: SESSION_END_BIN2 }
743
+ ];
744
+ function cursorConfigDirectory(env = process.env, home = homedir4(), platform = process.platform) {
745
+ const explicit = env.CURSOR_CONFIG_DIR?.trim();
746
+ if (explicit && isAbsolute5(explicit)) return explicit;
747
+ const xdg = env.XDG_CONFIG_HOME?.trim();
748
+ if (platform !== "darwin" && xdg && isAbsolute5(xdg)) return join7(xdg, "cursor");
749
+ return join7(home, ".cursor");
750
+ }
751
+ function cursorHooksPath(scope, root = projectRoot()) {
752
+ return scope === "user" ? join7(cursorConfigDirectory(), "hooks.json") : join7(root, ".cursor", "hooks.json");
753
+ }
754
+ function plainObject(value) {
755
+ return typeof value === "object" && value !== null && !Array.isArray(value);
756
+ }
757
+ function readCursorHooks(path) {
758
+ if (!existsSync5(path)) return {};
759
+ let parsed;
760
+ try {
761
+ parsed = JSON.parse(readFileSync5(path, "utf8"));
762
+ } catch {
763
+ throw new Error(`invalid Cursor hooks JSON at ${path}`);
764
+ }
765
+ if (!plainObject(parsed)) throw new Error(`Cursor hooks config must be an object: ${path}`);
766
+ if (parsed.version !== void 0 && parsed.version !== 1) {
767
+ throw new Error(`unsupported Cursor hooks version at ${path} (expected version 1)`);
768
+ }
769
+ if (parsed.hooks !== void 0) {
770
+ if (!plainObject(parsed.hooks)) throw new Error(`Cursor hooks must be an object: ${path}`);
771
+ for (const [event, entries] of Object.entries(parsed.hooks)) {
772
+ if (!Array.isArray(entries) || !entries.every(plainObject)) {
773
+ throw new Error(`Cursor hook list must contain objects: ${event} in ${path}`);
774
+ }
775
+ }
776
+ }
777
+ return parsed;
778
+ }
779
+ function commandFor(registration, scope) {
780
+ return stableHookCommand(
781
+ registration.bin,
782
+ `--agent cursor --event ${registration.event} --scope ${scope}`
783
+ );
784
+ }
785
+ var OWNED_COMMANDS = new Set(
786
+ ["project", "user"].flatMap(
787
+ (scope) => REGISTRATIONS2.map((registration) => commandFor(registration, scope))
788
+ )
789
+ );
790
+ function jsoncUnsafeCommand(command) {
791
+ return command.replace('case "$1" in [/]* ', 'case "$1" in /*').replace("*[/][/]*", "*//*").replace("*[/].[/]*", "*/./*").replace("*[/]..[/]*", "*/../*").replace("*[/].", "*/.").replace("*[/]..", "*/..").replace("?*[/]", "?*/");
792
+ }
793
+ var JSONC_UNSAFE_OWNED_COMMANDS = new Set([...OWNED_COMMANDS].map(jsoncUnsafeCommand));
794
+ function hookFor(registration, scope) {
795
+ return {
796
+ command: commandFor(registration, scope),
797
+ ...registration.matcher ? { matcher: registration.matcher } : {},
798
+ ...registration.timeout ? { timeout: registration.timeout } : {}
799
+ };
800
+ }
801
+ function exactHook(entry, registration, scope) {
802
+ return JSON.stringify(entry) === JSON.stringify(hookFor(registration, scope));
803
+ }
804
+ function hasExactCursorHandler(settings, event, bin, scope) {
805
+ const registration = REGISTRATIONS2.find(
806
+ (candidate) => candidate.event === event && candidate.bin === bin
807
+ );
808
+ return registration ? (settings.hooks?.[event] ?? []).some((entry) => exactHook(entry, registration, scope)) : false;
809
+ }
810
+ var FOOTER = {
811
+ type: "command",
812
+ command: stableHookCommand(STATUSLINE_BIN2),
813
+ updateIntervalMs: 1e3,
814
+ timeoutMs: 2e3
815
+ };
816
+ var JSONC_UNSAFE_FOOTER = {
817
+ ...FOOTER,
818
+ command: jsoncUnsafeCommand(FOOTER.command)
819
+ };
820
+
821
+ // src/hooks/cursor-coexistence.ts
822
+ var CURSOR_EVENTS2 = /* @__PURE__ */ new Set([
823
+ "sessionStart",
824
+ "beforeSubmitPrompt",
825
+ "preToolUse",
826
+ "postToolUse",
827
+ "postToolUseFailure",
828
+ "afterAgentResponse",
829
+ "subagentStop",
830
+ "stop",
831
+ "sessionEnd"
832
+ ]);
833
+ function cursorEvidence(parsed) {
834
+ return typeof parsed.cursor_version === "string" && parsed.cursor_version.length > 0 && typeof parsed.conversation_id === "string" && parsed.conversation_id.length > 0 && typeof parsed.generation_id === "string" && parsed.generation_id.length > 0 && Array.isArray(parsed.workspace_roots) && typeof parsed.hook_event_name === "string" && CURSOR_EVENTS2.has(parsed.hook_event_name);
835
+ }
836
+ function uniqueProjectRoot(parsed) {
837
+ if (!Array.isArray(parsed.workspace_roots)) return;
838
+ const roots = new Set(
839
+ parsed.workspace_roots.filter((value) => typeof value === "string" && value.length > 0).map((value) => resolveRepositoryContext(value)?.repoRoot).filter((value) => !!value)
840
+ );
841
+ return roots.size === 1 ? [...roots][0] : void 0;
842
+ }
843
+ function shouldSuppressImportedCursorHandler(parsed, bin) {
844
+ if (!cursorEvidence(parsed)) return false;
845
+ const project = uniqueProjectRoot(parsed);
846
+ if (project) {
847
+ try {
848
+ if (hasExactCursorHandler(
849
+ readCursorHooks(cursorHooksPath("project", project)),
850
+ parsed.hook_event_name,
851
+ bin,
852
+ "project"
853
+ )) {
854
+ return true;
855
+ }
856
+ } catch {
857
+ }
858
+ }
859
+ try {
860
+ return hasExactCursorHandler(
861
+ readCursorHooks(cursorHooksPath("user")),
862
+ parsed.hook_event_name,
863
+ bin,
864
+ "user"
865
+ );
866
+ } catch {
867
+ return false;
868
+ }
869
+ }
870
+
283
871
  // src/hooks/decision-feedback-core.ts
284
872
  function buildHookOutput(options) {
285
873
  const output = {};
@@ -293,13 +881,13 @@ function buildHookOutput(options) {
293
881
  return output;
294
882
  }
295
883
  function writeHookOutput(output, stream = process.stdout) {
296
- return new Promise((resolve7) => {
884
+ return new Promise((resolve9) => {
297
885
  let settled = false;
298
886
  const finish = (ok, retainErrorListener = false) => {
299
887
  if (settled) return;
300
888
  settled = true;
301
889
  if (!retainErrorListener) stream.off("error", onError);
302
- resolve7(ok);
890
+ resolve9(ok);
303
891
  };
304
892
  const onError = () => {
305
893
  if (settled) {
@@ -331,43 +919,46 @@ async function handoffHookOutput(output, acknowledge, stream = process.stdout) {
331
919
  return handedOff;
332
920
  }
333
921
 
922
+ // src/hooks/hook-stdin.ts
923
+ var DEFAULT_MAX_BYTES = 1024 * 1024;
924
+ function readHookStdin(timeoutMs = 1e3, maxBytes = DEFAULT_MAX_BYTES) {
925
+ return new Promise((resolve9, reject) => {
926
+ const chunks = [];
927
+ let bytes = 0;
928
+ let settled = false;
929
+ const finish = (error) => {
930
+ if (settled) return;
931
+ settled = true;
932
+ clearTimeout(timer);
933
+ process.stdin.removeAllListeners("data");
934
+ process.stdin.removeAllListeners("end");
935
+ process.stdin.removeAllListeners("error");
936
+ if (error) reject(error);
937
+ else resolve9(Buffer.concat(chunks, bytes).toString("utf-8"));
938
+ };
939
+ const timer = setTimeout(() => finish(new Error("stdin read timeout")), timeoutMs);
940
+ process.stdin.on("data", (chunk) => {
941
+ bytes += chunk.length;
942
+ if (bytes > maxBytes) {
943
+ finish(new Error("stdin envelope exceeds size limit"));
944
+ return;
945
+ }
946
+ chunks.push(chunk);
947
+ });
948
+ process.stdin.on("end", () => finish());
949
+ process.stdin.on("error", (error) => finish(error));
950
+ });
951
+ }
952
+
334
953
  // src/client.ts
335
- import { createHash as createHash2 } from "crypto";
336
- import { chmodSync as chmodSync2, existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
337
- import { dirname as dirname4 } from "path";
954
+ import { createHash as createHash4 } from "crypto";
955
+ import { chmodSync as chmodSync3, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync7, rmSync as rmSync4 } from "fs";
956
+ import { dirname as dirname7 } from "path";
338
957
 
339
958
  // src/lib/credentials.ts
340
- import { createHash } from "crypto";
341
- import { readFileSync as readFileSync2 } from "fs";
342
- import { join as join4 } from "path";
343
-
344
- // src/lib/paths.ts
345
- import { homedir as homedir2 } from "os";
346
- import { isAbsolute as isAbsolute2, join as join3, normalize } from "path";
347
- var UNSAFE_CONFIG_PATH_CHARACTERS = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
348
- function absolutePath(value, variable) {
349
- if (value !== void 0 && UNSAFE_CONFIG_PATH_CHARACTERS.test(value)) {
350
- throw new Error(`${variable} contains unsafe characters`);
351
- }
352
- if (!value || value.trim() !== value || !isAbsolute2(value)) return;
353
- const normalized = normalize(value);
354
- return normalized === value ? value : void 0;
355
- }
356
- function resolvePrimConfigDirectory(options = {}) {
357
- const env = options.env ?? process.env;
358
- const explicit = absolutePath(env.PRIM_CONFIG_DIR, "PRIM_CONFIG_DIR");
359
- if (explicit) return { path: explicit, source: "explicit" };
360
- const xdgConfigHome = absolutePath(env.XDG_CONFIG_HOME, "XDG_CONFIG_HOME");
361
- if (xdgConfigHome) return { path: join3(xdgConfigHome, "prim"), source: "xdg" };
362
- const home = absolutePath(options.homeDir ?? homedir2(), "HOME");
363
- if (!home) {
364
- throw new Error("cannot resolve Primitive config directory: HOME is not an absolute path");
365
- }
366
- return { path: join3(home, ".config", "prim"), source: "default" };
367
- }
368
- function primConfigDirectory(options = {}) {
369
- return resolvePrimConfigDirectory(options).path;
370
- }
959
+ import { createHash as createHash3 } from "crypto";
960
+ import { readFileSync as readFileSync6 } from "fs";
961
+ import { join as join8 } from "path";
371
962
 
372
963
  // src/lib/workos-connect.ts
373
964
  var TOKEN_PATH = "/oauth2/token";
@@ -515,20 +1106,20 @@ function parseWorkosConnectTokens(value, options = {}) {
515
1106
 
516
1107
  // src/lib/credentials.ts
517
1108
  var CONFIG_DIRECTORY = primConfigDirectory();
518
- var TOKEN_FILE_PATH = join4(CONFIG_DIRECTORY, "token");
519
- var REFRESH_TOKEN_PATH = join4(CONFIG_DIRECTORY, "refresh_token");
520
- var TOKEN_EXPIRES_PATH = join4(CONFIG_DIRECTORY, "token_expires_at");
521
- var TERMINAL_REFRESH_PATH = join4(CONFIG_DIRECTORY, "refresh_terminal");
522
- var CREDENTIAL_METADATA_PATH = join4(CONFIG_DIRECTORY, "credential_metadata.json");
523
- var CREDENTIAL_FAMILY_PATH = join4(CONFIG_DIRECTORY, "credential_family.json");
524
- var CREDENTIAL_MIGRATION_PATH = join4(CONFIG_DIRECTORY, "credential_migration.json");
525
- var CREDENTIAL_LOCK_PATH = join4(CONFIG_DIRECTORY, "credentials.lock");
1109
+ var TOKEN_FILE_PATH = join8(CONFIG_DIRECTORY, "token");
1110
+ var REFRESH_TOKEN_PATH = join8(CONFIG_DIRECTORY, "refresh_token");
1111
+ var TOKEN_EXPIRES_PATH = join8(CONFIG_DIRECTORY, "token_expires_at");
1112
+ var TERMINAL_REFRESH_PATH = join8(CONFIG_DIRECTORY, "refresh_terminal");
1113
+ var CREDENTIAL_METADATA_PATH = join8(CONFIG_DIRECTORY, "credential_metadata.json");
1114
+ var CREDENTIAL_FAMILY_PATH = join8(CONFIG_DIRECTORY, "credential_family.json");
1115
+ var CREDENTIAL_MIGRATION_PATH = join8(CONFIG_DIRECTORY, "credential_migration.json");
1116
+ var CREDENTIAL_LOCK_PATH = join8(CONFIG_DIRECTORY, "credentials.lock");
526
1117
  var MAX_CREDENTIAL_METADATA_BYTES = 4096;
527
1118
  var CREDENTIAL_MIGRATION_VERSION = 1;
528
1119
  var CREDENTIAL_MIGRATION_STATE = "family_bound";
529
1120
  function readTrimmed(path) {
530
1121
  try {
531
- const value = readFileSync2(path, "utf8").trim();
1122
+ const value = readFileSync6(path, "utf8").trim();
532
1123
  return value || void 0;
533
1124
  } catch {
534
1125
  return void 0;
@@ -561,7 +1152,7 @@ function resolveAuthCredential(options = {}) {
561
1152
  return { token: stored, source: "token_file" };
562
1153
  }
563
1154
  function credentialFingerprint(value) {
564
- return createHash("sha256").update(value).digest("hex");
1155
+ return createHash3("sha256").update(value).digest("hex");
565
1156
  }
566
1157
  function parseLegacyBrokerCredentialMetadata(value) {
567
1158
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
@@ -581,7 +1172,7 @@ function parseLegacyBrokerCredentialMetadata(value) {
581
1172
  function readBoundedCredentialJson(path) {
582
1173
  let raw;
583
1174
  try {
584
- raw = readFileSync2(path, "utf8");
1175
+ raw = readFileSync6(path, "utf8");
585
1176
  } catch (error) {
586
1177
  return error.code === "ENOENT" ? void 0 : null;
587
1178
  }
@@ -618,182 +1209,39 @@ function readCredentialMigrationState(path = CREDENTIAL_MIGRATION_PATH) {
618
1209
  const parsed = readBoundedCredentialJson(path);
619
1210
  if (parsed === void 0) return "absent";
620
1211
  return parsed !== null && isCredentialMigrationSentinel(parsed) ? "valid" : "invalid";
621
- }
622
- function readStoredCredentialMetadata(metadataPath = CREDENTIAL_METADATA_PATH, familyPath = CREDENTIAL_FAMILY_PATH, migrationPath = CREDENTIAL_MIGRATION_PATH) {
623
- const family = readCredentialFamily(familyPath);
624
- if (family) return family;
625
- const parsed = readBoundedCredentialJson(metadataPath);
626
- if (parsed === void 0) {
627
- return readCredentialMigrationState(migrationPath) === "absent" ? { state: "legacy_broker" } : { state: "invalid" };
628
- }
629
- if (parsed === null) return { state: "invalid" };
630
- try {
631
- return {
632
- state: "workos_connect",
633
- metadata: parseWorkosConnectCredentialMetadata(parsed)
634
- };
635
- } catch {
636
- return { state: "invalid" };
637
- }
638
- }
639
- function decodeJwtPayload(token) {
640
- const parts = token.split(".");
641
- if (parts.length !== 3) return void 0;
642
- try {
643
- const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
644
- if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return void 0;
645
- return payload;
646
- } catch {
647
- return void 0;
648
- }
649
- }
650
- function jwtExpiresAt(token) {
651
- const exp = decodeJwtPayload(token)?.exp;
652
- return typeof exp === "number" && Number.isFinite(exp) ? exp * 1e3 : void 0;
653
- }
654
-
655
- // src/lib/file-lock.ts
656
- import { randomBytes } from "crypto";
657
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
658
- import { dirname as dirname3, join as join5 } from "path";
659
-
660
- // src/lib/process-liveness.ts
661
- function processIsAlive(pid, probe = process.kill) {
662
- if (!Number.isSafeInteger(pid) || pid <= 0) return false;
663
- try {
664
- probe(pid, 0);
665
- return true;
666
- } catch (error) {
667
- return error.code === "EPERM";
668
- }
669
- }
670
-
671
- // src/lib/file-lock.ts
672
- var DEFAULT_TIMEOUT_MS = 3e4;
673
- var DEFAULT_POLL_MS = 50;
674
- var DEFAULT_INIT_GRACE_MS = 2e3;
675
- var DIRECTORY_MODE = 448;
676
- var FILE_MODE = 384;
677
- function ownerPath(lockDir) {
678
- return join5(lockDir, "owner.json");
679
- }
680
- function readOwner(lockDir) {
681
- try {
682
- const owner = JSON.parse(readFileSync3(ownerPath(lockDir), "utf8"));
683
- if (!Number.isInteger(owner.pid) || owner.pid <= 0 || typeof owner.nonce !== "string" || typeof owner.createdAt !== "number") {
684
- return null;
685
- }
686
- return owner;
687
- } catch {
688
- return null;
689
- }
690
- }
691
- function sameOwner(a, b) {
692
- return a?.pid === b.pid && a.nonce === b.nonce && a.createdAt === b.createdAt;
693
- }
694
- function tryTakeLock(lockDir, now) {
695
- try {
696
- mkdirSync3(lockDir, { mode: DIRECTORY_MODE });
697
- } catch (error) {
698
- if (error.code === "EEXIST") return null;
699
- throw error;
700
- }
701
- const owner = {
702
- pid: process.pid,
703
- nonce: randomBytes(16).toString("hex"),
704
- createdAt: now
705
- };
706
- try {
707
- writeFileSync2(ownerPath(lockDir), `${JSON.stringify(owner)}
708
- `, {
709
- encoding: "utf8",
710
- mode: FILE_MODE,
711
- flag: "wx"
712
- });
713
- return owner;
714
- } catch (error) {
715
- rmSync2(lockDir, { recursive: true, force: true });
716
- throw error;
717
- }
718
- }
719
- function recoverStaleLock(lockDir, now, alive, initGraceMs) {
720
- const owner = readOwner(lockDir);
721
- if (owner) {
722
- if (alive(owner.pid)) return false;
723
- if (!sameOwner(readOwner(lockDir), owner)) return false;
724
- rmSync2(lockDir, { recursive: true, force: true });
725
- return true;
726
- }
727
- try {
728
- const before = statSync(lockDir).mtimeMs;
729
- if (now - before < initGraceMs) return false;
730
- if (readOwner(lockDir)) return false;
731
- if (statSync(lockDir).mtimeMs !== before) return false;
732
- rmSync2(lockDir, { recursive: true, force: true });
733
- return true;
734
- } catch {
735
- return true;
736
- }
737
- }
738
- function abortError(signal) {
739
- if (signal.reason instanceof Error) return signal.reason;
740
- const error = new Error("The operation was aborted");
741
- error.name = "AbortError";
742
- return error;
743
- }
744
- function throwIfAborted(signal) {
745
- if (signal?.aborted) throw abortError(signal);
746
- }
747
- async function defaultSleep(ms, signal) {
748
- if (!signal) {
749
- await new Promise((resolve7) => setTimeout(resolve7, ms));
750
- return;
1212
+ }
1213
+ function readStoredCredentialMetadata(metadataPath = CREDENTIAL_METADATA_PATH, familyPath = CREDENTIAL_FAMILY_PATH, migrationPath = CREDENTIAL_MIGRATION_PATH) {
1214
+ const family = readCredentialFamily(familyPath);
1215
+ if (family) return family;
1216
+ const parsed = readBoundedCredentialJson(metadataPath);
1217
+ if (parsed === void 0) {
1218
+ return readCredentialMigrationState(migrationPath) === "absent" ? { state: "legacy_broker" } : { state: "invalid" };
751
1219
  }
752
- throwIfAborted(signal);
753
- await new Promise((resolve7, reject) => {
754
- const timer = setTimeout(() => {
755
- signal.removeEventListener("abort", onAbort);
756
- resolve7();
757
- }, ms);
758
- const onAbort = () => {
759
- clearTimeout(timer);
760
- reject(abortError(signal));
1220
+ if (parsed === null) return { state: "invalid" };
1221
+ try {
1222
+ return {
1223
+ state: "workos_connect",
1224
+ metadata: parseWorkosConnectCredentialMetadata(parsed)
761
1225
  };
762
- signal.addEventListener("abort", onAbort, { once: true });
763
- });
764
- }
765
- var syncSleeper = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
766
- async function withFileLock(lockDir, operation, options = {}) {
767
- mkdirSync3(dirname3(lockDir), { recursive: true, mode: DIRECTORY_MODE });
768
- const nowMs = options.nowMs ?? Date.now;
769
- const alive = options.processAlive ?? processIsAlive;
770
- const deadline = nowMs() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
771
- let owner = null;
772
- while (!owner) {
773
- throwIfAborted(options.signal);
774
- const now = nowMs();
775
- owner = tryTakeLock(lockDir, now);
776
- if (owner) break;
777
- recoverStaleLock(lockDir, now, alive, options.initGraceMs ?? DEFAULT_INIT_GRACE_MS);
778
- if (now >= deadline) {
779
- throw new Error(`timed out waiting for file lock ${lockDir}`);
780
- }
781
- if (options.sleep) {
782
- await options.sleep(options.pollMs ?? DEFAULT_POLL_MS);
783
- throwIfAborted(options.signal);
784
- } else {
785
- await defaultSleep(options.pollMs ?? DEFAULT_POLL_MS, options.signal);
786
- }
1226
+ } catch {
1227
+ return { state: "invalid" };
787
1228
  }
1229
+ }
1230
+ function decodeJwtPayload(token) {
1231
+ const parts = token.split(".");
1232
+ if (parts.length !== 3) return void 0;
788
1233
  try {
789
- throwIfAborted(options.signal);
790
- return await operation();
791
- } finally {
792
- if (sameOwner(readOwner(lockDir), owner)) {
793
- rmSync2(lockDir, { recursive: true, force: true });
794
- }
1234
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
1235
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return void 0;
1236
+ return payload;
1237
+ } catch {
1238
+ return void 0;
795
1239
  }
796
1240
  }
1241
+ function jwtExpiresAt(token) {
1242
+ const exp = decodeJwtPayload(token)?.exp;
1243
+ return typeof exp === "number" && Number.isFinite(exp) ? exp * 1e3 : void 0;
1244
+ }
797
1245
 
798
1246
  // src/lib/terminal-safe.ts
799
1247
  function replaceIsolatedSurrogates(value) {
@@ -848,7 +1296,7 @@ var DEFAULT_API_URL = "https://api.getprimitive.ai";
848
1296
  var AUTH_EXPIRED_MESSAGE = "Authentication expired. Run `prim auth login` to re-authenticate.";
849
1297
  function readTrimmed2(path) {
850
1298
  try {
851
- const value = readFileSync4(path, "utf8").trim();
1299
+ const value = readFileSync7(path, "utf8").trim();
852
1300
  return value || void 0;
853
1301
  } catch {
854
1302
  return void 0;
@@ -864,17 +1312,17 @@ function expiresAtFor(token, expiresIn) {
864
1312
  return expiresIn === void 0 ? jwtExpiresAt(token) : Date.now() + expiresIn * 1e3;
865
1313
  }
866
1314
  function ensureConfigDirectory() {
867
- const directory = dirname4(TOKEN_FILE_PATH);
868
- mkdirSync4(directory, { recursive: true, mode: CONFIG_DIR_MODE });
869
- chmodSync2(directory, CONFIG_DIR_MODE);
1315
+ const directory = dirname7(TOKEN_FILE_PATH);
1316
+ mkdirSync5(directory, { recursive: true, mode: CONFIG_DIR_MODE });
1317
+ chmodSync3(directory, CONFIG_DIR_MODE);
870
1318
  }
871
1319
  function atomicWrite(path, content) {
872
1320
  ensureConfigDirectory();
873
1321
  atomicWriteFile(path, content, { mode: CREDENTIAL_FILE_MODE });
874
1322
  }
875
1323
  function removeCredentialFile(path) {
876
- if (!existsSync3(path)) return false;
877
- rmSync3(path, { force: true });
1324
+ if (!existsSync6(path)) return false;
1325
+ rmSync4(path, { force: true });
878
1326
  return true;
879
1327
  }
880
1328
  function getTokenExpiresAt() {
@@ -889,7 +1337,7 @@ function isTokenExpiringSoon(credential) {
889
1337
  return expiresAt !== void 0 && Date.now() >= expiresAt - REFRESH_THRESHOLD_MS;
890
1338
  }
891
1339
  function refreshFingerprint(refreshToken2) {
892
- return createHash2("sha256").update(refreshToken2).digest("hex");
1340
+ return createHash4("sha256").update(refreshToken2).digest("hex");
893
1341
  }
894
1342
  function legacyBrokerCredentialMetadata(accessToken, refreshToken2) {
895
1343
  return {
@@ -1291,24 +1739,24 @@ function getClient() {
1291
1739
  }
1292
1740
 
1293
1741
  // src/commands/claude-plugin.ts
1294
- import { createHash as createHash4 } from "crypto";
1742
+ import { createHash as createHash6 } from "crypto";
1295
1743
  import {
1296
- constants as constants3,
1744
+ constants as constants4,
1297
1745
  closeSync as closeSync3,
1298
- existsSync as existsSync5,
1746
+ existsSync as existsSync8,
1299
1747
  fstatSync as fstatSync2,
1300
- lstatSync as lstatSync3,
1301
- mkdirSync as mkdirSync5,
1748
+ lstatSync as lstatSync4,
1749
+ mkdirSync as mkdirSync6,
1302
1750
  openSync as openSync3,
1303
- readFileSync as readFileSync6,
1751
+ readFileSync as readFileSync9,
1304
1752
  readSync as readSync2,
1305
- readdirSync,
1306
- realpathSync as realpathSync2,
1307
- rmSync as rmSync4,
1308
- rmdirSync
1753
+ readdirSync as readdirSync3,
1754
+ realpathSync as realpathSync3,
1755
+ rmSync as rmSync5,
1756
+ rmdirSync as rmdirSync2
1309
1757
  } from "fs";
1310
- import { homedir as homedir4 } from "os";
1311
- import { dirname as dirname7, isAbsolute as isAbsolute4, join as join9, relative as relative2, resolve as resolve4, sep as sep2 } from "path";
1758
+ import { homedir as homedir6 } from "os";
1759
+ import { dirname as dirname9, isAbsolute as isAbsolute6, join as join12, relative as relative2, resolve as resolve5, sep as sep2 } from "path";
1312
1760
  import { fileURLToPath as fileURLToPath3 } from "url";
1313
1761
 
1314
1762
  // node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/nodes/identity.js
@@ -7578,67 +8026,23 @@ function parse(src, reviver, options) {
7578
8026
  return doc.toJS(Object.assign({ reviver: _reviver }, options));
7579
8027
  }
7580
8028
 
7581
- // src/lib/git.ts
7582
- import { execFileSync } from "child_process";
7583
- import { createHash as createHash3 } from "crypto";
7584
- import { lstatSync, realpathSync } from "fs";
7585
- import { basename, dirname as dirname5, isAbsolute as isAbsolute3, relative, resolve as resolve2, sep } from "path";
7586
- var GIT_TIMEOUT_MS = 1e3;
7587
- var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
7588
- var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
7589
- function gitToplevel(cwd) {
7590
- try {
7591
- return execFileSync("git", ["rev-parse", "--show-toplevel"], {
7592
- cwd,
7593
- encoding: "utf-8",
7594
- stdio: ["ignore", "pipe", "ignore"],
7595
- timeout: GIT_TIMEOUT_MS
7596
- }).trim();
7597
- } catch {
7598
- return null;
7599
- }
7600
- }
7601
- function gitValue(cwd, args) {
7602
- try {
7603
- const value = execFileSync("git", args, {
7604
- cwd,
7605
- encoding: "utf-8",
7606
- stdio: ["ignore", "pipe", "ignore"],
7607
- timeout: GIT_TIMEOUT_MS
7608
- }).trim();
7609
- return value || void 0;
7610
- } catch {
7611
- return void 0;
7612
- }
7613
- }
7614
- function githubRepositoryFullName(cwd) {
7615
- const remote = gitValue(cwd, ["config", "--get", "remote.origin.url"]);
7616
- if (!remote || [...remote].some((char) => char < " " || char === "\x7F")) return null;
7617
- const match = /^(?:git@github\.com:([^?#]+)|(?:https|ssh):\/\/(?:[^/@]+@)?github\.com(?::\d+)?\/([^?#]+))$/i.exec(
7618
- remote
7619
- );
7620
- const path = match?.[1] ?? match?.[2];
7621
- if (!path) return null;
7622
- const parts = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "").split("/");
7623
- if (parts.length !== 2 || !GITHUB_OWNER_PATTERN.test(parts[0] ?? "") || !GITHUB_REPOSITORY_PATTERN.test(parts[1] ?? "")) {
7624
- return null;
7625
- }
7626
- return `${parts[0]}/${parts[1]}`;
7627
- }
7628
-
7629
8029
  // src/commands/skill.ts
8030
+ import { createHash as createHash5 } from "crypto";
7630
8031
  import {
7631
- constants as constants2,
8032
+ constants as constants3,
7632
8033
  closeSync as closeSync2,
7633
- existsSync as existsSync4,
8034
+ existsSync as existsSync7,
7634
8035
  fstatSync,
7635
- lstatSync as lstatSync2,
8036
+ lstatSync as lstatSync3,
7636
8037
  openSync as openSync2,
7637
- readFileSync as readFileSync5,
7638
- readSync
8038
+ readFileSync as readFileSync8,
8039
+ readSync,
8040
+ readdirSync as readdirSync2,
8041
+ rmdirSync,
8042
+ unlinkSync
7639
8043
  } from "fs";
7640
- import { homedir as homedir3 } from "os";
7641
- import { dirname as dirname6, join as join8, resolve as resolve3 } from "path";
8044
+ import { homedir as homedir5 } from "os";
8045
+ import { dirname as dirname8, join as join11, resolve as resolve4 } from "path";
7642
8046
  import { fileURLToPath as fileURLToPath2 } from "url";
7643
8047
 
7644
8048
  // node_modules/.pnpm/diff@5.2.2/node_modules/diff/lib/index.mjs
@@ -7808,7 +8212,7 @@ Diff.prototype = {
7808
8212
  tokenize: function tokenize(value) {
7809
8213
  return value.split("");
7810
8214
  },
7811
- join: function join7(chars) {
8215
+ join: function join10(chars) {
7812
8216
  return chars.join("");
7813
8217
  }
7814
8218
  };
@@ -7993,7 +8397,7 @@ arrayDiff.join = arrayDiff.removeEmpty = function(value) {
7993
8397
  };
7994
8398
 
7995
8399
  // src/commands/skill.ts
7996
- var __dirname = dirname6(fileURLToPath2(import.meta.url));
8400
+ var __dirname = dirname8(fileURLToPath2(import.meta.url));
7997
8401
  var SKILL_BEGIN = "<!-- BEGIN PRIM SKILL v1 -->";
7998
8402
  var SKILL_END = "<!-- END PRIM SKILL v1 -->";
7999
8403
  var CODEX_GUIDANCE_LIMIT_BYTES = 1024 * 1024;
@@ -8002,9 +8406,9 @@ var SKILL_BEGIN_BUFFER = Buffer.from(SKILL_BEGIN);
8002
8406
  var SKILL_END_BUFFER = Buffer.from(SKILL_END);
8003
8407
  function readCodexGuidance(path) {
8004
8408
  try {
8005
- const entry = lstatSync2(path);
8409
+ const entry = lstatSync3(path);
8006
8410
  if (entry.isSymbolicLink() || !entry.isFile()) return null;
8007
- const fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
8411
+ const fd = openSync2(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
8008
8412
  try {
8009
8413
  const opened = fstatSync(fd);
8010
8414
  if (!opened.isFile() || opened.size > CODEX_GUIDANCE_LIMIT_BYTES) return null;
@@ -8025,10 +8429,10 @@ function readCodexGuidance(path) {
8025
8429
  }
8026
8430
  }
8027
8431
  function effectiveCodexGuidance(scope) {
8028
- const override = readCodexGuidance(join8(scope, "AGENTS.override.md"));
8432
+ const override = readCodexGuidance(join11(scope, "AGENTS.override.md"));
8029
8433
  if (override === null) return null;
8030
8434
  if (override !== void 0 && override.toString("utf8").trim().length > 0) return override;
8031
- const base = readCodexGuidance(join8(scope, "AGENTS.md"));
8435
+ const base = readCodexGuidance(join11(scope, "AGENTS.md"));
8032
8436
  return base ?? null;
8033
8437
  }
8034
8438
  function hasUsablePrimBlock(content, visibleBytes) {
@@ -8049,12 +8453,12 @@ function hasUsablePrimBlock(content, visibleBytes) {
8049
8453
  return false;
8050
8454
  }
8051
8455
  }
8052
- function hasUsableCodexGuidance(projectRoot) {
8456
+ function hasUsableCodexGuidance(projectRoot2) {
8053
8457
  try {
8054
- const codexHome = process.env.CODEX_HOME?.trim() ? resolve3(process.env.CODEX_HOME) : join8(homedir3(), ".codex");
8458
+ const codexHome = process.env.CODEX_HOME?.trim() ? resolve4(process.env.CODEX_HOME) : join11(homedir5(), ".codex");
8055
8459
  const scopes = [
8056
8460
  { path: codexHome, visibleBytes: CODEX_GUIDANCE_LIMIT_BYTES },
8057
- { path: projectRoot, visibleBytes: CODEX_DEFAULT_PROJECT_GUIDANCE_BYTES }
8461
+ { path: projectRoot2, visibleBytes: CODEX_DEFAULT_PROJECT_GUIDANCE_BYTES }
8058
8462
  ];
8059
8463
  return scopes.some(({ path, visibleBytes }) => {
8060
8464
  const guidance = effectiveCodexGuidance(path);
@@ -8064,12 +8468,58 @@ function hasUsableCodexGuidance(projectRoot) {
8064
8468
  return false;
8065
8469
  }
8066
8470
  }
8471
+ var CURSOR_SKILL_OWNER = "@primitive.ai/prim";
8472
+ var CURSOR_SKILL_MANIFEST = ".prim-owned.json";
8473
+ function sha256(content) {
8474
+ return createHash5("sha256").update(content).digest("hex");
8475
+ }
8476
+ function cursorSkillDirectory(cwd, scope) {
8477
+ const base = scope === "user" ? cursorConfigDirectory() : gitToplevel(cwd) ?? cwd;
8478
+ return join11(base, scope === "user" ? "skills" : ".cursor/skills", "prim");
8479
+ }
8480
+ function cursorSkillPaths(cwd, scope) {
8481
+ const directory = cursorSkillDirectory(cwd, scope);
8482
+ return {
8483
+ directory,
8484
+ skill: join11(directory, "SKILL.md"),
8485
+ manifest: join11(directory, CURSOR_SKILL_MANIFEST)
8486
+ };
8487
+ }
8488
+ function readCursorSkillManifest(path) {
8489
+ try {
8490
+ const value = JSON.parse(readFileSync8(path, "utf8"));
8491
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
8492
+ const record = value;
8493
+ if (record.version !== 1 || record.owner !== CURSOR_SKILL_OWNER || typeof record.contentSha256 !== "string" || !/^[0-9a-f]{64}$/u.test(record.contentSha256)) {
8494
+ return;
8495
+ }
8496
+ return record;
8497
+ } catch {
8498
+ return;
8499
+ }
8500
+ }
8501
+ function cursorSkillStatus(cwd, scope) {
8502
+ const paths = cursorSkillPaths(cwd, scope);
8503
+ const manifest = readCursorSkillManifest(paths.manifest);
8504
+ const content = existsSync7(paths.skill) ? readFileSync8(paths.skill, "utf8") : void 0;
8505
+ const owned = manifest !== void 0;
8506
+ const installed = owned && content !== void 0 && sha256(content) === manifest.contentSha256;
8507
+ return {
8508
+ installed,
8509
+ owned,
8510
+ edited: owned && content !== void 0 && !installed,
8511
+ target: paths.skill
8512
+ };
8513
+ }
8514
+ function hasUsableCursorSkill(projectRoot2) {
8515
+ return cursorSkillStatus(projectRoot2, "project").installed || cursorSkillStatus(projectRoot2, "user").installed;
8516
+ }
8067
8517
  function loadSkill() {
8068
8518
  let dir = __dirname;
8069
- while (dir !== dirname6(dir)) {
8070
- const p = resolve3(dir, "SKILL.md");
8071
- if (existsSync4(p)) return readFileSync5(p, "utf-8");
8072
- dir = dirname6(dir);
8519
+ while (dir !== dirname8(dir)) {
8520
+ const p = resolve4(dir, "SKILL.md");
8521
+ if (existsSync7(p)) return readFileSync8(p, "utf-8");
8522
+ dir = dirname8(dir);
8073
8523
  }
8074
8524
  throw new Error("SKILL.md not found in package");
8075
8525
  }
@@ -8078,22 +8528,22 @@ function atomicWrite2(target, content) {
8078
8528
  }
8079
8529
 
8080
8530
  // src/commands/claude-plugin.ts
8081
- var __dirname2 = dirname7(fileURLToPath3(import.meta.url));
8531
+ var __dirname2 = dirname9(fileURLToPath3(import.meta.url));
8082
8532
  var PLUGIN_DESCRIPTION = "Primitive decision-graph guidance for the prim CLI \u2014 a model-invoked skill installed by prim skill install.";
8083
- var MAX_MANIFEST_BYTES = 64 * 1024;
8533
+ var MAX_MANIFEST_BYTES2 = 64 * 1024;
8084
8534
  var MAX_SKILL_BYTES = 1024 * 1024;
8085
8535
  var REFRESH_LOCK_TIMEOUT_MS = 150;
8086
8536
  var REFRESH_LOCK_POLL_MS = 10;
8087
8537
  function pluginDirFor(cwd, scope) {
8088
- const base = scope === "user" ? join9(homedir4(), ".claude") : join9(gitToplevel(cwd) ?? cwd, ".claude");
8089
- return join9(base, "skills", "prim");
8538
+ const base = scope === "user" ? join12(homedir6(), ".claude") : join12(gitToplevel(cwd) ?? cwd, ".claude");
8539
+ return join12(base, "skills", "prim");
8090
8540
  }
8091
8541
  function packageVersion2() {
8092
8542
  let dir = __dirname2;
8093
- while (dir !== dirname7(dir)) {
8094
- const p = resolve4(dir, "package.json");
8095
- if (existsSync5(p)) return JSON.parse(readFileSync6(p, "utf-8")).version;
8096
- dir = dirname7(dir);
8543
+ while (dir !== dirname9(dir)) {
8544
+ const p = resolve5(dir, "package.json");
8545
+ if (existsSync8(p)) return JSON.parse(readFileSync9(p, "utf-8")).version;
8546
+ dir = dirname9(dir);
8097
8547
  }
8098
8548
  return "0.0.0";
8099
8549
  }
@@ -8108,27 +8558,27 @@ function renderClaudePlugin() {
8108
8558
  }
8109
8559
  function pluginPaths(dir) {
8110
8560
  return {
8111
- manifestPath: join9(dir, ".claude-plugin", "plugin.json"),
8112
- skillPath: join9(dir, "SKILL.md")
8561
+ manifestPath: join12(dir, ".claude-plugin", "plugin.json"),
8562
+ skillPath: join12(dir, "SKILL.md")
8113
8563
  };
8114
8564
  }
8115
8565
  function pathIsInside(root, path) {
8116
8566
  const rel = relative2(root, path);
8117
- return rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute4(rel);
8567
+ return rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute6(rel);
8118
8568
  }
8119
- function managedPathsAreSafe(paths, projectRoot) {
8569
+ function managedPathsAreSafe(paths, projectRoot2) {
8120
8570
  try {
8121
8571
  const managed = [paths.manifestPath, paths.skillPath];
8122
- if (managed.some((path) => !lstatSync3(path).isFile())) return false;
8123
- if (projectRoot === void 0) return true;
8124
- const realRoot = realpathSync2(projectRoot);
8125
- return managed.every((path) => pathIsInside(realRoot, realpathSync2(path)));
8572
+ if (managed.some((path) => !lstatSync4(path).isFile())) return false;
8573
+ if (projectRoot2 === void 0) return true;
8574
+ const realRoot = realpathSync3(projectRoot2);
8575
+ return managed.every((path) => pathIsInside(realRoot, realpathSync3(path)));
8126
8576
  } catch {
8127
8577
  return false;
8128
8578
  }
8129
8579
  }
8130
8580
  function readBoundedRegularFile(path, maxBytes) {
8131
- const fd = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
8581
+ const fd = openSync3(path, constants4.O_RDONLY | constants4.O_NOFOLLOW);
8132
8582
  try {
8133
8583
  const stat = fstatSync2(fd);
8134
8584
  if (!stat.isFile() || stat.size > maxBytes) throw new Error("unsafe managed file");
@@ -8159,10 +8609,10 @@ function hasUsablePrimFrontmatter(skill) {
8159
8609
  return false;
8160
8610
  }
8161
8611
  }
8162
- function inspectRecognizedInstallation(paths, projectRoot) {
8163
- if (!existsSync5(paths.manifestPath) || !existsSync5(paths.skillPath)) return;
8164
- if (!managedPathsAreSafe(paths, projectRoot)) return;
8165
- const manifest = readBoundedRegularFile(paths.manifestPath, MAX_MANIFEST_BYTES);
8612
+ function inspectRecognizedInstallation(paths, projectRoot2) {
8613
+ if (!existsSync8(paths.manifestPath) || !existsSync8(paths.skillPath)) return;
8614
+ if (!managedPathsAreSafe(paths, projectRoot2)) return;
8615
+ const manifest = readBoundedRegularFile(paths.manifestPath, MAX_MANIFEST_BYTES2);
8166
8616
  const parsed = JSON.parse(manifest);
8167
8617
  if (parsed.name !== "prim") return;
8168
8618
  const skill = readBoundedRegularFile(paths.skillPath, MAX_SKILL_BYTES);
@@ -8174,23 +8624,23 @@ function inspectRecognizedInstallation(paths, projectRoot) {
8174
8624
  };
8175
8625
  }
8176
8626
  function refreshLockDir(physicalPluginDir) {
8177
- const cacheRoot = process.env.XDG_CACHE_HOME || join9(homedir4(), ".cache");
8178
- const identity = createHash4("sha256").update(physicalPluginDir).digest("hex");
8179
- return join9(cacheRoot, "prim", "skill-refresh-locks", `${identity}.lock`);
8627
+ const cacheRoot = process.env.XDG_CACHE_HOME || join12(homedir6(), ".cache");
8628
+ const identity = createHash6("sha256").update(physicalPluginDir).digest("hex");
8629
+ return join12(cacheRoot, "prim", "skill-refresh-locks", `${identity}.lock`);
8180
8630
  }
8181
8631
  async function refreshClaudePlugins(cwd, options = {}) {
8182
8632
  const result = { installed: 0, refreshed: 0 };
8183
8633
  const candidates = [];
8184
8634
  if (options.includeProject !== false) {
8185
- const projectRoot = options.projectRoot ?? gitToplevel(cwd);
8186
- if (projectRoot !== null && isAbsolute4(projectRoot)) {
8187
- candidates.push({ dir: join9(projectRoot, ".claude", "skills", "prim"), projectRoot });
8635
+ const projectRoot2 = options.projectRoot ?? gitToplevel(cwd);
8636
+ if (projectRoot2 !== null && isAbsolute6(projectRoot2)) {
8637
+ candidates.push({ dir: join12(projectRoot2, ".claude", "skills", "prim"), projectRoot: projectRoot2 });
8188
8638
  }
8189
8639
  }
8190
8640
  candidates.push({ dir: pluginDirFor(cwd, "user") });
8191
8641
  const seen = /* @__PURE__ */ new Set();
8192
8642
  let desired;
8193
- for (const { dir, projectRoot } of candidates) {
8643
+ for (const { dir, projectRoot: projectRoot2 } of candidates) {
8194
8644
  let counted = false;
8195
8645
  const markInstalled = () => {
8196
8646
  if (!counted) {
@@ -8203,21 +8653,21 @@ async function refreshClaudePlugins(cwd, options = {}) {
8203
8653
  const paths = { manifestPath, skillPath };
8204
8654
  try {
8205
8655
  let changed = false;
8206
- const current = inspectRecognizedInstallation(paths, projectRoot);
8656
+ const current = inspectRecognizedInstallation(paths, projectRoot2);
8207
8657
  if (!current) return;
8208
8658
  desired ??= renderClaudePlugin();
8209
- if (projectRoot !== void 0 && current.usable && compareSemver(current.manifestVersion, desired.version) === 1) {
8659
+ if (projectRoot2 !== void 0 && current.usable && compareSemver(current.manifestVersion, desired.version) === 1) {
8210
8660
  markInstalled();
8211
8661
  return;
8212
8662
  }
8213
8663
  if (current.manifest !== desired.manifest) {
8214
- if (!managedPathsAreSafe(paths, projectRoot)) throw new Error("unsafe managed path");
8664
+ if (!managedPathsAreSafe(paths, projectRoot2)) throw new Error("unsafe managed path");
8215
8665
  atomicWrite2(manifestPath, desired.manifest);
8216
8666
  if (!changed) result.refreshed += 1;
8217
8667
  changed = true;
8218
8668
  }
8219
8669
  if (current.skill !== desired.skill) {
8220
- if (!managedPathsAreSafe(paths, projectRoot)) throw new Error("unsafe managed path");
8670
+ if (!managedPathsAreSafe(paths, projectRoot2)) throw new Error("unsafe managed path");
8221
8671
  atomicWrite2(skillPath, desired.skill);
8222
8672
  if (!changed) result.refreshed += 1;
8223
8673
  changed = true;
@@ -8225,15 +8675,15 @@ async function refreshClaudePlugins(cwd, options = {}) {
8225
8675
  markInstalled();
8226
8676
  } catch {
8227
8677
  try {
8228
- if (inspectRecognizedInstallation(paths, projectRoot)?.usable) markInstalled();
8678
+ if (inspectRecognizedInstallation(paths, projectRoot2)?.usable) markInstalled();
8229
8679
  } catch {
8230
8680
  }
8231
8681
  }
8232
8682
  };
8233
8683
  try {
8234
8684
  const paths = pluginPaths(dir);
8235
- if (!existsSync5(paths.manifestPath) || !existsSync5(paths.skillPath)) continue;
8236
- const physicalPluginDir = realpathSync2(dir);
8685
+ if (!existsSync8(paths.manifestPath) || !existsSync8(paths.skillPath)) continue;
8686
+ const physicalPluginDir = realpathSync3(dir);
8237
8687
  if (seen.has(physicalPluginDir)) continue;
8238
8688
  seen.add(physicalPluginDir);
8239
8689
  await withFileLock(refreshLockDir(physicalPluginDir), refreshCandidate, {
@@ -8248,16 +8698,16 @@ async function refreshClaudePlugins(cwd, options = {}) {
8248
8698
 
8249
8699
  // src/daemon/client.ts
8250
8700
  import { createConnection } from "net";
8251
- import { join as join10 } from "path";
8701
+ import { join as join13 } from "path";
8252
8702
 
8253
8703
  // src/daemon/principal.ts
8254
- import { createHash as createHash5 } from "crypto";
8704
+ import { createHash as createHash7 } from "crypto";
8255
8705
  var PRINCIPAL_COMPONENT_MAX_LENGTH = 512;
8256
8706
  function boundedClaim(value) {
8257
8707
  return typeof value === "string" && value.length > 0 && value.length <= PRINCIPAL_COMPONENT_MAX_LENGTH ? value : void 0;
8258
8708
  }
8259
- function sha256(value) {
8260
- return createHash5("sha256").update(value).digest("hex");
8709
+ function sha2562(value) {
8710
+ return createHash7("sha256").update(value).digest("hex");
8261
8711
  }
8262
8712
  function resolveDaemonPrincipal(token = resolveAuthCredential()?.token) {
8263
8713
  if (!token) return void 0;
@@ -8267,9 +8717,9 @@ function resolveDaemonPrincipal(token = resolveAuthCredential()?.token) {
8267
8717
  if (!(subject && organizationId)) return void 0;
8268
8718
  const issuer = boundedClaim(payload?.iss) ?? "unknown-issuer";
8269
8719
  return {
8270
- principalId: sha256(`${issuer}\0${subject}`),
8720
+ principalId: sha2562(`${issuer}\0${subject}`),
8271
8721
  organizationId,
8272
- credentialFingerprint: sha256(token)
8722
+ credentialFingerprint: sha2562(token)
8273
8723
  };
8274
8724
  }
8275
8725
  function daemonPrincipalsMatch(caller, daemon) {
@@ -8298,7 +8748,7 @@ function parseDaemonResponseEnvelope(value) {
8298
8748
  }
8299
8749
 
8300
8750
  // src/daemon/client.ts
8301
- var SOCK_PATH = join10(primConfigDirectory(), "sock");
8751
+ var SOCK_PATH = join13(primConfigDirectory(), "sock");
8302
8752
  var DEFAULT_TIMEOUT_MS2 = 250;
8303
8753
  var nextRequestId = 1;
8304
8754
  function daemonRequest(method, params = {}, opts = {}) {
@@ -8311,7 +8761,7 @@ function daemonRequest(method, params = {}, opts = {}) {
8311
8761
  ...caller ? { caller } : {}
8312
8762
  };
8313
8763
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
8314
- return new Promise((resolve7) => {
8764
+ return new Promise((resolve9) => {
8315
8765
  let settled = false;
8316
8766
  let socket;
8317
8767
  let buffer = Buffer.alloc(0);
@@ -8326,7 +8776,7 @@ function daemonRequest(method, params = {}, opts = {}) {
8326
8776
  } catch {
8327
8777
  }
8328
8778
  }
8329
- resolve7(value);
8779
+ resolve9(value);
8330
8780
  };
8331
8781
  const timer = setTimeout(() => settle(null), timeoutMs);
8332
8782
  timer.unref();
@@ -8592,16 +9042,16 @@ async function acknowledgeDecisionFeedback(input, dependencies = {}) {
8592
9042
 
8593
9043
  // src/lib/activation.ts
8594
9044
  import { execFileSync as execFileSync2 } from "child_process";
8595
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
8596
- import { homedir as homedir5 } from "os";
8597
- import { join as join11 } from "path";
9045
+ import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
9046
+ import { homedir as homedir7 } from "os";
9047
+ import { join as join14 } from "path";
8598
9048
  var PRIM_ACTIVE_KEY = "prim.active";
8599
9049
  var PRIM_REPO_SYNC_ID_KEY = "prim.repoSyncId";
8600
9050
  var PRIM_REPO_SYNC_REPOSITORY_KEY = "prim.repoSyncRepository";
8601
9051
  var PRIM_REPO_BINDING_STATE_KEY = "prim.repoBindingState";
8602
9052
  var GIT_TIMEOUT_MS2 = 1e3;
8603
9053
  var REPO_SYNC_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
8604
- var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
9054
+ var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json", ".cursor/hooks.json"];
8605
9055
  var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
8606
9056
  function repoActiveFlag(cwd) {
8607
9057
  try {
@@ -8690,12 +9140,12 @@ function repositoryBindingState(cwd) {
8690
9140
  }
8691
9141
  function hasProjectPrimInstall(cwd) {
8692
9142
  const root = gitToplevel(cwd);
8693
- if (root === null || root === homedir5()) return false;
9143
+ if (root === null || root === homedir7()) return false;
8694
9144
  for (const rel of PROJECT_INSTALL_FILES) {
8695
- const path = join11(root, rel);
8696
- if (existsSync6(path)) {
9145
+ const path = join14(root, rel);
9146
+ if (existsSync9(path)) {
8697
9147
  try {
8698
- const content = readFileSync7(path, "utf-8");
9148
+ const content = readFileSync10(path, "utf-8");
8699
9149
  if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
8700
9150
  } catch {
8701
9151
  }
@@ -9536,7 +9986,7 @@ function validate23(data, { instancePath = "", parentData, parentDataProperty, r
9536
9986
  }
9537
9987
  validate23.evaluated = { "props": true, "dynamicProps": false, "dynamicItems": false };
9538
9988
  var isDecisionCollectScopeResponse = validate24;
9539
- var schema39 = { "type": "object", "properties": { "policy": { "anyOf": [{ "type": "object", "properties": { "repositories": { "type": "array", "items": { "type": "string" } }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } }, "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } }, "updatedAt": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": ["updatedAt"], "additionalProperties": {} }, { "type": "null" }] }, "collectScopeVersion": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "callerIncluded": { "type": "boolean" } }, "required": ["policy", "collectScopeVersion", "callerIncluded"], "additionalProperties": {} };
9989
+ var schema39 = { "type": "object", "properties": { "policy": { "anyOf": [{ "type": "object", "properties": { "repositories": { "type": "array", "items": { "type": "string" } }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } }, "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } }, "updatedAt": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": ["updatedAt"], "additionalProperties": {} }, { "type": "null" }] }, "collectScopeVersion": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "callerIncluded": { "type": "boolean" } }, "required": ["policy", "collectScopeVersion", "callerIncluded"], "additionalProperties": {} };
9540
9990
  function validate24(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
9541
9991
  ;
9542
9992
  let vErrors = null;
@@ -10087,7 +10537,7 @@ function validate24(data, { instancePath = "", parentData, parentDataProperty, r
10087
10537
  }
10088
10538
  errors++;
10089
10539
  }
10090
- if (!(data18 === "claude_code" || data18 === "codex" || data18 === "hermes")) {
10540
+ if (!(data18 === "claude_code" || data18 === "codex" || data18 === "cursor" || data18 === "hermes")) {
10091
10541
  const err34 = { instancePath: instancePath + "/policy/users/" + i4 + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/DecisionCollectScopeResponse/properties/policy/anyOf/0/properties/users/items/oneOf/2/properties/agent/enum", keyword: "enum", params: { allowedValues: schema39.properties.policy.anyOf[0].properties.users.items.oneOf[2].properties.agent.enum }, message: "must be equal to one of the allowed values" };
10092
10542
  if (vErrors === null) {
10093
10543
  vErrors = [err34];
@@ -10856,7 +11306,7 @@ function validate26(data, { instancePath = "", parentData, parentDataProperty, r
10856
11306
  return errors === 0;
10857
11307
  }
10858
11308
  validate26.evaluated = { "dynamicProps": true, "dynamicItems": false };
10859
- var schema45 = { "type": "object", "properties": { "intent": { "type": "string", "minLength": 1, "maxLength": 500 }, "attribution": { "type": "string", "enum": ["user", "agent", "unknown"] }, "kind": { "type": "string", "enum": ["change", "exploration", "task_execution", "unclear"] }, "rationale": { "type": "string" }, "area": { "type": "string" }, "decided": { "type": "array", "items": { "type": "string" } }, "alternatives": { "type": "array", "items": { "type": "string" } }, "confidence": { "type": "string", "enum": ["high", "medium", "low"] }, "reversibility": { "type": "string", "enum": ["high", "low"] }, "files": { "type": "array", "items": { "type": "string" } }, "protocolVersion": { "type": "number", "const": 3 }, "repoSyncId": { "type": "string" }, "scope": { "default": {}, "type": "object", "properties": { "location": { "default": {}, "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": {} }, "time": { "default": {}, "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "additionalProperties": {} }, "stageOverride": { "type": "string", "enum": ["candidate", "draft", "adopted"] } }, "required": ["intent"], "additionalProperties": {}, "x-primitive-runtime-refinements": ["degrade_invalid_rollout_fields", "degrade_invalid_location_scope", "degrade_invalid_time_scope"] };
11309
+ var schema45 = { "type": "object", "properties": { "intent": { "type": "string", "minLength": 1, "maxLength": 500 }, "attribution": { "type": "string", "enum": ["user", "agent", "unknown"] }, "kind": { "type": "string", "enum": ["change", "exploration", "task_execution", "unclear"] }, "rationale": { "type": "string" }, "area": { "type": "string" }, "decided": { "type": "array", "items": { "type": "string" } }, "alternatives": { "type": "array", "items": { "type": "string" } }, "confidence": { "type": "string", "enum": ["high", "medium", "low"] }, "reversibility": { "type": "string", "enum": ["high", "low"] }, "files": { "type": "array", "items": { "type": "string" } }, "protocolVersion": { "type": "number", "const": 3 }, "repoSyncId": { "type": "string" }, "scope": { "default": {}, "type": "object", "properties": { "location": { "default": {}, "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": {} }, "time": { "default": {}, "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "additionalProperties": {} }, "stageOverride": { "type": "string", "enum": ["candidate", "draft", "adopted"] } }, "required": ["intent"], "additionalProperties": {}, "x-primitive-runtime-refinements": ["degrade_invalid_rollout_fields", "degrade_invalid_location_scope", "degrade_invalid_time_scope"] };
10860
11310
  function validate27(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
10861
11311
  ;
10862
11312
  let vErrors = null;
@@ -11560,7 +12010,7 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r
11560
12010
  }
11561
12011
  errors++;
11562
12012
  }
11563
- if (!(data34 === "claude_code" || data34 === "codex" || data34 === "hermes")) {
12013
+ if (!(data34 === "claude_code" || data34 === "codex" || data34 === "cursor" || data34 === "hermes")) {
11564
12014
  const err19 = { instancePath: instancePath + "/scope/users/" + i6 + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/DecisionCreateRequest/properties/scope/properties/users/items/oneOf/2/properties/agent/enum", keyword: "enum", params: { allowedValues: schema45.properties.scope.properties.users.items.oneOf[2].properties.agent.enum }, message: "must be equal to one of the allowed values" };
11565
12015
  if (vErrors === null) {
11566
12016
  vErrors = [err19];
@@ -11788,7 +12238,7 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r
11788
12238
  return errors === 0;
11789
12239
  }
11790
12240
  validate27.evaluated = { "props": true, "dynamicProps": false, "dynamicItems": false };
11791
- var schema47 = { "type": "object", "properties": { "decisionId": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "createdAt": { "type": "number" }, "scope": { "type": "object", "properties": { "location": { "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "required": ["repository", "directories", "globs", "branches"], "additionalProperties": {} }, "time": { "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "required": ["location", "time", "users"], "additionalProperties": {} }, "scopeWarnings": { "type": "array", "items": { "type": "string" } } }, "required": ["decisionId", "shortId", "createdAt"], "additionalProperties": {} };
12241
+ var schema47 = { "type": "object", "properties": { "decisionId": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "createdAt": { "type": "number" }, "scope": { "type": "object", "properties": { "location": { "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "required": ["repository", "directories", "globs", "branches"], "additionalProperties": {} }, "time": { "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "required": ["location", "time", "users"], "additionalProperties": {} }, "scopeWarnings": { "type": "array", "items": { "type": "string" } } }, "required": ["decisionId", "shortId", "createdAt"], "additionalProperties": {} };
11792
12242
  function validate28(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
11793
12243
  ;
11794
12244
  let vErrors = null;
@@ -12324,7 +12774,7 @@ function validate28(data, { instancePath = "", parentData, parentDataProperty, r
12324
12774
  }
12325
12775
  errors++;
12326
12776
  }
12327
- if (!(data22 === "claude_code" || data22 === "codex" || data22 === "hermes")) {
12777
+ if (!(data22 === "claude_code" || data22 === "codex" || data22 === "cursor" || data22 === "hermes")) {
12328
12778
  const err19 = { instancePath: instancePath + "/scope/users/" + i3 + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/DecisionCreateResponse/properties/scope/properties/users/items/oneOf/2/properties/agent/enum", keyword: "enum", params: { allowedValues: schema47.properties.scope.properties.users.items.oneOf[2].properties.agent.enum }, message: "must be equal to one of the allowed values" };
12329
12779
  if (vErrors === null) {
12330
12780
  vErrors = [err19];
@@ -12556,7 +13006,7 @@ function validate28(data, { instancePath = "", parentData, parentDataProperty, r
12556
13006
  return errors === 0;
12557
13007
  }
12558
13008
  validate28.evaluated = { "props": true, "dynamicProps": false, "dynamicItems": false };
12559
- var schema49 = { "type": "object", "properties": { "decision": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "intent": { "type": "string" }, "intentKind": { "type": "string", "enum": ["change", "exploration", "task_execution", "unclear"] }, "rationale": { "type": "string" }, "decided": { "type": "array", "items": { "type": "string" } }, "alternatives": { "type": "array", "items": { "type": "string" } }, "area": { "type": "string" }, "producerKind": { "type": "string" }, "stage": { "type": "string", "enum": ["draft", "provisional", "adopted", "superseded", "abandoned"] }, "status": { "type": "string", "enum": ["active", "superseded", "under_review"] }, "supersededBy": { "anyOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] }, "confidence": { "type": "string", "enum": ["high", "medium", "low"] }, "reversibility": { "type": "string", "enum": ["high", "low"] }, "confirmed": { "type": "boolean" }, "respondedAt": { "type": "number" }, "fanOut": { "type": "number" }, "classifiedAt": { "type": "number" }, "authorName": { "type": "string" } }, "required": ["id", "intent", "intentKind", "alternatives", "stage", "status", "supersededBy", "confidence", "classifiedAt", "authorName"], "additionalProperties": {} }, "files": { "type": "array", "items": { "type": "string" } }, "flags": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": ["file_edit", "supersession", "invalidation", "confirmation_request"] }, "file": { "type": "string" }, "flaggedAt": { "type": "number" }, "acknowledgedAt": { "type": "number" }, "gateVerdict": { "type": "string" }, "reason": { "type": "string" } }, "required": ["type", "flaggedAt"], "additionalProperties": {} } }, "dependsOn": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "intent": { "type": "string" }, "area": { "type": "string" }, "authorName": { "type": "string" }, "classifiedAt": { "type": "number" }, "status": { "type": "string", "enum": ["active", "superseded", "under_review"] } }, "required": ["id", "intent", "authorName", "classifiedAt", "status"], "additionalProperties": {} } }, "dependents": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "intent": { "type": "string" }, "area": { "type": "string" }, "authorName": { "type": "string" }, "classifiedAt": { "type": "number" }, "status": { "type": "string", "enum": ["active", "superseded", "under_review"] } }, "required": ["id", "intent", "authorName", "classifiedAt", "status"], "additionalProperties": {} } }, "scope": { "type": "object", "properties": { "location": { "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "required": ["repository", "directories", "globs", "branches"], "additionalProperties": {} }, "time": { "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 }, "displayName": { "type": "string" } }, "required": ["kind", "userId", "displayName"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "required": ["location", "time", "users"], "additionalProperties": {} }, "truncated": { "type": "boolean" } }, "required": ["decision", "files", "flags", "dependsOn", "dependents", "scope", "truncated"], "additionalProperties": {} };
13009
+ var schema49 = { "type": "object", "properties": { "decision": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "intent": { "type": "string" }, "intentKind": { "type": "string", "enum": ["change", "exploration", "task_execution", "unclear"] }, "rationale": { "type": "string" }, "decided": { "type": "array", "items": { "type": "string" } }, "alternatives": { "type": "array", "items": { "type": "string" } }, "area": { "type": "string" }, "producerKind": { "type": "string" }, "stage": { "type": "string", "enum": ["draft", "provisional", "adopted", "superseded", "abandoned"] }, "status": { "type": "string", "enum": ["active", "superseded", "under_review"] }, "supersededBy": { "anyOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] }, "confidence": { "type": "string", "enum": ["high", "medium", "low"] }, "reversibility": { "type": "string", "enum": ["high", "low"] }, "confirmed": { "type": "boolean" }, "respondedAt": { "type": "number" }, "fanOut": { "type": "number" }, "classifiedAt": { "type": "number" }, "authorName": { "type": "string" } }, "required": ["id", "intent", "intentKind", "alternatives", "stage", "status", "supersededBy", "confidence", "classifiedAt", "authorName"], "additionalProperties": {} }, "files": { "type": "array", "items": { "type": "string" } }, "flags": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": ["file_edit", "supersession", "invalidation", "confirmation_request"] }, "file": { "type": "string" }, "flaggedAt": { "type": "number" }, "acknowledgedAt": { "type": "number" }, "gateVerdict": { "type": "string" }, "reason": { "type": "string" } }, "required": ["type", "flaggedAt"], "additionalProperties": {} } }, "dependsOn": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "intent": { "type": "string" }, "area": { "type": "string" }, "authorName": { "type": "string" }, "classifiedAt": { "type": "number" }, "status": { "type": "string", "enum": ["active", "superseded", "under_review"] } }, "required": ["id", "intent", "authorName", "classifiedAt", "status"], "additionalProperties": {} } }, "dependents": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "intent": { "type": "string" }, "area": { "type": "string" }, "authorName": { "type": "string" }, "classifiedAt": { "type": "number" }, "status": { "type": "string", "enum": ["active", "superseded", "under_review"] } }, "required": ["id", "intent", "authorName", "classifiedAt", "status"], "additionalProperties": {} } }, "scope": { "type": "object", "properties": { "location": { "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "required": ["repository", "directories", "globs", "branches"], "additionalProperties": {} }, "time": { "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 }, "displayName": { "type": "string" } }, "required": ["kind", "userId", "displayName"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "required": ["location", "time", "users"], "additionalProperties": {} }, "truncated": { "type": "boolean" } }, "required": ["decision", "files", "flags", "dependsOn", "dependents", "scope", "truncated"], "additionalProperties": {} };
12560
13010
  function validate29(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
12561
13011
  ;
12562
13012
  let vErrors = null;
@@ -13850,7 +14300,7 @@ function validate29(data, { instancePath = "", parentData, parentDataProperty, r
13850
14300
  }
13851
14301
  errors++;
13852
14302
  }
13853
- if (!(data70 === "claude_code" || data70 === "codex" || data70 === "hermes")) {
14303
+ if (!(data70 === "claude_code" || data70 === "codex" || data70 === "cursor" || data70 === "hermes")) {
13854
14304
  const err24 = { instancePath: instancePath + "/scope/users/" + i9 + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/DecisionDetailResponse/properties/scope/properties/users/items/oneOf/2/properties/agent/enum", keyword: "enum", params: { allowedValues: schema49.properties.scope.properties.users.items.oneOf[2].properties.agent.enum }, message: "must be equal to one of the allowed values" };
13855
14305
  if (vErrors === null) {
13856
14306
  vErrors = [err24];
@@ -14411,7 +14861,7 @@ function validate33(data, { instancePath = "", parentData, parentDataProperty, r
14411
14861
  return errors === 0;
14412
14862
  }
14413
14863
  validate33.evaluated = { "props": true, "dynamicProps": false, "dynamicItems": false };
14414
- var schema59 = { "type": "object", "properties": { "id": { "type": "string" }, "location": { "anyOf": [{ "default": {}, "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": {} }, { "type": "null" }] }, "time": { "anyOf": [{ "default": {}, "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, { "type": "null" }] }, "users": { "anyOf": [{ "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } }, { "type": "null" }] } }, "required": ["id"], "additionalProperties": {} };
14864
+ var schema59 = { "type": "object", "properties": { "id": { "type": "string" }, "location": { "anyOf": [{ "default": {}, "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": {} }, { "type": "null" }] }, "time": { "anyOf": [{ "default": {}, "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, { "type": "null" }] }, "users": { "anyOf": [{ "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } }, { "type": "null" }] } }, "required": ["id"], "additionalProperties": {} };
14415
14865
  function validate34(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
14416
14866
  ;
14417
14867
  let vErrors = null;
@@ -15052,7 +15502,7 @@ function validate34(data, { instancePath = "", parentData, parentDataProperty, r
15052
15502
  }
15053
15503
  errors++;
15054
15504
  }
15055
- if (!(data19 === "claude_code" || data19 === "codex" || data19 === "hermes")) {
15505
+ if (!(data19 === "claude_code" || data19 === "codex" || data19 === "cursor" || data19 === "hermes")) {
15056
15506
  const err38 = { instancePath: instancePath + "/users/" + i3 + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/DecisionRescopeRequest/properties/users/anyOf/0/items/oneOf/2/properties/agent/enum", keyword: "enum", params: { allowedValues: schema59.properties.users.anyOf[0].items.oneOf[2].properties.agent.enum }, message: "must be equal to one of the allowed values" };
15057
15507
  if (vErrors === null) {
15058
15508
  vErrors = [err38];
@@ -15280,7 +15730,7 @@ function validate34(data, { instancePath = "", parentData, parentDataProperty, r
15280
15730
  return errors === 0;
15281
15731
  }
15282
15732
  validate34.evaluated = { "props": true, "dynamicProps": false, "dynamicItems": false };
15283
- var schema61 = { "anyOf": [{ "type": "object", "properties": { "outcome": { "type": "string", "const": "no_op" }, "stage": { "type": "string", "enum": ["draft", "provisional", "adopted", "superseded", "abandoned"] }, "scope": { "type": "object", "properties": { "location": { "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "required": ["repository", "directories", "globs", "branches"], "additionalProperties": {} }, "time": { "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "required": ["location", "time", "users"], "additionalProperties": {} }, "scopeWarnings": { "type": "array", "items": { "type": "string" } } }, "required": ["outcome", "stage", "scope"], "additionalProperties": {} }, { "type": "object", "properties": { "outcome": { "type": "string", "const": "ok" }, "decisionId": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "stage": { "type": "string", "enum": ["draft", "provisional", "adopted", "superseded", "abandoned"] }, "scope": { "type": "object", "properties": { "location": { "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "required": ["repository", "directories", "globs", "branches"], "additionalProperties": {} }, "time": { "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "required": ["location", "time", "users"], "additionalProperties": {} }, "scopeWarnings": { "type": "array", "items": { "type": "string" } } }, "required": ["outcome", "decisionId", "stage", "scope"], "additionalProperties": {} }] };
15733
+ var schema61 = { "anyOf": [{ "type": "object", "properties": { "outcome": { "type": "string", "const": "no_op" }, "stage": { "type": "string", "enum": ["draft", "provisional", "adopted", "superseded", "abandoned"] }, "scope": { "type": "object", "properties": { "location": { "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "required": ["repository", "directories", "globs", "branches"], "additionalProperties": {} }, "time": { "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "required": ["location", "time", "users"], "additionalProperties": {} }, "scopeWarnings": { "type": "array", "items": { "type": "string" } } }, "required": ["outcome", "stage", "scope"], "additionalProperties": {} }, { "type": "object", "properties": { "outcome": { "type": "string", "const": "ok" }, "decisionId": { "type": "string", "minLength": 1 }, "shortId": { "type": "string" }, "stage": { "type": "string", "enum": ["draft", "provisional", "adopted", "superseded", "abandoned"] }, "scope": { "type": "object", "properties": { "location": { "type": "object", "properties": { "repository": { "type": "boolean" }, "directories": { "type": "array", "items": { "type": "string" } }, "globs": { "type": "array", "items": { "type": "string" } }, "branches": { "type": "array", "items": { "type": "string" } } }, "required": ["repository", "directories", "globs", "branches"], "additionalProperties": {} }, "time": { "type": "object", "properties": { "effectiveFrom": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "effectiveUntil": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "additionalProperties": {} }, "users": { "type": "array", "items": { "oneOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "user" }, "userId": { "type": "string", "minLength": 1 } }, "required": ["kind", "userId"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "role" }, "role": { "type": "string", "enum": ["owner", "admin", "member"] } }, "required": ["kind", "role"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "agent" }, "agent": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] } }, "required": ["kind", "agent"], "additionalProperties": false }, { "type": "object", "properties": { "kind": { "type": "string", "const": "credential" }, "credential": { "type": "string", "enum": ["workos_jwt", "workos_api_key", "service_token"] } }, "required": ["kind", "credential"], "additionalProperties": false }] } } }, "required": ["location", "time", "users"], "additionalProperties": {} }, "scopeWarnings": { "type": "array", "items": { "type": "string" } } }, "required": ["outcome", "decisionId", "stage", "scope"], "additionalProperties": {} }] };
15284
15734
  function validate35(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
15285
15735
  ;
15286
15736
  let vErrors = null;
@@ -15918,7 +16368,7 @@ function validate35(data, { instancePath = "", parentData, parentDataProperty, r
15918
16368
  }
15919
16369
  errors++;
15920
16370
  }
15921
- if (!(data21 === "claude_code" || data21 === "codex" || data21 === "hermes")) {
16371
+ if (!(data21 === "claude_code" || data21 === "codex" || data21 === "cursor" || data21 === "hermes")) {
15922
16372
  const err41 = { instancePath: instancePath + "/scope/users/" + i3 + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/DecisionRescopeResponse/anyOf/0/properties/scope/properties/users/items/oneOf/2/properties/agent/enum", keyword: "enum", params: { allowedValues: schema61.anyOf[0].properties.scope.properties.users.items.oneOf[2].properties.agent.enum }, message: "must be equal to one of the allowed values" };
15923
16373
  if (vErrors === null) {
15924
16374
  vErrors = [err41];
@@ -16842,7 +17292,7 @@ function validate35(data, { instancePath = "", parentData, parentDataProperty, r
16842
17292
  }
16843
17293
  errors++;
16844
17294
  }
16845
- if (!(data49 === "claude_code" || data49 === "codex" || data49 === "hermes")) {
17295
+ if (!(data49 === "claude_code" || data49 === "codex" || data49 === "cursor" || data49 === "hermes")) {
16846
17296
  const err100 = { instancePath: instancePath + "/scope/users/" + i8 + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/DecisionRescopeResponse/anyOf/1/properties/scope/properties/users/items/oneOf/2/properties/agent/enum", keyword: "enum", params: { allowedValues: schema61.anyOf[1].properties.scope.properties.users.items.oneOf[2].properties.agent.enum }, message: "must be equal to one of the allowed values" };
16847
17297
  if (vErrors === null) {
16848
17298
  vErrors = [err100];
@@ -22488,7 +22938,7 @@ function validate50(data, { instancePath = "", parentData, parentDataProperty, r
22488
22938
  return errors === 0;
22489
22939
  }
22490
22940
  validate50.evaluated = { "dynamicProps": true, "dynamicItems": false };
22491
- var schema93 = { "type": "object", "properties": { "batch": { "type": "array", "items": { "anyOf": [{ "type": "object", "properties": { "moveId": { "type": "string" }, "capturedAt": { "type": "number" }, "sessionId": { "type": "string" }, "invocationId": { "type": "string" }, "eventType": { "type": "string" }, "payload": {}, "env": {}, "capturedOrganizationId": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9_-]+$" }, "captureAuthorityKind": { "type": "string", "enum": ["workos", "service_token"] }, "decisionLifecycleProtocolVersion": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "producer": { "type": "string", "enum": ["claude_code", "codex", "hermes"] }, "toolOutcome": { "type": "string", "enum": ["succeeded", "returned", "failed", "interrupted", "prevented", "unknown"] }, "envelopeVersion": { "type": "number", "const": 4 } }, "required": ["moveId", "capturedAt", "sessionId", "eventType", "capturedOrganizationId", "captureAuthorityKind", "decisionLifecycleProtocolVersion", "envelopeVersion"], "additionalProperties": {} }, { "type": "object", "properties": { "moveId": { "type": "string" }, "capturedAt": { "type": "number" }, "sessionId": { "type": "string" }, "invocationId": { "type": "string" }, "eventType": { "type": "string" }, "payload": {}, "env": {}, "capturedOrganizationId": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9_-]+$" }, "captureAuthorityKind": { "type": "string", "enum": ["workos", "service_token"] }, "decisionLifecycleProtocolVersion": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "producer": { "type": "string", "enum": ["claude_code", "codex", "hermes"] }, "toolOutcome": { "type": "string", "enum": ["succeeded", "returned", "failed", "interrupted", "prevented", "unknown"] }, "envelopeVersion": { "type": "number", "exclusiveMaximum": 4 } }, "required": ["moveId", "capturedAt", "sessionId", "eventType"], "additionalProperties": {} }] } } }, "required": ["batch"], "additionalProperties": {} };
22941
+ var schema93 = { "type": "object", "properties": { "batch": { "type": "array", "items": { "anyOf": [{ "type": "object", "properties": { "moveId": { "type": "string" }, "capturedAt": { "type": "number" }, "sessionId": { "type": "string" }, "invocationId": { "type": "string" }, "eventType": { "type": "string" }, "payload": {}, "env": {}, "capturedOrganizationId": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9_-]+$" }, "captureAuthorityKind": { "type": "string", "enum": ["workos", "service_token"] }, "decisionLifecycleProtocolVersion": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "producer": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] }, "toolOutcome": { "type": "string", "enum": ["succeeded", "returned", "failed", "interrupted", "prevented", "unknown"] }, "envelopeVersion": { "type": "number", "const": 4 } }, "required": ["moveId", "capturedAt", "sessionId", "eventType", "capturedOrganizationId", "captureAuthorityKind", "decisionLifecycleProtocolVersion", "envelopeVersion"], "additionalProperties": {} }, { "type": "object", "properties": { "moveId": { "type": "string" }, "capturedAt": { "type": "number" }, "sessionId": { "type": "string" }, "invocationId": { "type": "string" }, "eventType": { "type": "string" }, "payload": {}, "env": {}, "capturedOrganizationId": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9_-]+$" }, "captureAuthorityKind": { "type": "string", "enum": ["workos", "service_token"] }, "decisionLifecycleProtocolVersion": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "producer": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] }, "toolOutcome": { "type": "string", "enum": ["succeeded", "returned", "failed", "interrupted", "prevented", "unknown"] }, "envelopeVersion": { "type": "number", "exclusiveMaximum": 4 } }, "required": ["moveId", "capturedAt", "sessionId", "eventType"], "additionalProperties": {} }] } } }, "required": ["batch"], "additionalProperties": {} };
22492
22942
  var pattern8 = new RegExp("^[A-Za-z0-9_-]+$", "u");
22493
22943
  function validate51(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
22494
22944
  ;
@@ -22742,7 +23192,7 @@ function validate51(data, { instancePath = "", parentData, parentDataProperty, r
22742
23192
  }
22743
23193
  errors++;
22744
23194
  }
22745
- if (!(data10 === "claude_code" || data10 === "codex" || data10 === "hermes")) {
23195
+ if (!(data10 === "claude_code" || data10 === "codex" || data10 === "cursor" || data10 === "hermes")) {
22746
23196
  const err16 = { instancePath: instancePath + "/batch/" + i0 + "/producer", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/MoveIngestRequest/properties/batch/items/anyOf/0/properties/producer/enum", keyword: "enum", params: { allowedValues: schema93.properties.batch.items.anyOf[0].properties.producer.enum }, message: "must be equal to one of the allowed values" };
22747
23197
  if (vErrors === null) {
22748
23198
  vErrors = [err16];
@@ -23054,7 +23504,7 @@ function validate51(data, { instancePath = "", parentData, parentDataProperty, r
23054
23504
  }
23055
23505
  errors++;
23056
23506
  }
23057
- if (!(data21 === "claude_code" || data21 === "codex" || data21 === "hermes")) {
23507
+ if (!(data21 === "claude_code" || data21 === "codex" || data21 === "cursor" || data21 === "hermes")) {
23058
23508
  const err38 = { instancePath: instancePath + "/batch/" + i0 + "/producer", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/MoveIngestRequest/properties/batch/items/anyOf/1/properties/producer/enum", keyword: "enum", params: { allowedValues: schema93.properties.batch.items.anyOf[1].properties.producer.enum }, message: "must be equal to one of the allowed values" };
23059
23509
  if (vErrors === null) {
23060
23510
  vErrors = [err38];
@@ -23191,7 +23641,7 @@ function validate51(data, { instancePath = "", parentData, parentDataProperty, r
23191
23641
  return errors === 0;
23192
23642
  }
23193
23643
  validate51.evaluated = { "props": true, "dynamicProps": false, "dynamicItems": false };
23194
- var schema95 = { "type": "object", "properties": { "protocolVersion": { "type": "number", "const": 3 }, "agent": { "type": "string", "enum": ["claude_code", "codex", "hermes"] }, "sessionId": { "type": "string", "pattern": "^[A-Za-z0-9._:-]{1,128}$" }, "invocationId": { "type": "string", "pattern": "^[A-Za-z0-9._:-]{1,128}$" }, "repoSyncId": { "type": "string", "pattern": "^[A-Za-z0-9]{1,64}$" }, "paths": { "maxItems": 32, "type": "array", "items": { "type": "string" } }, "coverage": { "type": "string", "enum": ["complete", "unverified"] }, "branch": { "type": "string", "maxLength": 255, "pattern": "^(?!@?$)(?!\\/)(?!.*\\/$)(?!.*\\/\\/)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)[^/]*\\.lock(?:\\/|$))(?!.*\\.\\.)(?!.*@\\{)(?!.*[~^:?*\\\\]).+$" }, "clientMode": { "type": "string", "enum": ["block", "warn"] }, "clientVersion": { "type": "string", "maxLength": 32, "pattern": "^[A-Za-z0-9][A-Za-z0-9._+-]*$" }, "proposal": { "type": "string" } }, "required": ["protocolVersion", "agent", "sessionId", "invocationId", "repoSyncId", "paths", "coverage", "proposal"], "additionalProperties": false, "x-primitive-runtime-refinements": ["canonical_repository_paths", "unique_repository_paths", "complete_coverage_requires_path", "proposal_utf8_max_6144", "degrade_invalid_rollout_fields", "degrade_invalid_branch"] };
23644
+ var schema95 = { "type": "object", "properties": { "protocolVersion": { "type": "number", "const": 3 }, "agent": { "type": "string", "enum": ["claude_code", "codex", "cursor", "hermes"] }, "sessionId": { "type": "string", "pattern": "^[A-Za-z0-9._:-]{1,128}$" }, "invocationId": { "type": "string", "pattern": "^[A-Za-z0-9._:-]{1,128}$" }, "repoSyncId": { "type": "string", "pattern": "^[A-Za-z0-9]{1,64}$" }, "paths": { "maxItems": 32, "type": "array", "items": { "type": "string" } }, "coverage": { "type": "string", "enum": ["complete", "unverified"] }, "branch": { "type": "string", "maxLength": 255, "pattern": "^(?!@?$)(?!\\/)(?!.*\\/$)(?!.*\\/\\/)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)[^/]*\\.lock(?:\\/|$))(?!.*\\.\\.)(?!.*@\\{)(?!.*[~^:?*\\\\]).+$" }, "clientMode": { "type": "string", "enum": ["block", "warn"] }, "clientVersion": { "type": "string", "maxLength": 32, "pattern": "^[A-Za-z0-9][A-Za-z0-9._+-]*$" }, "proposal": { "type": "string" } }, "required": ["protocolVersion", "agent", "sessionId", "invocationId", "repoSyncId", "paths", "coverage", "proposal"], "additionalProperties": false, "x-primitive-runtime-refinements": ["canonical_repository_paths", "unique_repository_paths", "complete_coverage_requires_path", "proposal_utf8_max_6144", "degrade_invalid_rollout_fields", "degrade_invalid_branch"] };
23195
23645
  var pattern10 = new RegExp("^[A-Za-z0-9._:-]{1,128}$", "u");
23196
23646
  var pattern12 = new RegExp("^[A-Za-z0-9]{1,64}$", "u");
23197
23647
  var pattern13 = new RegExp("^(?!@?$)(?!\\/)(?!.*\\/$)(?!.*\\/\\/)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)[^/]*\\.lock(?:\\/|$))(?!.*\\.\\.)(?!.*@\\{)(?!.*[~^:?*\\\\]).+$", "u");
@@ -23247,7 +23697,7 @@ function validate52(data, { instancePath = "", parentData, parentDataProperty, r
23247
23697
  validate52.errors = [{ instancePath: instancePath + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/PreflightRequestV3/properties/agent/type", keyword: "type", params: { type: "string" }, message: "must be string" }];
23248
23698
  return false;
23249
23699
  }
23250
- if (!(data1 === "claude_code" || data1 === "codex" || data1 === "hermes")) {
23700
+ if (!(data1 === "claude_code" || data1 === "codex" || data1 === "cursor" || data1 === "hermes")) {
23251
23701
  validate52.errors = [{ instancePath: instancePath + "/agent", schemaPath: "https://api.getprimitive.ai/contracts/cli-http-v1.schema.json#/$defs/PreflightRequestV3/properties/agent/enum", keyword: "enum", params: { allowedValues: schema95.properties.agent.enum }, message: "must be equal to one of the allowed values" }];
23252
23702
  return false;
23253
23703
  }
@@ -26539,7 +26989,7 @@ function optionalUserScopeMembers(record) {
26539
26989
  members.push({ kind: "role", role: candidate.role });
26540
26990
  continue;
26541
26991
  }
26542
- if (candidate.kind === "agent" && (candidate.agent === "claude_code" || candidate.agent === "codex" || candidate.agent === "hermes")) {
26992
+ if (candidate.kind === "agent" && (candidate.agent === "claude_code" || candidate.agent === "codex" || candidate.agent === "cursor" || candidate.agent === "hermes")) {
26543
26993
  members.push({ kind: "agent", agent: candidate.agent });
26544
26994
  continue;
26545
26995
  }
@@ -26629,22 +27079,22 @@ async function fetchAndCacheCollectScope(cwd, dependencies = defaultDependencies
26629
27079
 
26630
27080
  // src/lib/post-commit-hook.ts
26631
27081
  import { execFileSync as execFileSync3 } from "child_process";
26632
- import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
27082
+ import { createHash as createHash8, randomUUID as randomUUID2 } from "crypto";
26633
27083
  import {
26634
- constants as constants4,
27084
+ constants as constants5,
26635
27085
  closeSync as closeSync4,
26636
- existsSync as existsSync7,
27086
+ existsSync as existsSync10,
26637
27087
  fchmodSync as fchmodSync2,
26638
27088
  fsyncSync as fsyncSync2,
26639
- lstatSync as lstatSync4,
26640
- mkdirSync as mkdirSync6,
27089
+ lstatSync as lstatSync5,
27090
+ mkdirSync as mkdirSync7,
26641
27091
  openSync as openSync4,
26642
- readFileSync as readFileSync8,
26643
- renameSync as renameSync2,
26644
- unlinkSync,
26645
- writeFileSync as writeFileSync3
27092
+ readFileSync as readFileSync11,
27093
+ renameSync as renameSync3,
27094
+ unlinkSync as unlinkSync2,
27095
+ writeFileSync as writeFileSync4
26646
27096
  } from "fs";
26647
- import { basename as basename2, dirname as dirname8, isAbsolute as isAbsolute5, resolve as resolve5 } from "path";
27097
+ import { basename as basename2, dirname as dirname10, isAbsolute as isAbsolute7, resolve as resolve6 } from "path";
26648
27098
  var PRIM_POST_COMMIT_BLOCK_START = "# >>> prim post-commit hook >>>";
26649
27099
  var PRIM_POST_COMMIT_BLOCK_END = "# <<< prim post-commit hook <<<";
26650
27100
  var PRIM_CREATED_MARK = "prim-created-post-commit-hook";
@@ -26790,7 +27240,7 @@ var SHELL_QUOTED_RE = /^'(?:[^']|'"'"')*'$/u;
26790
27240
  function decodePinnedPath(token) {
26791
27241
  if (!token || !SHELL_QUOTED_RE.test(token)) return void 0;
26792
27242
  const value = token.slice(1, -1).replaceAll(`'"'"'`, "'");
26793
- if (!isAbsolute5(value) || value.includes("\0") || value.includes("\n") || value.includes("\r")) {
27243
+ if (!isAbsolute7(value) || value.includes("\0") || value.includes("\n") || value.includes("\r")) {
26794
27244
  return void 0;
26795
27245
  }
26796
27246
  return value;
@@ -26835,7 +27285,7 @@ function safeGitPath(root, value) {
26835
27285
  if (value.length === 0 || value !== value.trim() || value.includes("\0") || value.includes("\n") || value.includes("\r")) {
26836
27286
  throw new Error("Git returned an unsafe hooks path");
26837
27287
  }
26838
- return isAbsolute5(value) ? resolve5(value) : resolve5(root, value);
27288
+ return isAbsolute7(value) ? resolve6(value) : resolve6(root, value);
26839
27289
  }
26840
27290
  function resolveEffectiveManagedHook(spec, cwd = process.cwd()) {
26841
27291
  const gitRoot = gitToplevel(cwd);
@@ -26847,19 +27297,19 @@ function resolveEffectiveManagedHook(spec, cwd = process.cwd()) {
26847
27297
  timeout: GIT_TIMEOUT_MS3
26848
27298
  }).replace(/\r?\n$/u, "");
26849
27299
  const hooksDir = safeGitPath(gitRoot, value);
26850
- if (basename2(hooksDir) === "_" && basename2(dirname8(hooksDir)) === ".husky") {
27300
+ if (basename2(hooksDir) === "_" && basename2(dirname10(hooksDir)) === ".husky") {
26851
27301
  return {
26852
27302
  gitRoot,
26853
27303
  hooksDir,
26854
- hookPath: resolve5(dirname8(hooksDir), spec.hookName),
27304
+ hookPath: resolve6(dirname10(hooksDir), spec.hookName),
26855
27305
  kind: "husky_v9",
26856
- dispatcherPath: resolve5(hooksDir, spec.hookName)
27306
+ dispatcherPath: resolve6(hooksDir, spec.hookName)
26857
27307
  };
26858
27308
  }
26859
27309
  return {
26860
27310
  gitRoot,
26861
27311
  hooksDir,
26862
- hookPath: resolve5(hooksDir, spec.hookName),
27312
+ hookPath: resolve6(hooksDir, spec.hookName),
26863
27313
  kind: "direct"
26864
27314
  };
26865
27315
  }
@@ -26870,8 +27320,8 @@ function resolveEffectivePostRewriteHook(cwd = process.cwd()) {
26870
27320
  return resolveEffectiveManagedHook(POST_REWRITE_MANAGED_HOOK, cwd);
26871
27321
  }
26872
27322
  function assertSafeFile(path, spec) {
26873
- if (!existsSync7(path)) return void 0;
26874
- const stat = lstatSync4(path);
27323
+ if (!existsSync10(path)) return void 0;
27324
+ const stat = lstatSync5(path);
26875
27325
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_HOOK_BYTES) {
26876
27326
  throw new Error(`unsafe ${spec.hookName} hook target: ${path}`);
26877
27327
  }
@@ -26892,16 +27342,16 @@ function assertUsableHuskyDispatcher(target, spec) {
26892
27342
  if (!HUSKY_V9_DISPATCHERS.has(normalizedDispatcher)) {
26893
27343
  throw new Error(`unrecognized Husky ${spec.hookName} dispatcher: ${target.dispatcherPath}`);
26894
27344
  }
26895
- const huskyRuntime = resolve5(dirname8(target.dispatcherPath), "h");
27345
+ const huskyRuntime = resolve6(dirname10(target.dispatcherPath), "h");
26896
27346
  const runtimeStat = assertSafeFile(huskyRuntime, spec);
26897
27347
  const runtime = runtimeStat ? readHookFile(huskyRuntime) : void 0;
26898
- const runtimeHash = runtime ? createHash6("sha256").update(runtime).digest("hex") : void 0;
27348
+ const runtimeHash = runtime ? createHash8("sha256").update(runtime).digest("hex") : void 0;
26899
27349
  if (!runtimeHash || !HUSKY_V9_RUNTIME_HASHES.has(runtimeHash)) {
26900
27350
  throw new Error(`unrecognized Husky v9 hook runtime: ${huskyRuntime}`);
26901
27351
  }
26902
27352
  }
26903
27353
  function readHookFile(path) {
26904
- const value = readFileSync8(path);
27354
+ const value = readFileSync11(path);
26905
27355
  return Buffer.isBuffer(value) ? value : Buffer.from(value);
26906
27356
  }
26907
27357
  function decodeHookText(content, spec) {
@@ -27034,7 +27484,7 @@ function errorCode(error) {
27034
27484
  function assertTargetUnchanged(path, expected, spec, stat) {
27035
27485
  let current;
27036
27486
  try {
27037
- current = lstatSync4(path);
27487
+ current = lstatSync5(path);
27038
27488
  } catch (error) {
27039
27489
  if (expected === void 0 && errorCode(error) === "ENOENT") return;
27040
27490
  throw new Error(`${spec.hookName} hook changed concurrently: ${path}`);
@@ -27047,9 +27497,9 @@ function assertTargetUnchanged(path, expected, spec, stat) {
27047
27497
  }
27048
27498
  }
27049
27499
  function rewriteHookAtomically(path, next, expected, spec, stat) {
27050
- const parent = dirname8(path);
27051
- mkdirSync6(parent, { recursive: true });
27052
- const temporary = resolve5(
27500
+ const parent = dirname10(path);
27501
+ mkdirSync7(parent, { recursive: true });
27502
+ const temporary = resolve6(
27053
27503
  parent,
27054
27504
  `.${basename2(path)}.prim-${String(process.pid)}-${randomUUID2()}.tmp`
27055
27505
  );
@@ -27057,16 +27507,16 @@ function rewriteHookAtomically(path, next, expected, spec, stat) {
27057
27507
  try {
27058
27508
  fd = openSync4(
27059
27509
  temporary,
27060
- constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY | constants4.O_NOFOLLOW,
27510
+ constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY | constants5.O_NOFOLLOW,
27061
27511
  stat?.mode ?? 493
27062
27512
  );
27063
- writeFileSync3(fd, next);
27513
+ writeFileSync4(fd, next);
27064
27514
  fchmodSync2(fd, (stat?.mode ?? 493) & 4095);
27065
27515
  fsyncSync2(fd);
27066
27516
  closeSync4(fd);
27067
27517
  fd = void 0;
27068
27518
  assertTargetUnchanged(path, expected, spec, stat);
27069
- renameSync2(temporary, path);
27519
+ renameSync3(temporary, path);
27070
27520
  } finally {
27071
27521
  if (fd !== void 0) {
27072
27522
  try {
@@ -27075,7 +27525,7 @@ function rewriteHookAtomically(path, next, expected, spec, stat) {
27075
27525
  }
27076
27526
  }
27077
27527
  try {
27078
- unlinkSync(temporary);
27528
+ unlinkSync2(temporary);
27079
27529
  } catch (error) {
27080
27530
  if (errorCode(error) !== "ENOENT") {
27081
27531
  }
@@ -27172,22 +27622,22 @@ async function bindRepository(root, options) {
27172
27622
  import { execFileSync as execFileSync4 } from "child_process";
27173
27623
  import { randomUUID as randomUUID3 } from "crypto";
27174
27624
  import {
27175
- constants as constants5,
27176
- chmodSync as chmodSync3,
27625
+ constants as constants6,
27626
+ chmodSync as chmodSync4,
27177
27627
  closeSync as closeSync5,
27178
27628
  fchmodSync as fchmodSync3,
27179
27629
  fsyncSync as fsyncSync3,
27180
27630
  linkSync,
27181
- lstatSync as lstatSync5,
27182
- mkdirSync as mkdirSync7,
27631
+ lstatSync as lstatSync6,
27632
+ mkdirSync as mkdirSync8,
27183
27633
  openSync as openSync5,
27184
- readFileSync as readFileSync9,
27185
- statSync as statSync2,
27186
- unlinkSync as unlinkSync2,
27187
- writeFileSync as writeFileSync4
27634
+ readFileSync as readFileSync12,
27635
+ statSync as statSync3,
27636
+ unlinkSync as unlinkSync3,
27637
+ writeFileSync as writeFileSync5
27188
27638
  } from "fs";
27189
- import { dirname as dirname9, isAbsolute as isAbsolute6, join as join12, resolve as resolve6 } from "path";
27190
- var WORKSPACE_ID_RELATIVE_PATH = join12("prim", "workspace-id");
27639
+ import { dirname as dirname11, isAbsolute as isAbsolute8, join as join15, resolve as resolve7 } from "path";
27640
+ var WORKSPACE_ID_RELATIVE_PATH = join15("prim", "workspace-id");
27191
27641
  var MAX_ANCESTORS = 64;
27192
27642
  var MAX_GIT_FILE_BYTES = 4096;
27193
27643
  var MAX_ID_FILE_BYTES = 128;
@@ -27233,7 +27683,7 @@ function resolveWorkspaceIdPath(cwd) {
27233
27683
  }
27234
27684
  return {
27235
27685
  status: "resolved",
27236
- path: isAbsolute6(gitPath) ? resolve6(gitPath) : resolve6(cwd, gitPath)
27686
+ path: isAbsolute8(gitPath) ? resolve7(gitPath) : resolve7(cwd, gitPath)
27237
27687
  };
27238
27688
  } catch (error) {
27239
27689
  const code = errorCode2(error);
@@ -27244,21 +27694,21 @@ function resolveWorkspaceIdPath(cwd) {
27244
27694
  }
27245
27695
  }
27246
27696
  function resolveFromFilesystem(cwd) {
27247
- let current = resolve6(cwd);
27697
+ let current = resolve7(cwd);
27248
27698
  for (let depth = 0; depth < MAX_ANCESTORS; depth += 1) {
27249
- const dotGit = join12(current, ".git");
27699
+ const dotGit = join15(current, ".git");
27250
27700
  try {
27251
- const stat = lstatSync5(dotGit);
27701
+ const stat = lstatSync6(dotGit);
27252
27702
  if (stat.isDirectory() && isGitDirectory(dotGit)) {
27253
- return join12(dotGit, WORKSPACE_ID_RELATIVE_PATH);
27703
+ return join15(dotGit, WORKSPACE_ID_RELATIVE_PATH);
27254
27704
  }
27255
27705
  if (stat.isFile() && stat.size <= MAX_GIT_FILE_BYTES) {
27256
- const contents = readFileSync9(dotGit, "utf8");
27706
+ const contents = readFileSync12(dotGit, "utf8");
27257
27707
  const match = /^gitdir: (.+?)(?:\r?\n)?$/.exec(contents);
27258
27708
  if (match?.[1]) {
27259
- const gitDirectory = isAbsolute6(match[1]) ? resolve6(match[1]) : resolve6(current, match[1]);
27709
+ const gitDirectory = isAbsolute8(match[1]) ? resolve7(match[1]) : resolve7(current, match[1]);
27260
27710
  if (isGitDirectory(gitDirectory)) {
27261
- return join12(gitDirectory, WORKSPACE_ID_RELATIVE_PATH);
27711
+ return join15(gitDirectory, WORKSPACE_ID_RELATIVE_PATH);
27262
27712
  }
27263
27713
  }
27264
27714
  }
@@ -27269,7 +27719,7 @@ function resolveFromFilesystem(cwd) {
27269
27719
  return null;
27270
27720
  }
27271
27721
  }
27272
- const parent = dirname9(current);
27722
+ const parent = dirname11(current);
27273
27723
  if (parent === current) break;
27274
27724
  current = parent;
27275
27725
  }
@@ -27277,7 +27727,7 @@ function resolveFromFilesystem(cwd) {
27277
27727
  }
27278
27728
  function isGitDirectory(path) {
27279
27729
  try {
27280
- return statSync2(join12(path, "HEAD")).isFile();
27730
+ return statSync3(join15(path, "HEAD")).isFile();
27281
27731
  } catch {
27282
27732
  return false;
27283
27733
  }
@@ -27285,7 +27735,7 @@ function isGitDirectory(path) {
27285
27735
  function inspectPath(path) {
27286
27736
  let stat;
27287
27737
  try {
27288
- stat = lstatSync5(path);
27738
+ stat = lstatSync6(path);
27289
27739
  } catch (error) {
27290
27740
  const code = errorCode2(error);
27291
27741
  if (code === "ENOENT" || code === "ENOTDIR") return { status: "missing", path };
@@ -27296,7 +27746,7 @@ function inspectPath(path) {
27296
27746
  return { status: "corrupt", path, reason: "invalid_content" };
27297
27747
  }
27298
27748
  try {
27299
- const contents = readFileSync9(path, "utf8");
27749
+ const contents = readFileSync12(path, "utf8");
27300
27750
  const workspaceId = contents.endsWith("\n") ? contents.slice(0, -1) : contents;
27301
27751
  if ((contents === workspaceId || contents === `${workspaceId}
27302
27752
  `) && isCanonicalWorkspaceId(workspaceId)) {
@@ -27313,22 +27763,22 @@ function inspectPath(path) {
27313
27763
  }
27314
27764
  }
27315
27765
  function createWorkspaceId(path) {
27316
- const parent = dirname9(path);
27766
+ const parent = dirname11(path);
27317
27767
  const workspaceId = randomUUID3();
27318
- const temporaryPath = join12(parent, `.workspace-id-${process.pid}-${randomUUID3()}.tmp`);
27768
+ const temporaryPath = join15(parent, `.workspace-id-${process.pid}-${randomUUID3()}.tmp`);
27319
27769
  let temporaryCreated = false;
27320
27770
  let publishing = false;
27321
27771
  try {
27322
- mkdirSync7(parent, { recursive: true, mode: 448 });
27323
- chmodSync3(parent, 448);
27772
+ mkdirSync8(parent, { recursive: true, mode: 448 });
27773
+ chmodSync4(parent, 448);
27324
27774
  const fd = openSync5(
27325
27775
  temporaryPath,
27326
- constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY,
27776
+ constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY,
27327
27777
  384
27328
27778
  );
27329
27779
  temporaryCreated = true;
27330
27780
  try {
27331
- writeFileSync4(fd, `${workspaceId}
27781
+ writeFileSync5(fd, `${workspaceId}
27332
27782
  `, "utf8");
27333
27783
  fchmodSync3(fd, 384);
27334
27784
  fsyncSync3(fd);
@@ -27349,7 +27799,7 @@ function createWorkspaceId(path) {
27349
27799
  } finally {
27350
27800
  if (temporaryCreated) {
27351
27801
  try {
27352
- unlinkSync2(temporaryPath);
27802
+ unlinkSync3(temporaryPath);
27353
27803
  } catch {
27354
27804
  }
27355
27805
  }
@@ -27375,23 +27825,23 @@ function errorCode2(error) {
27375
27825
  }
27376
27826
 
27377
27827
  // src/hooks/codex-context.ts
27378
- import { createHash as createHash7, randomBytes as randomBytes3 } from "crypto";
27828
+ import { createHash as createHash9, randomBytes as randomBytes4 } from "crypto";
27379
27829
  import {
27380
- chmodSync as chmodSync4,
27381
- existsSync as existsSync8,
27382
- mkdirSync as mkdirSync8,
27383
- readFileSync as readFileSync10,
27384
- readdirSync as readdirSync2,
27385
- renameSync as renameSync3,
27386
- rmSync as rmSync5,
27387
- statSync as statSync3,
27388
- unlinkSync as unlinkSync3,
27389
- writeFileSync as writeFileSync5
27830
+ chmodSync as chmodSync5,
27831
+ existsSync as existsSync11,
27832
+ mkdirSync as mkdirSync9,
27833
+ readFileSync as readFileSync13,
27834
+ readdirSync as readdirSync4,
27835
+ renameSync as renameSync4,
27836
+ rmSync as rmSync6,
27837
+ statSync as statSync4,
27838
+ unlinkSync as unlinkSync4,
27839
+ writeFileSync as writeFileSync6
27390
27840
  } from "fs";
27391
- import { join as join13 } from "path";
27841
+ import { join as join16 } from "path";
27392
27842
 
27393
27843
  // src/daemon/decision-digest-cache.ts
27394
- import { randomBytes as randomBytes2 } from "crypto";
27844
+ import { randomBytes as randomBytes3 } from "crypto";
27395
27845
  var DECISION_DIGEST_CACHE_LIMIT = 100;
27396
27846
  var DECISION_DIGEST_CACHE_WINDOW = "24h";
27397
27847
  var DECISION_DIGEST_CACHE_PATH = `/api/cli/decisions/recent?limit=${String(DECISION_DIGEST_CACHE_LIMIT)}&since=${DECISION_DIGEST_CACHE_WINDOW}`;
@@ -27516,15 +27966,13 @@ var CODEX_DRAFT_DIGEST_MAX_SEEN_IDS = 4096;
27516
27966
  var CODEX_DIGEST_STATE_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
27517
27967
  var CODEX_DIGEST_STATE_MAX_FILES = 256;
27518
27968
  var STATE_VERSION = 1;
27519
- var STATE_DIRECTORY = ["codex", "decision-digests"];
27520
27969
  var DRAFT_STATE_VERSION = 2;
27521
- var DRAFT_STATE_DIRECTORY = ["codex", "decision-draft-deliveries"];
27522
27970
  var NO_CURSOR = -1;
27523
- function stateRoot() {
27524
- return join13(primConfigDirectory(), ...STATE_DIRECTORY);
27971
+ function stateRoot(namespace) {
27972
+ return join16(primConfigDirectory(), namespace, "decision-digests");
27525
27973
  }
27526
- function draftStateRoot() {
27527
- return join13(primConfigDirectory(), ...DRAFT_STATE_DIRECTORY);
27974
+ function draftStateRoot(namespace) {
27975
+ return join16(primConfigDirectory(), namespace, "decision-draft-deliveries");
27528
27976
  }
27529
27977
  function workspaceFor(cwd) {
27530
27978
  try {
@@ -27535,13 +27983,13 @@ function workspaceFor(cwd) {
27535
27983
  }
27536
27984
  function statePath(args) {
27537
27985
  const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}`;
27538
- const key = createHash7("sha256").update(identity).digest("hex");
27539
- return join13(stateRoot(), `${key}.json`);
27986
+ const key = createHash9("sha256").update(identity).digest("hex");
27987
+ return join16(stateRoot(args.namespace), `${key}.json`);
27540
27988
  }
27541
27989
  function draftStatePath(args) {
27542
27990
  const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}\0${args.principalId}\0${args.organizationId}\0${args.credentialFingerprint}`;
27543
- const key = createHash7("sha256").update(identity).digest("hex");
27544
- return join13(draftStateRoot(), `${key}.json`);
27991
+ const key = createHash9("sha256").update(identity).digest("hex");
27992
+ return join16(draftStateRoot(args.namespace), `${key}.json`);
27545
27993
  }
27546
27994
  function parseState(value) {
27547
27995
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
@@ -27580,58 +28028,58 @@ function parseDraftState(value) {
27580
28028
  };
27581
28029
  }
27582
28030
  function readState(path) {
27583
- if (!existsSync8(path)) return void 0;
28031
+ if (!existsSync11(path)) return void 0;
27584
28032
  try {
27585
- return parseState(JSON.parse(readFileSync10(path, "utf8")));
28033
+ return parseState(JSON.parse(readFileSync13(path, "utf8")));
27586
28034
  } catch {
27587
28035
  return void 0;
27588
28036
  }
27589
28037
  }
27590
28038
  function readDraftState(path) {
27591
- if (!existsSync8(path)) return void 0;
28039
+ if (!existsSync11(path)) return void 0;
27592
28040
  try {
27593
- return parseDraftState(JSON.parse(readFileSync10(path, "utf8")));
28041
+ return parseDraftState(JSON.parse(readFileSync13(path, "utf8")));
27594
28042
  } catch {
27595
28043
  return void 0;
27596
28044
  }
27597
28045
  }
27598
28046
  function writeStateFile(path, directory, state) {
27599
- mkdirSync8(directory, { recursive: true, mode: 448 });
27600
- chmodSync4(directory, 448);
27601
- const temporary = join13(
28047
+ mkdirSync9(directory, { recursive: true, mode: 448 });
28048
+ chmodSync5(directory, 448);
28049
+ const temporary = join16(
27602
28050
  directory,
27603
- `.${path.slice(directory.length + 1)}.tmp-${process.pid}-${randomBytes3(8).toString("hex")}`
28051
+ `.${path.slice(directory.length + 1)}.tmp-${process.pid}-${randomBytes4(8).toString("hex")}`
27604
28052
  );
27605
28053
  try {
27606
- writeFileSync5(temporary, `${JSON.stringify(state)}
28054
+ writeFileSync6(temporary, `${JSON.stringify(state)}
27607
28055
  `, {
27608
28056
  encoding: "utf8",
27609
28057
  mode: 384,
27610
28058
  flag: "wx"
27611
28059
  });
27612
- chmodSync4(temporary, 384);
27613
- renameSync3(temporary, path);
28060
+ chmodSync5(temporary, 384);
28061
+ renameSync4(temporary, path);
27614
28062
  } finally {
27615
28063
  try {
27616
- unlinkSync3(temporary);
28064
+ unlinkSync4(temporary);
27617
28065
  } catch {
27618
28066
  }
27619
28067
  }
27620
28068
  }
27621
28069
  function cleanupStateFiles(directory, now) {
27622
- if (!existsSync8(directory)) return;
28070
+ if (!existsSync11(directory)) return;
27623
28071
  let names;
27624
28072
  try {
27625
- names = readdirSync2(directory);
28073
+ names = readdirSync4(directory);
27626
28074
  } catch {
27627
28075
  return;
27628
28076
  }
27629
28077
  const files = [];
27630
28078
  const residue = [];
27631
28079
  for (const name of names) {
27632
- const path = join13(directory, name);
28080
+ const path = join16(directory, name);
27633
28081
  try {
27634
- const mtimeMs = statSync3(path).mtimeMs;
28082
+ const mtimeMs = statSync4(path).mtimeMs;
27635
28083
  if (name.endsWith(".json")) {
27636
28084
  files.push({ path, mtimeMs });
27637
28085
  } else if ((name.includes(".json.tmp-") || name.endsWith(".json.lock")) && now - mtimeMs > CODEX_DIGEST_STATE_RETENTION_MS) {
@@ -27644,14 +28092,14 @@ function cleanupStateFiles(directory, now) {
27644
28092
  const excess = files.filter((file) => !stale.some((candidate) => candidate.path === file.path)).sort((a, b) => a.mtimeMs - b.mtimeMs).slice(0, Math.max(0, files.length - CODEX_DIGEST_STATE_MAX_FILES));
27645
28093
  for (const file of [...stale, ...excess]) {
27646
28094
  try {
27647
- unlinkSync3(file.path);
28095
+ unlinkSync4(file.path);
27648
28096
  } catch {
27649
28097
  }
27650
28098
  }
27651
28099
  for (const path of residue) {
27652
28100
  try {
27653
- if (now - statSync3(path).mtimeMs > CODEX_DIGEST_STATE_RETENTION_MS) {
27654
- rmSync5(path, { recursive: true, force: true });
28101
+ if (now - statSync4(path).mtimeMs > CODEX_DIGEST_STATE_RETENTION_MS) {
28102
+ rmSync6(path, { recursive: true, force: true });
27655
28103
  }
27656
28104
  } catch {
27657
28105
  }
@@ -27732,14 +28180,14 @@ function mergeState(latest, args) {
27732
28180
  updatedAt: args.now
27733
28181
  };
27734
28182
  }
27735
- async function commitState(path, args) {
28183
+ async function commitState(path, args, namespace) {
27736
28184
  try {
27737
28185
  await withFileLock(
27738
28186
  `${path}.lock`,
27739
28187
  () => {
27740
28188
  const latest = readState(path);
27741
- writeStateFile(path, stateRoot(), mergeState(latest, args));
27742
- cleanupStateFiles(stateRoot(), args.now);
28189
+ writeStateFile(path, stateRoot(namespace), mergeState(latest, args));
28190
+ cleanupStateFiles(stateRoot(namespace), args.now);
27743
28191
  },
27744
28192
  { timeoutMs: 50, pollMs: 10 }
27745
28193
  );
@@ -27761,7 +28209,7 @@ function mergeDraftState(latest, args) {
27761
28209
  updatedAt: args.now
27762
28210
  };
27763
28211
  }
27764
- async function commitDraftState(path, args) {
28212
+ async function commitDraftState(path, args, namespace) {
27765
28213
  try {
27766
28214
  let next;
27767
28215
  await withFileLock(
@@ -27769,8 +28217,8 @@ async function commitDraftState(path, args) {
27769
28217
  () => {
27770
28218
  const latest = readDraftState(path);
27771
28219
  next = mergeDraftState(latest, args);
27772
- writeStateFile(path, draftStateRoot(), next);
27773
- cleanupStateFiles(draftStateRoot(), args.now);
28220
+ writeStateFile(path, draftStateRoot(namespace), next);
28221
+ cleanupStateFiles(draftStateRoot(namespace), args.now);
27774
28222
  },
27775
28223
  { timeoutMs: 50, pollMs: 10 }
27776
28224
  );
@@ -27780,9 +28228,10 @@ async function commitDraftState(path, args) {
27780
28228
  }
27781
28229
  }
27782
28230
  async function prepareCodexContext(options) {
28231
+ const namespace = options.namespace ?? "codex";
27783
28232
  const siteUrl = getSiteUrl();
27784
28233
  const workspace = workspaceFor(options.cwd);
27785
- const path = statePath({ cwd: options.cwd, sessionId: options.sessionId, siteUrl });
28234
+ const path = statePath({ cwd: options.cwd, sessionId: options.sessionId, siteUrl, namespace });
27786
28235
  const previous = readState(path);
27787
28236
  const principal = resolveDaemonPrincipal();
27788
28237
  const privatePath = principal ? draftStatePath({
@@ -27791,7 +28240,8 @@ async function prepareCodexContext(options) {
27791
28240
  siteUrl,
27792
28241
  principalId: principal.principalId,
27793
28242
  organizationId: principal.organizationId,
27794
- credentialFingerprint: principal.credentialFingerprint
28243
+ credentialFingerprint: principal.credentialFingerprint,
28244
+ namespace
27795
28245
  }) : void 0;
27796
28246
  const previousDraft = privatePath ? readDraftState(privatePath) : void 0;
27797
28247
  const startup = options.startup === true;
@@ -27881,32 +28331,40 @@ async function prepareCodexContext(options) {
27881
28331
  acknowledge: async (handedOff) => {
27882
28332
  if (!handedOff || acknowledged) return;
27883
28333
  acknowledged = true;
27884
- await commitState(path, {
27885
- sessionId: options.sessionId,
27886
- siteUrl,
27887
- workspace,
27888
- startedAt,
27889
- watermarkMs: pageWatermark,
27890
- seenIds: freshRows.map((row) => row.id),
27891
- report,
27892
- feedAvailable,
27893
- now: Date.now()
27894
- });
28334
+ await commitState(
28335
+ path,
28336
+ {
28337
+ sessionId: options.sessionId,
28338
+ siteUrl,
28339
+ workspace,
28340
+ startedAt,
28341
+ watermarkMs: pageWatermark,
28342
+ seenIds: freshRows.map((row) => row.id),
28343
+ report,
28344
+ feedAvailable,
28345
+ now: Date.now()
28346
+ },
28347
+ namespace
28348
+ );
27895
28349
  if (privatePath === void 0 || principal === void 0 || draftPageToken === void 0) {
27896
28350
  return;
27897
28351
  }
27898
28352
  let persistedDraft = previousDraft;
27899
28353
  if (draftDigest?.deliveredIds.length) {
27900
- persistedDraft = await commitDraftState(privatePath, {
27901
- sessionId: options.sessionId,
27902
- siteUrl,
27903
- workspace,
27904
- principalId: principal.principalId,
27905
- organizationId: principal.organizationId,
27906
- credentialFingerprint: principal.credentialFingerprint,
27907
- deliveredIds: draftDigest.deliveredIds,
27908
- now: Date.now()
27909
- });
28354
+ persistedDraft = await commitDraftState(
28355
+ privatePath,
28356
+ {
28357
+ sessionId: options.sessionId,
28358
+ siteUrl,
28359
+ workspace,
28360
+ principalId: principal.principalId,
28361
+ organizationId: principal.organizationId,
28362
+ credentialFingerprint: principal.credentialFingerprint,
28363
+ deliveredIds: draftDigest.deliveredIds,
28364
+ now: Date.now()
28365
+ },
28366
+ namespace
28367
+ );
27910
28368
  }
27911
28369
  const seenDrafts = new Set(persistedDraft?.seenDraftIds ?? []);
27912
28370
  const deliveredOnThisPage = draftPageActionableIds.filter((id) => seenDrafts.has(id));
@@ -27924,7 +28382,9 @@ async function prepareCodexContext(options) {
27924
28382
  }
27925
28383
 
27926
28384
  // src/hooks/normalize.ts
27927
- import { createHash as createHash8 } from "crypto";
28385
+ import { createHash as createHash10 } from "crypto";
28386
+ import { realpathSync as realpathSync4 } from "fs";
28387
+ import { isAbsolute as isAbsolute9, resolve as resolve8 } from "path";
27928
28388
  var HERMES_EVENT_MAP = {
27929
28389
  on_session_start: "SessionStart",
27930
28390
  on_session_end: "SessionEnd",
@@ -27934,7 +28394,125 @@ var HERMES_EVENT_MAP = {
27934
28394
  post_tool_call: "PostToolUse",
27935
28395
  subagent_stop: "SubagentStop"
27936
28396
  };
28397
+ var CURSOR_EVENT_MAP = {
28398
+ sessionStart: "SessionStart",
28399
+ beforeSubmitPrompt: "UserPromptSubmit",
28400
+ preToolUse: "PreToolUse",
28401
+ postToolUse: "PostToolUse",
28402
+ postToolUseFailure: "PostToolUseFailure",
28403
+ afterAgentResponse: "AssistantResponse",
28404
+ subagentStop: "SubagentStop",
28405
+ stop: "Stop",
28406
+ sessionEnd: "SessionEnd"
28407
+ };
28408
+ var CURSOR_TOOL_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse", "PostToolUseFailure"]);
28409
+ var SAFE_CORRELATION_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/u;
28410
+ var CURSOR_LOCAL_ONLY_FIELDS = /* @__PURE__ */ new Set([
28411
+ "conversation_id",
28412
+ "generation_id",
28413
+ "user_email",
28414
+ "account_email",
28415
+ "transcript_path",
28416
+ "transcript",
28417
+ "thought",
28418
+ "thoughts",
28419
+ "thinking",
28420
+ "tool_output"
28421
+ ]);
28422
+ function requiredId(value, field) {
28423
+ if (typeof value !== "string" || value.length === 0) {
28424
+ throw new TypeError(`Cursor hook envelope is missing ${field}`);
28425
+ }
28426
+ return value;
28427
+ }
28428
+ function cursorToolUseId(value) {
28429
+ if (SAFE_CORRELATION_ID_RE.test(value)) return value;
28430
+ return `cursor:tool:v1:${createHash10("sha256").update(value).digest("hex")}`;
28431
+ }
28432
+ function workspaceCandidates(value) {
28433
+ if (!Array.isArray(value)) return [];
28434
+ const roots = value.filter((item) => typeof item === "string" && item.length > 0);
28435
+ if (roots.some((root) => !isAbsolute9(root))) {
28436
+ throw new TypeError("Cursor workspace roots must be absolute");
28437
+ }
28438
+ return roots;
28439
+ }
28440
+ function pathIdentity(path) {
28441
+ try {
28442
+ return realpathSync4.native(path);
28443
+ } catch {
28444
+ return resolve8(path);
28445
+ }
28446
+ }
28447
+ function resolveCursorCwd(parsed) {
28448
+ const roots = workspaceCandidates(parsed.workspace_roots).map(pathIdentity);
28449
+ const rootRepositories = roots.map((root) => resolveRepositoryContext(root)?.repoRoot);
28450
+ const repositoryRoots = new Set(rootRepositories.filter((root) => !!root));
28451
+ if (repositoryRoots.size > 1) {
28452
+ throw new TypeError("Cursor hook envelope spans multiple repositories");
28453
+ }
28454
+ if (roots.length > 1 && rootRepositories.some((root) => root === void 0)) {
28455
+ throw new TypeError("Cursor hook envelope has ambiguous workspace roots");
28456
+ }
28457
+ const expectedRepo = [...repositoryRoots][0];
28458
+ const rawCwd = typeof parsed.cwd === "string" && parsed.cwd.length > 0 ? parsed.cwd : void 0;
28459
+ if (rawCwd && !isAbsolute9(rawCwd)) {
28460
+ throw new TypeError("Cursor hook cwd must be absolute");
28461
+ }
28462
+ const envelopeCwd = rawCwd ? pathIdentity(rawCwd) : void 0;
28463
+ const input = typeof parsed.tool_input === "object" && parsed.tool_input !== null && !Array.isArray(parsed.tool_input) ? parsed.tool_input : void 0;
28464
+ const rawToolCwd = parsed.tool_name === "Shell" && typeof input?.working_directory === "string" ? input.working_directory : void 0;
28465
+ const base = envelopeCwd ?? (roots.length === 1 ? roots[0] : expectedRepo);
28466
+ if (rawToolCwd && !isAbsolute9(rawToolCwd) && !base) {
28467
+ throw new TypeError("relative Cursor tool cwd has no workspace base");
28468
+ }
28469
+ const toolCwd = rawToolCwd ? pathIdentity(isAbsolute9(rawToolCwd) ? rawToolCwd : resolve8(base, rawToolCwd)) : void 0;
28470
+ const selected = toolCwd ?? envelopeCwd ?? (roots.length === 1 ? roots[0] : expectedRepo);
28471
+ if (!selected) {
28472
+ throw new TypeError("Cursor hook envelope has no unambiguous workspace");
28473
+ }
28474
+ const selectedRepo = resolveRepositoryContext(selected)?.repoRoot;
28475
+ if (expectedRepo && selectedRepo !== expectedRepo) {
28476
+ throw new TypeError("Cursor hook cwd conflicts with workspace repository");
28477
+ }
28478
+ return selected;
28479
+ }
28480
+ function normalizeCursorEnvelope(parsed) {
28481
+ const rawEvent = requiredId(parsed.hook_event_name, "hook_event_name");
28482
+ const event = CURSOR_EVENT_MAP[rawEvent];
28483
+ if (!event) throw new TypeError(`unsupported Cursor hook event: ${rawEvent}`);
28484
+ const sessionId = requiredId(parsed.conversation_id, "conversation_id");
28485
+ const turnId = requiredId(parsed.generation_id, "generation_id");
28486
+ if (parsed.session_id !== void 0 && parsed.session_id !== sessionId) {
28487
+ throw new TypeError("Cursor conversation_id conflicts with session_id");
28488
+ }
28489
+ if (parsed.turn_id !== void 0 && parsed.turn_id !== turnId) {
28490
+ throw new TypeError("Cursor generation_id conflicts with turn_id");
28491
+ }
28492
+ const rawToolUseId = CURSOR_TOOL_EVENTS.has(event) ? requiredId(parsed.tool_use_id, "tool_use_id") : void 0;
28493
+ const normalized = {
28494
+ ...Object.fromEntries(
28495
+ Object.entries(parsed).filter(([key]) => !CURSOR_LOCAL_ONLY_FIELDS.has(key))
28496
+ ),
28497
+ hook_event_name: event,
28498
+ session_id: sessionId,
28499
+ turn_id: turnId,
28500
+ cwd: resolveCursorCwd(parsed),
28501
+ ...rawToolUseId === void 0 ? {} : { tool_use_id: cursorToolUseId(rawToolUseId) }
28502
+ };
28503
+ if (typeof parsed.tool_output === "string") {
28504
+ try {
28505
+ normalized.tool_response = JSON.parse(parsed.tool_output);
28506
+ } catch {
28507
+ normalized.tool_response = parsed.tool_output;
28508
+ }
28509
+ }
28510
+ return normalized;
28511
+ }
27937
28512
  function normalizeEnvelope(parsed, agent) {
28513
+ if (agent === "cursor") {
28514
+ return normalizeCursorEnvelope(parsed);
28515
+ }
27938
28516
  if (agent === "codex") {
27939
28517
  const event = parsed.hook_event_name;
27940
28518
  if ((event === "PreToolUse" || event === "PostToolUse") && (typeof parsed.tool_use_id !== "string" || parsed.tool_use_id.length === 0)) {
@@ -27946,7 +28524,7 @@ function normalizeEnvelope(parsed, agent) {
27946
28524
  ]);
27947
28525
  return {
27948
28526
  ...parsed,
27949
- tool_use_id: `codex:fallback:v1:${createHash8("sha256").update(stableToolIdentity).digest("hex")}`
28527
+ tool_use_id: `codex:fallback:v1:${createHash10("sha256").update(stableToolIdentity).digest("hex")}`
27950
28528
  };
27951
28529
  }
27952
28530
  return parsed;
@@ -28048,24 +28626,24 @@ async function processSessionStart(raw, agent, feedbackSignal) {
28048
28626
  { timeoutMs: DAEMON_TIMEOUT_MS }
28049
28627
  );
28050
28628
  const cwd = envelope.cwd ?? process.cwd();
28051
- let projectRoot = null;
28629
+ let projectRoot2 = null;
28052
28630
  let active = false;
28053
28631
  let skillState = { installed: 0, refreshed: 0 };
28054
28632
  let claudeBinding;
28055
28633
  if (agent === "claude_code") {
28056
28634
  const activeProject = await activeProjectRoot(cwd);
28057
- projectRoot = activeProject?.root ?? null;
28058
- active = projectRoot !== null;
28635
+ projectRoot2 = activeProject?.root ?? null;
28636
+ active = projectRoot2 !== null;
28059
28637
  claudeBinding = activeProject?.binding;
28060
28638
  try {
28061
28639
  skillState = await refreshClaudePlugins(
28062
28640
  cwd,
28063
- active && projectRoot !== null ? { includeProject: true, projectRoot } : { includeProject: false }
28641
+ active && projectRoot2 !== null ? { includeProject: true, projectRoot: projectRoot2 } : { includeProject: false }
28064
28642
  );
28065
28643
  } catch {
28066
28644
  }
28067
28645
  }
28068
- if (isSessionEnded() && agent !== "codex") {
28646
+ if (isSessionEnded() && agent !== "codex" && agent !== "cursor") {
28069
28647
  const notice = reauthNoticeFields(agent);
28070
28648
  if (notice) {
28071
28649
  return {
@@ -28084,11 +28662,11 @@ async function processSessionStart(raw, agent, feedbackSignal) {
28084
28662
  });
28085
28663
  } catch {
28086
28664
  }
28087
- projectRoot = (await activeProjectRoot(cwd))?.root ?? null;
28088
- active = projectRoot !== null;
28665
+ projectRoot2 = (await activeProjectRoot(cwd))?.root ?? null;
28666
+ active = projectRoot2 !== null;
28089
28667
  let proactive = false;
28090
28668
  try {
28091
- proactive = projectRoot !== null && active && hasUsableCodexGuidance(projectRoot);
28669
+ proactive = projectRoot2 !== null && active && hasUsableCodexGuidance(projectRoot2);
28092
28670
  } catch {
28093
28671
  }
28094
28672
  const additionalContext = [proactive ? CODEX_PRIM_REMINDER : void 0, context?.context].filter((value) => value !== void 0).join("\n\n");
@@ -28099,6 +28677,34 @@ async function processSessionStart(raw, agent, feedbackSignal) {
28099
28677
  }
28100
28678
  };
28101
28679
  }
28680
+ if (agent === "cursor") {
28681
+ const activeProject = await activeProjectRoot(cwd);
28682
+ projectRoot2 = activeProject?.root ?? null;
28683
+ active = projectRoot2 !== null;
28684
+ let context;
28685
+ try {
28686
+ context = await prepareCodexContext({
28687
+ cwd,
28688
+ sessionId: envelope.session_id,
28689
+ startup: true,
28690
+ includeDigest: true,
28691
+ namespace: "cursor"
28692
+ });
28693
+ } catch {
28694
+ }
28695
+ let proactive = false;
28696
+ try {
28697
+ proactive = projectRoot2 !== null && active && hasUsableCursorSkill(projectRoot2);
28698
+ } catch {
28699
+ }
28700
+ const additionalContext = [proactive ? CODEX_PRIM_REMINDER : void 0, context?.context].filter((value) => value !== void 0).join("\n\n");
28701
+ return {
28702
+ output: additionalContext ? { additional_context: additionalContext } : {},
28703
+ acknowledge: async () => {
28704
+ await context?.acknowledge(true);
28705
+ }
28706
+ };
28707
+ }
28102
28708
  if (agent === "claude_code") {
28103
28709
  const additionalContext = [
28104
28710
  claudeBinding ? claudeBindingReminder(claudeBinding) : void 0,
@@ -28145,33 +28751,26 @@ function emitOutput(output, acknowledge) {
28145
28751
  outputAttempted = true;
28146
28752
  return handoffHookOutput(output, acknowledge);
28147
28753
  }
28148
- function readStdin() {
28149
- return new Promise((resolve7, reject) => {
28150
- const chunks = [];
28151
- const timer = setTimeout(() => {
28152
- reject(new Error("stdin read timeout"));
28153
- }, STDIN_TIMEOUT_MS);
28154
- process.stdin.on("data", (chunk) => chunks.push(chunk));
28155
- process.stdin.on("end", () => {
28156
- clearTimeout(timer);
28157
- resolve7(Buffer.concat(chunks).toString("utf-8"));
28158
- });
28159
- process.stdin.on("error", (err) => {
28160
- clearTimeout(timer);
28161
- reject(err);
28162
- });
28163
- });
28164
- }
28165
28754
  async function main() {
28166
28755
  warmBinCache();
28167
28756
  const agent = parseAgent(process.argv);
28168
28757
  let raw;
28169
28758
  try {
28170
- raw = await readStdin();
28759
+ raw = await readHookStdin(STDIN_TIMEOUT_MS);
28171
28760
  } catch {
28172
28761
  await emitOutput(buildHookOutput({}));
28173
28762
  return;
28174
28763
  }
28764
+ if (agent !== "cursor") {
28765
+ try {
28766
+ const incoming = JSON.parse(raw);
28767
+ if (shouldSuppressImportedCursorHandler(incoming, "prim-session-start")) {
28768
+ await emitOutput({});
28769
+ return;
28770
+ }
28771
+ } catch {
28772
+ }
28773
+ }
28175
28774
  const result = await processSessionStart(raw, agent);
28176
28775
  await emitOutput(result.output, result.acknowledge);
28177
28776
  }