@primitive.ai/prim 0.1.0-alpha.92 → 0.1.0-alpha.94
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 +84 -10
- package/SKILL.md +5 -2
- package/dist/{chunk-67VNLLBN.js → chunk-2GP5UFV7.js} +1 -0
- package/dist/{chunk-26P57LAR.js → chunk-AXQQMX2U.js} +203 -9
- package/dist/{chunk-M6RIJVRE.js → chunk-DSWT6BCC.js} +78 -4
- package/dist/{chunk-FBAYCS3X.js → chunk-DXJQR4QI.js} +150 -38
- package/dist/chunk-FXF4JWK2.js +162 -0
- package/dist/{chunk-PCVGBE5B.js → chunk-G2VMA2QO.js} +2 -1
- package/dist/{chunk-NAHHFSLH.js → chunk-ICM2XCXS.js} +2 -2
- package/dist/{chunk-5HRR4AOR.js → chunk-IMK4JKTA.js} +1 -1
- package/dist/{chunk-OAAP2MY6.js → chunk-K7D2EYBS.js} +2 -2
- package/dist/{chunk-ADUTW66W.js → chunk-O7YAW7G6.js} +6 -6
- package/dist/{chunk-5AFRMOPX.js → chunk-OH3EZXYX.js} +1 -1
- package/dist/{chunk-KFXF2V5G.js → chunk-OTHKG76J.js} +1 -1
- package/dist/chunk-OYHYCK4K.js +13 -0
- package/dist/{chunk-FKXFUUWJ.js → chunk-TYAHC25F.js} +1 -1
- package/dist/{chunk-5GYQ4J3D.js → chunk-UKUAAAXE.js} +2 -2
- package/dist/{chunk-LGGMJYY4.js → chunk-WUXFLE7X.js} +720 -369
- package/dist/{chunk-WX52U7Z2.js → chunk-ZU563PQI.js} +58 -2
- package/dist/daemon/server.js +372 -123
- package/dist/hook-runtime/daemon/server.js +372 -123
- package/dist/hook-runtime/hooks/post-commit.js +192 -58
- package/dist/hook-runtime/hooks/post-rewrite.js +58 -40
- package/dist/hook-runtime/hooks/post-tool-use.js +150 -67
- package/dist/hook-runtime/hooks/pre-tool-use.js +87 -15
- package/dist/hook-runtime/hooks/prim-hook.js +197 -107
- package/dist/hook-runtime/hooks/session-end.js +53 -5
- package/dist/hook-runtime/hooks/session-start.js +880 -280
- package/dist/hook-runtime/index.js +4008 -2438
- package/dist/hook-runtime/statusline-main.js +23 -1
- package/dist/hooks/post-commit.js +15 -8
- package/dist/hooks/post-rewrite.js +11 -8
- package/dist/hooks/post-tool-use.js +13 -15
- package/dist/hooks/pre-commit.js +4 -4
- package/dist/hooks/pre-tool-use.js +10 -12
- package/dist/hooks/prim-hook.js +17 -14
- package/dist/hooks/session-end.js +4 -4
- package/dist/hooks/session-start.js +35 -45
- package/dist/index.js +1401 -620
- package/dist/statusline-main.js +23 -1
- package/package.json +1 -1
- package/dist/chunk-BTNNEH2W.js +0 -79
|
@@ -242,6 +242,13 @@ function pinnedNpxCommand(bin, args = [], options = {}) {
|
|
|
242
242
|
}
|
|
243
243
|
var STABLE_HOOK_ARGUMENTS_RE = /^[-A-Za-z0-9_ ]*$/u;
|
|
244
244
|
var STABLE_HOOK_BIN_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/u;
|
|
245
|
+
var PRIM_CONFIG_SHELL_RESOLVER = [
|
|
246
|
+
'prim_absolute() { case "$1" in [/]* ) ;; *) return 1 ;; esac; case "$1" in [[:space:]]*|*[[:space:]]|*[/][/]*|*[/].[/]*|*[/]..[/]*|*[/].|*[/]..|?*[/]) return 1 ;; esac; }; ',
|
|
247
|
+
'prim_config=${PRIM_CONFIG_DIR:-}; if ! prim_absolute "$prim_config"; then prim_config=${XDG_CONFIG_HOME:-}; if prim_absolute "$prim_config"; then ',
|
|
248
|
+
'case "$prim_config" in /) prim_config=/prim ;; *) prim_config="$prim_config/prim" ;; esac; ',
|
|
249
|
+
'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; ',
|
|
250
|
+
"fi; fi; "
|
|
251
|
+
].join("");
|
|
245
252
|
function stableHookCommand(bin, args = "") {
|
|
246
253
|
if (!STABLE_HOOK_BIN_RE.test(bin) || !STABLE_HOOK_ARGUMENTS_RE.test(args)) {
|
|
247
254
|
throw new Error("invalid stable hook command");
|
|
@@ -251,11 +258,7 @@ function stableHookCommand(bin, args = "") {
|
|
|
251
258
|
// it as inert data during the rolling window so an older uninstall or
|
|
252
259
|
// reinstall can remove the stable entry without invoking npm or PATH.
|
|
253
260
|
`prim_legacy_reader='-p ${PKG_NAME}@stable prim-shim.sh ${bin} '; `,
|
|
254
|
-
|
|
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; ",
|
|
261
|
+
PRIM_CONFIG_SHELL_RESOLVER,
|
|
259
262
|
`case "$prim_config" in /) prim_launcher=/${STABLE_HOOK_LAUNCHER_NAME} ;; *) prim_launcher="$prim_config/${STABLE_HOOK_LAUNCHER_NAME}" ;; esac; `,
|
|
260
263
|
'exec "$prim_launcher" "$@"'
|
|
261
264
|
].join("");
|
|
@@ -427,14 +430,14 @@ function throwIfAborted(signal) {
|
|
|
427
430
|
}
|
|
428
431
|
async function defaultSleep(ms, signal) {
|
|
429
432
|
if (!signal) {
|
|
430
|
-
await new Promise((
|
|
433
|
+
await new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
431
434
|
return;
|
|
432
435
|
}
|
|
433
436
|
throwIfAborted(signal);
|
|
434
|
-
await new Promise((
|
|
437
|
+
await new Promise((resolve10, reject) => {
|
|
435
438
|
const timer = setTimeout(() => {
|
|
436
439
|
signal.removeEventListener("abort", onAbort);
|
|
437
|
-
|
|
440
|
+
resolve10();
|
|
438
441
|
}, ms);
|
|
439
442
|
const onAbort = () => {
|
|
440
443
|
clearTimeout(timer);
|
|
@@ -476,6 +479,144 @@ async function withFileLock(lockDir, operation, options = {}) {
|
|
|
476
479
|
}
|
|
477
480
|
}
|
|
478
481
|
|
|
482
|
+
// src/lib/git-hook-contract.ts
|
|
483
|
+
var MANAGED_GIT_HOOK_NAMES = ["pre-commit", "post-commit", "post-rewrite"];
|
|
484
|
+
var GIT_HOOK_ENTRYPOINT_NAME = "prim-git-hook-v1";
|
|
485
|
+
var GIT_HOOK_CONTRACT_VERSION = 1;
|
|
486
|
+
var CONTRACT_LINE_PREFIX = "# prim git hook v";
|
|
487
|
+
var CONTRACT_LINE_RE = /^# prim git hook v([0-9]+)\b/mu;
|
|
488
|
+
function blockMarkers(hook) {
|
|
489
|
+
return { start: `# >>> prim ${hook} hook >>>`, end: `# <<< prim ${hook} hook <<<` };
|
|
490
|
+
}
|
|
491
|
+
function entrypointCommand(hook) {
|
|
492
|
+
const script = [
|
|
493
|
+
PRIM_CONFIG_SHELL_RESOLVER,
|
|
494
|
+
`case "$prim_config" in /) prim_entry=/${GIT_HOOK_ENTRYPOINT_NAME} ;; `,
|
|
495
|
+
`*) prim_entry="$prim_config/${GIT_HOOK_ENTRYPOINT_NAME}" ;; esac; `,
|
|
496
|
+
'[ -x "$prim_entry" ] || exit 0; exec "$prim_entry" "$@"'
|
|
497
|
+
].join("");
|
|
498
|
+
return `/bin/sh -c ${shellQuote(script)} ${GIT_HOOK_ENTRYPOINT_NAME} ${hook} "$@"`;
|
|
499
|
+
}
|
|
500
|
+
function managedHookBlock(hook) {
|
|
501
|
+
const { start, end } = blockMarkers(hook);
|
|
502
|
+
const header = `${CONTRACT_LINE_PREFIX}${GIT_HOOK_CONTRACT_VERSION}: a no-op unless prim is installed and enabled here (see \`prim hooks snippet ${hook}\`).`;
|
|
503
|
+
if (hook !== "post-rewrite") {
|
|
504
|
+
return `${start}
|
|
505
|
+
${header}
|
|
506
|
+
# shellcheck disable=SC2016
|
|
507
|
+
${entrypointCommand(hook)} || :
|
|
508
|
+
${end}`;
|
|
509
|
+
}
|
|
510
|
+
return `${start}
|
|
511
|
+
${header}
|
|
512
|
+
prim_rewrite_stdin=$(mktemp 2>/dev/null) || prim_rewrite_stdin=
|
|
513
|
+
if test -n "\${prim_rewrite_stdin}" && cat >"\${prim_rewrite_stdin}"; then
|
|
514
|
+
# shellcheck disable=SC2016
|
|
515
|
+
${entrypointCommand(hook)} <"\${prim_rewrite_stdin}" || :
|
|
516
|
+
exec <"\${prim_rewrite_stdin}"
|
|
517
|
+
fi
|
|
518
|
+
if test -n "\${prim_rewrite_stdin}"; then rm -f "\${prim_rewrite_stdin}"; fi
|
|
519
|
+
unset prim_rewrite_stdin
|
|
520
|
+
${end}`;
|
|
521
|
+
}
|
|
522
|
+
var OPEN_WORDS = /* @__PURE__ */ new Set(["then", "do", "else", "{", "(", "&&", "||", "|"]);
|
|
523
|
+
function canonicalHookBlock(text) {
|
|
524
|
+
const tokens = [];
|
|
525
|
+
let word = "";
|
|
526
|
+
let quote;
|
|
527
|
+
const separate = () => {
|
|
528
|
+
const last = tokens.at(-1);
|
|
529
|
+
if (last !== void 0 && last !== ";" && !OPEN_WORDS.has(last)) tokens.push(";");
|
|
530
|
+
};
|
|
531
|
+
const flush = () => {
|
|
532
|
+
if (word === "") return;
|
|
533
|
+
const last = tokens.at(-1);
|
|
534
|
+
if (last !== void 0 && /^[0-9]*(?:<|>|>>|<&|>&)$/u.test(last))
|
|
535
|
+
tokens[tokens.length - 1] += word;
|
|
536
|
+
else tokens.push(word);
|
|
537
|
+
word = "";
|
|
538
|
+
};
|
|
539
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
540
|
+
const char = text[index];
|
|
541
|
+
if (quote) {
|
|
542
|
+
word += char;
|
|
543
|
+
if (char === quote) quote = void 0;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (char === "'" || char === '"') {
|
|
547
|
+
quote = char;
|
|
548
|
+
word += char;
|
|
549
|
+
} else if (char === "#" && word === "") {
|
|
550
|
+
const end = text.indexOf("\n", index);
|
|
551
|
+
const comment = text.slice(index, end === -1 ? text.length : end);
|
|
552
|
+
tokens.push(comment.replace(/\s+/gu, " ").trim());
|
|
553
|
+
index = (end === -1 ? text.length : end) - 1;
|
|
554
|
+
} else if (char === "\n" || char === ";") {
|
|
555
|
+
flush();
|
|
556
|
+
if (char === ";" && text[index + 1] === ";") {
|
|
557
|
+
tokens.push(";;");
|
|
558
|
+
index += 1;
|
|
559
|
+
} else {
|
|
560
|
+
separate();
|
|
561
|
+
}
|
|
562
|
+
} else if (/\s/u.test(char)) {
|
|
563
|
+
flush();
|
|
564
|
+
} else {
|
|
565
|
+
word += char;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
flush();
|
|
569
|
+
while (tokens.at(-1) === ";") tokens.pop();
|
|
570
|
+
return tokens.join("\0");
|
|
571
|
+
}
|
|
572
|
+
function hookBlockContractVersion(block) {
|
|
573
|
+
const match = CONTRACT_LINE_RE.exec(block);
|
|
574
|
+
return match?.[1] ? Number(match[1]) : void 0;
|
|
575
|
+
}
|
|
576
|
+
var GIT_HOOK_ENTRYPOINT_CONTENT = `#!/bin/sh
|
|
577
|
+
# prim git hook entrypoint v1 \u2014 managed by prim; do not edit.
|
|
578
|
+
# Run by the "# >>> prim <hook> hook >>>" block in a Git hook file. Acts only
|
|
579
|
+
# where prim is active (git config prim.active true) and never fails the hook.
|
|
580
|
+
case "$0" in */*) prim_dir=\${0%/*} ;; *) exit 0 ;; esac
|
|
581
|
+
prim_launcher="$prim_dir/${STABLE_HOOK_LAUNCHER_NAME}"
|
|
582
|
+
[ -x "$prim_launcher" ] || exit 0
|
|
583
|
+
[ "$(git config --get prim.active 2>/dev/null)" = "true" ] || exit 0
|
|
584
|
+
prim_hook=\${1-}
|
|
585
|
+
if [ "$#" -gt 0 ]; then shift; fi
|
|
586
|
+
case "$prim_hook" in
|
|
587
|
+
pre-commit)
|
|
588
|
+
"$prim_launcher" prim-pre-commit "$@" || :
|
|
589
|
+
;;
|
|
590
|
+
post-commit)
|
|
591
|
+
prim_sha=$(git rev-parse --verify HEAD 2>/dev/null) || exit 0
|
|
592
|
+
case "$prim_sha" in *[!0-9a-f]* | "") exit 0 ;; esac
|
|
593
|
+
case "\${#prim_sha}" in 40 | 64) ;; *) exit 0 ;; esac
|
|
594
|
+
prim_branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null) || prim_branch=
|
|
595
|
+
prim_observed=$(mktemp "\${TMPDIR:-/tmp}/prim-post-commit-observed.XXXXXXXX" 2>/dev/null) || prim_observed=
|
|
596
|
+
(
|
|
597
|
+
trap '' HUP
|
|
598
|
+
trap 'if [ -n "$prim_observed" ]; then rm -f "$prim_observed"; fi' 0
|
|
599
|
+
PRIM_COMMIT_SHA=$prim_sha PRIM_COMMIT_BRANCH=$prim_branch PRIM_COMMIT_OBSERVED_FILE=$prim_observed "$prim_launcher" prim-post-commit
|
|
600
|
+
) </dev/null >/dev/null 2>&1 &
|
|
601
|
+
;;
|
|
602
|
+
post-rewrite)
|
|
603
|
+
case "\${1-}" in amend | rebase) prim_source=$1 ;; *) exit 0 ;; esac
|
|
604
|
+
prim_pairs=$(mktemp "\${TMPDIR:-/tmp}/prim-post-rewrite-pairs.XXXXXXXX" 2>/dev/null) || exit 0
|
|
605
|
+
if ! chmod 600 "$prim_pairs" 2>/dev/null || ! cat >"$prim_pairs"; then
|
|
606
|
+
rm -f "$prim_pairs"
|
|
607
|
+
exit 0
|
|
608
|
+
fi
|
|
609
|
+
prim_branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null) || prim_branch=
|
|
610
|
+
(
|
|
611
|
+
trap '' HUP
|
|
612
|
+
trap 'rm -f "$prim_pairs"' 0
|
|
613
|
+
PRIM_REWRITE_SOURCE=$prim_source PRIM_REWRITE_BRANCH=$prim_branch PRIM_REWRITE_PAIRS_FILE=$prim_pairs "$prim_launcher" prim-post-rewrite
|
|
614
|
+
) </dev/null >/dev/null 2>&1 &
|
|
615
|
+
;;
|
|
616
|
+
esac
|
|
617
|
+
exit 0
|
|
618
|
+
`;
|
|
619
|
+
|
|
479
620
|
// src/lib/paths.ts
|
|
480
621
|
import { homedir as homedir2 } from "os";
|
|
481
622
|
import { isAbsolute as isAbsolute2, join as join4, normalize } from "path";
|
|
@@ -529,6 +670,24 @@ var SOURCE_ENTRIES = Object.fromEntries(
|
|
|
529
670
|
target.replace(/^dist\//u, "")
|
|
530
671
|
])
|
|
531
672
|
);
|
|
673
|
+
function hookRuntimePaths(options = {}) {
|
|
674
|
+
const env = options.env ?? process.env;
|
|
675
|
+
const configDir = primConfigDirectory({
|
|
676
|
+
...options,
|
|
677
|
+
env,
|
|
678
|
+
homeDir: options.homeDir ?? env.HOME ?? ""
|
|
679
|
+
});
|
|
680
|
+
const runtimeDir = join5(configDir, "hook-runtime");
|
|
681
|
+
return {
|
|
682
|
+
configDir,
|
|
683
|
+
launcher: join5(configDir, STABLE_HOOK_LAUNCHER_NAME),
|
|
684
|
+
gitHookEntrypoint: join5(configDir, GIT_HOOK_ENTRYPOINT_NAME),
|
|
685
|
+
runtimeDir,
|
|
686
|
+
releasesDir: join5(runtimeDir, "releases"),
|
|
687
|
+
current: join5(runtimeDir, "current"),
|
|
688
|
+
selectionLock: join5(runtimeDir, "selection.lock")
|
|
689
|
+
};
|
|
690
|
+
}
|
|
532
691
|
var STABLE_HOOK_LAUNCHER_CONTENT = `#!/bin/sh
|
|
533
692
|
set -eu
|
|
534
693
|
case "$0" in */*) prim_launcher_dir=\${0%/*} ;; *) exit 78 ;; esac
|
|
@@ -548,6 +707,40 @@ case "$prim_node" in /*) ;; *) exit 69 ;; esac
|
|
|
548
707
|
[ -x "$prim_node" ] && [ -f "$prim_entry" ] || exit 69
|
|
549
708
|
exec "$prim_node" "$prim_entry" "$@"
|
|
550
709
|
`;
|
|
710
|
+
function gitHookEntrypointCurrent(paths) {
|
|
711
|
+
try {
|
|
712
|
+
return readFileSync3(paths.gitHookEntrypoint, "utf8") === GIT_HOOK_ENTRYPOINT_CONTENT;
|
|
713
|
+
} catch {
|
|
714
|
+
return false;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function inspectGitHookEntrypoint(options = {}) {
|
|
718
|
+
let paths;
|
|
719
|
+
try {
|
|
720
|
+
paths = hookRuntimePaths(options);
|
|
721
|
+
} catch {
|
|
722
|
+
return "invalid";
|
|
723
|
+
}
|
|
724
|
+
try {
|
|
725
|
+
const entrypoint = lstatIfPresent(paths.gitHookEntrypoint);
|
|
726
|
+
if (!entrypoint) return "missing";
|
|
727
|
+
if (!entrypoint.isFile() || (Number(entrypoint.mode) & 64) === 0 || !gitHookEntrypointCurrent(paths) || readFileSync3(paths.launcher, "utf8") !== STABLE_HOOK_LAUNCHER_CONTENT || !lstatIfPresent(paths.current)?.isFile()) {
|
|
728
|
+
return "invalid";
|
|
729
|
+
}
|
|
730
|
+
return "ready";
|
|
731
|
+
} catch {
|
|
732
|
+
return "invalid";
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
function lstatIfPresent(path) {
|
|
736
|
+
try {
|
|
737
|
+
return lstatSync(path);
|
|
738
|
+
} catch (error) {
|
|
739
|
+
const code = error.code;
|
|
740
|
+
if (code === "ENOENT" || code === "ENOTDIR") return void 0;
|
|
741
|
+
throw error;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
551
744
|
|
|
552
745
|
// src/commands/claude-install.ts
|
|
553
746
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
@@ -881,13 +1074,13 @@ function buildHookOutput(options) {
|
|
|
881
1074
|
return output;
|
|
882
1075
|
}
|
|
883
1076
|
function writeHookOutput(output, stream = process.stdout) {
|
|
884
|
-
return new Promise((
|
|
1077
|
+
return new Promise((resolve10) => {
|
|
885
1078
|
let settled = false;
|
|
886
1079
|
const finish = (ok, retainErrorListener = false) => {
|
|
887
1080
|
if (settled) return;
|
|
888
1081
|
settled = true;
|
|
889
1082
|
if (!retainErrorListener) stream.off("error", onError);
|
|
890
|
-
|
|
1083
|
+
resolve10(ok);
|
|
891
1084
|
};
|
|
892
1085
|
const onError = () => {
|
|
893
1086
|
if (settled) {
|
|
@@ -922,7 +1115,7 @@ async function handoffHookOutput(output, acknowledge, stream = process.stdout) {
|
|
|
922
1115
|
// src/hooks/hook-stdin.ts
|
|
923
1116
|
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
924
1117
|
function readHookStdin(timeoutMs = 1e3, maxBytes = DEFAULT_MAX_BYTES) {
|
|
925
|
-
return new Promise((
|
|
1118
|
+
return new Promise((resolve10, reject) => {
|
|
926
1119
|
const chunks = [];
|
|
927
1120
|
let bytes = 0;
|
|
928
1121
|
let settled = false;
|
|
@@ -934,7 +1127,7 @@ function readHookStdin(timeoutMs = 1e3, maxBytes = DEFAULT_MAX_BYTES) {
|
|
|
934
1127
|
process.stdin.removeAllListeners("end");
|
|
935
1128
|
process.stdin.removeAllListeners("error");
|
|
936
1129
|
if (error) reject(error);
|
|
937
|
-
else
|
|
1130
|
+
else resolve10(Buffer.concat(chunks, bytes).toString("utf-8"));
|
|
938
1131
|
};
|
|
939
1132
|
const timer = setTimeout(() => finish(new Error("stdin read timeout")), timeoutMs);
|
|
940
1133
|
process.stdin.on("data", (chunk) => {
|
|
@@ -5191,10 +5384,10 @@ function resolveBlockMap({ composeNode: composeNode2, composeEmptyNode: composeE
|
|
|
5191
5384
|
let offset = bm.offset;
|
|
5192
5385
|
let commentEnd = null;
|
|
5193
5386
|
for (const collItem of bm.items) {
|
|
5194
|
-
const { start, key, sep:
|
|
5387
|
+
const { start, key, sep: sep4, value } = collItem;
|
|
5195
5388
|
const keyProps = resolveProps(start, {
|
|
5196
5389
|
indicator: "explicit-key-ind",
|
|
5197
|
-
next: key ??
|
|
5390
|
+
next: key ?? sep4?.[0],
|
|
5198
5391
|
offset,
|
|
5199
5392
|
onError,
|
|
5200
5393
|
parentIndent: bm.indent,
|
|
@@ -5208,7 +5401,7 @@ function resolveBlockMap({ composeNode: composeNode2, composeEmptyNode: composeE
|
|
|
5208
5401
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
5209
5402
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
5210
5403
|
}
|
|
5211
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
5404
|
+
if (!keyProps.anchor && !keyProps.tag && !sep4) {
|
|
5212
5405
|
commentEnd = keyProps.end;
|
|
5213
5406
|
if (keyProps.comment) {
|
|
5214
5407
|
if (map2.comment)
|
|
@@ -5232,7 +5425,7 @@ function resolveBlockMap({ composeNode: composeNode2, composeEmptyNode: composeE
|
|
|
5232
5425
|
ctx.atKey = false;
|
|
5233
5426
|
if (mapIncludes(ctx, map2.items, keyNode))
|
|
5234
5427
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
5235
|
-
const valueProps = resolveProps(
|
|
5428
|
+
const valueProps = resolveProps(sep4 ?? [], {
|
|
5236
5429
|
indicator: "map-value-ind",
|
|
5237
5430
|
next: value,
|
|
5238
5431
|
offset: keyNode.range[2],
|
|
@@ -5248,7 +5441,7 @@ function resolveBlockMap({ composeNode: composeNode2, composeEmptyNode: composeE
|
|
|
5248
5441
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
5249
5442
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
5250
5443
|
}
|
|
5251
|
-
const valueNode = value ? composeNode2(ctx, value, valueProps, onError) : composeEmptyNode2(ctx, offset,
|
|
5444
|
+
const valueNode = value ? composeNode2(ctx, value, valueProps, onError) : composeEmptyNode2(ctx, offset, sep4, null, valueProps, onError);
|
|
5252
5445
|
if (ctx.schema.compat)
|
|
5253
5446
|
flowIndentCheck(bm.indent, value, onError);
|
|
5254
5447
|
offset = valueNode.range[2];
|
|
@@ -5324,7 +5517,7 @@ function resolveEnd(end, offset, reqSpace, onError) {
|
|
|
5324
5517
|
let comment = "";
|
|
5325
5518
|
if (end) {
|
|
5326
5519
|
let hasSpace = false;
|
|
5327
|
-
let
|
|
5520
|
+
let sep4 = "";
|
|
5328
5521
|
for (const token of end) {
|
|
5329
5522
|
const { source, type } = token;
|
|
5330
5523
|
switch (type) {
|
|
@@ -5338,13 +5531,13 @@ function resolveEnd(end, offset, reqSpace, onError) {
|
|
|
5338
5531
|
if (!comment)
|
|
5339
5532
|
comment = cb;
|
|
5340
5533
|
else
|
|
5341
|
-
comment +=
|
|
5342
|
-
|
|
5534
|
+
comment += sep4 + cb;
|
|
5535
|
+
sep4 = "";
|
|
5343
5536
|
break;
|
|
5344
5537
|
}
|
|
5345
5538
|
case "newline":
|
|
5346
5539
|
if (comment)
|
|
5347
|
-
|
|
5540
|
+
sep4 += source;
|
|
5348
5541
|
hasSpace = true;
|
|
5349
5542
|
break;
|
|
5350
5543
|
default:
|
|
@@ -5373,18 +5566,18 @@ function resolveFlowCollection({ composeNode: composeNode2, composeEmptyNode: co
|
|
|
5373
5566
|
let offset = fc.offset + fc.start.source.length;
|
|
5374
5567
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
5375
5568
|
const collItem = fc.items[i];
|
|
5376
|
-
const { start, key, sep:
|
|
5569
|
+
const { start, key, sep: sep4, value } = collItem;
|
|
5377
5570
|
const props = resolveProps(start, {
|
|
5378
5571
|
flow: fcName,
|
|
5379
5572
|
indicator: "explicit-key-ind",
|
|
5380
|
-
next: key ??
|
|
5573
|
+
next: key ?? sep4?.[0],
|
|
5381
5574
|
offset,
|
|
5382
5575
|
onError,
|
|
5383
5576
|
parentIndent: fc.indent,
|
|
5384
5577
|
startOnNewline: false
|
|
5385
5578
|
});
|
|
5386
5579
|
if (!props.found) {
|
|
5387
|
-
if (!props.anchor && !props.tag && !
|
|
5580
|
+
if (!props.anchor && !props.tag && !sep4 && !value) {
|
|
5388
5581
|
if (i === 0 && props.comma)
|
|
5389
5582
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
5390
5583
|
else if (i < fc.items.length - 1)
|
|
@@ -5438,8 +5631,8 @@ function resolveFlowCollection({ composeNode: composeNode2, composeEmptyNode: co
|
|
|
5438
5631
|
}
|
|
5439
5632
|
}
|
|
5440
5633
|
}
|
|
5441
|
-
if (!isMap2 && !
|
|
5442
|
-
const valueNode = value ? composeNode2(ctx, value, props, onError) : composeEmptyNode2(ctx, props.end,
|
|
5634
|
+
if (!isMap2 && !sep4 && !props.found) {
|
|
5635
|
+
const valueNode = value ? composeNode2(ctx, value, props, onError) : composeEmptyNode2(ctx, props.end, sep4, null, props, onError);
|
|
5443
5636
|
coll.items.push(valueNode);
|
|
5444
5637
|
offset = valueNode.range[2];
|
|
5445
5638
|
if (isBlock(value))
|
|
@@ -5451,7 +5644,7 @@ function resolveFlowCollection({ composeNode: composeNode2, composeEmptyNode: co
|
|
|
5451
5644
|
if (isBlock(key))
|
|
5452
5645
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
5453
5646
|
ctx.atKey = false;
|
|
5454
|
-
const valueProps = resolveProps(
|
|
5647
|
+
const valueProps = resolveProps(sep4 ?? [], {
|
|
5455
5648
|
flow: fcName,
|
|
5456
5649
|
indicator: "map-value-ind",
|
|
5457
5650
|
next: value,
|
|
@@ -5462,8 +5655,8 @@ function resolveFlowCollection({ composeNode: composeNode2, composeEmptyNode: co
|
|
|
5462
5655
|
});
|
|
5463
5656
|
if (valueProps.found) {
|
|
5464
5657
|
if (!isMap2 && !props.found && ctx.options.strict) {
|
|
5465
|
-
if (
|
|
5466
|
-
for (const st of
|
|
5658
|
+
if (sep4)
|
|
5659
|
+
for (const st of sep4) {
|
|
5467
5660
|
if (st === valueProps.found)
|
|
5468
5661
|
break;
|
|
5469
5662
|
if (st.type === "newline") {
|
|
@@ -5480,7 +5673,7 @@ function resolveFlowCollection({ composeNode: composeNode2, composeEmptyNode: co
|
|
|
5480
5673
|
else
|
|
5481
5674
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
5482
5675
|
}
|
|
5483
|
-
const valueNode = value ? composeNode2(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode2(ctx, valueProps.end,
|
|
5676
|
+
const valueNode = value ? composeNode2(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode2(ctx, valueProps.end, sep4, null, valueProps, onError) : null;
|
|
5484
5677
|
if (valueNode) {
|
|
5485
5678
|
if (isBlock(value))
|
|
5486
5679
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -5640,7 +5833,7 @@ function resolveBlockScalar(ctx, scalar, onError) {
|
|
|
5640
5833
|
chompStart = i + 1;
|
|
5641
5834
|
}
|
|
5642
5835
|
let value = "";
|
|
5643
|
-
let
|
|
5836
|
+
let sep4 = "";
|
|
5644
5837
|
let prevMoreIndented = false;
|
|
5645
5838
|
for (let i = 0; i < contentStart; ++i)
|
|
5646
5839
|
value += lines[i][0].slice(trimIndent) + "\n";
|
|
@@ -5657,24 +5850,24 @@ function resolveBlockScalar(ctx, scalar, onError) {
|
|
|
5657
5850
|
indent = "";
|
|
5658
5851
|
}
|
|
5659
5852
|
if (type === Scalar.BLOCK_LITERAL) {
|
|
5660
|
-
value +=
|
|
5661
|
-
|
|
5853
|
+
value += sep4 + indent.slice(trimIndent) + content;
|
|
5854
|
+
sep4 = "\n";
|
|
5662
5855
|
} else if (indent.length > trimIndent || content[0] === " ") {
|
|
5663
|
-
if (
|
|
5664
|
-
|
|
5665
|
-
else if (!prevMoreIndented &&
|
|
5666
|
-
|
|
5667
|
-
value +=
|
|
5668
|
-
|
|
5856
|
+
if (sep4 === " ")
|
|
5857
|
+
sep4 = "\n";
|
|
5858
|
+
else if (!prevMoreIndented && sep4 === "\n")
|
|
5859
|
+
sep4 = "\n\n";
|
|
5860
|
+
value += sep4 + indent.slice(trimIndent) + content;
|
|
5861
|
+
sep4 = "\n";
|
|
5669
5862
|
prevMoreIndented = true;
|
|
5670
5863
|
} else if (content === "") {
|
|
5671
|
-
if (
|
|
5864
|
+
if (sep4 === "\n")
|
|
5672
5865
|
value += "\n";
|
|
5673
5866
|
else
|
|
5674
|
-
|
|
5867
|
+
sep4 = "\n";
|
|
5675
5868
|
} else {
|
|
5676
|
-
value +=
|
|
5677
|
-
|
|
5869
|
+
value += sep4 + content;
|
|
5870
|
+
sep4 = " ";
|
|
5678
5871
|
prevMoreIndented = false;
|
|
5679
5872
|
}
|
|
5680
5873
|
}
|
|
@@ -5848,25 +6041,25 @@ function foldLines(source) {
|
|
|
5848
6041
|
if (!match)
|
|
5849
6042
|
return source;
|
|
5850
6043
|
let res = match[1];
|
|
5851
|
-
let
|
|
6044
|
+
let sep4 = " ";
|
|
5852
6045
|
let pos = first.lastIndex;
|
|
5853
6046
|
line.lastIndex = pos;
|
|
5854
6047
|
while (match = line.exec(source)) {
|
|
5855
6048
|
if (match[1] === "") {
|
|
5856
|
-
if (
|
|
5857
|
-
res +=
|
|
6049
|
+
if (sep4 === "\n")
|
|
6050
|
+
res += sep4;
|
|
5858
6051
|
else
|
|
5859
|
-
|
|
6052
|
+
sep4 = "\n";
|
|
5860
6053
|
} else {
|
|
5861
|
-
res +=
|
|
5862
|
-
|
|
6054
|
+
res += sep4 + match[1];
|
|
6055
|
+
sep4 = " ";
|
|
5863
6056
|
}
|
|
5864
6057
|
pos = line.lastIndex;
|
|
5865
6058
|
}
|
|
5866
6059
|
const last = /[ \t]*(.*)/sy;
|
|
5867
6060
|
last.lastIndex = pos;
|
|
5868
6061
|
match = last.exec(source);
|
|
5869
|
-
return res +
|
|
6062
|
+
return res + sep4 + (match?.[1] ?? "");
|
|
5870
6063
|
}
|
|
5871
6064
|
function doubleQuotedValue(source, onError) {
|
|
5872
6065
|
let res = "";
|
|
@@ -7483,18 +7676,18 @@ var Parser = class {
|
|
|
7483
7676
|
if (this.type === "map-value-ind") {
|
|
7484
7677
|
const prev = getPrevProps(this.peek(2));
|
|
7485
7678
|
const start = getFirstKeyStartProps(prev);
|
|
7486
|
-
let
|
|
7679
|
+
let sep4;
|
|
7487
7680
|
if (scalar.end) {
|
|
7488
|
-
|
|
7489
|
-
|
|
7681
|
+
sep4 = scalar.end;
|
|
7682
|
+
sep4.push(this.sourceToken);
|
|
7490
7683
|
delete scalar.end;
|
|
7491
7684
|
} else
|
|
7492
|
-
|
|
7685
|
+
sep4 = [this.sourceToken];
|
|
7493
7686
|
const map2 = {
|
|
7494
7687
|
type: "block-map",
|
|
7495
7688
|
offset: scalar.offset,
|
|
7496
7689
|
indent: scalar.indent,
|
|
7497
|
-
items: [{ start, key: scalar, sep:
|
|
7690
|
+
items: [{ start, key: scalar, sep: sep4 }]
|
|
7498
7691
|
};
|
|
7499
7692
|
this.onKeyLine = true;
|
|
7500
7693
|
this.stack[this.stack.length - 1] = map2;
|
|
@@ -7647,15 +7840,15 @@ var Parser = class {
|
|
|
7647
7840
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
7648
7841
|
const start2 = getFirstKeyStartProps(it.start);
|
|
7649
7842
|
const key = it.key;
|
|
7650
|
-
const
|
|
7651
|
-
|
|
7843
|
+
const sep4 = it.sep;
|
|
7844
|
+
sep4.push(this.sourceToken);
|
|
7652
7845
|
delete it.key;
|
|
7653
7846
|
delete it.sep;
|
|
7654
7847
|
this.stack.push({
|
|
7655
7848
|
type: "block-map",
|
|
7656
7849
|
offset: this.offset,
|
|
7657
7850
|
indent: this.indent,
|
|
7658
|
-
items: [{ start: start2, key, sep:
|
|
7851
|
+
items: [{ start: start2, key, sep: sep4 }]
|
|
7659
7852
|
});
|
|
7660
7853
|
} else if (start.length > 0) {
|
|
7661
7854
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -7849,13 +8042,13 @@ var Parser = class {
|
|
|
7849
8042
|
const prev = getPrevProps(parent);
|
|
7850
8043
|
const start = getFirstKeyStartProps(prev);
|
|
7851
8044
|
fixFlowSeqItems(fc);
|
|
7852
|
-
const
|
|
7853
|
-
|
|
8045
|
+
const sep4 = fc.end.splice(1, fc.end.length);
|
|
8046
|
+
sep4.push(this.sourceToken);
|
|
7854
8047
|
const map2 = {
|
|
7855
8048
|
type: "block-map",
|
|
7856
8049
|
offset: fc.offset,
|
|
7857
8050
|
indent: fc.indent,
|
|
7858
|
-
items: [{ start, key: fc, sep:
|
|
8051
|
+
items: [{ start, key: fc, sep: sep4 }]
|
|
7859
8052
|
};
|
|
7860
8053
|
this.onKeyLine = true;
|
|
7861
8054
|
this.stack[this.stack.length - 1] = map2;
|
|
@@ -8761,7 +8954,7 @@ function daemonRequest(method, params = {}, opts = {}) {
|
|
|
8761
8954
|
...caller ? { caller } : {}
|
|
8762
8955
|
};
|
|
8763
8956
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
8764
|
-
return new Promise((
|
|
8957
|
+
return new Promise((resolve10) => {
|
|
8765
8958
|
let settled = false;
|
|
8766
8959
|
let socket;
|
|
8767
8960
|
let buffer = Buffer.alloc(0);
|
|
@@ -8776,7 +8969,7 @@ function daemonRequest(method, params = {}, opts = {}) {
|
|
|
8776
8969
|
} catch {
|
|
8777
8970
|
}
|
|
8778
8971
|
}
|
|
8779
|
-
|
|
8972
|
+
resolve10(value);
|
|
8780
8973
|
};
|
|
8781
8974
|
const timer = setTimeout(() => settle(null), timeoutMs);
|
|
8782
8975
|
timer.unref();
|
|
@@ -8837,6 +9030,14 @@ function daemonRequest(method, params = {}, opts = {}) {
|
|
|
8837
9030
|
|
|
8838
9031
|
// src/daemon/self-heal.ts
|
|
8839
9032
|
import { spawn } from "child_process";
|
|
9033
|
+
|
|
9034
|
+
// src/lib/unattended.ts
|
|
9035
|
+
var UNATTENDED_ENV = "PRIM_UNATTENDED";
|
|
9036
|
+
function unattendedEnv(env = process.env) {
|
|
9037
|
+
return { ...env, [UNATTENDED_ENV]: "1" };
|
|
9038
|
+
}
|
|
9039
|
+
|
|
9040
|
+
// src/daemon/self-heal.ts
|
|
8840
9041
|
function kickDaemonEnsure(options = {}) {
|
|
8841
9042
|
const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
|
|
8842
9043
|
if (!primEntry) {
|
|
@@ -8844,14 +9045,18 @@ function kickDaemonEnsure(options = {}) {
|
|
|
8844
9045
|
}
|
|
8845
9046
|
try {
|
|
8846
9047
|
const args = [primEntry, "daemon", "ensure"];
|
|
8847
|
-
if ((options.platform ?? process.platform) === "darwin") {
|
|
9048
|
+
if (options.latestBootstrap ?? (options.platform ?? process.platform) === "darwin") {
|
|
8848
9049
|
args.push("--latest-bootstrap");
|
|
8849
9050
|
}
|
|
8850
9051
|
const child = (options.spawnProcess ?? spawn)(
|
|
8851
9052
|
options.nodeEntry ?? process.execPath,
|
|
8852
9053
|
args,
|
|
8853
|
-
|
|
9054
|
+
// Unattended: neither SessionStart's repair nor a drift heal is a person
|
|
9055
|
+
// running prim, so the child (and its descendants) never heal in turn.
|
|
9056
|
+
{ detached: true, stdio: "ignore", env: unattendedEnv() }
|
|
8854
9057
|
);
|
|
9058
|
+
child.once("error", () => {
|
|
9059
|
+
});
|
|
8855
9060
|
child.unref();
|
|
8856
9061
|
return true;
|
|
8857
9062
|
} catch {
|
|
@@ -27077,8 +27282,59 @@ async function fetchAndCacheCollectScope(cwd, dependencies = defaultDependencies
|
|
|
27077
27282
|
return dependencies.writeCached(cwd, response);
|
|
27078
27283
|
}
|
|
27079
27284
|
|
|
27080
|
-
// src/lib/
|
|
27285
|
+
// src/lib/commit-heartbeat.ts
|
|
27081
27286
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
27287
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
27288
|
+
import { dirname as dirname10, join as join15, resolve as resolve6 } from "path";
|
|
27289
|
+
var WIRED_PATH = "prim/git-hooks-wired";
|
|
27290
|
+
var GIT_TIMEOUT_MS3 = 1e3;
|
|
27291
|
+
var MAX_STAMP_BYTES = 1024;
|
|
27292
|
+
function commonPath(cwd, relative4) {
|
|
27293
|
+
const value = execFileSync3("git", ["rev-parse", "--git-common-dir"], {
|
|
27294
|
+
cwd,
|
|
27295
|
+
encoding: "utf8",
|
|
27296
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27297
|
+
timeout: GIT_TIMEOUT_MS3
|
|
27298
|
+
}).replace(/\r?\n$/u, "");
|
|
27299
|
+
if (value === "" || /[\0\r\n]/u.test(value)) throw new Error("Git returned an unsafe path");
|
|
27300
|
+
return join15(resolve6(cwd, value), relative4);
|
|
27301
|
+
}
|
|
27302
|
+
function ensurePrivateParent(path) {
|
|
27303
|
+
mkdirSync7(dirname10(dirname10(path)), { recursive: true, mode: 448 });
|
|
27304
|
+
mkdirSync7(dirname10(path), { recursive: true, mode: 448 });
|
|
27305
|
+
}
|
|
27306
|
+
function writeStamp(path, at) {
|
|
27307
|
+
ensurePrivateParent(path);
|
|
27308
|
+
atomicWriteFile(path, `${JSON.stringify({ at })}
|
|
27309
|
+
`, { mode: 384 });
|
|
27310
|
+
}
|
|
27311
|
+
function readStampAt(path) {
|
|
27312
|
+
try {
|
|
27313
|
+
const raw = readFileSync11(path, "utf8");
|
|
27314
|
+
if (raw.length > MAX_STAMP_BYTES) return void 0;
|
|
27315
|
+
const parsed = JSON.parse(raw);
|
|
27316
|
+
return typeof parsed.at === "number" && Number.isSafeInteger(parsed.at) && parsed.at > 0 ? parsed.at : void 0;
|
|
27317
|
+
} catch {
|
|
27318
|
+
return void 0;
|
|
27319
|
+
}
|
|
27320
|
+
}
|
|
27321
|
+
function recordHooksWired(cwd, options = {}) {
|
|
27322
|
+
try {
|
|
27323
|
+
const path = commonPath(cwd, WIRED_PATH);
|
|
27324
|
+
if (options.onlyIfAbsent && readStampAt(path) !== void 0) return;
|
|
27325
|
+
writeStamp(path, options.now ?? Date.now());
|
|
27326
|
+
} catch {
|
|
27327
|
+
}
|
|
27328
|
+
}
|
|
27329
|
+
function clearHooksWired(cwd) {
|
|
27330
|
+
try {
|
|
27331
|
+
unlinkSync2(commonPath(cwd, WIRED_PATH));
|
|
27332
|
+
} catch {
|
|
27333
|
+
}
|
|
27334
|
+
}
|
|
27335
|
+
|
|
27336
|
+
// src/lib/git-hooks.ts
|
|
27337
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
27082
27338
|
import { createHash as createHash8, randomUUID as randomUUID2 } from "crypto";
|
|
27083
27339
|
import {
|
|
27084
27340
|
constants as constants5,
|
|
@@ -27087,50 +27343,28 @@ import {
|
|
|
27087
27343
|
fchmodSync as fchmodSync2,
|
|
27088
27344
|
fsyncSync as fsyncSync2,
|
|
27089
27345
|
lstatSync as lstatSync5,
|
|
27090
|
-
mkdirSync as
|
|
27346
|
+
mkdirSync as mkdirSync8,
|
|
27091
27347
|
openSync as openSync4,
|
|
27092
|
-
readFileSync as
|
|
27348
|
+
readFileSync as readFileSync12,
|
|
27349
|
+
realpathSync as realpathSync4,
|
|
27093
27350
|
renameSync as renameSync3,
|
|
27094
|
-
unlinkSync as
|
|
27351
|
+
unlinkSync as unlinkSync3,
|
|
27095
27352
|
writeFileSync as writeFileSync4
|
|
27096
27353
|
} from "fs";
|
|
27097
|
-
import { basename as basename2, dirname as
|
|
27098
|
-
|
|
27099
|
-
|
|
27100
|
-
|
|
27101
|
-
var
|
|
27102
|
-
var
|
|
27103
|
-
|
|
27104
|
-
|
|
27105
|
-
|
|
27106
|
-
|
|
27107
|
-
|
|
27108
|
-
|
|
27109
|
-
|
|
27110
|
-
|
|
27111
|
-
"#!/usr/bin/env sh",
|
|
27112
|
-
"#!/bin/bash",
|
|
27113
|
-
"#!/usr/bin/env bash",
|
|
27114
|
-
"#!/bin/zsh",
|
|
27115
|
-
"#!/usr/bin/env zsh"
|
|
27116
|
-
]);
|
|
27117
|
-
var HUSKY_V9_DISPATCHERS = /* @__PURE__ */ new Set([
|
|
27118
|
-
'#!/usr/bin/env sh\n. "${0%/*}/h"',
|
|
27119
|
-
'#!/usr/bin/env sh\n. "$(dirname "$0")/h"'
|
|
27120
|
-
]);
|
|
27121
|
-
var HUSKY_DIRECT_DISPATCHER = '#!/bin/sh\nexec sh "$(dirname "$0")/../post-commit"\n';
|
|
27122
|
-
var HUSKY_V9_RUNTIME_HASHES = /* @__PURE__ */ new Set([
|
|
27123
|
-
"d6aaa6f3f7c11008c525e649161df4a93152f1e55d0887fa1d7b8d622735d380",
|
|
27124
|
-
"b02cb91cab7125a9e906be723a1b85297fc435a8307d429ae845546b4ee0ecde",
|
|
27125
|
-
"9db5d129c4eb822a773157d98cd8fb8f1b156b85e3193117fefe527ca6d45e91",
|
|
27126
|
-
"3b2cb21335e544b4ebce658ca47af4a69d2c77026cc4d3f8b2227c34d062b207",
|
|
27127
|
-
"548990e1c11285694096184993d907b258d41235461a7ca2551e3b53ff9d3a38",
|
|
27128
|
-
"c029a943acea5d6e2ddbaa271a1fb22c2da55d56b5c6fb59ca804ac9a4018708",
|
|
27129
|
-
"ae3413a8fe2b39372de48bdc9691f42055f089c0c7529834b2fb07a131585b6a",
|
|
27130
|
-
"70200b200ca709b0622784f93839a5b2872333a917a09afddefd7dc2d8cdc680"
|
|
27131
|
-
]);
|
|
27132
|
-
function currentPostCommitBlock() {
|
|
27133
|
-
return `${PRIM_POST_COMMIT_BLOCK_START}
|
|
27354
|
+
import { basename as basename2, dirname as dirname11, isAbsolute as isAbsolute8, join as join16, relative as relative3, resolve as resolve7, sep as sep3 } from "path";
|
|
27355
|
+
|
|
27356
|
+
// src/lib/git-hook-legacy.ts
|
|
27357
|
+
import { isAbsolute as isAbsolute7 } from "path";
|
|
27358
|
+
var PRIM_MANAGED_MARK = "prim-managed-hook";
|
|
27359
|
+
var PRE_COMMIT = { hookName: "pre-commit", binName: "prim-pre-commit" };
|
|
27360
|
+
function blockMarkers2(spec) {
|
|
27361
|
+
return {
|
|
27362
|
+
start: `# >>> prim ${spec.hookName} hook >>>`,
|
|
27363
|
+
end: `# <<< prim ${spec.hookName} hook <<<`
|
|
27364
|
+
};
|
|
27365
|
+
}
|
|
27366
|
+
function legacyPostCommitBlock() {
|
|
27367
|
+
return `# >>> prim post-commit hook >>>
|
|
27134
27368
|
if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
27135
27369
|
prim_commit_sha=$(git rev-parse --verify HEAD 2>/dev/null) || prim_commit_sha=
|
|
27136
27370
|
case "$prim_commit_sha" in *[!0-9a-f]*|"") prim_commit_sha= ;; esac
|
|
@@ -27159,13 +27393,10 @@ if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
|
27159
27393
|
fi
|
|
27160
27394
|
unset prim_commit_sha
|
|
27161
27395
|
fi
|
|
27162
|
-
|
|
27396
|
+
# <<< prim post-commit hook <<<`;
|
|
27163
27397
|
}
|
|
27164
|
-
function
|
|
27165
|
-
return
|
|
27166
|
-
}
|
|
27167
|
-
function postRewriteHookBlock() {
|
|
27168
|
-
return `${PRIM_POST_REWRITE_BLOCK_START}
|
|
27398
|
+
function legacyPostRewriteBlock() {
|
|
27399
|
+
return `# >>> prim post-rewrite hook >>>
|
|
27169
27400
|
if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
27170
27401
|
case "$1" in amend|rebase) prim_rewrite_source="$1" ;; *) prim_rewrite_source= ;; esac
|
|
27171
27402
|
if [ -n "$prim_rewrite_source" ]; then
|
|
@@ -27204,22 +27435,159 @@ if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
|
27204
27435
|
fi
|
|
27205
27436
|
unset prim_rewrite_source
|
|
27206
27437
|
fi
|
|
27207
|
-
|
|
27208
|
-
}
|
|
27209
|
-
var
|
|
27210
|
-
|
|
27211
|
-
|
|
27212
|
-
|
|
27213
|
-
|
|
27214
|
-
|
|
27215
|
-
}
|
|
27216
|
-
|
|
27217
|
-
|
|
27218
|
-
|
|
27219
|
-
|
|
27220
|
-
|
|
27221
|
-
|
|
27438
|
+
# <<< prim post-rewrite hook <<<`;
|
|
27439
|
+
}
|
|
27440
|
+
var PRE_COMMIT_GATE = 'if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then';
|
|
27441
|
+
function preCommitLadder(fallback, failSoft) {
|
|
27442
|
+
const soft = failSoft ? " || true" : "";
|
|
27443
|
+
return `if command -v prim-pre-commit >/dev/null 2>&1; then
|
|
27444
|
+
prim-pre-commit${soft}
|
|
27445
|
+
elif [ -f "./node_modules/.bin/prim-pre-commit" ]; then
|
|
27446
|
+
./node_modules/.bin/prim-pre-commit${soft}
|
|
27447
|
+
else
|
|
27448
|
+
${fallback} 2>/dev/null || true
|
|
27449
|
+
fi`;
|
|
27450
|
+
}
|
|
27451
|
+
var LADDER_ALPHA_2 = preCommitLadder("npx --yes @primitive.ai/prim pre-commit-hook", false);
|
|
27452
|
+
var LADDER_ALPHA_9 = preCommitLadder("npx --yes -p @primitive.ai/prim prim-pre-commit", false);
|
|
27453
|
+
var LADDER_ALPHA_35 = preCommitLadder("npx --yes -p @primitive.ai/prim prim-pre-commit", true);
|
|
27454
|
+
var PINNED_NPX_PRE_COMMIT = String.raw`npx --yes(?: --ignore-scripts)? -p @primitive\.ai/prim@[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)? prim-pre-commit`;
|
|
27455
|
+
var PINNED_PRE_COMMIT_RE = new RegExp(
|
|
27456
|
+
String.raw`^\{ (?:${PINNED_NPX_PRE_COMMIT}|if \[ -x (?<node>'.*') \] && \[ -f (?<entry>'.*') \]; then \k<node> \k<entry>; else ${PINNED_NPX_PRE_COMMIT}; fi); \} \|\| true$`,
|
|
27457
|
+
"u"
|
|
27458
|
+
);
|
|
27459
|
+
var SHELL_QUOTED_RE = /^'(?:[^']|'"'"')*'$/u;
|
|
27460
|
+
function decodePinnedPath(token) {
|
|
27461
|
+
if (!token || !SHELL_QUOTED_RE.test(token)) return void 0;
|
|
27462
|
+
const value = token.slice(1, -1).replaceAll(`'"'"'`, "'");
|
|
27463
|
+
if (!isAbsolute7(value) || value.includes("\0") || value.includes("\n") || value.includes("\r")) {
|
|
27464
|
+
return void 0;
|
|
27465
|
+
}
|
|
27466
|
+
return value;
|
|
27467
|
+
}
|
|
27468
|
+
function isPinnedPreCommitInvocation(line) {
|
|
27469
|
+
const match = PINNED_PRE_COMMIT_RE.exec(line);
|
|
27470
|
+
if (!match) return false;
|
|
27471
|
+
if (match.groups?.node === void 0) return true;
|
|
27472
|
+
return Boolean(
|
|
27473
|
+
decodePinnedPath(match.groups.node) && decodePinnedPath(match.groups.entry)?.endsWith("/dist/hooks/pre-commit.js")
|
|
27474
|
+
);
|
|
27475
|
+
}
|
|
27476
|
+
function isLegacyPreCommitBody(body) {
|
|
27477
|
+
if (body === LADDER_ALPHA_2 || body === LADDER_ALPHA_9 || body === LADDER_ALPHA_35) return true;
|
|
27478
|
+
if (isPinnedPreCommitInvocation(body)) return true;
|
|
27479
|
+
const prefix = `${PRE_COMMIT_GATE}
|
|
27480
|
+
`;
|
|
27481
|
+
if (!body.startsWith(prefix) || !body.endsWith("\nfi")) return false;
|
|
27482
|
+
const inner = body.slice(prefix.length, -"\nfi".length);
|
|
27483
|
+
return inner === LADDER_ALPHA_35 || isPinnedPreCommitInvocation(inner);
|
|
27484
|
+
}
|
|
27485
|
+
function isLegacyOwnedPreCommitScript(content) {
|
|
27486
|
+
const original = (ladder) => `#!/bin/sh
|
|
27487
|
+
# prim pre-commit hook \u2014 auto-syncs affected specs on commit
|
|
27488
|
+
# Installed by: prim hooks install
|
|
27489
|
+
|
|
27490
|
+
# Find the nearest node_modules/.bin with prim, or use npx
|
|
27491
|
+
${ladder}
|
|
27492
|
+
`;
|
|
27493
|
+
if (content === original(LADDER_ALPHA_2) || content === original(LADDER_ALPHA_9)) return true;
|
|
27494
|
+
if (content === `#!/bin/sh
|
|
27495
|
+
# prim pre-commit hook \u2014 installed by: prim hooks install
|
|
27496
|
+
|
|
27497
|
+
${LADDER_ALPHA_9}
|
|
27498
|
+
`) {
|
|
27499
|
+
return true;
|
|
27500
|
+
}
|
|
27501
|
+
const prefix = `#!/bin/sh
|
|
27502
|
+
# prim pre-commit hook \u2014 installed by: prim hooks install (${PRIM_MANAGED_MARK})
|
|
27503
|
+
|
|
27504
|
+
`;
|
|
27505
|
+
if (!content.startsWith(prefix) || !content.endsWith("\n")) return false;
|
|
27506
|
+
const body = content.slice(prefix.length, -1);
|
|
27507
|
+
return body === LADDER_ALPHA_35 || isPinnedPreCommitInvocation(body);
|
|
27508
|
+
}
|
|
27509
|
+
var PINNED_PACKAGE_VERSION_RE = /@primitive\.ai\/prim@[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?/gu;
|
|
27510
|
+
function legacyInlineHookBlock(hookName) {
|
|
27511
|
+
return hookName === "post-commit" ? legacyPostCommitBlock() : legacyPostRewriteBlock();
|
|
27512
|
+
}
|
|
27513
|
+
function isRecognizedLegacyBlock(hookName, block) {
|
|
27514
|
+
if (hookName === "pre-commit") {
|
|
27515
|
+
const { start, end } = blockMarkers2(PRE_COMMIT);
|
|
27516
|
+
const prefix = `${start}
|
|
27517
|
+
`;
|
|
27518
|
+
const suffix = `
|
|
27519
|
+
${end}`;
|
|
27520
|
+
if (!block.startsWith(prefix) || !block.endsWith(suffix)) return false;
|
|
27521
|
+
return isLegacyPreCommitBody(block.slice(prefix.length, -suffix.length));
|
|
27522
|
+
}
|
|
27523
|
+
const expected = legacyInlineHookBlock(hookName);
|
|
27524
|
+
const normalize2 = (text) => text.replace(PINNED_PACKAGE_VERSION_RE, "@primitive.ai/prim@<version>");
|
|
27525
|
+
return normalize2(block) === normalize2(expected);
|
|
27526
|
+
}
|
|
27527
|
+
|
|
27528
|
+
// src/lib/git-hooks.ts
|
|
27529
|
+
var LEGACY_PRIM_CREATED_MARK = "prim-created-hook";
|
|
27530
|
+
var LEGACY_PRIM_OWNED_HEADER = "# prim post-commit hook \u2014 installed by: prim hooks install (prim-managed-hook)";
|
|
27531
|
+
var LEGACY_GLOBAL_OWNED_HEADER = "# prim global post-commit hook (core.hooksPath) \u2014 managed by prim; do not edit.";
|
|
27532
|
+
var MAX_HOOK_BYTES = 1048576;
|
|
27533
|
+
var GIT_TIMEOUT_MS4 = 1e3;
|
|
27534
|
+
var SUPPORTED_SHEBANGS = /* @__PURE__ */ new Set([
|
|
27535
|
+
"#!/bin/sh",
|
|
27536
|
+
"#!/usr/bin/env sh",
|
|
27537
|
+
"#!/bin/bash",
|
|
27538
|
+
"#!/usr/bin/env bash",
|
|
27539
|
+
"#!/bin/zsh",
|
|
27540
|
+
"#!/usr/bin/env zsh"
|
|
27541
|
+
]);
|
|
27542
|
+
var HUSKY_V9_DISPATCHERS = /* @__PURE__ */ new Set([
|
|
27543
|
+
'#!/usr/bin/env sh\n. "${0%/*}/h"',
|
|
27544
|
+
'#!/usr/bin/env sh\n. "$(dirname "$0")/h"'
|
|
27545
|
+
]);
|
|
27546
|
+
var HUSKY_DIRECT_DISPATCHER = '#!/bin/sh\nexec sh "$(dirname "$0")/../post-commit"\n';
|
|
27547
|
+
var HUSKY_V9_RUNTIME_HASHES = /* @__PURE__ */ new Set([
|
|
27548
|
+
"d6aaa6f3f7c11008c525e649161df4a93152f1e55d0887fa1d7b8d622735d380",
|
|
27549
|
+
"b02cb91cab7125a9e906be723a1b85297fc435a8307d429ae845546b4ee0ecde",
|
|
27550
|
+
"9db5d129c4eb822a773157d98cd8fb8f1b156b85e3193117fefe527ca6d45e91",
|
|
27551
|
+
"3b2cb21335e544b4ebce658ca47af4a69d2c77026cc4d3f8b2227c34d062b207",
|
|
27552
|
+
"548990e1c11285694096184993d907b258d41235461a7ca2551e3b53ff9d3a38",
|
|
27553
|
+
"c029a943acea5d6e2ddbaa271a1fb22c2da55d56b5c6fb59ca804ac9a4018708",
|
|
27554
|
+
"ae3413a8fe2b39372de48bdc9691f42055f089c0c7529834b2fb07a131585b6a",
|
|
27555
|
+
"70200b200ca709b0622784f93839a5b2872333a917a09afddefd7dc2d8cdc680"
|
|
27556
|
+
]);
|
|
27557
|
+
function managedHookSpecFor(hookName) {
|
|
27558
|
+
const { start, end } = blockMarkers(hookName);
|
|
27559
|
+
return {
|
|
27560
|
+
hookName,
|
|
27561
|
+
blockStart: start,
|
|
27562
|
+
blockEnd: end,
|
|
27563
|
+
createdMark: `prim-created-${hookName}-hook`
|
|
27564
|
+
};
|
|
27565
|
+
}
|
|
27566
|
+
var MANAGED_HOOK_SPECS = {
|
|
27567
|
+
"pre-commit": managedHookSpecFor("pre-commit"),
|
|
27568
|
+
"post-commit": managedHookSpecFor("post-commit"),
|
|
27569
|
+
"post-rewrite": managedHookSpecFor("post-rewrite")
|
|
27222
27570
|
};
|
|
27571
|
+
function managedHookSpec(hookName) {
|
|
27572
|
+
return MANAGED_HOOK_SPECS[hookName];
|
|
27573
|
+
}
|
|
27574
|
+
function gitHooksMode(options = {}) {
|
|
27575
|
+
try {
|
|
27576
|
+
const value = execFileSync4(
|
|
27577
|
+
"git",
|
|
27578
|
+
["config", ...options.global ? ["--global"] : [], "--get", "prim.gitHooks"],
|
|
27579
|
+
{
|
|
27580
|
+
cwd: options.cwd,
|
|
27581
|
+
encoding: "utf8",
|
|
27582
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27583
|
+
timeout: GIT_TIMEOUT_MS4
|
|
27584
|
+
}
|
|
27585
|
+
).trim();
|
|
27586
|
+
return value === "manual" ? "manual" : "auto";
|
|
27587
|
+
} catch {
|
|
27588
|
+
return "auto";
|
|
27589
|
+
}
|
|
27590
|
+
}
|
|
27223
27591
|
function legacyFloatingInvocation() {
|
|
27224
27592
|
return `if command -v prim-post-commit >/dev/null 2>&1; then
|
|
27225
27593
|
prim-post-commit || true
|
|
@@ -27236,15 +27604,6 @@ fi
|
|
|
27236
27604
|
`);
|
|
27237
27605
|
}
|
|
27238
27606
|
var PINNED_INVOCATION_RE = /^\{ if \[ -x (?<node>'.*') \] && \[ -f (?<entry>'.*') \]; then \k<node> \k<entry>; else npx --yes -p @primitive\.ai\/prim@(?<version>[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?) prim-post-commit; fi; \} \|\| true$/u;
|
|
27239
|
-
var SHELL_QUOTED_RE = /^'(?:[^']|'"'"')*'$/u;
|
|
27240
|
-
function decodePinnedPath(token) {
|
|
27241
|
-
if (!token || !SHELL_QUOTED_RE.test(token)) return void 0;
|
|
27242
|
-
const value = token.slice(1, -1).replaceAll(`'"'"'`, "'");
|
|
27243
|
-
if (!isAbsolute7(value) || value.includes("\0") || value.includes("\n") || value.includes("\r")) {
|
|
27244
|
-
return void 0;
|
|
27245
|
-
}
|
|
27246
|
-
return value;
|
|
27247
|
-
}
|
|
27248
27607
|
function isPinnedInvocation(value) {
|
|
27249
27608
|
const match = PINNED_INVOCATION_RE.exec(value);
|
|
27250
27609
|
const node = decodePinnedPath(match?.groups?.node);
|
|
@@ -27277,48 +27636,122 @@ function isKnownLegacyGlobalGate(value) {
|
|
|
27277
27636
|
}
|
|
27278
27637
|
function createdScaffold(spec) {
|
|
27279
27638
|
return Buffer.from(`#!/bin/sh
|
|
27280
|
-
${spec.
|
|
27639
|
+
${managedHookBlock(spec.hookName)}
|
|
27281
27640
|
# ${spec.createdMark}
|
|
27282
27641
|
`);
|
|
27283
27642
|
}
|
|
27643
|
+
function oldCreatedPrefixes(spec) {
|
|
27644
|
+
return [
|
|
27645
|
+
Buffer.from(`#!/bin/sh
|
|
27646
|
+
# ${spec.createdMark}
|
|
27647
|
+
|
|
27648
|
+
`),
|
|
27649
|
+
Buffer.from(`#!/bin/sh
|
|
27650
|
+
# ${LEGACY_PRIM_CREATED_MARK}
|
|
27651
|
+
|
|
27652
|
+
`)
|
|
27653
|
+
];
|
|
27654
|
+
}
|
|
27655
|
+
function isOwnedStandalonePreCommit(content) {
|
|
27656
|
+
return content === "#!/bin/sh\nprim-pre-commit\n" || isLegacyOwnedPreCommitScript(content);
|
|
27657
|
+
}
|
|
27658
|
+
function realpathLoose(path) {
|
|
27659
|
+
const missing = [];
|
|
27660
|
+
let current = resolve7(path);
|
|
27661
|
+
for (; ; ) {
|
|
27662
|
+
try {
|
|
27663
|
+
return join16(realpathSync4.native(current), ...missing);
|
|
27664
|
+
} catch {
|
|
27665
|
+
const parent = dirname11(current);
|
|
27666
|
+
if (parent === current) return resolve7(path);
|
|
27667
|
+
missing.unshift(basename2(current));
|
|
27668
|
+
current = parent;
|
|
27669
|
+
}
|
|
27670
|
+
}
|
|
27671
|
+
}
|
|
27672
|
+
function isWithin(path, directory) {
|
|
27673
|
+
const rel = relative3(directory, path);
|
|
27674
|
+
return rel === "" || !isAbsolute8(rel) && rel !== ".." && !rel.startsWith(`..${sep3}`);
|
|
27675
|
+
}
|
|
27676
|
+
function primGitHooksDirectory() {
|
|
27677
|
+
try {
|
|
27678
|
+
return join16(primConfigDirectory(), "git-hooks");
|
|
27679
|
+
} catch {
|
|
27680
|
+
return void 0;
|
|
27681
|
+
}
|
|
27682
|
+
}
|
|
27683
|
+
function otherWorktreeRoots(gitRoot) {
|
|
27684
|
+
try {
|
|
27685
|
+
const self = realpathLoose(gitRoot);
|
|
27686
|
+
return execFileSync4("git", ["worktree", "list", "--porcelain"], {
|
|
27687
|
+
cwd: gitRoot,
|
|
27688
|
+
encoding: "utf8",
|
|
27689
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27690
|
+
timeout: GIT_TIMEOUT_MS4
|
|
27691
|
+
}).split("\n").flatMap((line) => line.startsWith("worktree ") ? [realpathLoose(line.slice(9))] : []).filter((root) => root !== self);
|
|
27692
|
+
} catch {
|
|
27693
|
+
return [];
|
|
27694
|
+
}
|
|
27695
|
+
}
|
|
27696
|
+
function hookLocation(hookPath, dirs) {
|
|
27697
|
+
const path = realpathLoose(hookPath);
|
|
27698
|
+
if (isWithin(path, realpathLoose(dirs.gitDir)) || isWithin(path, realpathLoose(dirs.commonDir))) {
|
|
27699
|
+
return "repository";
|
|
27700
|
+
}
|
|
27701
|
+
const primDir = primGitHooksDirectory();
|
|
27702
|
+
if (primDir !== void 0 && isWithin(path, realpathLoose(primDir))) return "prim";
|
|
27703
|
+
const root = realpathLoose(dirs.gitRoot);
|
|
27704
|
+
if (!isWithin(path, root)) return "external";
|
|
27705
|
+
const nested = otherWorktreeRoots(dirs.gitRoot).filter((other) => isWithin(other, root));
|
|
27706
|
+
return nested.some((other) => isWithin(path, other)) ? "external" : "worktree";
|
|
27707
|
+
}
|
|
27284
27708
|
function safeGitPath(root, value) {
|
|
27285
27709
|
if (value.length === 0 || value !== value.trim() || value.includes("\0") || value.includes("\n") || value.includes("\r")) {
|
|
27286
27710
|
throw new Error("Git returned an unsafe hooks path");
|
|
27287
27711
|
}
|
|
27288
|
-
return
|
|
27712
|
+
return isAbsolute8(value) ? resolve7(value) : resolve7(root, value);
|
|
27289
27713
|
}
|
|
27290
|
-
function
|
|
27714
|
+
function resolveEffectiveGitHook(hookName, cwd = process.cwd()) {
|
|
27291
27715
|
const gitRoot = gitToplevel(cwd);
|
|
27292
27716
|
if (!gitRoot) throw new Error("not a git repository");
|
|
27293
|
-
const
|
|
27294
|
-
|
|
27295
|
-
|
|
27296
|
-
|
|
27297
|
-
|
|
27298
|
-
|
|
27299
|
-
|
|
27300
|
-
|
|
27717
|
+
const lines = execFileSync4(
|
|
27718
|
+
"git",
|
|
27719
|
+
["rev-parse", "--git-path", "hooks", "--absolute-git-dir", "--git-common-dir"],
|
|
27720
|
+
{
|
|
27721
|
+
cwd: gitRoot,
|
|
27722
|
+
encoding: "utf8",
|
|
27723
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27724
|
+
timeout: GIT_TIMEOUT_MS4
|
|
27725
|
+
}
|
|
27726
|
+
).replace(/\r?\n$/u, "").split(/\r?\n/u);
|
|
27727
|
+
if (lines.length !== 3) throw new Error("Git returned unexpected repository paths");
|
|
27728
|
+
const [hooksValue, gitDirValue, commonDirValue] = lines;
|
|
27729
|
+
const hooksDir = safeGitPath(gitRoot, hooksValue);
|
|
27730
|
+
const dirs = {
|
|
27731
|
+
gitRoot,
|
|
27732
|
+
gitDir: safeGitPath(gitRoot, gitDirValue),
|
|
27733
|
+
commonDir: safeGitPath(gitRoot, commonDirValue)
|
|
27734
|
+
};
|
|
27735
|
+
if (basename2(hooksDir) === "_" && basename2(dirname11(hooksDir)) === ".husky") {
|
|
27736
|
+
const hookPath2 = resolve7(dirname11(hooksDir), hookName);
|
|
27301
27737
|
return {
|
|
27302
27738
|
gitRoot,
|
|
27303
27739
|
hooksDir,
|
|
27304
|
-
hookPath:
|
|
27740
|
+
hookPath: hookPath2,
|
|
27305
27741
|
kind: "husky_v9",
|
|
27306
|
-
dispatcherPath:
|
|
27742
|
+
dispatcherPath: resolve7(hooksDir, hookName),
|
|
27743
|
+
location: hookLocation(hookPath2, dirs)
|
|
27307
27744
|
};
|
|
27308
27745
|
}
|
|
27746
|
+
const hookPath = resolve7(hooksDir, hookName);
|
|
27309
27747
|
return {
|
|
27310
27748
|
gitRoot,
|
|
27311
27749
|
hooksDir,
|
|
27312
|
-
hookPath
|
|
27313
|
-
kind: "direct"
|
|
27750
|
+
hookPath,
|
|
27751
|
+
kind: "direct",
|
|
27752
|
+
location: hookLocation(hookPath, dirs)
|
|
27314
27753
|
};
|
|
27315
27754
|
}
|
|
27316
|
-
function resolveEffectivePostCommitHook(cwd = process.cwd()) {
|
|
27317
|
-
return resolveEffectiveManagedHook(POST_COMMIT_MANAGED_HOOK, cwd);
|
|
27318
|
-
}
|
|
27319
|
-
function resolveEffectivePostRewriteHook(cwd = process.cwd()) {
|
|
27320
|
-
return resolveEffectiveManagedHook(POST_REWRITE_MANAGED_HOOK, cwd);
|
|
27321
|
-
}
|
|
27322
27755
|
function assertSafeFile(path, spec) {
|
|
27323
27756
|
if (!existsSync10(path)) return void 0;
|
|
27324
27757
|
const stat = lstatSync5(path);
|
|
@@ -27342,7 +27775,7 @@ function assertUsableHuskyDispatcher(target, spec) {
|
|
|
27342
27775
|
if (!HUSKY_V9_DISPATCHERS.has(normalizedDispatcher)) {
|
|
27343
27776
|
throw new Error(`unrecognized Husky ${spec.hookName} dispatcher: ${target.dispatcherPath}`);
|
|
27344
27777
|
}
|
|
27345
|
-
const huskyRuntime =
|
|
27778
|
+
const huskyRuntime = resolve7(dirname11(target.dispatcherPath), "h");
|
|
27346
27779
|
const runtimeStat = assertSafeFile(huskyRuntime, spec);
|
|
27347
27780
|
const runtime = runtimeStat ? readHookFile(huskyRuntime) : void 0;
|
|
27348
27781
|
const runtimeHash = runtime ? createHash8("sha256").update(runtime).digest("hex") : void 0;
|
|
@@ -27351,7 +27784,7 @@ function assertUsableHuskyDispatcher(target, spec) {
|
|
|
27351
27784
|
}
|
|
27352
27785
|
}
|
|
27353
27786
|
function readHookFile(path) {
|
|
27354
|
-
const value =
|
|
27787
|
+
const value = readFileSync12(path);
|
|
27355
27788
|
return Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
27356
27789
|
}
|
|
27357
27790
|
function decodeHookText(content, spec) {
|
|
@@ -27400,16 +27833,107 @@ function blockRange(content, spec) {
|
|
|
27400
27833
|
}
|
|
27401
27834
|
const start = starts[0];
|
|
27402
27835
|
const end = ends[0] + endMarker.length;
|
|
27403
|
-
const
|
|
27404
|
-
|
|
27836
|
+
const text = content.subarray(start, end).toString("utf8");
|
|
27837
|
+
if (canonicalHookBlock(text) === canonicalHookBlock(managedHookBlock(spec.hookName))) {
|
|
27838
|
+
return { kind: "current", start, end };
|
|
27839
|
+
}
|
|
27840
|
+
const version = hookBlockContractVersion(text);
|
|
27841
|
+
if (version !== void 0 && version > GIT_HOOK_CONTRACT_VERSION) {
|
|
27842
|
+
return { kind: "newer", start, end };
|
|
27843
|
+
}
|
|
27844
|
+
return { kind: "stale", start, end };
|
|
27845
|
+
}
|
|
27846
|
+
var HUSKY_V8_SOURCE_RE = /^\.[ \t]+"\$\(dirname(?:[ \t]+--)?[ \t]+"\$0"\)\/_\/husky\.sh"[ \t]*$/mu;
|
|
27847
|
+
function blockInsertionPoint(content, allowShebangless, spec, huskyV8) {
|
|
27848
|
+
const shebangEnd = shellInsertionPoint(content, allowShebangless, spec);
|
|
27849
|
+
if (!huskyV8) return shebangEnd;
|
|
27850
|
+
const rest = content.subarray(shebangEnd).toString("utf8");
|
|
27851
|
+
const match = HUSKY_V8_SOURCE_RE.exec(rest);
|
|
27852
|
+
if (!match) return shebangEnd;
|
|
27853
|
+
const lineEnd = match.index + match[0].length;
|
|
27854
|
+
const afterLine = rest[lineEnd] === "\n" ? lineEnd + 1 : lineEnd;
|
|
27855
|
+
return shebangEnd + Buffer.byteLength(rest.slice(0, afterLine), "utf8");
|
|
27856
|
+
}
|
|
27857
|
+
var HUSKY_SPEC_MAJOR_RE = /^\s*[~^]?=?\s*v?([0-9]+)(?:\.(?:[0-9]+|[xX*])){0,2}(?:-[0-9A-Za-z.-]+)?\s*$/u;
|
|
27858
|
+
function declaredHuskyMajor(dir) {
|
|
27859
|
+
for (let current = resolve7(dir); ; ) {
|
|
27860
|
+
const manifest = join16(current, "package.json");
|
|
27861
|
+
if (existsSync10(manifest)) {
|
|
27862
|
+
try {
|
|
27863
|
+
if (lstatSync5(manifest).size > MAX_HOOK_BYTES) return void 0;
|
|
27864
|
+
const parsed = JSON.parse(readFileSync12(manifest, "utf8"));
|
|
27865
|
+
const range = parsed.devDependencies?.husky ?? parsed.dependencies?.husky;
|
|
27866
|
+
const major = typeof range === "string" ? HUSKY_SPEC_MAJOR_RE.exec(range)?.[1] : void 0;
|
|
27867
|
+
return major === void 0 ? void 0 : Number(major);
|
|
27868
|
+
} catch {
|
|
27869
|
+
return void 0;
|
|
27870
|
+
}
|
|
27871
|
+
}
|
|
27872
|
+
const parent = dirname11(current);
|
|
27873
|
+
if (parent === current || existsSync10(join16(current, ".git"))) return void 0;
|
|
27874
|
+
current = parent;
|
|
27875
|
+
}
|
|
27876
|
+
}
|
|
27877
|
+
function isHuskyV8Hook(hookPath) {
|
|
27878
|
+
const runtime = join16(dirname11(hookPath), "_", "husky.sh");
|
|
27879
|
+
try {
|
|
27880
|
+
const stat = lstatSync5(runtime);
|
|
27881
|
+
return stat.isFile() && stat.size <= MAX_HOOK_BYTES && readFileSync12(runtime, "utf8").includes("husky_skip_init");
|
|
27882
|
+
} catch (error) {
|
|
27883
|
+
if (errorCode(error) !== "ENOENT") return false;
|
|
27884
|
+
}
|
|
27885
|
+
const major = declaredHuskyMajor(dirname11(dirname11(hookPath)));
|
|
27886
|
+
return major !== void 0 && major <= 8;
|
|
27887
|
+
}
|
|
27888
|
+
var TERMINAL_LINE_RE = /^(?:exit|return)(?:\s|;|$)/u;
|
|
27889
|
+
var REDIRECT_OPERATOR_RE = /^[0-9]*(?:<<?|>>?|<&|>&|<>|>\|)$/u;
|
|
27890
|
+
var REDIRECT_WORD_RE = /^[0-9]*(?:<|>)/u;
|
|
27891
|
+
function isReplacingExec(line) {
|
|
27892
|
+
if (!/^exec(?:\s|$)/u.test(line)) return false;
|
|
27893
|
+
const words = line.slice(4).trim().split(/\s+/u).filter(Boolean);
|
|
27894
|
+
for (let index = 0; index < words.length; index += 1) {
|
|
27895
|
+
const word = words[index];
|
|
27896
|
+
if (word.startsWith("#")) break;
|
|
27897
|
+
if (REDIRECT_OPERATOR_RE.test(word)) {
|
|
27898
|
+
index += 1;
|
|
27899
|
+
continue;
|
|
27900
|
+
}
|
|
27901
|
+
if (!REDIRECT_WORD_RE.test(word)) return true;
|
|
27902
|
+
}
|
|
27903
|
+
return false;
|
|
27904
|
+
}
|
|
27905
|
+
function terminatesBefore(content, from, offset) {
|
|
27906
|
+
const lines = content.subarray(from, offset).toString("utf8").split("\n");
|
|
27907
|
+
return lines.some((line) => TERMINAL_LINE_RE.test(line) || isReplacingExec(line));
|
|
27908
|
+
}
|
|
27909
|
+
function mayTerminateBefore(content, from, offset) {
|
|
27910
|
+
return content.subarray(from, offset).toString("utf8").split(/\n|;|&&|\|\||\||[{()]|\b(?:then|do|else)\b/u).some((part) => {
|
|
27911
|
+
const command = part.trim();
|
|
27912
|
+
return TERMINAL_LINE_RE.test(command) || isReplacingExec(command);
|
|
27913
|
+
});
|
|
27914
|
+
}
|
|
27915
|
+
function withoutRange(content, start, end) {
|
|
27916
|
+
const suffix = content.subarray(end);
|
|
27917
|
+
return Buffer.concat([
|
|
27918
|
+
content.subarray(0, start),
|
|
27919
|
+
suffix.at(0) === 10 ? suffix.subarray(1) : suffix
|
|
27920
|
+
]);
|
|
27921
|
+
}
|
|
27922
|
+
function insertBlock(content, at, spec) {
|
|
27923
|
+
return Buffer.concat([
|
|
27924
|
+
content.subarray(0, at),
|
|
27925
|
+
Buffer.from(managedHookBlock(spec.hookName)),
|
|
27926
|
+
Buffer.from("\n"),
|
|
27927
|
+
content.subarray(at)
|
|
27928
|
+
]);
|
|
27405
27929
|
}
|
|
27406
|
-
function
|
|
27407
|
-
const
|
|
27408
|
-
if (
|
|
27930
|
+
function migratedLegacyContent(existing, allowShebangless, spec, huskyV8) {
|
|
27931
|
+
const text = existing.toString("utf8");
|
|
27932
|
+
if (spec.hookName === "pre-commit" && isOwnedStandalonePreCommit(text)) {
|
|
27409
27933
|
return createdScaffold(spec);
|
|
27410
27934
|
}
|
|
27411
|
-
|
|
27412
|
-
if (
|
|
27935
|
+
if (spec.hookName !== "post-commit") return void 0;
|
|
27936
|
+
if (text.startsWith(`#!/bin/sh
|
|
27413
27937
|
${LEGACY_GLOBAL_OWNED_HEADER}
|
|
27414
27938
|
`)) {
|
|
27415
27939
|
const gateAt = existing.indexOf(Buffer.from('if [ "$(git config --get prim.active'));
|
|
@@ -27424,15 +27948,13 @@ ${LEGACY_GLOBAL_OWNED_HEADER}
|
|
|
27424
27948
|
existing.subarray(0, gateAt),
|
|
27425
27949
|
existing.subarray(chainAt)
|
|
27426
27950
|
]);
|
|
27427
|
-
|
|
27428
|
-
|
|
27429
|
-
withoutLegacyGate
|
|
27430
|
-
|
|
27431
|
-
|
|
27432
|
-
withoutLegacyGate.subarray(shebangEnd2)
|
|
27433
|
-
]);
|
|
27951
|
+
return insertBlock(
|
|
27952
|
+
withoutLegacyGate,
|
|
27953
|
+
blockInsertionPoint(withoutLegacyGate, allowShebangless, spec, huskyV8),
|
|
27954
|
+
spec
|
|
27955
|
+
);
|
|
27434
27956
|
}
|
|
27435
|
-
if (
|
|
27957
|
+
if (text.startsWith(`#!/bin/sh
|
|
27436
27958
|
${LEGACY_PRIM_OWNED_HEADER}
|
|
27437
27959
|
`)) {
|
|
27438
27960
|
const prefixLength = legacyProjectPrefixLength(existing);
|
|
@@ -27441,42 +27963,59 @@ ${LEGACY_PRIM_OWNED_HEADER}
|
|
|
27441
27963
|
}
|
|
27442
27964
|
return Buffer.concat([createdScaffold(spec), existing.subarray(prefixLength)]);
|
|
27443
27965
|
}
|
|
27444
|
-
|
|
27445
|
-
|
|
27446
|
-
|
|
27447
|
-
|
|
27448
|
-
|
|
27449
|
-
|
|
27450
|
-
|
|
27451
|
-
|
|
27452
|
-
|
|
27453
|
-
|
|
27454
|
-
];
|
|
27455
|
-
const oldCreated = oldCreatedPrefixes.some(
|
|
27966
|
+
return void 0;
|
|
27967
|
+
}
|
|
27968
|
+
function mergedContent(existing, allowShebangless, spec, relocate, huskyV8) {
|
|
27969
|
+
if (!existing) return { content: createdScaffold(spec), migrates: false };
|
|
27970
|
+
const range = blockRange(existing, spec);
|
|
27971
|
+
if (range.kind === "absent") {
|
|
27972
|
+
const migrated = migratedLegacyContent(existing, allowShebangless, spec, huskyV8);
|
|
27973
|
+
if (migrated) return { content: migrated, migrates: true };
|
|
27974
|
+
} else if (range.kind !== "newer") {
|
|
27975
|
+
const oldCreated = oldCreatedPrefixes(spec).some(
|
|
27456
27976
|
(prefix) => range.start === prefix.length && existing.subarray(0, prefix.length).equals(prefix)
|
|
27457
27977
|
);
|
|
27458
27978
|
if (oldCreated) {
|
|
27459
27979
|
const suffix = existing.subarray(range.end);
|
|
27460
27980
|
const tail = suffix.at(0) === 10 ? suffix.subarray(1) : suffix;
|
|
27461
|
-
return
|
|
27981
|
+
return {
|
|
27982
|
+
content: Buffer.concat([createdScaffold(spec), tail]),
|
|
27983
|
+
migrates: range.kind === "stale"
|
|
27984
|
+
};
|
|
27462
27985
|
}
|
|
27463
27986
|
}
|
|
27464
27987
|
const shebangEnd = shellInsertionPoint(existing, allowShebangless, spec);
|
|
27465
|
-
|
|
27466
|
-
|
|
27467
|
-
|
|
27468
|
-
|
|
27469
|
-
|
|
27470
|
-
existing
|
|
27471
|
-
|
|
27472
|
-
|
|
27988
|
+
const insertion = blockInsertionPoint(existing, allowShebangless, spec, huskyV8);
|
|
27989
|
+
switch (range.kind) {
|
|
27990
|
+
case "newer":
|
|
27991
|
+
return { content: existing, migrates: false };
|
|
27992
|
+
case "absent":
|
|
27993
|
+
return { content: insertBlock(existing, insertion, spec), migrates: false };
|
|
27994
|
+
case "current":
|
|
27995
|
+
case "stale": {
|
|
27996
|
+
const misplaced = terminatesBefore(existing, shebangEnd, range.start) || range.start < insertion;
|
|
27997
|
+
if (relocate && misplaced) {
|
|
27998
|
+
const without = withoutRange(existing, range.start, range.end);
|
|
27999
|
+
return {
|
|
28000
|
+
content: insertBlock(
|
|
28001
|
+
without,
|
|
28002
|
+
blockInsertionPoint(without, allowShebangless, spec, huskyV8),
|
|
28003
|
+
spec
|
|
28004
|
+
),
|
|
28005
|
+
migrates: range.kind === "stale"
|
|
28006
|
+
};
|
|
28007
|
+
}
|
|
28008
|
+
if (range.kind === "current") return { content: existing, migrates: false };
|
|
28009
|
+
return {
|
|
28010
|
+
content: Buffer.concat([
|
|
28011
|
+
existing.subarray(0, range.start),
|
|
28012
|
+
Buffer.from(managedHookBlock(spec.hookName)),
|
|
28013
|
+
existing.subarray(range.end)
|
|
28014
|
+
]),
|
|
28015
|
+
migrates: true
|
|
28016
|
+
};
|
|
28017
|
+
}
|
|
27473
28018
|
}
|
|
27474
|
-
return Buffer.concat([
|
|
27475
|
-
withoutBlock.subarray(0, shebangEnd),
|
|
27476
|
-
block,
|
|
27477
|
-
Buffer.from("\n"),
|
|
27478
|
-
withoutBlock.subarray(shebangEnd)
|
|
27479
|
-
]);
|
|
27480
28019
|
}
|
|
27481
28020
|
function errorCode(error) {
|
|
27482
28021
|
return typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
@@ -27497,9 +28036,9 @@ function assertTargetUnchanged(path, expected, spec, stat) {
|
|
|
27497
28036
|
}
|
|
27498
28037
|
}
|
|
27499
28038
|
function rewriteHookAtomically(path, next, expected, spec, stat) {
|
|
27500
|
-
const parent =
|
|
27501
|
-
|
|
27502
|
-
const temporary =
|
|
28039
|
+
const parent = dirname11(path);
|
|
28040
|
+
mkdirSync8(parent, { recursive: true });
|
|
28041
|
+
const temporary = resolve7(
|
|
27503
28042
|
parent,
|
|
27504
28043
|
`.${basename2(path)}.prim-${String(process.pid)}-${randomUUID2()}.tmp`
|
|
27505
28044
|
);
|
|
@@ -27525,41 +28064,71 @@ function rewriteHookAtomically(path, next, expected, spec, stat) {
|
|
|
27525
28064
|
}
|
|
27526
28065
|
}
|
|
27527
28066
|
try {
|
|
27528
|
-
|
|
28067
|
+
unlinkSync3(temporary);
|
|
27529
28068
|
} catch (error) {
|
|
27530
28069
|
if (errorCode(error) !== "ENOENT") {
|
|
27531
28070
|
}
|
|
27532
28071
|
}
|
|
27533
28072
|
}
|
|
27534
28073
|
}
|
|
27535
|
-
function
|
|
27536
|
-
|
|
27537
|
-
|
|
27538
|
-
|
|
27539
|
-
|
|
27540
|
-
|
|
27541
|
-
|
|
28074
|
+
function workingLegacyBlock(content, target, spec) {
|
|
28075
|
+
try {
|
|
28076
|
+
const range = blockRange(content, spec);
|
|
28077
|
+
if (range.kind !== "stale") return false;
|
|
28078
|
+
const block = content.subarray(range.start, range.end).toString("utf8");
|
|
28079
|
+
const from = shellInsertionPoint(content, target.kind === "husky_v9", spec);
|
|
28080
|
+
return isRecognizedLegacyBlock(spec.hookName, block) && !mayTerminateBefore(content, from, range.start);
|
|
28081
|
+
} catch {
|
|
28082
|
+
return false;
|
|
28083
|
+
}
|
|
27542
28084
|
}
|
|
27543
|
-
|
|
28085
|
+
var AMBIENT_LOCATIONS = ["repository", "prim"];
|
|
28086
|
+
var EXPLICIT_LOCATIONS = ["repository", "prim", "worktree"];
|
|
28087
|
+
function ensureTarget(target, spec, policy) {
|
|
28088
|
+
const result = { hookName: spec.hookName, path: target.hookPath, kind: target.kind };
|
|
28089
|
+
const skip = (outcome) => ({
|
|
28090
|
+
...result,
|
|
28091
|
+
changed: false,
|
|
28092
|
+
outcome
|
|
28093
|
+
});
|
|
27544
28094
|
assertUsableHuskyDispatcher(target, spec);
|
|
27545
28095
|
const stat = assertSafeFile(target.hookPath, spec);
|
|
27546
28096
|
if (stat && target.kind === "direct" && (stat.mode & 64) === 0) {
|
|
27547
28097
|
throw new Error(`existing ${spec.hookName} hook is not executable: ${target.hookPath}`);
|
|
27548
28098
|
}
|
|
27549
28099
|
const existing = stat ? readHookFile(target.hookPath) : void 0;
|
|
27550
|
-
|
|
27551
|
-
|
|
27552
|
-
|
|
27553
|
-
|
|
27554
|
-
|
|
27555
|
-
|
|
27556
|
-
|
|
27557
|
-
|
|
27558
|
-
|
|
27559
|
-
|
|
27560
|
-
|
|
28100
|
+
if (policy.repairOnly && (!existing || blockRange(existing, spec).kind === "absent")) {
|
|
28101
|
+
return skip("skipped");
|
|
28102
|
+
}
|
|
28103
|
+
const huskyV8 = isHuskyV8Hook(target.hookPath);
|
|
28104
|
+
const next = mergedContent(existing, target.kind === "husky_v9", spec, policy.relocate, huskyV8);
|
|
28105
|
+
if (existing && next.content.equals(existing)) return skip("unchanged");
|
|
28106
|
+
if (!policy.writable(target.location)) {
|
|
28107
|
+
if (target.location === "worktree") return skip("deferred");
|
|
28108
|
+
return skip(existing && workingLegacyBlock(existing, target, spec) ? "kept" : "external");
|
|
28109
|
+
}
|
|
28110
|
+
if (next.migrates && inspectGitHookEntrypoint() !== "ready") return skip("runtime_missing");
|
|
28111
|
+
rewriteHookAtomically(target.hookPath, next.content, existing, spec, stat);
|
|
28112
|
+
return { ...result, changed: true, outcome: existing ? "updated" : "created" };
|
|
28113
|
+
}
|
|
28114
|
+
function ensureEffectiveGitHook(hookName, cwd = process.cwd(), options = {}) {
|
|
28115
|
+
const target = resolveEffectiveGitHook(hookName, cwd);
|
|
28116
|
+
if (gitHooksMode({ cwd: target.gitRoot }) === "manual") {
|
|
28117
|
+
return {
|
|
28118
|
+
hookName,
|
|
28119
|
+
path: target.hookPath,
|
|
28120
|
+
kind: target.kind,
|
|
28121
|
+
changed: false,
|
|
28122
|
+
outcome: "manual"
|
|
28123
|
+
};
|
|
27561
28124
|
}
|
|
27562
|
-
|
|
28125
|
+
const explicit = (options.context ?? "explicit") === "explicit";
|
|
28126
|
+
const allowed = explicit ? EXPLICIT_LOCATIONS : AMBIENT_LOCATIONS;
|
|
28127
|
+
return ensureTarget(target, managedHookSpec(hookName), {
|
|
28128
|
+
writable: (location) => allowed.includes(location),
|
|
28129
|
+
relocate: explicit,
|
|
28130
|
+
repairOnly: options.repairOnly === true
|
|
28131
|
+
});
|
|
27563
28132
|
}
|
|
27564
28133
|
|
|
27565
28134
|
// src/lib/repository-binding.ts
|
|
@@ -27619,7 +28188,7 @@ async function bindRepository(root, options) {
|
|
|
27619
28188
|
}
|
|
27620
28189
|
|
|
27621
28190
|
// src/lib/workspace-id.ts
|
|
27622
|
-
import { execFileSync as
|
|
28191
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
27623
28192
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
27624
28193
|
import {
|
|
27625
28194
|
constants as constants6,
|
|
@@ -27629,15 +28198,15 @@ import {
|
|
|
27629
28198
|
fsyncSync as fsyncSync3,
|
|
27630
28199
|
linkSync,
|
|
27631
28200
|
lstatSync as lstatSync6,
|
|
27632
|
-
mkdirSync as
|
|
28201
|
+
mkdirSync as mkdirSync9,
|
|
27633
28202
|
openSync as openSync5,
|
|
27634
|
-
readFileSync as
|
|
28203
|
+
readFileSync as readFileSync13,
|
|
27635
28204
|
statSync as statSync3,
|
|
27636
|
-
unlinkSync as
|
|
28205
|
+
unlinkSync as unlinkSync4,
|
|
27637
28206
|
writeFileSync as writeFileSync5
|
|
27638
28207
|
} from "fs";
|
|
27639
|
-
import { dirname as
|
|
27640
|
-
var WORKSPACE_ID_RELATIVE_PATH =
|
|
28208
|
+
import { dirname as dirname12, isAbsolute as isAbsolute9, join as join17, resolve as resolve8 } from "path";
|
|
28209
|
+
var WORKSPACE_ID_RELATIVE_PATH = join17("prim", "workspace-id");
|
|
27641
28210
|
var MAX_ANCESTORS = 64;
|
|
27642
28211
|
var MAX_GIT_FILE_BYTES = 4096;
|
|
27643
28212
|
var MAX_ID_FILE_BYTES = 128;
|
|
@@ -27665,7 +28234,7 @@ function resolveWorkspaceIdPath(cwd) {
|
|
|
27665
28234
|
if (fromFilesystem) return { status: "resolved", path: fromFilesystem };
|
|
27666
28235
|
}
|
|
27667
28236
|
try {
|
|
27668
|
-
const output =
|
|
28237
|
+
const output = execFileSync5(
|
|
27669
28238
|
"git",
|
|
27670
28239
|
["rev-parse", "--is-inside-work-tree", "--git-path", WORKSPACE_ID_RELATIVE_PATH],
|
|
27671
28240
|
{
|
|
@@ -27683,7 +28252,7 @@ function resolveWorkspaceIdPath(cwd) {
|
|
|
27683
28252
|
}
|
|
27684
28253
|
return {
|
|
27685
28254
|
status: "resolved",
|
|
27686
|
-
path:
|
|
28255
|
+
path: isAbsolute9(gitPath) ? resolve8(gitPath) : resolve8(cwd, gitPath)
|
|
27687
28256
|
};
|
|
27688
28257
|
} catch (error) {
|
|
27689
28258
|
const code = errorCode2(error);
|
|
@@ -27694,21 +28263,21 @@ function resolveWorkspaceIdPath(cwd) {
|
|
|
27694
28263
|
}
|
|
27695
28264
|
}
|
|
27696
28265
|
function resolveFromFilesystem(cwd) {
|
|
27697
|
-
let current =
|
|
28266
|
+
let current = resolve8(cwd);
|
|
27698
28267
|
for (let depth = 0; depth < MAX_ANCESTORS; depth += 1) {
|
|
27699
|
-
const dotGit =
|
|
28268
|
+
const dotGit = join17(current, ".git");
|
|
27700
28269
|
try {
|
|
27701
28270
|
const stat = lstatSync6(dotGit);
|
|
27702
28271
|
if (stat.isDirectory() && isGitDirectory(dotGit)) {
|
|
27703
|
-
return
|
|
28272
|
+
return join17(dotGit, WORKSPACE_ID_RELATIVE_PATH);
|
|
27704
28273
|
}
|
|
27705
28274
|
if (stat.isFile() && stat.size <= MAX_GIT_FILE_BYTES) {
|
|
27706
|
-
const contents =
|
|
28275
|
+
const contents = readFileSync13(dotGit, "utf8");
|
|
27707
28276
|
const match = /^gitdir: (.+?)(?:\r?\n)?$/.exec(contents);
|
|
27708
28277
|
if (match?.[1]) {
|
|
27709
|
-
const gitDirectory =
|
|
28278
|
+
const gitDirectory = isAbsolute9(match[1]) ? resolve8(match[1]) : resolve8(current, match[1]);
|
|
27710
28279
|
if (isGitDirectory(gitDirectory)) {
|
|
27711
|
-
return
|
|
28280
|
+
return join17(gitDirectory, WORKSPACE_ID_RELATIVE_PATH);
|
|
27712
28281
|
}
|
|
27713
28282
|
}
|
|
27714
28283
|
}
|
|
@@ -27719,7 +28288,7 @@ function resolveFromFilesystem(cwd) {
|
|
|
27719
28288
|
return null;
|
|
27720
28289
|
}
|
|
27721
28290
|
}
|
|
27722
|
-
const parent =
|
|
28291
|
+
const parent = dirname12(current);
|
|
27723
28292
|
if (parent === current) break;
|
|
27724
28293
|
current = parent;
|
|
27725
28294
|
}
|
|
@@ -27727,7 +28296,7 @@ function resolveFromFilesystem(cwd) {
|
|
|
27727
28296
|
}
|
|
27728
28297
|
function isGitDirectory(path) {
|
|
27729
28298
|
try {
|
|
27730
|
-
return statSync3(
|
|
28299
|
+
return statSync3(join17(path, "HEAD")).isFile();
|
|
27731
28300
|
} catch {
|
|
27732
28301
|
return false;
|
|
27733
28302
|
}
|
|
@@ -27746,7 +28315,7 @@ function inspectPath(path) {
|
|
|
27746
28315
|
return { status: "corrupt", path, reason: "invalid_content" };
|
|
27747
28316
|
}
|
|
27748
28317
|
try {
|
|
27749
|
-
const contents =
|
|
28318
|
+
const contents = readFileSync13(path, "utf8");
|
|
27750
28319
|
const workspaceId = contents.endsWith("\n") ? contents.slice(0, -1) : contents;
|
|
27751
28320
|
if ((contents === workspaceId || contents === `${workspaceId}
|
|
27752
28321
|
`) && isCanonicalWorkspaceId(workspaceId)) {
|
|
@@ -27763,13 +28332,13 @@ function inspectPath(path) {
|
|
|
27763
28332
|
}
|
|
27764
28333
|
}
|
|
27765
28334
|
function createWorkspaceId(path) {
|
|
27766
|
-
const parent =
|
|
28335
|
+
const parent = dirname12(path);
|
|
27767
28336
|
const workspaceId = randomUUID3();
|
|
27768
|
-
const temporaryPath =
|
|
28337
|
+
const temporaryPath = join17(parent, `.workspace-id-${process.pid}-${randomUUID3()}.tmp`);
|
|
27769
28338
|
let temporaryCreated = false;
|
|
27770
28339
|
let publishing = false;
|
|
27771
28340
|
try {
|
|
27772
|
-
|
|
28341
|
+
mkdirSync9(parent, { recursive: true, mode: 448 });
|
|
27773
28342
|
chmodSync4(parent, 448);
|
|
27774
28343
|
const fd = openSync5(
|
|
27775
28344
|
temporaryPath,
|
|
@@ -27799,7 +28368,7 @@ function createWorkspaceId(path) {
|
|
|
27799
28368
|
} finally {
|
|
27800
28369
|
if (temporaryCreated) {
|
|
27801
28370
|
try {
|
|
27802
|
-
|
|
28371
|
+
unlinkSync4(temporaryPath);
|
|
27803
28372
|
} catch {
|
|
27804
28373
|
}
|
|
27805
28374
|
}
|
|
@@ -27829,16 +28398,16 @@ import { createHash as createHash9, randomBytes as randomBytes4 } from "crypto";
|
|
|
27829
28398
|
import {
|
|
27830
28399
|
chmodSync as chmodSync5,
|
|
27831
28400
|
existsSync as existsSync11,
|
|
27832
|
-
mkdirSync as
|
|
27833
|
-
readFileSync as
|
|
27834
|
-
readdirSync as
|
|
28401
|
+
mkdirSync as mkdirSync10,
|
|
28402
|
+
readFileSync as readFileSync14,
|
|
28403
|
+
readdirSync as readdirSync5,
|
|
27835
28404
|
renameSync as renameSync4,
|
|
27836
28405
|
rmSync as rmSync6,
|
|
27837
28406
|
statSync as statSync4,
|
|
27838
|
-
unlinkSync as
|
|
28407
|
+
unlinkSync as unlinkSync5,
|
|
27839
28408
|
writeFileSync as writeFileSync6
|
|
27840
28409
|
} from "fs";
|
|
27841
|
-
import { join as
|
|
28410
|
+
import { join as join18 } from "path";
|
|
27842
28411
|
|
|
27843
28412
|
// src/daemon/decision-digest-cache.ts
|
|
27844
28413
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
@@ -27853,6 +28422,27 @@ function isActionablePrivateDraft(row) {
|
|
|
27853
28422
|
return typeof candidate.id === "string" && SAFE_PRIVATE_DRAFT_ID.test(candidate.id) && typeof candidate.intent === "string" && candidate.authorIsSelf === true && candidate.stage === "draft" && (candidate.intentKind === void 0 || candidate.intentKind === "change");
|
|
27854
28423
|
}
|
|
27855
28424
|
|
|
28425
|
+
// src/lib/delivery-backlog.ts
|
|
28426
|
+
var MS_PER_SECOND = 1e3;
|
|
28427
|
+
var MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
28428
|
+
var MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
28429
|
+
var MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
28430
|
+
function isNonNegativeInteger(value) {
|
|
28431
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
28432
|
+
}
|
|
28433
|
+
function isPositiveInteger(value) {
|
|
28434
|
+
return isNonNegativeInteger(value) && value > 0;
|
|
28435
|
+
}
|
|
28436
|
+
function deliveryBacklogState(ingestion) {
|
|
28437
|
+
if (ingestion?.healthy !== false) return void 0;
|
|
28438
|
+
const failures = ingestion.consecutiveFailures;
|
|
28439
|
+
const failedWithoutProgress = failures !== 0 && !(isPositiveInteger(failures) && isPositiveInteger(ingestion.lastFailedDrainAcknowledgedCount));
|
|
28440
|
+
if (failedWithoutProgress) return "failing";
|
|
28441
|
+
const retained = ingestion.lastRetainedBucketCount;
|
|
28442
|
+
if (!isNonNegativeInteger(retained)) return "failing";
|
|
28443
|
+
return retained > 0 ? "retained" : "draining";
|
|
28444
|
+
}
|
|
28445
|
+
|
|
27856
28446
|
// src/lib/presence.ts
|
|
27857
28447
|
var DECISION_ORIGIN = "https://app.getprimitive.ai";
|
|
27858
28448
|
function validDecisionUrl(value) {
|
|
@@ -27927,7 +28517,8 @@ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {
|
|
|
27927
28517
|
if (snapshot.ingestion?.healthy === false) {
|
|
27928
28518
|
const pending = snapshot.ingestion.pendingCount;
|
|
27929
28519
|
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
27930
|
-
|
|
28520
|
+
const delivery = deliveryBacklogState(snapshot.ingestion) === "draining" && snapshot.heartbeat?.healthy === true ? "draining" : "stalled";
|
|
28521
|
+
return `primitive ${version} (daemon: degraded \xB7 delivery: ${delivery}${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
27931
28522
|
}
|
|
27932
28523
|
if (snapshot.heartbeat?.healthy === false) {
|
|
27933
28524
|
return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
@@ -27969,10 +28560,10 @@ var STATE_VERSION = 1;
|
|
|
27969
28560
|
var DRAFT_STATE_VERSION = 2;
|
|
27970
28561
|
var NO_CURSOR = -1;
|
|
27971
28562
|
function stateRoot(namespace) {
|
|
27972
|
-
return
|
|
28563
|
+
return join18(primConfigDirectory(), namespace, "decision-digests");
|
|
27973
28564
|
}
|
|
27974
28565
|
function draftStateRoot(namespace) {
|
|
27975
|
-
return
|
|
28566
|
+
return join18(primConfigDirectory(), namespace, "decision-draft-deliveries");
|
|
27976
28567
|
}
|
|
27977
28568
|
function workspaceFor(cwd) {
|
|
27978
28569
|
try {
|
|
@@ -27984,12 +28575,12 @@ function workspaceFor(cwd) {
|
|
|
27984
28575
|
function statePath(args) {
|
|
27985
28576
|
const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}`;
|
|
27986
28577
|
const key = createHash9("sha256").update(identity).digest("hex");
|
|
27987
|
-
return
|
|
28578
|
+
return join18(stateRoot(args.namespace), `${key}.json`);
|
|
27988
28579
|
}
|
|
27989
28580
|
function draftStatePath(args) {
|
|
27990
28581
|
const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}\0${args.principalId}\0${args.organizationId}\0${args.credentialFingerprint}`;
|
|
27991
28582
|
const key = createHash9("sha256").update(identity).digest("hex");
|
|
27992
|
-
return
|
|
28583
|
+
return join18(draftStateRoot(args.namespace), `${key}.json`);
|
|
27993
28584
|
}
|
|
27994
28585
|
function parseState(value) {
|
|
27995
28586
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -28030,7 +28621,7 @@ function parseDraftState(value) {
|
|
|
28030
28621
|
function readState(path) {
|
|
28031
28622
|
if (!existsSync11(path)) return void 0;
|
|
28032
28623
|
try {
|
|
28033
|
-
return parseState(JSON.parse(
|
|
28624
|
+
return parseState(JSON.parse(readFileSync14(path, "utf8")));
|
|
28034
28625
|
} catch {
|
|
28035
28626
|
return void 0;
|
|
28036
28627
|
}
|
|
@@ -28038,15 +28629,15 @@ function readState(path) {
|
|
|
28038
28629
|
function readDraftState(path) {
|
|
28039
28630
|
if (!existsSync11(path)) return void 0;
|
|
28040
28631
|
try {
|
|
28041
|
-
return parseDraftState(JSON.parse(
|
|
28632
|
+
return parseDraftState(JSON.parse(readFileSync14(path, "utf8")));
|
|
28042
28633
|
} catch {
|
|
28043
28634
|
return void 0;
|
|
28044
28635
|
}
|
|
28045
28636
|
}
|
|
28046
28637
|
function writeStateFile(path, directory, state) {
|
|
28047
|
-
|
|
28638
|
+
mkdirSync10(directory, { recursive: true, mode: 448 });
|
|
28048
28639
|
chmodSync5(directory, 448);
|
|
28049
|
-
const temporary =
|
|
28640
|
+
const temporary = join18(
|
|
28050
28641
|
directory,
|
|
28051
28642
|
`.${path.slice(directory.length + 1)}.tmp-${process.pid}-${randomBytes4(8).toString("hex")}`
|
|
28052
28643
|
);
|
|
@@ -28061,7 +28652,7 @@ function writeStateFile(path, directory, state) {
|
|
|
28061
28652
|
renameSync4(temporary, path);
|
|
28062
28653
|
} finally {
|
|
28063
28654
|
try {
|
|
28064
|
-
|
|
28655
|
+
unlinkSync5(temporary);
|
|
28065
28656
|
} catch {
|
|
28066
28657
|
}
|
|
28067
28658
|
}
|
|
@@ -28070,14 +28661,14 @@ function cleanupStateFiles(directory, now) {
|
|
|
28070
28661
|
if (!existsSync11(directory)) return;
|
|
28071
28662
|
let names;
|
|
28072
28663
|
try {
|
|
28073
|
-
names =
|
|
28664
|
+
names = readdirSync5(directory);
|
|
28074
28665
|
} catch {
|
|
28075
28666
|
return;
|
|
28076
28667
|
}
|
|
28077
28668
|
const files = [];
|
|
28078
28669
|
const residue = [];
|
|
28079
28670
|
for (const name of names) {
|
|
28080
|
-
const path =
|
|
28671
|
+
const path = join18(directory, name);
|
|
28081
28672
|
try {
|
|
28082
28673
|
const mtimeMs = statSync4(path).mtimeMs;
|
|
28083
28674
|
if (name.endsWith(".json")) {
|
|
@@ -28092,7 +28683,7 @@ function cleanupStateFiles(directory, now) {
|
|
|
28092
28683
|
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));
|
|
28093
28684
|
for (const file of [...stale, ...excess]) {
|
|
28094
28685
|
try {
|
|
28095
|
-
|
|
28686
|
+
unlinkSync5(file.path);
|
|
28096
28687
|
} catch {
|
|
28097
28688
|
}
|
|
28098
28689
|
}
|
|
@@ -28383,8 +28974,8 @@ async function prepareCodexContext(options) {
|
|
|
28383
28974
|
|
|
28384
28975
|
// src/hooks/normalize.ts
|
|
28385
28976
|
import { createHash as createHash10 } from "crypto";
|
|
28386
|
-
import { realpathSync as
|
|
28387
|
-
import { isAbsolute as
|
|
28977
|
+
import { realpathSync as realpathSync5 } from "fs";
|
|
28978
|
+
import { isAbsolute as isAbsolute10, resolve as resolve9 } from "path";
|
|
28388
28979
|
var HERMES_EVENT_MAP = {
|
|
28389
28980
|
on_session_start: "SessionStart",
|
|
28390
28981
|
on_session_end: "SessionEnd",
|
|
@@ -28432,16 +29023,16 @@ function cursorToolUseId(value) {
|
|
|
28432
29023
|
function workspaceCandidates(value) {
|
|
28433
29024
|
if (!Array.isArray(value)) return [];
|
|
28434
29025
|
const roots = value.filter((item) => typeof item === "string" && item.length > 0);
|
|
28435
|
-
if (roots.some((root) => !
|
|
29026
|
+
if (roots.some((root) => !isAbsolute10(root))) {
|
|
28436
29027
|
throw new TypeError("Cursor workspace roots must be absolute");
|
|
28437
29028
|
}
|
|
28438
29029
|
return roots;
|
|
28439
29030
|
}
|
|
28440
29031
|
function pathIdentity(path) {
|
|
28441
29032
|
try {
|
|
28442
|
-
return
|
|
29033
|
+
return realpathSync5.native(path);
|
|
28443
29034
|
} catch {
|
|
28444
|
-
return
|
|
29035
|
+
return resolve9(path);
|
|
28445
29036
|
}
|
|
28446
29037
|
}
|
|
28447
29038
|
function resolveCursorCwd(parsed) {
|
|
@@ -28456,17 +29047,17 @@ function resolveCursorCwd(parsed) {
|
|
|
28456
29047
|
}
|
|
28457
29048
|
const expectedRepo = [...repositoryRoots][0];
|
|
28458
29049
|
const rawCwd = typeof parsed.cwd === "string" && parsed.cwd.length > 0 ? parsed.cwd : void 0;
|
|
28459
|
-
if (rawCwd && !
|
|
29050
|
+
if (rawCwd && !isAbsolute10(rawCwd)) {
|
|
28460
29051
|
throw new TypeError("Cursor hook cwd must be absolute");
|
|
28461
29052
|
}
|
|
28462
29053
|
const envelopeCwd = rawCwd ? pathIdentity(rawCwd) : void 0;
|
|
28463
29054
|
const input = typeof parsed.tool_input === "object" && parsed.tool_input !== null && !Array.isArray(parsed.tool_input) ? parsed.tool_input : void 0;
|
|
28464
29055
|
const rawToolCwd = parsed.tool_name === "Shell" && typeof input?.working_directory === "string" ? input.working_directory : void 0;
|
|
28465
29056
|
const base = envelopeCwd ?? (roots.length === 1 ? roots[0] : expectedRepo);
|
|
28466
|
-
if (rawToolCwd && !
|
|
29057
|
+
if (rawToolCwd && !isAbsolute10(rawToolCwd) && !base) {
|
|
28467
29058
|
throw new TypeError("relative Cursor tool cwd has no workspace base");
|
|
28468
29059
|
}
|
|
28469
|
-
const toolCwd = rawToolCwd ? pathIdentity(
|
|
29060
|
+
const toolCwd = rawToolCwd ? pathIdentity(isAbsolute10(rawToolCwd) ? rawToolCwd : resolve9(base, rawToolCwd)) : void 0;
|
|
28470
29061
|
const selected = toolCwd ?? envelopeCwd ?? (roots.length === 1 ? roots[0] : expectedRepo);
|
|
28471
29062
|
if (!selected) {
|
|
28472
29063
|
throw new TypeError("Cursor hook envelope has no unambiguous workspace");
|
|
@@ -28560,7 +29151,11 @@ var CODEX_PRIM_REMINDER = primReminder(CODEX_PRIM_ACTION, CODEX_PRIM_ACTION);
|
|
|
28560
29151
|
async function activeProjectRoot(cwd) {
|
|
28561
29152
|
try {
|
|
28562
29153
|
const root = gitToplevel(cwd);
|
|
28563
|
-
if (!root
|
|
29154
|
+
if (!root) return null;
|
|
29155
|
+
if (!isRepoActiveForCapture(cwd)) {
|
|
29156
|
+
if (repoActiveFlag(root) === "false") clearHooksWired(root);
|
|
29157
|
+
return null;
|
|
29158
|
+
}
|
|
28564
29159
|
let shellGateActive = repoActiveFlag(root) === "true";
|
|
28565
29160
|
if (!shellGateActive) {
|
|
28566
29161
|
try {
|
|
@@ -28570,13 +29165,18 @@ async function activeProjectRoot(cwd) {
|
|
|
28570
29165
|
}
|
|
28571
29166
|
}
|
|
28572
29167
|
if (shellGateActive) {
|
|
28573
|
-
|
|
28574
|
-
|
|
28575
|
-
|
|
28576
|
-
|
|
28577
|
-
|
|
28578
|
-
|
|
28579
|
-
|
|
29168
|
+
for (const hookName of MANAGED_GIT_HOOK_NAMES) {
|
|
29169
|
+
try {
|
|
29170
|
+
const result = ensureEffectiveGitHook(hookName, root, {
|
|
29171
|
+
context: "ambient",
|
|
29172
|
+
repairOnly: hookName === "pre-commit"
|
|
29173
|
+
});
|
|
29174
|
+
if (hookName === "post-commit" && // Manual mode: the user's own wiring is expected to reach prim too.
|
|
29175
|
+
["created", "updated", "unchanged", "manual"].includes(result.outcome)) {
|
|
29176
|
+
recordHooksWired(root, { onlyIfAbsent: true });
|
|
29177
|
+
}
|
|
29178
|
+
} catch {
|
|
29179
|
+
}
|
|
28580
29180
|
}
|
|
28581
29181
|
}
|
|
28582
29182
|
try {
|