@primitive.ai/prim 0.1.0-alpha.93 → 0.1.0-alpha.96

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 (41) hide show
  1. package/README.md +57 -12
  2. package/SKILL.md +5 -2
  3. package/dist/{chunk-UKUAAAXE.js → chunk-27ADT6FQ.js} +2 -2
  4. package/dist/{chunk-OTHKG76J.js → chunk-35FMRWJH.js} +1 -1
  5. package/dist/{chunk-DXJQR4QI.js → chunk-4GHANPWQ.js} +2 -2
  6. package/dist/{chunk-DCJN6LZH.js → chunk-7KABYYWG.js} +747 -430
  7. package/dist/{chunk-LFFLHSQO.js → chunk-CU7RW7XU.js} +63 -6
  8. package/dist/{chunk-H4UNYFUC.js → chunk-DHOQGHKQ.js} +184 -1
  9. package/dist/{chunk-2GP5UFV7.js → chunk-DWRSEC34.js} +5 -3
  10. package/dist/{chunk-36TWVNBE.js → chunk-FVDBULGZ.js} +9 -11
  11. package/dist/{chunk-G2VMA2QO.js → chunk-LY6NJ7KL.js} +6 -4
  12. package/dist/{chunk-ZB4F75SZ.js → chunk-MXVCP33E.js} +5 -1
  13. package/dist/{chunk-TYAHC25F.js → chunk-QCBB5HFX.js} +1 -1
  14. package/dist/{chunk-C3VAA5CG.js → chunk-RK4ZS3HG.js} +2 -2
  15. package/dist/{chunk-ZU563PQI.js → chunk-ROLYLXJS.js} +2 -2
  16. package/dist/{chunk-OH3EZXYX.js → chunk-UO2U6IKF.js} +1 -1
  17. package/dist/{chunk-K7D2EYBS.js → chunk-URLLSALP.js} +2 -2
  18. package/dist/chunk-XEDJ5N3M.js +177 -0
  19. package/dist/{chunk-X64HTHUF.js → chunk-Y5W4TZM7.js} +202 -10
  20. package/dist/{chunk-ICM2XCXS.js → chunk-YYE2NLWX.js} +2 -2
  21. package/dist/{chunk-DSWT6BCC.js → chunk-Z6NPNLOT.js} +825 -665
  22. package/dist/daemon/server.js +836 -540
  23. package/dist/hook-runtime/daemon/server.js +836 -540
  24. package/dist/hook-runtime/hooks/post-commit.js +978 -566
  25. package/dist/hook-runtime/hooks/post-rewrite.js +836 -540
  26. package/dist/hook-runtime/hooks/post-tool-use.js +1215 -796
  27. package/dist/hook-runtime/hooks/pre-tool-use.js +2100 -1741
  28. package/dist/hook-runtime/hooks/prim-hook.js +1312 -861
  29. package/dist/hook-runtime/hooks/session-end.js +232 -134
  30. package/dist/hook-runtime/hooks/session-start.js +1799 -935
  31. package/dist/hook-runtime/index.js +3628 -2461
  32. package/dist/hooks/post-commit.js +14 -10
  33. package/dist/hooks/post-rewrite.js +10 -10
  34. package/dist/hooks/post-tool-use.js +20 -20
  35. package/dist/hooks/pre-commit.js +4 -4
  36. package/dist/hooks/pre-tool-use.js +29 -17
  37. package/dist/hooks/prim-hook.js +22 -18
  38. package/dist/hooks/session-end.js +11 -5
  39. package/dist/hooks/session-start.js +46 -28
  40. package/dist/index.js +623 -390
  41. package/package.json +1 -1
@@ -385,6 +385,157 @@ function daemonRequest(method, params = {}, opts = {}) {
385
385
  });
386
386
  }
387
387
 
388
+ // src/lib/activation.ts
389
+ import { execFileSync as execFileSync2 } from "child_process";
390
+ import { existsSync, readFileSync as readFileSync2 } from "fs";
391
+ import { homedir as homedir2 } from "os";
392
+ import { join as join4 } from "path";
393
+
394
+ // src/lib/git.ts
395
+ import { execFileSync } from "child_process";
396
+ import { createHash as createHash3 } from "crypto";
397
+ import { lstatSync, realpathSync } from "fs";
398
+ import { basename, dirname, isAbsolute as isAbsolute2, relative, resolve, sep } from "path";
399
+ var GIT_TIMEOUT_MS = 1e3;
400
+ var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
401
+ var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
402
+ function gitToplevel(cwd) {
403
+ try {
404
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
405
+ cwd,
406
+ encoding: "utf-8",
407
+ stdio: ["ignore", "pipe", "ignore"],
408
+ timeout: GIT_TIMEOUT_MS
409
+ }).trim();
410
+ } catch {
411
+ return null;
412
+ }
413
+ }
414
+ function gitValue(cwd, args) {
415
+ try {
416
+ const value = execFileSync("git", args, {
417
+ cwd,
418
+ encoding: "utf-8",
419
+ stdio: ["ignore", "pipe", "ignore"],
420
+ timeout: GIT_TIMEOUT_MS
421
+ }).trim();
422
+ return value || void 0;
423
+ } catch {
424
+ return void 0;
425
+ }
426
+ }
427
+ function githubRepositoryFullName(cwd) {
428
+ const remote = gitValue(cwd, ["config", "--get", "remote.origin.url"]);
429
+ if (!remote || [...remote].some((char) => char < " " || char === "\x7F")) return null;
430
+ const match = /^(?:git@github\.com:([^?#]+)|(?:https|ssh):\/\/(?:[^/@]+@)?github\.com(?::\d+)?\/([^?#]+))$/i.exec(
431
+ remote
432
+ );
433
+ const path = match?.[1] ?? match?.[2];
434
+ if (!path) return null;
435
+ const parts = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "").split("/");
436
+ if (parts.length !== 2 || !GITHUB_OWNER_PATTERN.test(parts[0] ?? "") || !GITHUB_REPOSITORY_PATTERN.test(parts[1] ?? "")) {
437
+ return null;
438
+ }
439
+ return `${parts[0]}/${parts[1]}`;
440
+ }
441
+ function normalizeOriginRemote(remote) {
442
+ const value = remote.trim();
443
+ if (!value) return void 0;
444
+ const scp = !value.includes("://") ? /^(?:[^@/\s]+@)?(?<host>[^:/\s]+):(?<path>.+)$/.exec(value)?.groups : void 0;
445
+ if (scp?.host && scp.path) {
446
+ const path = scp.path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
447
+ return path ? `${scp.host.toLowerCase()}/${path}` : void 0;
448
+ }
449
+ try {
450
+ const url = new URL(value);
451
+ url.username = "";
452
+ url.password = "";
453
+ url.search = "";
454
+ url.hash = "";
455
+ const path = url.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
456
+ if (url.host && path) return `${url.host.toLowerCase()}/${path}`;
457
+ return void 0;
458
+ } catch {
459
+ return void 0;
460
+ }
461
+ }
462
+ function opaqueRepoKey(source, value) {
463
+ return `repo_v1_${createHash3("sha256").update(`${source}\0${value}`).digest("hex")}`;
464
+ }
465
+ function resolveRepositoryContext(cwd = process.cwd()) {
466
+ const top = gitToplevel(cwd);
467
+ if (!top) return null;
468
+ let repoRoot;
469
+ try {
470
+ repoRoot = realpathSync.native(top);
471
+ } catch {
472
+ repoRoot = resolve(top);
473
+ }
474
+ const origin = gitValue(repoRoot, ["config", "--get", "remote.origin.url"]);
475
+ const repoFullName = githubRepositoryFullName(repoRoot) ?? void 0;
476
+ const normalizedOrigin = origin ? normalizeOriginRemote(origin) : void 0;
477
+ if (normalizedOrigin) {
478
+ return {
479
+ repoRoot,
480
+ repoKey: opaqueRepoKey("origin", normalizedOrigin),
481
+ identitySource: "origin",
482
+ repoFullName
483
+ };
484
+ }
485
+ const roots = gitValue(repoRoot, ["rev-list", "--max-parents=0", "HEAD"])?.split(/\s+/).filter(Boolean).sort();
486
+ if (roots && roots.length > 0) {
487
+ return {
488
+ repoRoot,
489
+ repoKey: opaqueRepoKey("root_commit", roots.join("\n")),
490
+ identitySource: "root_commit",
491
+ repoFullName
492
+ };
493
+ }
494
+ return { repoRoot, repoFullName };
495
+ }
496
+
497
+ // src/lib/activation.ts
498
+ var PRIM_ACTIVE_KEY = "prim.active";
499
+ var GIT_TIMEOUT_MS2 = 1e3;
500
+ var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json", ".cursor/hooks.json"];
501
+ var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
502
+ function repoActiveFlag(cwd) {
503
+ try {
504
+ const value = execFileSync2("git", ["config", "--get", PRIM_ACTIVE_KEY], {
505
+ cwd,
506
+ encoding: "utf-8",
507
+ stdio: ["ignore", "pipe", "ignore"],
508
+ timeout: GIT_TIMEOUT_MS2
509
+ }).trim();
510
+ if (value === "true") return "true";
511
+ if (value === "false") return "false";
512
+ return void 0;
513
+ } catch {
514
+ return void 0;
515
+ }
516
+ }
517
+ function hasProjectPrimInstall(cwd) {
518
+ const root = gitToplevel(cwd);
519
+ if (root === null || root === homedir2()) return false;
520
+ for (const rel of PROJECT_INSTALL_FILES) {
521
+ const path = join4(root, rel);
522
+ if (existsSync(path)) {
523
+ try {
524
+ const content = readFileSync2(path, "utf-8");
525
+ if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
526
+ } catch {
527
+ }
528
+ }
529
+ }
530
+ return false;
531
+ }
532
+ function isRepoActiveForCapture(cwd) {
533
+ const flag = repoActiveFlag(cwd);
534
+ if (flag === "true") return true;
535
+ if (flag === "false") return false;
536
+ return hasProjectPrimInstall(cwd);
537
+ }
538
+
388
539
  // src/hooks/agent.ts
389
540
  function parseAgent(argv) {
390
541
  const i = argv.indexOf("--agent");
@@ -396,9 +547,9 @@ function parseAgent(argv) {
396
547
  }
397
548
 
398
549
  // src/commands/cursor-install.ts
399
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
400
- import { homedir as homedir3 } from "os";
401
- import { isAbsolute as isAbsolute5, join as join8 } from "path";
550
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
551
+ import { homedir as homedir4 } from "os";
552
+ import { isAbsolute as isAbsolute5, join as join9 } from "path";
402
553
 
403
554
  // src/lib/atomic-file.ts
404
555
  import { randomUUID } from "crypto";
@@ -413,11 +564,11 @@ import {
413
564
  rmSync,
414
565
  writeFileSync
415
566
  } from "fs";
416
- import { dirname } from "path";
567
+ import { dirname as dirname2 } from "path";
417
568
 
418
569
  // src/lib/bin-path.ts
419
- import { existsSync, readFileSync as readFileSync2 } from "fs";
420
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join4 } from "path";
570
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
571
+ import { dirname as dirname3, isAbsolute as isAbsolute3, join as join5 } from "path";
421
572
  import { fileURLToPath } from "url";
422
573
  var PKG_NAME = "@primitive.ai/prim";
423
574
  var STABLE_HOOK_LAUNCHER_NAME = "prim-hook-launcher-v1";
@@ -426,6 +577,13 @@ function shellQuote(value) {
426
577
  }
427
578
  var STABLE_HOOK_ARGUMENTS_RE = /^[-A-Za-z0-9_ ]*$/u;
428
579
  var STABLE_HOOK_BIN_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/u;
580
+ var PRIM_CONFIG_SHELL_RESOLVER = [
581
+ 'prim_absolute() { case "$1" in [/]* ) ;; *) return 1 ;; esac; case "$1" in [[:space:]]*|*[[:space:]]|*[/][/]*|*[/].[/]*|*[/]..[/]*|*[/].|*[/]..|?*[/]) return 1 ;; esac; }; ',
582
+ 'prim_config=${PRIM_CONFIG_DIR:-}; if ! prim_absolute "$prim_config"; then prim_config=${XDG_CONFIG_HOME:-}; if prim_absolute "$prim_config"; then ',
583
+ 'case "$prim_config" in /) prim_config=/prim ;; *) prim_config="$prim_config/prim" ;; esac; ',
584
+ '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; ',
585
+ "fi; fi; "
586
+ ].join("");
429
587
  function stableHookCommand(bin, args = "") {
430
588
  if (!STABLE_HOOK_BIN_RE.test(bin) || !STABLE_HOOK_ARGUMENTS_RE.test(args)) {
431
589
  throw new Error("invalid stable hook command");
@@ -435,11 +593,7 @@ function stableHookCommand(bin, args = "") {
435
593
  // it as inert data during the rolling window so an older uninstall or
436
594
  // reinstall can remove the stable entry without invoking npm or PATH.
437
595
  `prim_legacy_reader='-p ${PKG_NAME}@stable prim-shim.sh ${bin} '; `,
438
- 'prim_absolute() { case "$1" in [/]* ) ;; *) return 1 ;; esac; case "$1" in [[:space:]]*|*[[:space:]]|*[/][/]*|*[/].[/]*|*[/]..[/]*|*[/].|*[/]..|?*[/]) return 1 ;; esac; }; ',
439
- 'prim_config=${PRIM_CONFIG_DIR:-}; if ! prim_absolute "$prim_config"; then prim_config=${XDG_CONFIG_HOME:-}; if prim_absolute "$prim_config"; then ',
440
- 'case "$prim_config" in /) prim_config=/prim ;; *) prim_config="$prim_config/prim" ;; esac; ',
441
- '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; ',
442
- "fi; fi; ",
596
+ PRIM_CONFIG_SHELL_RESOLVER,
443
597
  `case "$prim_config" in /) prim_launcher=/${STABLE_HOOK_LAUNCHER_NAME} ;; *) prim_launcher="$prim_config/${STABLE_HOOK_LAUNCHER_NAME}" ;; esac; `,
444
598
  'exec "$prim_launcher" "$@"'
445
599
  ].join("");
@@ -451,33 +605,78 @@ function detachedHookShimCommand(bin, args = "") {
451
605
  }
452
606
 
453
607
  // src/lib/hook-runtime.ts
454
- import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
608
+ import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
455
609
  import {
456
610
  constants as constants2,
457
611
  accessSync,
458
612
  chmodSync,
459
613
  copyFileSync,
460
- existsSync as existsSync3,
461
- lstatSync,
614
+ existsSync as existsSync4,
615
+ lstatSync as lstatSync2,
462
616
  mkdirSync as mkdirSync3,
463
617
  mkdtempSync,
464
- readFileSync as readFileSync4,
618
+ readFileSync as readFileSync5,
465
619
  readdirSync,
466
- realpathSync,
620
+ realpathSync as realpathSync2,
467
621
  renameSync as renameSync2,
468
622
  rmSync as rmSync3,
469
623
  statSync as statSync2,
470
624
  utimesSync,
471
625
  writeFileSync as writeFileSync3
472
626
  } from "fs";
473
- import { delimiter, dirname as dirname4, isAbsolute as isAbsolute3, join as join6, resolve } from "path";
627
+ import { delimiter, dirname as dirname5, isAbsolute as isAbsolute4, join as join7, resolve as resolve2 } from "path";
474
628
 
475
629
  // src/lib/file-lock.ts
476
630
  import { randomBytes } from "crypto";
477
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
478
- import { dirname as dirname3, join as join5 } from "path";
631
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
632
+ import { dirname as dirname4, join as join6 } from "path";
479
633
  var syncSleeper = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
480
634
 
635
+ // src/lib/git-hook-contract.ts
636
+ var GIT_HOOK_ENTRYPOINT_CONTENT = `#!/bin/sh
637
+ # prim git hook entrypoint v1 \u2014 managed by prim; do not edit.
638
+ # Run by the "# >>> prim <hook> hook >>>" block in a Git hook file. Acts only
639
+ # where prim is active (git config prim.active true) and never fails the hook.
640
+ case "$0" in */*) prim_dir=\${0%/*} ;; *) exit 0 ;; esac
641
+ prim_launcher="$prim_dir/${STABLE_HOOK_LAUNCHER_NAME}"
642
+ [ -x "$prim_launcher" ] || exit 0
643
+ [ "$(git config --get prim.active 2>/dev/null)" = "true" ] || exit 0
644
+ prim_hook=\${1-}
645
+ if [ "$#" -gt 0 ]; then shift; fi
646
+ case "$prim_hook" in
647
+ pre-commit)
648
+ "$prim_launcher" prim-pre-commit "$@" || :
649
+ ;;
650
+ post-commit)
651
+ prim_sha=$(git rev-parse --verify HEAD 2>/dev/null) || exit 0
652
+ case "$prim_sha" in *[!0-9a-f]* | "") exit 0 ;; esac
653
+ case "\${#prim_sha}" in 40 | 64) ;; *) exit 0 ;; esac
654
+ prim_branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null) || prim_branch=
655
+ prim_observed=$(mktemp "\${TMPDIR:-/tmp}/prim-post-commit-observed.XXXXXXXX" 2>/dev/null) || prim_observed=
656
+ (
657
+ trap '' HUP
658
+ trap 'if [ -n "$prim_observed" ]; then rm -f "$prim_observed"; fi' 0
659
+ PRIM_COMMIT_SHA=$prim_sha PRIM_COMMIT_BRANCH=$prim_branch PRIM_COMMIT_OBSERVED_FILE=$prim_observed "$prim_launcher" prim-post-commit
660
+ ) </dev/null >/dev/null 2>&1 &
661
+ ;;
662
+ post-rewrite)
663
+ case "\${1-}" in amend | rebase) prim_source=$1 ;; *) exit 0 ;; esac
664
+ prim_pairs=$(mktemp "\${TMPDIR:-/tmp}/prim-post-rewrite-pairs.XXXXXXXX" 2>/dev/null) || exit 0
665
+ if ! chmod 600 "$prim_pairs" 2>/dev/null || ! cat >"$prim_pairs"; then
666
+ rm -f "$prim_pairs"
667
+ exit 0
668
+ fi
669
+ prim_branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null) || prim_branch=
670
+ (
671
+ trap '' HUP
672
+ trap 'rm -f "$prim_pairs"' 0
673
+ PRIM_REWRITE_SOURCE=$prim_source PRIM_REWRITE_BRANCH=$prim_branch PRIM_REWRITE_PAIRS_FILE=$prim_pairs "$prim_launcher" prim-post-rewrite
674
+ ) </dev/null >/dev/null 2>&1 &
675
+ ;;
676
+ esac
677
+ exit 0
678
+ `;
679
+
481
680
  // src/lib/hook-runtime.ts
482
681
  var RELEASE_PREFIX = "release-";
483
682
  var MAX_MANIFEST_BYTES = 64 * 1024;
@@ -524,114 +723,9 @@ exec "$prim_node" "$prim_entry" "$@"
524
723
  `;
525
724
 
526
725
  // src/commands/claude-install.ts
527
- import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
528
- import { homedir as homedir2 } from "os";
529
- import { join as join7 } from "path";
530
-
531
- // src/lib/git.ts
532
- import { execFileSync } from "child_process";
533
- import { createHash as createHash4 } from "crypto";
534
- import { lstatSync as lstatSync2, realpathSync as realpathSync2 } from "fs";
535
- import { basename, dirname as dirname5, isAbsolute as isAbsolute4, relative, resolve as resolve2, sep } from "path";
536
- var GIT_TIMEOUT_MS = 1e3;
537
- var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
538
- var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
539
- function gitToplevel(cwd) {
540
- try {
541
- return execFileSync("git", ["rev-parse", "--show-toplevel"], {
542
- cwd,
543
- encoding: "utf-8",
544
- stdio: ["ignore", "pipe", "ignore"],
545
- timeout: GIT_TIMEOUT_MS
546
- }).trim();
547
- } catch {
548
- return null;
549
- }
550
- }
551
- function gitValue(cwd, args) {
552
- try {
553
- const value = execFileSync("git", args, {
554
- cwd,
555
- encoding: "utf-8",
556
- stdio: ["ignore", "pipe", "ignore"],
557
- timeout: GIT_TIMEOUT_MS
558
- }).trim();
559
- return value || void 0;
560
- } catch {
561
- return void 0;
562
- }
563
- }
564
- function githubRepositoryFullName(cwd) {
565
- const remote = gitValue(cwd, ["config", "--get", "remote.origin.url"]);
566
- if (!remote || [...remote].some((char) => char < " " || char === "\x7F")) return null;
567
- const match = /^(?:git@github\.com:([^?#]+)|(?:https|ssh):\/\/(?:[^/@]+@)?github\.com(?::\d+)?\/([^?#]+))$/i.exec(
568
- remote
569
- );
570
- const path = match?.[1] ?? match?.[2];
571
- if (!path) return null;
572
- const parts = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "").split("/");
573
- if (parts.length !== 2 || !GITHUB_OWNER_PATTERN.test(parts[0] ?? "") || !GITHUB_REPOSITORY_PATTERN.test(parts[1] ?? "")) {
574
- return null;
575
- }
576
- return `${parts[0]}/${parts[1]}`;
577
- }
578
- function normalizeOriginRemote(remote) {
579
- const value = remote.trim();
580
- if (!value) return void 0;
581
- const scp = !value.includes("://") ? /^(?:[^@/\s]+@)?(?<host>[^:/\s]+):(?<path>.+)$/.exec(value)?.groups : void 0;
582
- if (scp?.host && scp.path) {
583
- const path = scp.path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
584
- return path ? `${scp.host.toLowerCase()}/${path}` : void 0;
585
- }
586
- try {
587
- const url = new URL(value);
588
- url.username = "";
589
- url.password = "";
590
- url.search = "";
591
- url.hash = "";
592
- const path = url.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
593
- if (url.host && path) return `${url.host.toLowerCase()}/${path}`;
594
- return void 0;
595
- } catch {
596
- return void 0;
597
- }
598
- }
599
- function opaqueRepoKey(source, value) {
600
- return `repo_v1_${createHash4("sha256").update(`${source}\0${value}`).digest("hex")}`;
601
- }
602
- function resolveRepositoryContext(cwd = process.cwd()) {
603
- const top = gitToplevel(cwd);
604
- if (!top) return null;
605
- let repoRoot;
606
- try {
607
- repoRoot = realpathSync2.native(top);
608
- } catch {
609
- repoRoot = resolve2(top);
610
- }
611
- const origin = gitValue(repoRoot, ["config", "--get", "remote.origin.url"]);
612
- const repoFullName = githubRepositoryFullName(repoRoot) ?? void 0;
613
- const normalizedOrigin = origin ? normalizeOriginRemote(origin) : void 0;
614
- if (normalizedOrigin) {
615
- return {
616
- repoRoot,
617
- repoKey: opaqueRepoKey("origin", normalizedOrigin),
618
- identitySource: "origin",
619
- repoFullName
620
- };
621
- }
622
- const roots = gitValue(repoRoot, ["rev-list", "--max-parents=0", "HEAD"])?.split(/\s+/).filter(Boolean).sort();
623
- if (roots && roots.length > 0) {
624
- return {
625
- repoRoot,
626
- repoKey: opaqueRepoKey("root_commit", roots.join("\n")),
627
- identitySource: "root_commit",
628
- repoFullName
629
- };
630
- }
631
- return { repoRoot, repoFullName };
632
- }
633
-
634
- // src/commands/claude-install.ts
726
+ import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
727
+ import { homedir as homedir3 } from "os";
728
+ import { join as join8 } from "path";
635
729
  var CAPTURE_BIN = "prim-hook";
636
730
  var GATE_BIN = "prim-pre-tool-use";
637
731
  var POST_TOOL_USE_BIN = "prim-post-tool-use";
@@ -640,7 +734,7 @@ var SESSION_END_BIN = "prim-session-end";
640
734
  var STATUSLINE_BIN = "prim-statusline";
641
735
  var STATUSLINE_ARGS = "";
642
736
  var STATUSLINE_COMMAND = stableHookCommand(STATUSLINE_BIN, STATUSLINE_ARGS);
643
- var USER_SCOPE_PATH = join7(homedir2(), ".claude", "settings.json");
737
+ var USER_SCOPE_PATH = join8(homedir3(), ".claude", "settings.json");
644
738
  function projectRoot() {
645
739
  return gitToplevel() ?? process.cwd();
646
740
  }
@@ -715,24 +809,24 @@ var REGISTRATIONS2 = [
715
809
  { event: "sessionStart", bin: SESSION_START_BIN2 },
716
810
  { event: "sessionEnd", bin: SESSION_END_BIN2 }
717
811
  ];
718
- function cursorConfigDirectory(env = process.env, home = homedir3(), platform = process.platform) {
812
+ function cursorConfigDirectory(env = process.env, home = homedir4(), platform = process.platform) {
719
813
  const explicit = env.CURSOR_CONFIG_DIR?.trim();
720
814
  if (explicit && isAbsolute5(explicit)) return explicit;
721
815
  const xdg = env.XDG_CONFIG_HOME?.trim();
722
- if (platform !== "darwin" && xdg && isAbsolute5(xdg)) return join8(xdg, "cursor");
723
- return join8(home, ".cursor");
816
+ if (platform !== "darwin" && xdg && isAbsolute5(xdg)) return join9(xdg, "cursor");
817
+ return join9(home, ".cursor");
724
818
  }
725
819
  function cursorHooksPath(scope, root = projectRoot()) {
726
- return scope === "user" ? join8(cursorConfigDirectory(), "hooks.json") : join8(root, ".cursor", "hooks.json");
820
+ return scope === "user" ? join9(cursorConfigDirectory(), "hooks.json") : join9(root, ".cursor", "hooks.json");
727
821
  }
728
822
  function plainObject(value) {
729
823
  return typeof value === "object" && value !== null && !Array.isArray(value);
730
824
  }
731
825
  function readCursorHooks(path) {
732
- if (!existsSync5(path)) return {};
826
+ if (!existsSync6(path)) return {};
733
827
  let parsed;
734
828
  try {
735
- parsed = JSON.parse(readFileSync6(path, "utf8"));
829
+ parsed = JSON.parse(readFileSync7(path, "utf8"));
736
830
  } catch {
737
831
  throw new Error(`invalid Cursor hooks JSON at ${path}`);
738
832
  }
@@ -1066,6 +1160,10 @@ async function main() {
1066
1160
  emit();
1067
1161
  return;
1068
1162
  }
1163
+ if (!isRepoActiveForCapture(envelope.cwd ?? process.cwd())) {
1164
+ emit();
1165
+ return;
1166
+ }
1069
1167
  await daemonRequest(
1070
1168
  "session_end",
1071
1169
  { sessionId: envelope.session_id },