@primitive.ai/prim 0.1.0-alpha.93 → 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 +50 -8
- package/SKILL.md +5 -2
- package/dist/{chunk-X64HTHUF.js → chunk-AXQQMX2U.js} +200 -8
- package/dist/chunk-FXF4JWK2.js +162 -0
- package/dist/{chunk-C3VAA5CG.js → chunk-IMK4JKTA.js} +1 -1
- package/dist/{chunk-36TWVNBE.js → chunk-O7YAW7G6.js} +1 -1
- package/dist/{chunk-DCJN6LZH.js → chunk-WUXFLE7X.js} +684 -365
- package/dist/hook-runtime/hooks/post-commit.js +154 -38
- package/dist/hook-runtime/hooks/post-tool-use.js +55 -5
- package/dist/hook-runtime/hooks/pre-tool-use.js +55 -5
- package/dist/hook-runtime/hooks/prim-hook.js +55 -5
- package/dist/hook-runtime/hooks/session-end.js +53 -5
- package/dist/hook-runtime/hooks/session-start.js +843 -277
- package/dist/hook-runtime/index.js +2709 -1880
- package/dist/hooks/post-commit.js +4 -0
- package/dist/hooks/post-tool-use.js +3 -3
- package/dist/hooks/pre-tool-use.js +3 -3
- package/dist/hooks/prim-hook.js +3 -3
- package/dist/hooks/session-end.js +2 -2
- package/dist/hooks/session-start.js +28 -14
- package/dist/index.js +553 -339
- package/package.json +1 -1
|
@@ -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();
|
|
@@ -27089,8 +27282,59 @@ async function fetchAndCacheCollectScope(cwd, dependencies = defaultDependencies
|
|
|
27089
27282
|
return dependencies.writeCached(cwd, response);
|
|
27090
27283
|
}
|
|
27091
27284
|
|
|
27092
|
-
// src/lib/
|
|
27285
|
+
// src/lib/commit-heartbeat.ts
|
|
27093
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";
|
|
27094
27338
|
import { createHash as createHash8, randomUUID as randomUUID2 } from "crypto";
|
|
27095
27339
|
import {
|
|
27096
27340
|
constants as constants5,
|
|
@@ -27099,50 +27343,28 @@ import {
|
|
|
27099
27343
|
fchmodSync as fchmodSync2,
|
|
27100
27344
|
fsyncSync as fsyncSync2,
|
|
27101
27345
|
lstatSync as lstatSync5,
|
|
27102
|
-
mkdirSync as
|
|
27346
|
+
mkdirSync as mkdirSync8,
|
|
27103
27347
|
openSync as openSync4,
|
|
27104
|
-
readFileSync as
|
|
27348
|
+
readFileSync as readFileSync12,
|
|
27349
|
+
realpathSync as realpathSync4,
|
|
27105
27350
|
renameSync as renameSync3,
|
|
27106
|
-
unlinkSync as
|
|
27351
|
+
unlinkSync as unlinkSync3,
|
|
27107
27352
|
writeFileSync as writeFileSync4
|
|
27108
27353
|
} from "fs";
|
|
27109
|
-
import { basename as basename2, dirname as
|
|
27110
|
-
|
|
27111
|
-
|
|
27112
|
-
|
|
27113
|
-
var
|
|
27114
|
-
var
|
|
27115
|
-
|
|
27116
|
-
|
|
27117
|
-
|
|
27118
|
-
|
|
27119
|
-
|
|
27120
|
-
|
|
27121
|
-
|
|
27122
|
-
|
|
27123
|
-
"#!/usr/bin/env sh",
|
|
27124
|
-
"#!/bin/bash",
|
|
27125
|
-
"#!/usr/bin/env bash",
|
|
27126
|
-
"#!/bin/zsh",
|
|
27127
|
-
"#!/usr/bin/env zsh"
|
|
27128
|
-
]);
|
|
27129
|
-
var HUSKY_V9_DISPATCHERS = /* @__PURE__ */ new Set([
|
|
27130
|
-
'#!/usr/bin/env sh\n. "${0%/*}/h"',
|
|
27131
|
-
'#!/usr/bin/env sh\n. "$(dirname "$0")/h"'
|
|
27132
|
-
]);
|
|
27133
|
-
var HUSKY_DIRECT_DISPATCHER = '#!/bin/sh\nexec sh "$(dirname "$0")/../post-commit"\n';
|
|
27134
|
-
var HUSKY_V9_RUNTIME_HASHES = /* @__PURE__ */ new Set([
|
|
27135
|
-
"d6aaa6f3f7c11008c525e649161df4a93152f1e55d0887fa1d7b8d622735d380",
|
|
27136
|
-
"b02cb91cab7125a9e906be723a1b85297fc435a8307d429ae845546b4ee0ecde",
|
|
27137
|
-
"9db5d129c4eb822a773157d98cd8fb8f1b156b85e3193117fefe527ca6d45e91",
|
|
27138
|
-
"3b2cb21335e544b4ebce658ca47af4a69d2c77026cc4d3f8b2227c34d062b207",
|
|
27139
|
-
"548990e1c11285694096184993d907b258d41235461a7ca2551e3b53ff9d3a38",
|
|
27140
|
-
"c029a943acea5d6e2ddbaa271a1fb22c2da55d56b5c6fb59ca804ac9a4018708",
|
|
27141
|
-
"ae3413a8fe2b39372de48bdc9691f42055f089c0c7529834b2fb07a131585b6a",
|
|
27142
|
-
"70200b200ca709b0622784f93839a5b2872333a917a09afddefd7dc2d8cdc680"
|
|
27143
|
-
]);
|
|
27144
|
-
function currentPostCommitBlock() {
|
|
27145
|
-
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 >>>
|
|
27146
27368
|
if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
27147
27369
|
prim_commit_sha=$(git rev-parse --verify HEAD 2>/dev/null) || prim_commit_sha=
|
|
27148
27370
|
case "$prim_commit_sha" in *[!0-9a-f]*|"") prim_commit_sha= ;; esac
|
|
@@ -27171,13 +27393,10 @@ if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
|
27171
27393
|
fi
|
|
27172
27394
|
unset prim_commit_sha
|
|
27173
27395
|
fi
|
|
27174
|
-
|
|
27396
|
+
# <<< prim post-commit hook <<<`;
|
|
27175
27397
|
}
|
|
27176
|
-
function
|
|
27177
|
-
return
|
|
27178
|
-
}
|
|
27179
|
-
function postRewriteHookBlock() {
|
|
27180
|
-
return `${PRIM_POST_REWRITE_BLOCK_START}
|
|
27398
|
+
function legacyPostRewriteBlock() {
|
|
27399
|
+
return `# >>> prim post-rewrite hook >>>
|
|
27181
27400
|
if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
27182
27401
|
case "$1" in amend|rebase) prim_rewrite_source="$1" ;; *) prim_rewrite_source= ;; esac
|
|
27183
27402
|
if [ -n "$prim_rewrite_source" ]; then
|
|
@@ -27216,22 +27435,159 @@ if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
|
|
|
27216
27435
|
fi
|
|
27217
27436
|
unset prim_rewrite_source
|
|
27218
27437
|
fi
|
|
27219
|
-
|
|
27220
|
-
}
|
|
27221
|
-
var
|
|
27222
|
-
|
|
27223
|
-
|
|
27224
|
-
|
|
27225
|
-
|
|
27226
|
-
|
|
27227
|
-
}
|
|
27228
|
-
|
|
27229
|
-
|
|
27230
|
-
|
|
27231
|
-
|
|
27232
|
-
|
|
27233
|
-
|
|
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")
|
|
27234
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
|
+
}
|
|
27235
27591
|
function legacyFloatingInvocation() {
|
|
27236
27592
|
return `if command -v prim-post-commit >/dev/null 2>&1; then
|
|
27237
27593
|
prim-post-commit || true
|
|
@@ -27248,15 +27604,6 @@ fi
|
|
|
27248
27604
|
`);
|
|
27249
27605
|
}
|
|
27250
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;
|
|
27251
|
-
var SHELL_QUOTED_RE = /^'(?:[^']|'"'"')*'$/u;
|
|
27252
|
-
function decodePinnedPath(token) {
|
|
27253
|
-
if (!token || !SHELL_QUOTED_RE.test(token)) return void 0;
|
|
27254
|
-
const value = token.slice(1, -1).replaceAll(`'"'"'`, "'");
|
|
27255
|
-
if (!isAbsolute7(value) || value.includes("\0") || value.includes("\n") || value.includes("\r")) {
|
|
27256
|
-
return void 0;
|
|
27257
|
-
}
|
|
27258
|
-
return value;
|
|
27259
|
-
}
|
|
27260
27607
|
function isPinnedInvocation(value) {
|
|
27261
27608
|
const match = PINNED_INVOCATION_RE.exec(value);
|
|
27262
27609
|
const node = decodePinnedPath(match?.groups?.node);
|
|
@@ -27289,48 +27636,122 @@ function isKnownLegacyGlobalGate(value) {
|
|
|
27289
27636
|
}
|
|
27290
27637
|
function createdScaffold(spec) {
|
|
27291
27638
|
return Buffer.from(`#!/bin/sh
|
|
27292
|
-
${spec.
|
|
27639
|
+
${managedHookBlock(spec.hookName)}
|
|
27293
27640
|
# ${spec.createdMark}
|
|
27294
27641
|
`);
|
|
27295
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
|
+
}
|
|
27296
27708
|
function safeGitPath(root, value) {
|
|
27297
27709
|
if (value.length === 0 || value !== value.trim() || value.includes("\0") || value.includes("\n") || value.includes("\r")) {
|
|
27298
27710
|
throw new Error("Git returned an unsafe hooks path");
|
|
27299
27711
|
}
|
|
27300
|
-
return
|
|
27712
|
+
return isAbsolute8(value) ? resolve7(value) : resolve7(root, value);
|
|
27301
27713
|
}
|
|
27302
|
-
function
|
|
27714
|
+
function resolveEffectiveGitHook(hookName, cwd = process.cwd()) {
|
|
27303
27715
|
const gitRoot = gitToplevel(cwd);
|
|
27304
27716
|
if (!gitRoot) throw new Error("not a git repository");
|
|
27305
|
-
const
|
|
27306
|
-
|
|
27307
|
-
|
|
27308
|
-
|
|
27309
|
-
|
|
27310
|
-
|
|
27311
|
-
|
|
27312
|
-
|
|
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);
|
|
27313
27737
|
return {
|
|
27314
27738
|
gitRoot,
|
|
27315
27739
|
hooksDir,
|
|
27316
|
-
hookPath:
|
|
27740
|
+
hookPath: hookPath2,
|
|
27317
27741
|
kind: "husky_v9",
|
|
27318
|
-
dispatcherPath:
|
|
27742
|
+
dispatcherPath: resolve7(hooksDir, hookName),
|
|
27743
|
+
location: hookLocation(hookPath2, dirs)
|
|
27319
27744
|
};
|
|
27320
27745
|
}
|
|
27746
|
+
const hookPath = resolve7(hooksDir, hookName);
|
|
27321
27747
|
return {
|
|
27322
27748
|
gitRoot,
|
|
27323
27749
|
hooksDir,
|
|
27324
|
-
hookPath
|
|
27325
|
-
kind: "direct"
|
|
27750
|
+
hookPath,
|
|
27751
|
+
kind: "direct",
|
|
27752
|
+
location: hookLocation(hookPath, dirs)
|
|
27326
27753
|
};
|
|
27327
27754
|
}
|
|
27328
|
-
function resolveEffectivePostCommitHook(cwd = process.cwd()) {
|
|
27329
|
-
return resolveEffectiveManagedHook(POST_COMMIT_MANAGED_HOOK, cwd);
|
|
27330
|
-
}
|
|
27331
|
-
function resolveEffectivePostRewriteHook(cwd = process.cwd()) {
|
|
27332
|
-
return resolveEffectiveManagedHook(POST_REWRITE_MANAGED_HOOK, cwd);
|
|
27333
|
-
}
|
|
27334
27755
|
function assertSafeFile(path, spec) {
|
|
27335
27756
|
if (!existsSync10(path)) return void 0;
|
|
27336
27757
|
const stat = lstatSync5(path);
|
|
@@ -27354,7 +27775,7 @@ function assertUsableHuskyDispatcher(target, spec) {
|
|
|
27354
27775
|
if (!HUSKY_V9_DISPATCHERS.has(normalizedDispatcher)) {
|
|
27355
27776
|
throw new Error(`unrecognized Husky ${spec.hookName} dispatcher: ${target.dispatcherPath}`);
|
|
27356
27777
|
}
|
|
27357
|
-
const huskyRuntime =
|
|
27778
|
+
const huskyRuntime = resolve7(dirname11(target.dispatcherPath), "h");
|
|
27358
27779
|
const runtimeStat = assertSafeFile(huskyRuntime, spec);
|
|
27359
27780
|
const runtime = runtimeStat ? readHookFile(huskyRuntime) : void 0;
|
|
27360
27781
|
const runtimeHash = runtime ? createHash8("sha256").update(runtime).digest("hex") : void 0;
|
|
@@ -27363,7 +27784,7 @@ function assertUsableHuskyDispatcher(target, spec) {
|
|
|
27363
27784
|
}
|
|
27364
27785
|
}
|
|
27365
27786
|
function readHookFile(path) {
|
|
27366
|
-
const value =
|
|
27787
|
+
const value = readFileSync12(path);
|
|
27367
27788
|
return Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
27368
27789
|
}
|
|
27369
27790
|
function decodeHookText(content, spec) {
|
|
@@ -27412,16 +27833,107 @@ function blockRange(content, spec) {
|
|
|
27412
27833
|
}
|
|
27413
27834
|
const start = starts[0];
|
|
27414
27835
|
const end = ends[0] + endMarker.length;
|
|
27415
|
-
const
|
|
27416
|
-
|
|
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
|
+
}
|
|
27417
27876
|
}
|
|
27418
|
-
function
|
|
27419
|
-
const
|
|
27420
|
-
|
|
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
|
+
]);
|
|
27929
|
+
}
|
|
27930
|
+
function migratedLegacyContent(existing, allowShebangless, spec, huskyV8) {
|
|
27931
|
+
const text = existing.toString("utf8");
|
|
27932
|
+
if (spec.hookName === "pre-commit" && isOwnedStandalonePreCommit(text)) {
|
|
27421
27933
|
return createdScaffold(spec);
|
|
27422
27934
|
}
|
|
27423
|
-
|
|
27424
|
-
if (
|
|
27935
|
+
if (spec.hookName !== "post-commit") return void 0;
|
|
27936
|
+
if (text.startsWith(`#!/bin/sh
|
|
27425
27937
|
${LEGACY_GLOBAL_OWNED_HEADER}
|
|
27426
27938
|
`)) {
|
|
27427
27939
|
const gateAt = existing.indexOf(Buffer.from('if [ "$(git config --get prim.active'));
|
|
@@ -27436,15 +27948,13 @@ ${LEGACY_GLOBAL_OWNED_HEADER}
|
|
|
27436
27948
|
existing.subarray(0, gateAt),
|
|
27437
27949
|
existing.subarray(chainAt)
|
|
27438
27950
|
]);
|
|
27439
|
-
|
|
27440
|
-
|
|
27441
|
-
withoutLegacyGate
|
|
27442
|
-
|
|
27443
|
-
|
|
27444
|
-
withoutLegacyGate.subarray(shebangEnd2)
|
|
27445
|
-
]);
|
|
27951
|
+
return insertBlock(
|
|
27952
|
+
withoutLegacyGate,
|
|
27953
|
+
blockInsertionPoint(withoutLegacyGate, allowShebangless, spec, huskyV8),
|
|
27954
|
+
spec
|
|
27955
|
+
);
|
|
27446
27956
|
}
|
|
27447
|
-
if (
|
|
27957
|
+
if (text.startsWith(`#!/bin/sh
|
|
27448
27958
|
${LEGACY_PRIM_OWNED_HEADER}
|
|
27449
27959
|
`)) {
|
|
27450
27960
|
const prefixLength = legacyProjectPrefixLength(existing);
|
|
@@ -27453,42 +27963,59 @@ ${LEGACY_PRIM_OWNED_HEADER}
|
|
|
27453
27963
|
}
|
|
27454
27964
|
return Buffer.concat([createdScaffold(spec), existing.subarray(prefixLength)]);
|
|
27455
27965
|
}
|
|
27456
|
-
|
|
27457
|
-
|
|
27458
|
-
|
|
27459
|
-
|
|
27460
|
-
|
|
27461
|
-
|
|
27462
|
-
|
|
27463
|
-
|
|
27464
|
-
|
|
27465
|
-
|
|
27466
|
-
];
|
|
27467
|
-
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(
|
|
27468
27976
|
(prefix) => range.start === prefix.length && existing.subarray(0, prefix.length).equals(prefix)
|
|
27469
27977
|
);
|
|
27470
27978
|
if (oldCreated) {
|
|
27471
27979
|
const suffix = existing.subarray(range.end);
|
|
27472
27980
|
const tail = suffix.at(0) === 10 ? suffix.subarray(1) : suffix;
|
|
27473
|
-
return
|
|
27981
|
+
return {
|
|
27982
|
+
content: Buffer.concat([createdScaffold(spec), tail]),
|
|
27983
|
+
migrates: range.kind === "stale"
|
|
27984
|
+
};
|
|
27474
27985
|
}
|
|
27475
27986
|
}
|
|
27476
27987
|
const shebangEnd = shellInsertionPoint(existing, allowShebangless, spec);
|
|
27477
|
-
|
|
27478
|
-
|
|
27479
|
-
|
|
27480
|
-
|
|
27481
|
-
|
|
27482
|
-
existing
|
|
27483
|
-
|
|
27484
|
-
|
|
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
|
+
}
|
|
27485
28018
|
}
|
|
27486
|
-
return Buffer.concat([
|
|
27487
|
-
withoutBlock.subarray(0, shebangEnd),
|
|
27488
|
-
block,
|
|
27489
|
-
Buffer.from("\n"),
|
|
27490
|
-
withoutBlock.subarray(shebangEnd)
|
|
27491
|
-
]);
|
|
27492
28019
|
}
|
|
27493
28020
|
function errorCode(error) {
|
|
27494
28021
|
return typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
@@ -27509,9 +28036,9 @@ function assertTargetUnchanged(path, expected, spec, stat) {
|
|
|
27509
28036
|
}
|
|
27510
28037
|
}
|
|
27511
28038
|
function rewriteHookAtomically(path, next, expected, spec, stat) {
|
|
27512
|
-
const parent =
|
|
27513
|
-
|
|
27514
|
-
const temporary =
|
|
28039
|
+
const parent = dirname11(path);
|
|
28040
|
+
mkdirSync8(parent, { recursive: true });
|
|
28041
|
+
const temporary = resolve7(
|
|
27515
28042
|
parent,
|
|
27516
28043
|
`.${basename2(path)}.prim-${String(process.pid)}-${randomUUID2()}.tmp`
|
|
27517
28044
|
);
|
|
@@ -27537,41 +28064,71 @@ function rewriteHookAtomically(path, next, expected, spec, stat) {
|
|
|
27537
28064
|
}
|
|
27538
28065
|
}
|
|
27539
28066
|
try {
|
|
27540
|
-
|
|
28067
|
+
unlinkSync3(temporary);
|
|
27541
28068
|
} catch (error) {
|
|
27542
28069
|
if (errorCode(error) !== "ENOENT") {
|
|
27543
28070
|
}
|
|
27544
28071
|
}
|
|
27545
28072
|
}
|
|
27546
28073
|
}
|
|
27547
|
-
function
|
|
27548
|
-
|
|
27549
|
-
|
|
27550
|
-
|
|
27551
|
-
|
|
27552
|
-
|
|
27553
|
-
|
|
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
|
+
}
|
|
27554
28084
|
}
|
|
27555
|
-
|
|
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
|
+
});
|
|
27556
28094
|
assertUsableHuskyDispatcher(target, spec);
|
|
27557
28095
|
const stat = assertSafeFile(target.hookPath, spec);
|
|
27558
28096
|
if (stat && target.kind === "direct" && (stat.mode & 64) === 0) {
|
|
27559
28097
|
throw new Error(`existing ${spec.hookName} hook is not executable: ${target.hookPath}`);
|
|
27560
28098
|
}
|
|
27561
28099
|
const existing = stat ? readHookFile(target.hookPath) : void 0;
|
|
27562
|
-
|
|
27563
|
-
|
|
27564
|
-
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
|
|
27568
|
-
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27572
|
-
|
|
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
|
+
};
|
|
27573
28124
|
}
|
|
27574
|
-
|
|
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
|
+
});
|
|
27575
28132
|
}
|
|
27576
28133
|
|
|
27577
28134
|
// src/lib/repository-binding.ts
|
|
@@ -27631,7 +28188,7 @@ async function bindRepository(root, options) {
|
|
|
27631
28188
|
}
|
|
27632
28189
|
|
|
27633
28190
|
// src/lib/workspace-id.ts
|
|
27634
|
-
import { execFileSync as
|
|
28191
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
27635
28192
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
27636
28193
|
import {
|
|
27637
28194
|
constants as constants6,
|
|
@@ -27641,15 +28198,15 @@ import {
|
|
|
27641
28198
|
fsyncSync as fsyncSync3,
|
|
27642
28199
|
linkSync,
|
|
27643
28200
|
lstatSync as lstatSync6,
|
|
27644
|
-
mkdirSync as
|
|
28201
|
+
mkdirSync as mkdirSync9,
|
|
27645
28202
|
openSync as openSync5,
|
|
27646
|
-
readFileSync as
|
|
28203
|
+
readFileSync as readFileSync13,
|
|
27647
28204
|
statSync as statSync3,
|
|
27648
|
-
unlinkSync as
|
|
28205
|
+
unlinkSync as unlinkSync4,
|
|
27649
28206
|
writeFileSync as writeFileSync5
|
|
27650
28207
|
} from "fs";
|
|
27651
|
-
import { dirname as
|
|
27652
|
-
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");
|
|
27653
28210
|
var MAX_ANCESTORS = 64;
|
|
27654
28211
|
var MAX_GIT_FILE_BYTES = 4096;
|
|
27655
28212
|
var MAX_ID_FILE_BYTES = 128;
|
|
@@ -27677,7 +28234,7 @@ function resolveWorkspaceIdPath(cwd) {
|
|
|
27677
28234
|
if (fromFilesystem) return { status: "resolved", path: fromFilesystem };
|
|
27678
28235
|
}
|
|
27679
28236
|
try {
|
|
27680
|
-
const output =
|
|
28237
|
+
const output = execFileSync5(
|
|
27681
28238
|
"git",
|
|
27682
28239
|
["rev-parse", "--is-inside-work-tree", "--git-path", WORKSPACE_ID_RELATIVE_PATH],
|
|
27683
28240
|
{
|
|
@@ -27695,7 +28252,7 @@ function resolveWorkspaceIdPath(cwd) {
|
|
|
27695
28252
|
}
|
|
27696
28253
|
return {
|
|
27697
28254
|
status: "resolved",
|
|
27698
|
-
path:
|
|
28255
|
+
path: isAbsolute9(gitPath) ? resolve8(gitPath) : resolve8(cwd, gitPath)
|
|
27699
28256
|
};
|
|
27700
28257
|
} catch (error) {
|
|
27701
28258
|
const code = errorCode2(error);
|
|
@@ -27706,21 +28263,21 @@ function resolveWorkspaceIdPath(cwd) {
|
|
|
27706
28263
|
}
|
|
27707
28264
|
}
|
|
27708
28265
|
function resolveFromFilesystem(cwd) {
|
|
27709
|
-
let current =
|
|
28266
|
+
let current = resolve8(cwd);
|
|
27710
28267
|
for (let depth = 0; depth < MAX_ANCESTORS; depth += 1) {
|
|
27711
|
-
const dotGit =
|
|
28268
|
+
const dotGit = join17(current, ".git");
|
|
27712
28269
|
try {
|
|
27713
28270
|
const stat = lstatSync6(dotGit);
|
|
27714
28271
|
if (stat.isDirectory() && isGitDirectory(dotGit)) {
|
|
27715
|
-
return
|
|
28272
|
+
return join17(dotGit, WORKSPACE_ID_RELATIVE_PATH);
|
|
27716
28273
|
}
|
|
27717
28274
|
if (stat.isFile() && stat.size <= MAX_GIT_FILE_BYTES) {
|
|
27718
|
-
const contents =
|
|
28275
|
+
const contents = readFileSync13(dotGit, "utf8");
|
|
27719
28276
|
const match = /^gitdir: (.+?)(?:\r?\n)?$/.exec(contents);
|
|
27720
28277
|
if (match?.[1]) {
|
|
27721
|
-
const gitDirectory =
|
|
28278
|
+
const gitDirectory = isAbsolute9(match[1]) ? resolve8(match[1]) : resolve8(current, match[1]);
|
|
27722
28279
|
if (isGitDirectory(gitDirectory)) {
|
|
27723
|
-
return
|
|
28280
|
+
return join17(gitDirectory, WORKSPACE_ID_RELATIVE_PATH);
|
|
27724
28281
|
}
|
|
27725
28282
|
}
|
|
27726
28283
|
}
|
|
@@ -27731,7 +28288,7 @@ function resolveFromFilesystem(cwd) {
|
|
|
27731
28288
|
return null;
|
|
27732
28289
|
}
|
|
27733
28290
|
}
|
|
27734
|
-
const parent =
|
|
28291
|
+
const parent = dirname12(current);
|
|
27735
28292
|
if (parent === current) break;
|
|
27736
28293
|
current = parent;
|
|
27737
28294
|
}
|
|
@@ -27739,7 +28296,7 @@ function resolveFromFilesystem(cwd) {
|
|
|
27739
28296
|
}
|
|
27740
28297
|
function isGitDirectory(path) {
|
|
27741
28298
|
try {
|
|
27742
|
-
return statSync3(
|
|
28299
|
+
return statSync3(join17(path, "HEAD")).isFile();
|
|
27743
28300
|
} catch {
|
|
27744
28301
|
return false;
|
|
27745
28302
|
}
|
|
@@ -27758,7 +28315,7 @@ function inspectPath(path) {
|
|
|
27758
28315
|
return { status: "corrupt", path, reason: "invalid_content" };
|
|
27759
28316
|
}
|
|
27760
28317
|
try {
|
|
27761
|
-
const contents =
|
|
28318
|
+
const contents = readFileSync13(path, "utf8");
|
|
27762
28319
|
const workspaceId = contents.endsWith("\n") ? contents.slice(0, -1) : contents;
|
|
27763
28320
|
if ((contents === workspaceId || contents === `${workspaceId}
|
|
27764
28321
|
`) && isCanonicalWorkspaceId(workspaceId)) {
|
|
@@ -27775,13 +28332,13 @@ function inspectPath(path) {
|
|
|
27775
28332
|
}
|
|
27776
28333
|
}
|
|
27777
28334
|
function createWorkspaceId(path) {
|
|
27778
|
-
const parent =
|
|
28335
|
+
const parent = dirname12(path);
|
|
27779
28336
|
const workspaceId = randomUUID3();
|
|
27780
|
-
const temporaryPath =
|
|
28337
|
+
const temporaryPath = join17(parent, `.workspace-id-${process.pid}-${randomUUID3()}.tmp`);
|
|
27781
28338
|
let temporaryCreated = false;
|
|
27782
28339
|
let publishing = false;
|
|
27783
28340
|
try {
|
|
27784
|
-
|
|
28341
|
+
mkdirSync9(parent, { recursive: true, mode: 448 });
|
|
27785
28342
|
chmodSync4(parent, 448);
|
|
27786
28343
|
const fd = openSync5(
|
|
27787
28344
|
temporaryPath,
|
|
@@ -27811,7 +28368,7 @@ function createWorkspaceId(path) {
|
|
|
27811
28368
|
} finally {
|
|
27812
28369
|
if (temporaryCreated) {
|
|
27813
28370
|
try {
|
|
27814
|
-
|
|
28371
|
+
unlinkSync4(temporaryPath);
|
|
27815
28372
|
} catch {
|
|
27816
28373
|
}
|
|
27817
28374
|
}
|
|
@@ -27841,16 +28398,16 @@ import { createHash as createHash9, randomBytes as randomBytes4 } from "crypto";
|
|
|
27841
28398
|
import {
|
|
27842
28399
|
chmodSync as chmodSync5,
|
|
27843
28400
|
existsSync as existsSync11,
|
|
27844
|
-
mkdirSync as
|
|
27845
|
-
readFileSync as
|
|
27846
|
-
readdirSync as
|
|
28401
|
+
mkdirSync as mkdirSync10,
|
|
28402
|
+
readFileSync as readFileSync14,
|
|
28403
|
+
readdirSync as readdirSync5,
|
|
27847
28404
|
renameSync as renameSync4,
|
|
27848
28405
|
rmSync as rmSync6,
|
|
27849
28406
|
statSync as statSync4,
|
|
27850
|
-
unlinkSync as
|
|
28407
|
+
unlinkSync as unlinkSync5,
|
|
27851
28408
|
writeFileSync as writeFileSync6
|
|
27852
28409
|
} from "fs";
|
|
27853
|
-
import { join as
|
|
28410
|
+
import { join as join18 } from "path";
|
|
27854
28411
|
|
|
27855
28412
|
// src/daemon/decision-digest-cache.ts
|
|
27856
28413
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
@@ -28003,10 +28560,10 @@ var STATE_VERSION = 1;
|
|
|
28003
28560
|
var DRAFT_STATE_VERSION = 2;
|
|
28004
28561
|
var NO_CURSOR = -1;
|
|
28005
28562
|
function stateRoot(namespace) {
|
|
28006
|
-
return
|
|
28563
|
+
return join18(primConfigDirectory(), namespace, "decision-digests");
|
|
28007
28564
|
}
|
|
28008
28565
|
function draftStateRoot(namespace) {
|
|
28009
|
-
return
|
|
28566
|
+
return join18(primConfigDirectory(), namespace, "decision-draft-deliveries");
|
|
28010
28567
|
}
|
|
28011
28568
|
function workspaceFor(cwd) {
|
|
28012
28569
|
try {
|
|
@@ -28018,12 +28575,12 @@ function workspaceFor(cwd) {
|
|
|
28018
28575
|
function statePath(args) {
|
|
28019
28576
|
const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}`;
|
|
28020
28577
|
const key = createHash9("sha256").update(identity).digest("hex");
|
|
28021
|
-
return
|
|
28578
|
+
return join18(stateRoot(args.namespace), `${key}.json`);
|
|
28022
28579
|
}
|
|
28023
28580
|
function draftStatePath(args) {
|
|
28024
28581
|
const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}\0${args.principalId}\0${args.organizationId}\0${args.credentialFingerprint}`;
|
|
28025
28582
|
const key = createHash9("sha256").update(identity).digest("hex");
|
|
28026
|
-
return
|
|
28583
|
+
return join18(draftStateRoot(args.namespace), `${key}.json`);
|
|
28027
28584
|
}
|
|
28028
28585
|
function parseState(value) {
|
|
28029
28586
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -28064,7 +28621,7 @@ function parseDraftState(value) {
|
|
|
28064
28621
|
function readState(path) {
|
|
28065
28622
|
if (!existsSync11(path)) return void 0;
|
|
28066
28623
|
try {
|
|
28067
|
-
return parseState(JSON.parse(
|
|
28624
|
+
return parseState(JSON.parse(readFileSync14(path, "utf8")));
|
|
28068
28625
|
} catch {
|
|
28069
28626
|
return void 0;
|
|
28070
28627
|
}
|
|
@@ -28072,15 +28629,15 @@ function readState(path) {
|
|
|
28072
28629
|
function readDraftState(path) {
|
|
28073
28630
|
if (!existsSync11(path)) return void 0;
|
|
28074
28631
|
try {
|
|
28075
|
-
return parseDraftState(JSON.parse(
|
|
28632
|
+
return parseDraftState(JSON.parse(readFileSync14(path, "utf8")));
|
|
28076
28633
|
} catch {
|
|
28077
28634
|
return void 0;
|
|
28078
28635
|
}
|
|
28079
28636
|
}
|
|
28080
28637
|
function writeStateFile(path, directory, state) {
|
|
28081
|
-
|
|
28638
|
+
mkdirSync10(directory, { recursive: true, mode: 448 });
|
|
28082
28639
|
chmodSync5(directory, 448);
|
|
28083
|
-
const temporary =
|
|
28640
|
+
const temporary = join18(
|
|
28084
28641
|
directory,
|
|
28085
28642
|
`.${path.slice(directory.length + 1)}.tmp-${process.pid}-${randomBytes4(8).toString("hex")}`
|
|
28086
28643
|
);
|
|
@@ -28095,7 +28652,7 @@ function writeStateFile(path, directory, state) {
|
|
|
28095
28652
|
renameSync4(temporary, path);
|
|
28096
28653
|
} finally {
|
|
28097
28654
|
try {
|
|
28098
|
-
|
|
28655
|
+
unlinkSync5(temporary);
|
|
28099
28656
|
} catch {
|
|
28100
28657
|
}
|
|
28101
28658
|
}
|
|
@@ -28104,14 +28661,14 @@ function cleanupStateFiles(directory, now) {
|
|
|
28104
28661
|
if (!existsSync11(directory)) return;
|
|
28105
28662
|
let names;
|
|
28106
28663
|
try {
|
|
28107
|
-
names =
|
|
28664
|
+
names = readdirSync5(directory);
|
|
28108
28665
|
} catch {
|
|
28109
28666
|
return;
|
|
28110
28667
|
}
|
|
28111
28668
|
const files = [];
|
|
28112
28669
|
const residue = [];
|
|
28113
28670
|
for (const name of names) {
|
|
28114
|
-
const path =
|
|
28671
|
+
const path = join18(directory, name);
|
|
28115
28672
|
try {
|
|
28116
28673
|
const mtimeMs = statSync4(path).mtimeMs;
|
|
28117
28674
|
if (name.endsWith(".json")) {
|
|
@@ -28126,7 +28683,7 @@ function cleanupStateFiles(directory, now) {
|
|
|
28126
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));
|
|
28127
28684
|
for (const file of [...stale, ...excess]) {
|
|
28128
28685
|
try {
|
|
28129
|
-
|
|
28686
|
+
unlinkSync5(file.path);
|
|
28130
28687
|
} catch {
|
|
28131
28688
|
}
|
|
28132
28689
|
}
|
|
@@ -28417,8 +28974,8 @@ async function prepareCodexContext(options) {
|
|
|
28417
28974
|
|
|
28418
28975
|
// src/hooks/normalize.ts
|
|
28419
28976
|
import { createHash as createHash10 } from "crypto";
|
|
28420
|
-
import { realpathSync as
|
|
28421
|
-
import { isAbsolute as
|
|
28977
|
+
import { realpathSync as realpathSync5 } from "fs";
|
|
28978
|
+
import { isAbsolute as isAbsolute10, resolve as resolve9 } from "path";
|
|
28422
28979
|
var HERMES_EVENT_MAP = {
|
|
28423
28980
|
on_session_start: "SessionStart",
|
|
28424
28981
|
on_session_end: "SessionEnd",
|
|
@@ -28466,16 +29023,16 @@ function cursorToolUseId(value) {
|
|
|
28466
29023
|
function workspaceCandidates(value) {
|
|
28467
29024
|
if (!Array.isArray(value)) return [];
|
|
28468
29025
|
const roots = value.filter((item) => typeof item === "string" && item.length > 0);
|
|
28469
|
-
if (roots.some((root) => !
|
|
29026
|
+
if (roots.some((root) => !isAbsolute10(root))) {
|
|
28470
29027
|
throw new TypeError("Cursor workspace roots must be absolute");
|
|
28471
29028
|
}
|
|
28472
29029
|
return roots;
|
|
28473
29030
|
}
|
|
28474
29031
|
function pathIdentity(path) {
|
|
28475
29032
|
try {
|
|
28476
|
-
return
|
|
29033
|
+
return realpathSync5.native(path);
|
|
28477
29034
|
} catch {
|
|
28478
|
-
return
|
|
29035
|
+
return resolve9(path);
|
|
28479
29036
|
}
|
|
28480
29037
|
}
|
|
28481
29038
|
function resolveCursorCwd(parsed) {
|
|
@@ -28490,17 +29047,17 @@ function resolveCursorCwd(parsed) {
|
|
|
28490
29047
|
}
|
|
28491
29048
|
const expectedRepo = [...repositoryRoots][0];
|
|
28492
29049
|
const rawCwd = typeof parsed.cwd === "string" && parsed.cwd.length > 0 ? parsed.cwd : void 0;
|
|
28493
|
-
if (rawCwd && !
|
|
29050
|
+
if (rawCwd && !isAbsolute10(rawCwd)) {
|
|
28494
29051
|
throw new TypeError("Cursor hook cwd must be absolute");
|
|
28495
29052
|
}
|
|
28496
29053
|
const envelopeCwd = rawCwd ? pathIdentity(rawCwd) : void 0;
|
|
28497
29054
|
const input = typeof parsed.tool_input === "object" && parsed.tool_input !== null && !Array.isArray(parsed.tool_input) ? parsed.tool_input : void 0;
|
|
28498
29055
|
const rawToolCwd = parsed.tool_name === "Shell" && typeof input?.working_directory === "string" ? input.working_directory : void 0;
|
|
28499
29056
|
const base = envelopeCwd ?? (roots.length === 1 ? roots[0] : expectedRepo);
|
|
28500
|
-
if (rawToolCwd && !
|
|
29057
|
+
if (rawToolCwd && !isAbsolute10(rawToolCwd) && !base) {
|
|
28501
29058
|
throw new TypeError("relative Cursor tool cwd has no workspace base");
|
|
28502
29059
|
}
|
|
28503
|
-
const toolCwd = rawToolCwd ? pathIdentity(
|
|
29060
|
+
const toolCwd = rawToolCwd ? pathIdentity(isAbsolute10(rawToolCwd) ? rawToolCwd : resolve9(base, rawToolCwd)) : void 0;
|
|
28504
29061
|
const selected = toolCwd ?? envelopeCwd ?? (roots.length === 1 ? roots[0] : expectedRepo);
|
|
28505
29062
|
if (!selected) {
|
|
28506
29063
|
throw new TypeError("Cursor hook envelope has no unambiguous workspace");
|
|
@@ -28594,7 +29151,11 @@ var CODEX_PRIM_REMINDER = primReminder(CODEX_PRIM_ACTION, CODEX_PRIM_ACTION);
|
|
|
28594
29151
|
async function activeProjectRoot(cwd) {
|
|
28595
29152
|
try {
|
|
28596
29153
|
const root = gitToplevel(cwd);
|
|
28597
|
-
if (!root
|
|
29154
|
+
if (!root) return null;
|
|
29155
|
+
if (!isRepoActiveForCapture(cwd)) {
|
|
29156
|
+
if (repoActiveFlag(root) === "false") clearHooksWired(root);
|
|
29157
|
+
return null;
|
|
29158
|
+
}
|
|
28598
29159
|
let shellGateActive = repoActiveFlag(root) === "true";
|
|
28599
29160
|
if (!shellGateActive) {
|
|
28600
29161
|
try {
|
|
@@ -28604,13 +29165,18 @@ async function activeProjectRoot(cwd) {
|
|
|
28604
29165
|
}
|
|
28605
29166
|
}
|
|
28606
29167
|
if (shellGateActive) {
|
|
28607
|
-
|
|
28608
|
-
|
|
28609
|
-
|
|
28610
|
-
|
|
28611
|
-
|
|
28612
|
-
|
|
28613
|
-
|
|
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
|
+
}
|
|
28614
29180
|
}
|
|
28615
29181
|
}
|
|
28616
29182
|
try {
|