@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.
- package/README.md +57 -12
- package/SKILL.md +5 -2
- package/dist/{chunk-UKUAAAXE.js → chunk-27ADT6FQ.js} +2 -2
- package/dist/{chunk-OTHKG76J.js → chunk-35FMRWJH.js} +1 -1
- package/dist/{chunk-DXJQR4QI.js → chunk-4GHANPWQ.js} +2 -2
- package/dist/{chunk-DCJN6LZH.js → chunk-7KABYYWG.js} +747 -430
- package/dist/{chunk-LFFLHSQO.js → chunk-CU7RW7XU.js} +63 -6
- package/dist/{chunk-H4UNYFUC.js → chunk-DHOQGHKQ.js} +184 -1
- package/dist/{chunk-2GP5UFV7.js → chunk-DWRSEC34.js} +5 -3
- package/dist/{chunk-36TWVNBE.js → chunk-FVDBULGZ.js} +9 -11
- package/dist/{chunk-G2VMA2QO.js → chunk-LY6NJ7KL.js} +6 -4
- package/dist/{chunk-ZB4F75SZ.js → chunk-MXVCP33E.js} +5 -1
- package/dist/{chunk-TYAHC25F.js → chunk-QCBB5HFX.js} +1 -1
- package/dist/{chunk-C3VAA5CG.js → chunk-RK4ZS3HG.js} +2 -2
- package/dist/{chunk-ZU563PQI.js → chunk-ROLYLXJS.js} +2 -2
- package/dist/{chunk-OH3EZXYX.js → chunk-UO2U6IKF.js} +1 -1
- package/dist/{chunk-K7D2EYBS.js → chunk-URLLSALP.js} +2 -2
- package/dist/chunk-XEDJ5N3M.js +177 -0
- package/dist/{chunk-X64HTHUF.js → chunk-Y5W4TZM7.js} +202 -10
- package/dist/{chunk-ICM2XCXS.js → chunk-YYE2NLWX.js} +2 -2
- package/dist/{chunk-DSWT6BCC.js → chunk-Z6NPNLOT.js} +825 -665
- package/dist/daemon/server.js +836 -540
- package/dist/hook-runtime/daemon/server.js +836 -540
- package/dist/hook-runtime/hooks/post-commit.js +978 -566
- package/dist/hook-runtime/hooks/post-rewrite.js +836 -540
- package/dist/hook-runtime/hooks/post-tool-use.js +1215 -796
- package/dist/hook-runtime/hooks/pre-tool-use.js +2100 -1741
- package/dist/hook-runtime/hooks/prim-hook.js +1312 -861
- package/dist/hook-runtime/hooks/session-end.js +232 -134
- package/dist/hook-runtime/hooks/session-start.js +1799 -935
- package/dist/hook-runtime/index.js +3628 -2461
- package/dist/hooks/post-commit.js +14 -10
- package/dist/hooks/post-rewrite.js +10 -10
- package/dist/hooks/post-tool-use.js +20 -20
- package/dist/hooks/pre-commit.js +4 -4
- package/dist/hooks/pre-tool-use.js +29 -17
- package/dist/hooks/prim-hook.js +22 -18
- package/dist/hooks/session-end.js +11 -5
- package/dist/hooks/session-start.js +46 -28
- package/dist/index.js +623 -390
- 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
|
|
400
|
-
import { homedir as
|
|
401
|
-
import { isAbsolute as isAbsolute5, join as
|
|
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
|
|
420
|
-
import { dirname as
|
|
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
|
-
|
|
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
|
|
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
|
|
461
|
-
lstatSync,
|
|
614
|
+
existsSync as existsSync4,
|
|
615
|
+
lstatSync as lstatSync2,
|
|
462
616
|
mkdirSync as mkdirSync3,
|
|
463
617
|
mkdtempSync,
|
|
464
|
-
readFileSync as
|
|
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
|
|
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
|
|
478
|
-
import { dirname as
|
|
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
|
|
528
|
-
import { homedir as
|
|
529
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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
|
|
723
|
-
return
|
|
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" ?
|
|
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 (!
|
|
826
|
+
if (!existsSync6(path)) return {};
|
|
733
827
|
let parsed;
|
|
734
828
|
try {
|
|
735
|
-
parsed = JSON.parse(
|
|
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 },
|