@primitive.ai/prim 0.1.0-alpha.94 → 0.1.0-alpha.97

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 (40) hide show
  1. package/README.md +7 -4
  2. package/dist/{chunk-UKUAAAXE.js → chunk-27ADT6FQ.js} +2 -2
  3. package/dist/{chunk-TYAHC25F.js → chunk-2MBV7ZMV.js} +1 -1
  4. package/dist/{chunk-OTHKG76J.js → chunk-35FMRWJH.js} +1 -1
  5. package/dist/{chunk-O7YAW7G6.js → chunk-3I4GEWXQ.js} +9 -11
  6. package/dist/{chunk-DXJQR4QI.js → chunk-4GHANPWQ.js} +2 -2
  7. package/dist/{chunk-WUXFLE7X.js → chunk-7KABYYWG.js} +64 -66
  8. package/dist/{chunk-LFFLHSQO.js → chunk-AJYZB5PF.js} +63 -6
  9. package/dist/{chunk-H4UNYFUC.js → chunk-DHOQGHKQ.js} +184 -1
  10. package/dist/{chunk-2GP5UFV7.js → chunk-DWRSEC34.js} +5 -3
  11. package/dist/{chunk-ZU563PQI.js → chunk-FBPZ4YUD.js} +2 -2
  12. package/dist/{chunk-G2VMA2QO.js → chunk-LY6NJ7KL.js} +6 -4
  13. package/dist/{chunk-ZB4F75SZ.js → chunk-MXVCP33E.js} +5 -1
  14. package/dist/{chunk-ICM2XCXS.js → chunk-OW4GJBLW.js} +2 -2
  15. package/dist/{chunk-K7D2EYBS.js → chunk-QNPTWTBG.js} +2 -2
  16. package/dist/{chunk-IMK4JKTA.js → chunk-RK4ZS3HG.js} +2 -2
  17. package/dist/{chunk-OH3EZXYX.js → chunk-UO2U6IKF.js} +1 -1
  18. package/dist/{chunk-FXF4JWK2.js → chunk-XEDJ5N3M.js} +17 -2
  19. package/dist/{chunk-AXQQMX2U.js → chunk-Y5W4TZM7.js} +2 -2
  20. package/dist/{chunk-DSWT6BCC.js → chunk-ZYK3TBEW.js} +2542 -2250
  21. package/dist/daemon/server.js +2547 -2119
  22. package/dist/hook-runtime/daemon/server.js +2547 -2119
  23. package/dist/hook-runtime/hooks/post-commit.js +2547 -2119
  24. package/dist/hook-runtime/hooks/post-rewrite.js +2547 -2119
  25. package/dist/hook-runtime/hooks/post-tool-use.js +2874 -2373
  26. package/dist/hook-runtime/hooks/pre-tool-use.js +3766 -3325
  27. package/dist/hook-runtime/hooks/prim-hook.js +2973 -2440
  28. package/dist/hook-runtime/hooks/session-end.js +179 -129
  29. package/dist/hook-runtime/hooks/session-start.js +2651 -2221
  30. package/dist/hook-runtime/index.js +2596 -2126
  31. package/dist/hooks/post-commit.js +11 -11
  32. package/dist/hooks/post-rewrite.js +10 -10
  33. package/dist/hooks/post-tool-use.js +20 -20
  34. package/dist/hooks/pre-commit.js +4 -4
  35. package/dist/hooks/pre-tool-use.js +29 -17
  36. package/dist/hooks/prim-hook.js +22 -18
  37. package/dist/hooks/session-end.js +11 -5
  38. package/dist/hooks/session-start.js +26 -22
  39. package/dist/index.js +79 -60
  40. 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";
@@ -454,31 +605,31 @@ function detachedHookShimCommand(bin, args = "") {
454
605
  }
455
606
 
456
607
  // src/lib/hook-runtime.ts
457
- import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
608
+ import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
458
609
  import {
459
610
  constants as constants2,
460
611
  accessSync,
461
612
  chmodSync,
462
613
  copyFileSync,
463
- existsSync as existsSync3,
464
- lstatSync,
614
+ existsSync as existsSync4,
615
+ lstatSync as lstatSync2,
465
616
  mkdirSync as mkdirSync3,
466
617
  mkdtempSync,
467
- readFileSync as readFileSync4,
618
+ readFileSync as readFileSync5,
468
619
  readdirSync,
469
- realpathSync,
620
+ realpathSync as realpathSync2,
470
621
  renameSync as renameSync2,
471
622
  rmSync as rmSync3,
472
623
  statSync as statSync2,
473
624
  utimesSync,
474
625
  writeFileSync as writeFileSync3
475
626
  } from "fs";
476
- 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";
477
628
 
478
629
  // src/lib/file-lock.ts
479
630
  import { randomBytes } from "crypto";
480
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
481
- 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";
482
633
  var syncSleeper = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
483
634
 
484
635
  // src/lib/git-hook-contract.ts
@@ -572,114 +723,9 @@ exec "$prim_node" "$prim_entry" "$@"
572
723
  `;
573
724
 
574
725
  // src/commands/claude-install.ts
575
- import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
576
- import { homedir as homedir2 } from "os";
577
- import { join as join7 } from "path";
578
-
579
- // src/lib/git.ts
580
- import { execFileSync } from "child_process";
581
- import { createHash as createHash4 } from "crypto";
582
- import { lstatSync as lstatSync2, realpathSync as realpathSync2 } from "fs";
583
- import { basename, dirname as dirname5, isAbsolute as isAbsolute4, relative, resolve as resolve2, sep } from "path";
584
- var GIT_TIMEOUT_MS = 1e3;
585
- var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
586
- var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
587
- function gitToplevel(cwd) {
588
- try {
589
- return execFileSync("git", ["rev-parse", "--show-toplevel"], {
590
- cwd,
591
- encoding: "utf-8",
592
- stdio: ["ignore", "pipe", "ignore"],
593
- timeout: GIT_TIMEOUT_MS
594
- }).trim();
595
- } catch {
596
- return null;
597
- }
598
- }
599
- function gitValue(cwd, args) {
600
- try {
601
- const value = execFileSync("git", args, {
602
- cwd,
603
- encoding: "utf-8",
604
- stdio: ["ignore", "pipe", "ignore"],
605
- timeout: GIT_TIMEOUT_MS
606
- }).trim();
607
- return value || void 0;
608
- } catch {
609
- return void 0;
610
- }
611
- }
612
- function githubRepositoryFullName(cwd) {
613
- const remote = gitValue(cwd, ["config", "--get", "remote.origin.url"]);
614
- if (!remote || [...remote].some((char) => char < " " || char === "\x7F")) return null;
615
- const match = /^(?:git@github\.com:([^?#]+)|(?:https|ssh):\/\/(?:[^/@]+@)?github\.com(?::\d+)?\/([^?#]+))$/i.exec(
616
- remote
617
- );
618
- const path = match?.[1] ?? match?.[2];
619
- if (!path) return null;
620
- const parts = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "").split("/");
621
- if (parts.length !== 2 || !GITHUB_OWNER_PATTERN.test(parts[0] ?? "") || !GITHUB_REPOSITORY_PATTERN.test(parts[1] ?? "")) {
622
- return null;
623
- }
624
- return `${parts[0]}/${parts[1]}`;
625
- }
626
- function normalizeOriginRemote(remote) {
627
- const value = remote.trim();
628
- if (!value) return void 0;
629
- const scp = !value.includes("://") ? /^(?:[^@/\s]+@)?(?<host>[^:/\s]+):(?<path>.+)$/.exec(value)?.groups : void 0;
630
- if (scp?.host && scp.path) {
631
- const path = scp.path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
632
- return path ? `${scp.host.toLowerCase()}/${path}` : void 0;
633
- }
634
- try {
635
- const url = new URL(value);
636
- url.username = "";
637
- url.password = "";
638
- url.search = "";
639
- url.hash = "";
640
- const path = url.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
641
- if (url.host && path) return `${url.host.toLowerCase()}/${path}`;
642
- return void 0;
643
- } catch {
644
- return void 0;
645
- }
646
- }
647
- function opaqueRepoKey(source, value) {
648
- return `repo_v1_${createHash4("sha256").update(`${source}\0${value}`).digest("hex")}`;
649
- }
650
- function resolveRepositoryContext(cwd = process.cwd()) {
651
- const top = gitToplevel(cwd);
652
- if (!top) return null;
653
- let repoRoot;
654
- try {
655
- repoRoot = realpathSync2.native(top);
656
- } catch {
657
- repoRoot = resolve2(top);
658
- }
659
- const origin = gitValue(repoRoot, ["config", "--get", "remote.origin.url"]);
660
- const repoFullName = githubRepositoryFullName(repoRoot) ?? void 0;
661
- const normalizedOrigin = origin ? normalizeOriginRemote(origin) : void 0;
662
- if (normalizedOrigin) {
663
- return {
664
- repoRoot,
665
- repoKey: opaqueRepoKey("origin", normalizedOrigin),
666
- identitySource: "origin",
667
- repoFullName
668
- };
669
- }
670
- const roots = gitValue(repoRoot, ["rev-list", "--max-parents=0", "HEAD"])?.split(/\s+/).filter(Boolean).sort();
671
- if (roots && roots.length > 0) {
672
- return {
673
- repoRoot,
674
- repoKey: opaqueRepoKey("root_commit", roots.join("\n")),
675
- identitySource: "root_commit",
676
- repoFullName
677
- };
678
- }
679
- return { repoRoot, repoFullName };
680
- }
681
-
682
- // 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";
683
729
  var CAPTURE_BIN = "prim-hook";
684
730
  var GATE_BIN = "prim-pre-tool-use";
685
731
  var POST_TOOL_USE_BIN = "prim-post-tool-use";
@@ -688,7 +734,7 @@ var SESSION_END_BIN = "prim-session-end";
688
734
  var STATUSLINE_BIN = "prim-statusline";
689
735
  var STATUSLINE_ARGS = "";
690
736
  var STATUSLINE_COMMAND = stableHookCommand(STATUSLINE_BIN, STATUSLINE_ARGS);
691
- var USER_SCOPE_PATH = join7(homedir2(), ".claude", "settings.json");
737
+ var USER_SCOPE_PATH = join8(homedir3(), ".claude", "settings.json");
692
738
  function projectRoot() {
693
739
  return gitToplevel() ?? process.cwd();
694
740
  }
@@ -763,24 +809,24 @@ var REGISTRATIONS2 = [
763
809
  { event: "sessionStart", bin: SESSION_START_BIN2 },
764
810
  { event: "sessionEnd", bin: SESSION_END_BIN2 }
765
811
  ];
766
- function cursorConfigDirectory(env = process.env, home = homedir3(), platform = process.platform) {
812
+ function cursorConfigDirectory(env = process.env, home = homedir4(), platform = process.platform) {
767
813
  const explicit = env.CURSOR_CONFIG_DIR?.trim();
768
814
  if (explicit && isAbsolute5(explicit)) return explicit;
769
815
  const xdg = env.XDG_CONFIG_HOME?.trim();
770
- if (platform !== "darwin" && xdg && isAbsolute5(xdg)) return join8(xdg, "cursor");
771
- return join8(home, ".cursor");
816
+ if (platform !== "darwin" && xdg && isAbsolute5(xdg)) return join9(xdg, "cursor");
817
+ return join9(home, ".cursor");
772
818
  }
773
819
  function cursorHooksPath(scope, root = projectRoot()) {
774
- 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");
775
821
  }
776
822
  function plainObject(value) {
777
823
  return typeof value === "object" && value !== null && !Array.isArray(value);
778
824
  }
779
825
  function readCursorHooks(path) {
780
- if (!existsSync5(path)) return {};
826
+ if (!existsSync6(path)) return {};
781
827
  let parsed;
782
828
  try {
783
- parsed = JSON.parse(readFileSync6(path, "utf8"));
829
+ parsed = JSON.parse(readFileSync7(path, "utf8"));
784
830
  } catch {
785
831
  throw new Error(`invalid Cursor hooks JSON at ${path}`);
786
832
  }
@@ -1114,6 +1160,10 @@ async function main() {
1114
1160
  emit();
1115
1161
  return;
1116
1162
  }
1163
+ if (!isRepoActiveForCapture(envelope.cwd ?? process.cwd())) {
1164
+ emit();
1165
+ return;
1166
+ }
1117
1167
  await daemonRequest(
1118
1168
  "session_end",
1119
1169
  { sessionId: envelope.session_id },