@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/dist/index.js CHANGED
@@ -2,23 +2,30 @@
2
2
  import {
3
3
  bindRepository,
4
4
  bindRepositoryWithClient,
5
- ensureEffectivePostCommitHook,
6
- ensureEffectivePostRewriteHook,
7
- ensurePostCommitHookAtPath,
8
- ensurePostRewriteHookAtPath,
9
- inspectEffectivePostCommitHook,
10
- inspectEffectivePostRewriteHook,
5
+ ensureEffectiveGitHook,
6
+ ensureGitHookAtPath,
7
+ externalHookRemedy,
8
+ gitHooksMode,
9
+ hasCurrentHookBlock,
10
+ inspectEffectiveGitHook,
11
+ isLegacyOwnedGlobalHook,
12
+ isOwnedStandalonePreCommit,
11
13
  kickDaemonEnsure,
12
- postCommitHookBlock,
13
- postRewriteHookBlock,
14
14
  printJson,
15
+ projectGitHookTarget,
16
+ projectHooksDir,
15
17
  registerSkillCommands,
18
+ resolveEffectiveGitHook,
16
19
  resolveRepositoryBinding,
17
- uninstallPostCommitHookAtPath,
18
- uninstallPostRewriteHookAtPath,
19
- uninstallProjectPostCommitHook,
20
- uninstallProjectPostRewriteHook
21
- } from "./chunk-DCJN6LZH.js";
20
+ uninstallGitHookAtPath,
21
+ uninstallProjectGitHook
22
+ } from "./chunk-WUXFLE7X.js";
23
+ import {
24
+ POST_COMMIT_GRACE_MS,
25
+ clearHooksWired,
26
+ inspectPostCommitFiring,
27
+ recordHooksWired
28
+ } from "./chunk-FXF4JWK2.js";
22
29
  import {
23
30
  requireDurableIngestAcknowledgement
24
31
  } from "./chunk-TYAHC25F.js";
@@ -78,6 +85,7 @@ import {
78
85
  isMoveIngestRequest,
79
86
  isRepoActiveForCapture,
80
87
  isValidRepoSyncId,
88
+ repoActiveFlag,
81
89
  repoSyncId,
82
90
  repositoryBindingState,
83
91
  setRepoActive,
@@ -101,6 +109,7 @@ import {
101
109
  terminalSafeLine
102
110
  } from "./chunk-G2VMA2QO.js";
103
111
  import {
112
+ MANAGED_GIT_HOOK_NAMES,
104
113
  assertOwnedHookRuntime,
105
114
  atomicWrite,
106
115
  binFile,
@@ -110,15 +119,16 @@ import {
110
119
  entryHasCommand,
111
120
  hookCommandResolutions,
112
121
  hookRuntimePaths,
122
+ inspectGitHookEntrypoint,
113
123
  inspectHookRuntime,
114
124
  inspectHookRuntimeResolutions,
115
125
  inspectHookRuntimeResolutions2,
116
126
  makeRegistration,
127
+ managedHookBlock,
117
128
  packageRoot,
118
129
  packageVersion,
119
130
  performStatus,
120
131
  performStatus2,
121
- pinnedHookCommand,
122
132
  pinnedNpxArgs,
123
133
  projectRoot,
124
134
  readSettings,
@@ -129,7 +139,7 @@ import {
129
139
  stableNodePath,
130
140
  stageHookRuntime,
131
141
  stripCommand
132
- } from "./chunk-X64HTHUF.js";
142
+ } from "./chunk-AXQQMX2U.js";
133
143
  import {
134
144
  canonicalGitRoot,
135
145
  canonicalRepositoryPath,
@@ -157,7 +167,7 @@ import {
157
167
 
158
168
  // src/index.ts
159
169
  import { readFileSync as readFileSync10 } from "fs";
160
- import { dirname as dirname7, resolve as resolve4 } from "path";
170
+ import { dirname as dirname6, resolve as resolve4 } from "path";
161
171
  import { fileURLToPath as fileURLToPath2 } from "url";
162
172
  import { Command } from "commander";
163
173
  import updateNotifier from "update-notifier";
@@ -1352,8 +1362,8 @@ import {
1352
1362
  writeFileSync
1353
1363
  } from "fs";
1354
1364
  import { homedir } from "os";
1355
- import { dirname, join, resolve } from "path";
1356
- import { Option } from "commander";
1365
+ import { join, resolve } from "path";
1366
+ import { Argument, Option } from "commander";
1357
1367
  var PRE_COMMIT = { hookName: "pre-commit", binName: "prim-pre-commit" };
1358
1368
  var POST_COMMIT = { hookName: "post-commit", binName: "prim-post-commit" };
1359
1369
  var POST_REWRITE = { hookName: "post-rewrite", binName: "prim-post-rewrite" };
@@ -1369,84 +1379,15 @@ var PRIM_BLOCK_START = blockMarkers(PRE_COMMIT).start;
1369
1379
  var PRIM_BLOCK_END = blockMarkers(PRE_COMMIT).end;
1370
1380
  var PRIM_MANAGED_MARK = "prim-managed-hook";
1371
1381
  var PRIM_CREATED_MARK = "prim-created-hook";
1372
- function hookShim(binName) {
1373
- return `{ ${pinnedHookCommand(binName)}; } || true`;
1374
- }
1375
- function gatedShim(binName) {
1376
- return `if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
1377
- ${hookShim(binName)}
1378
- fi`;
1379
- }
1380
- function dotGitScript(spec) {
1381
- return `#!/bin/sh
1382
- # prim ${spec.hookName} hook \u2014 installed by: prim hooks install (${PRIM_MANAGED_MARK})
1383
-
1384
- ${hookShim(spec.binName)}
1385
- `;
1386
- }
1387
- function isOwnedStandalonePreCommit(content) {
1388
- if (content === "#!/bin/sh\nprim-pre-commit\n") return true;
1389
- const prefix = `#!/bin/sh
1390
- # prim pre-commit hook \u2014 installed by: prim hooks install (${PRIM_MANAGED_MARK})
1391
-
1392
- `;
1393
- const suffix = "; } || true\n";
1394
- if (!content.startsWith(prefix) || !content.endsWith(suffix)) return false;
1395
- const body = content.slice(prefix.length, -suffix.length);
1396
- if (!body.startsWith("{ ") || /[\r\n]/u.test(body)) return false;
1397
- return commandMatchesBin(body.slice(2), PRE_COMMIT.binName);
1398
- }
1399
- function huskyBlock(spec) {
1400
- if (spec.hookName === POST_COMMIT.hookName) return postCommitHookBlock();
1401
- if (spec.hookName === POST_REWRITE.hookName) return postRewriteHookBlock();
1402
- const { start, end } = blockMarkers(spec);
1403
- return `${start}
1404
- ${hookShim(spec.binName)}
1405
- ${end}`;
1406
- }
1407
- function gatedBlock(spec) {
1408
- if (spec.hookName === POST_COMMIT.hookName) return postCommitHookBlock();
1409
- if (spec.hookName === POST_REWRITE.hookName) return postRewriteHookBlock();
1410
- const { start, end } = blockMarkers(spec);
1411
- return `${start}
1412
- ${gatedShim(spec.binName)}
1413
- ${end}`;
1414
- }
1415
- function primBlockRange(existing, spec) {
1416
- const { start, end } = blockMarkers(spec);
1417
- const starts = existing.split(start).length - 1;
1418
- const ends = existing.split(end).length - 1;
1419
- if (starts === 0 && ends === 0) return null;
1420
- const from = existing.indexOf(start);
1421
- const endAt = existing.indexOf(end);
1422
- if (starts !== 1 || ends !== 1 || endAt < from) {
1423
- throw new Error(`malformed Prim hook markers for ${spec.hookName}`);
1424
- }
1425
- return { start: from, through: endAt + end.length };
1426
- }
1427
- function mergePrimBlock(hookPath, block, spec) {
1428
- if (existsSync2(hookPath)) {
1429
- const existing = readFileSync2(hookPath, "utf-8");
1430
- const range = primBlockRange(existing, spec);
1431
- if (range) {
1432
- const refreshed = existing.slice(0, range.start) + block + existing.slice(range.through);
1433
- if (refreshed === existing) return false;
1434
- writeFileSync(hookPath, refreshed, { mode: 493 });
1435
- return true;
1436
- }
1437
- if (containsPrimHook(existing, spec.binName)) return false;
1438
- const separator = existing.endsWith("\n") ? "\n" : "\n\n";
1439
- writeFileSync(hookPath, `${existing}${separator}${block}
1440
- `, { mode: 493 });
1382
+ function stageGitHookRuntime() {
1383
+ try {
1384
+ stageHookRuntime();
1441
1385
  return true;
1386
+ } catch (error) {
1387
+ const detail = error instanceof Error ? error.message : String(error);
1388
+ console.error(`[prim] git hooks stay inert until the hook runtime is staged: ${detail}`);
1389
+ return false;
1442
1390
  }
1443
- mkdirSync(dirname(hookPath), { recursive: true });
1444
- writeFileSync(hookPath, `#!/bin/sh
1445
- # ${PRIM_CREATED_MARK}
1446
-
1447
- ${block}
1448
- `, { mode: 493 });
1449
- return true;
1450
1391
  }
1451
1392
  function getGitRoot() {
1452
1393
  const root = gitToplevel();
@@ -1473,52 +1414,6 @@ function detectHusky(gitRoot) {
1473
1414
  }
1474
1415
  return false;
1475
1416
  }
1476
- function containsPrimHook(content, binName = PRE_COMMIT.binName) {
1477
- return content.includes(binName);
1478
- }
1479
- function installToHusky(gitRoot, spec = PRE_COMMIT) {
1480
- const hookPath = resolve(gitRoot, ".husky", spec.hookName);
1481
- const existed = existsSync2(hookPath);
1482
- const wrote = mergePrimBlock(hookPath, huskyBlock(spec), spec);
1483
- if (!wrote) {
1484
- console.log(`Prim ${spec.hookName} hook is already installed in .husky/${spec.hookName}.`);
1485
- } else if (existed) {
1486
- console.log(`Appended prim hook block to .husky/${spec.hookName}.`);
1487
- } else {
1488
- console.log(`Created .husky/${spec.hookName} with prim hook block.`);
1489
- }
1490
- }
1491
- function installToDotGit(gitRoot, spec = PRE_COMMIT) {
1492
- const hooksDir = projectHooksDir(gitRoot);
1493
- const hookPath = resolve(hooksDir, spec.hookName);
1494
- if (!existsSync2(hooksDir)) {
1495
- mkdirSync(hooksDir, { recursive: true });
1496
- }
1497
- if (existsSync2(hookPath)) {
1498
- const existing = readFileSync2(hookPath, "utf-8");
1499
- if (containsPrimHook(existing, spec.binName)) {
1500
- console.log(`Prim ${spec.hookName} hook is already installed at ${hookPath}.`);
1501
- return;
1502
- }
1503
- console.log(`A ${spec.hookName} hook already exists at ${hookPath}.`);
1504
- console.log("To replace it, run: prim hooks uninstall && prim hooks install");
1505
- return;
1506
- }
1507
- writeFileSync(hookPath, dotGitScript(spec), { mode: 493 });
1508
- console.log(`Installed ${spec.hookName} hook at ${hookPath}`);
1509
- }
1510
- function projectHooksDir(gitRoot) {
1511
- const commonDir = execFileSync("git", ["rev-parse", "--git-common-dir"], {
1512
- cwd: gitRoot,
1513
- encoding: "utf-8",
1514
- stdio: ["ignore", "pipe", "ignore"],
1515
- timeout: GIT_TIMEOUT_MS
1516
- }).trim();
1517
- if (commonDir === "") {
1518
- throw new Error("git returned an empty common directory");
1519
- }
1520
- return resolve(gitRoot, commonDir, "hooks");
1521
- }
1522
1417
  var PRIM_GIT_HOOKS_DIR = join(primConfigDirectory(), "git-hooks");
1523
1418
  function expandTilde(p) {
1524
1419
  return p.startsWith("~/") ? join(homedir(), p.slice(2)) : p;
@@ -1557,21 +1452,17 @@ var PASSTHROUGH_HOOKS = [
1557
1452
  // with a query protocol, not a lifecycle event, so a bare exec stub is wrong.)
1558
1453
  ];
1559
1454
  function globalHookScript(spec) {
1560
- const chainExit = spec.hookName === PRE_COMMIT.hookName ? "|| exit $?" : "|| true";
1561
- const managedBlock = spec.hookName === POST_COMMIT.hookName ? postCommitHookBlock() : spec.hookName === POST_REWRITE.hookName ? postRewriteHookBlock() : void 0;
1562
- const invocation = managedBlock ?? gatedShim(spec.binName);
1563
- const managedRepoGuard = managedBlock ? ` && ! grep -Fq '${blockMarkers(spec).start.slice(0, -3)}'">>>" "$repo_hook" 2>/dev/null` : "";
1564
- const beforeComments = managedBlock ? `${invocation}
1565
- ` : "";
1566
- const afterComments = managedBlock ? "" : `${invocation}
1567
- `;
1455
+ const preCommit = spec.hookName === PRE_COMMIT.hookName;
1456
+ const chainExit = preCommit ? "|| exit $?" : "|| true";
1457
+ const managedRepoGuard = preCommit ? "" : ` && ! grep -Fq '${blockMarkers(spec).start.slice(0, -3)}'">>>" "$repo_hook" 2>/dev/null`;
1568
1458
  return `#!/bin/sh
1569
- ${beforeComments}# prim global ${spec.hookName} hook (core.hooksPath) \u2014 managed by prim; do not edit.
1459
+ ${managedHookBlock(spec.hookName)}
1460
+ # prim global ${spec.hookName} hook (core.hooksPath) \u2014 managed by prim; do not edit.
1570
1461
  # Install/uninstall: prim hooks install|uninstall --scope user
1571
1462
  # Runs prim only where activated \u2014 'prim enable' (this repo) or
1572
1463
  # 'git config --global prim.active true' (every repo). Chains to the repo's own
1573
1464
  # hook regardless, so inactive repos are unaffected.
1574
- ${afterComments}common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || exit 0
1465
+ common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || exit 0
1575
1466
  repo_hook="$common_dir/hooks/${spec.hookName}"
1576
1467
  if [ -x "$repo_hook" ] && ! grep -q '${PRIM_MANAGED_MARK}' "$repo_hook" 2>/dev/null${managedRepoGuard}; then
1577
1468
  "$repo_hook" "$@" ${chainExit}
@@ -1596,30 +1487,12 @@ function expectedOwnedHookContent(hookName) {
1596
1487
  }
1597
1488
  return null;
1598
1489
  }
1599
- var PINNED_PACKAGE_VERSION_RE = /@primitive\.ai\/prim@[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?/gu;
1600
- function normalizeOwnedGlobalHook(content, hookName) {
1601
- if (hookName === PRE_COMMIT.hookName) {
1602
- const lines = content.split("\n");
1603
- const invocationIndexes = lines.flatMap(
1604
- (line2, index) => line2.startsWith("{ ") && line2.endsWith("; } || true") ? [index] : []
1605
- );
1606
- if (invocationIndexes.length !== 1) return null;
1607
- const invocationIndex = invocationIndexes[0];
1608
- const line = lines[invocationIndex];
1609
- const command = line.slice(2, -"; } || true".length);
1610
- if (!commandMatchesBin(command, PRE_COMMIT.binName)) return null;
1611
- lines[invocationIndex] = "{ <recognized Prim pre-commit invocation>; } || true";
1612
- return lines.join("\n");
1613
- }
1614
- return content.replace(PINNED_PACKAGE_VERSION_RE, "@primitive.ai/prim@<version>");
1615
- }
1616
1490
  function isExpectedOwnedGlobalHook(content, hookName) {
1617
1491
  const expected = expectedOwnedHookContent(hookName);
1618
1492
  if (expected === null) return false;
1619
1493
  if (content === expected) return true;
1620
- const normalized = normalizeOwnedGlobalHook(content, hookName);
1621
- const normalizedExpected = normalizeOwnedGlobalHook(expected, hookName);
1622
- return normalized !== null && normalized === normalizedExpected;
1494
+ const managed = HOOKS.find((spec) => spec.hookName === hookName);
1495
+ return managed !== void 0 && isLegacyOwnedGlobalHook(content, managed.hookName);
1623
1496
  }
1624
1497
  function lstatIfPresent(path) {
1625
1498
  try {
@@ -1654,6 +1527,12 @@ function assertOwnedHooksDirectorySafeToRemove() {
1654
1527
  return entries;
1655
1528
  }
1656
1529
  function writeOwnHooks() {
1530
+ if (inspectGitHookEntrypoint() !== "ready") {
1531
+ console.error(
1532
+ `[prim] did not write prim's global hooks in ${PRIM_GIT_HOOKS_DIR}: the hook runtime they run is not staged yet. Run \`prim setup\` (or an agent install such as \`prim claude install\`) first, then retry.`
1533
+ );
1534
+ return false;
1535
+ }
1657
1536
  if (!existsSync2(PRIM_GIT_HOOKS_DIR)) {
1658
1537
  mkdirSync(PRIM_GIT_HOOKS_DIR, { recursive: true });
1659
1538
  }
@@ -1665,47 +1544,17 @@ function writeOwnHooks() {
1665
1544
  for (const name of PASSTHROUGH_HOOKS) {
1666
1545
  writeFileSync(resolve(PRIM_GIT_HOOKS_DIR, name), passThroughScript(name), { mode: 493 });
1667
1546
  }
1547
+ return true;
1668
1548
  }
1669
1549
  function refreshOwnedGlobalHooks() {
1670
1550
  if (!isOurHooksDir(gitConfigGet("--global"))) return false;
1671
- writeOwnHooks();
1672
- return true;
1551
+ return writeOwnHooks();
1673
1552
  }
1674
1553
  function appendPrimBlock(hookPath, spec) {
1675
- if (spec.hookName === POST_COMMIT.hookName) {
1676
- ensurePostCommitHookAtPath(hookPath);
1677
- return;
1678
- }
1679
- if (spec.hookName === POST_REWRITE.hookName) {
1680
- ensurePostRewriteHookAtPath(hookPath);
1681
- return;
1682
- }
1683
- mergePrimBlock(hookPath, gatedBlock(spec), spec);
1554
+ ensureGitHookAtPath(spec.hookName, hookPath);
1684
1555
  }
1685
1556
  function stripPrimBlock(hookPath, spec) {
1686
- if (spec.hookName === POST_COMMIT.hookName) {
1687
- uninstallPostCommitHookAtPath(hookPath);
1688
- return;
1689
- }
1690
- if (spec.hookName === POST_REWRITE.hookName) {
1691
- uninstallPostRewriteHookAtPath(hookPath);
1692
- return;
1693
- }
1694
- if (!existsSync2(hookPath)) return;
1695
- const existing = readFileSync2(hookPath, "utf-8");
1696
- const primCreated = existing.includes(PRIM_CREATED_MARK);
1697
- const range = primBlockRange(existing, spec);
1698
- if (!range) return;
1699
- const out = (existing.slice(0, range.start) + existing.slice(range.through)).replace(
1700
- /\n{2,}$/,
1701
- "\n"
1702
- );
1703
- const remainder = out.replaceAll("#!/bin/sh", "").replaceAll(`# ${PRIM_CREATED_MARK}`, "").trim();
1704
- if (primCreated && remainder === "") {
1705
- unlinkSync(hookPath);
1706
- return;
1707
- }
1708
- writeFileSync(hookPath, out, { mode: 493 });
1557
+ uninstallGitHookAtPath(spec.hookName, hookPath);
1709
1558
  }
1710
1559
  function planPreCommitRemoval(hookPath, options) {
1711
1560
  if (!existsSync2(hookPath)) return { path: hookPath, action: "missing" };
@@ -1713,7 +1562,8 @@ function planPreCommitRemoval(hookPath, options) {
1713
1562
  if (options.allowOwnedStandalone && isOwnedStandalonePreCommit(existing)) {
1714
1563
  return { path: hookPath, action: "remove-owned-file" };
1715
1564
  }
1716
- if (primBlockRange(existing, PRE_COMMIT)) {
1565
+ const { start, end } = blockMarkers(PRE_COMMIT);
1566
+ if (existing.includes(start) || existing.includes(end)) {
1717
1567
  return { path: hookPath, action: "strip-block" };
1718
1568
  }
1719
1569
  if (existing.includes(PRE_COMMIT.binName) || existing.includes(PRIM_MANAGED_MARK) || existing.includes(PRIM_CREATED_MARK)) {
@@ -1723,79 +1573,172 @@ function planPreCommitRemoval(hookPath, options) {
1723
1573
  }
1724
1574
  return { path: hookPath, action: "leave-foreign" };
1725
1575
  }
1726
- function applyPreCommitRemoval(plan) {
1727
- if (plan.action === "remove-owned-file") {
1728
- unlinkSync(plan.path);
1729
- console.log(`Removed pre-commit hook at ${plan.path}`);
1730
- } else if (plan.action === "strip-block") {
1731
- stripPrimBlock(plan.path, PRE_COMMIT);
1732
- console.log(`Removed the Prim pre-commit block from ${plan.path}.`);
1733
- } else if (plan.action === "leave-foreign") {
1576
+ function applyPreCommitRemoval(plan, husky) {
1577
+ if (plan.action === "missing") return;
1578
+ if (plan.action === "leave-foreign") {
1734
1579
  console.log(`Left pre-commit hook at ${plan.path} untouched (not a Prim hook).`);
1580
+ return;
1735
1581
  }
1582
+ const result = uninstallGitHookAtPath(PRE_COMMIT.hookName, plan.path, { husky });
1583
+ console.log(
1584
+ result.removedFile ? `Removed pre-commit hook at ${plan.path}` : `Removed the Prim pre-commit block from ${plan.path}.`
1585
+ );
1736
1586
  }
1737
1587
  function uninstallProjectHooks(gitRoot) {
1738
- const plans = [
1739
- planPreCommitRemoval(resolve(projectHooksDir(gitRoot), PRE_COMMIT.hookName), {
1588
+ const configured = projectGitHookTarget(PRE_COMMIT.hookName, gitRoot);
1589
+ const destinations = [
1590
+ {
1591
+ path: resolve(projectHooksDir(gitRoot), PRE_COMMIT.hookName),
1592
+ husky: false,
1740
1593
  allowOwnedStandalone: true
1741
- }),
1742
- planPreCommitRemoval(resolve(gitRoot, ".husky", PRE_COMMIT.hookName), {
1594
+ },
1595
+ {
1596
+ path: resolve(gitRoot, ".husky", PRE_COMMIT.hookName),
1597
+ husky: true,
1743
1598
  allowOwnedStandalone: false
1744
- })
1745
- ];
1746
- for (const plan of plans) applyPreCommitRemoval(plan);
1747
- for (const [hookName, uninstall] of [
1748
- [POST_COMMIT.hookName, uninstallProjectPostCommitHook],
1749
- [POST_REWRITE.hookName, uninstallProjectPostRewriteHook]
1750
- ]) {
1751
- const result = uninstall(gitRoot);
1752
- if (!result.changed) {
1753
- console.log(`No Prim ${hookName} block found at ${result.path}.`);
1599
+ },
1600
+ // A shared hooks dir is never stripped on this repository's behalf.
1601
+ ...configured.location === "external" ? [] : [
1602
+ {
1603
+ path: configured.hookPath,
1604
+ husky: configured.kind === "husky_v9",
1605
+ allowOwnedStandalone: false
1606
+ }
1607
+ ]
1608
+ ].filter(
1609
+ (destination, index, all) => all.findIndex((other) => other.path === destination.path) === index
1610
+ );
1611
+ const plans = destinations.map(({ path, husky, allowOwnedStandalone }) => ({
1612
+ plan: planPreCommitRemoval(path, { allowOwnedStandalone }),
1613
+ husky
1614
+ }));
1615
+ for (const { plan, husky } of plans) applyPreCommitRemoval(plan, husky);
1616
+ for (const spec of [POST_COMMIT, POST_REWRITE]) {
1617
+ const result = uninstallProjectGitHook(spec.hookName, gitRoot);
1618
+ if (result.skipped === "external") {
1619
+ console.log(
1620
+ `Left ${result.path} alone: it is outside this repository, and other repositories may run it.`
1621
+ );
1622
+ } else if (!result.changed) {
1623
+ console.log(`No Prim ${spec.hookName} block found at ${result.path}.`);
1754
1624
  } else if (result.removedFile) {
1755
- console.log(`Removed Prim-created ${hookName} hook at ${result.path}.`);
1625
+ console.log(`Removed Prim-created ${spec.hookName} hook at ${result.path}.`);
1756
1626
  } else {
1757
- console.log(`Removed the Prim ${hookName} block from ${result.path}.`);
1627
+ console.log(`Removed the Prim ${spec.hookName} block from ${result.path}.`);
1758
1628
  }
1759
1629
  }
1760
1630
  }
1761
- function installGlobalHooks(opts = {}) {
1631
+ function globalHooksPathIsPrims() {
1632
+ return isOurHooksDir(gitConfigGet("--global"));
1633
+ }
1634
+ var EXIT_GLOBAL_HOOKS_NOT_INSTALLED = 3;
1635
+ var INSTALLED_OUTCOMES = /* @__PURE__ */ new Set([
1636
+ "installed",
1637
+ "refreshed",
1638
+ "added",
1639
+ "present"
1640
+ ]);
1641
+ function planGlobalHooks(opts = {}) {
1762
1642
  const global = gitConfigGet("--global");
1763
- if (global === "") {
1764
- const system = gitConfigGet("--system");
1765
- if (system !== "" && !isOurHooksDir(system)) {
1766
- if (!opts.force) {
1643
+ if (gitHooksMode({ global: true }) === "manual") return { action: "manual", global };
1644
+ if (isOurHooksDir(global)) return { action: "refresh" };
1645
+ if (global !== "") {
1646
+ const dir = expandTilde(global);
1647
+ return HOOKS.every((spec) => hasCurrentHookBlock(spec.hookName, resolve(dir, spec.hookName))) ? { action: "present_in_dir", global } : { action: "add_to_dir", global };
1648
+ }
1649
+ const system = gitConfigGet("--system");
1650
+ if (system !== "" && !isOurHooksDir(system)) {
1651
+ return opts.force ? { action: "set_pointer", overridesSystem: system } : { action: "system_declined", system };
1652
+ }
1653
+ return { action: "set_pointer" };
1654
+ }
1655
+ function planIsMachineWide(plan) {
1656
+ return plan.action === "set_pointer" || plan.action === "add_to_dir";
1657
+ }
1658
+ function installGlobalHooks(opts = {}) {
1659
+ const plan = planGlobalHooks(opts);
1660
+ switch (plan.action) {
1661
+ case "manual":
1662
+ console.error(
1663
+ isOurHooksDir(plan.global) ? `[prim] prim.gitHooks=manual: left hook files alone, but prim's global hooks in ${PRIM_GIT_HOOKS_DIR} are still active. Run \`prim hooks uninstall --scope user\` to stop them.` : "[prim] prim.gitHooks=manual: left hook files and core.hooksPath untouched. Wire prim yourself with `prim hooks snippet <hook>`."
1664
+ );
1665
+ return "manual";
1666
+ case "system_declined":
1667
+ console.error(
1668
+ `[prim] system core.hooksPath is set to ${plan.system}; a --global set would override it, and prim chains only to .git/hooks (not a system dir), so those hooks would stop firing. Skipped. Either place \`prim hooks snippet <hook>\` in ${plan.system} yourself, or override it with \`prim hooks install --scope user --global-hooks-path --force\`. It changes every repository's hooks: an agent must ask the user before running it.`
1669
+ );
1670
+ return "system_declined";
1671
+ case "refresh":
1672
+ stageGitHookRuntime();
1673
+ if (!writeOwnHooks()) return "runtime_missing";
1674
+ console.log(
1675
+ `Prim global git hooks already active (${PRIM_GIT_HOOKS_DIR}); refreshed scripts.`
1676
+ );
1677
+ return "refreshed";
1678
+ case "present_in_dir":
1679
+ if (!hookRuntimeReady()) {
1767
1680
  console.error(
1768
- `[prim] system core.hooksPath is set to ${system}; a --global set would override it, and prim chains only to .git/hooks (not a system dir), so those hooks would stop firing. Skipping \u2014 re-run with --force to override, or run per-repo \`prim hooks install\`.`
1681
+ `[prim] prim's hooks are in ${plan.global}, but the hook runtime they run is not staged, so they do nothing yet. Run \`prim setup\` (or an agent install such as \`prim claude install\`), then retry.`
1769
1682
  );
1770
- return false;
1683
+ return "runtime_missing";
1771
1684
  }
1772
- console.error(
1773
- `[prim] --force: overriding system core.hooksPath ${system}; its hooks will no longer fire (prim chains only to .git/hooks).`
1774
- );
1685
+ console.log(`Prim hooks already present in core.hooksPath dir ${plan.global}.`);
1686
+ return "present";
1687
+ case "set_pointer":
1688
+ case "add_to_dir":
1689
+ break;
1690
+ }
1691
+ if (!hookRuntimeReady()) {
1692
+ console.error(
1693
+ `[prim] did not install prim's global hooks: the hook runtime they run is not staged yet. Run \`prim setup\` (or an agent install such as \`prim claude install\`) first, then retry.`
1694
+ );
1695
+ return "runtime_missing";
1696
+ }
1697
+ if (!opts.machineWide) {
1698
+ console.error(
1699
+ plan.action === "set_pointer" ? plan.overridesSystem !== void 0 ? `[prim] Left git's global hooks alone: setting core.hooksPath would override the system core.hooksPath ${plan.overridesSystem} (its hooks stop firing) and route every repository's hooks through prim. To do that, run \`prim hooks install --scope user --global-hooks-path --force\`. It changes every repository's hooks: an agent must ask the user before running it.` : "[prim] Left git's global hooks alone: setting core.hooksPath would route every repository's hooks through prim. `prim enable` wires each repository you activate. To route every repository through prim instead, run `prim hooks install --scope user --global-hooks-path`. It changes every repository's hooks: an agent must ask the user before running it." : `[prim] Left the hooks in ${plan.global} alone: every repository runs them, so prim edits them only with consent. Repositories that run hooks from there lack prim's hooks until you run \`prim hooks install --scope user --global-hooks-path\`. It changes every repository's hooks: an agent must ask the user before running it.`
1700
+ );
1701
+ return "not_requested";
1702
+ }
1703
+ stageGitHookRuntime();
1704
+ if (plan.action === "add_to_dir") {
1705
+ const dir = expandTilde(plan.global);
1706
+ for (const spec of HOOKS) {
1707
+ appendPrimBlock(resolve(dir, spec.hookName), spec);
1775
1708
  }
1776
- writeOwnHooks();
1777
- execFileSync("git", ["config", "--global", "core.hooksPath", PRIM_GIT_HOOKS_DIR], {
1778
- stdio: ["ignore", "ignore", "pipe"],
1779
- timeout: GIT_TIMEOUT_MS
1780
- });
1781
1709
  console.log(
1782
- `Installed prim global git hooks; set core.hooksPath to ${PRIM_GIT_HOOKS_DIR}. Repos are opt-in: run \`prim enable\` in each repo to capture, or \`git config --global prim.active true\` for all.`
1710
+ `Added prim hooks into existing core.hooksPath dir ${plan.global} (pointer unchanged).`
1783
1711
  );
1784
- return true;
1712
+ return "added";
1785
1713
  }
1786
- if (isOurHooksDir(global)) {
1787
- writeOwnHooks();
1788
- console.log(`Prim global git hooks already active (${PRIM_GIT_HOOKS_DIR}); refreshed scripts.`);
1789
- return true;
1790
- }
1791
- const dir = expandTilde(global);
1792
- for (const spec of HOOKS) {
1793
- appendPrimBlock(resolve(dir, spec.hookName), spec);
1714
+ if (plan.overridesSystem !== void 0) {
1715
+ console.error(
1716
+ `[prim] --force: overriding system core.hooksPath ${plan.overridesSystem}; its hooks will no longer fire (prim chains only to .git/hooks).`
1717
+ );
1794
1718
  }
1719
+ if (!writeOwnHooks()) return "runtime_missing";
1720
+ execFileSync("git", ["config", "--global", "core.hooksPath", PRIM_GIT_HOOKS_DIR], {
1721
+ stdio: ["ignore", "ignore", "pipe"],
1722
+ timeout: GIT_TIMEOUT_MS
1723
+ });
1795
1724
  console.log(
1796
- `Appended prim hooks into existing core.hooksPath dir ${global} (pointer unchanged).`
1725
+ `Installed prim global git hooks; set core.hooksPath to ${PRIM_GIT_HOOKS_DIR}. Repos are opt-in: run \`prim enable\` in each repo to capture, or \`git config --global prim.active true\` for all.`
1726
+ );
1727
+ return "installed";
1728
+ }
1729
+ function hookRuntimeReady() {
1730
+ if (inspectGitHookEntrypoint() === "ready") return true;
1731
+ stageGitHookRuntime();
1732
+ return inspectGitHookEntrypoint() === "ready";
1733
+ }
1734
+ async function consentToMachineWideHooks(plan, flag, nonInteractive) {
1735
+ if (!planIsMachineWide(plan) || !hookRuntimeReady()) return false;
1736
+ if (flag) return true;
1737
+ if (nonInteractive || !process.stdin.isTTY) return false;
1738
+ return askConfirmation(
1739
+ plan.action === "set_pointer" ? plan.overridesSystem !== void 0 ? `Set git's global core.hooksPath so every repository's hooks run through prim? This overrides the system core.hooksPath ${plan.overridesSystem}, whose hooks stop firing.` : "Set git's global core.hooksPath so every repository's hooks run through prim?" : `Add prim's hooks to ${plan.action === "add_to_dir" ? plan.global : "the global hooks dir"}, which every repository runs?`,
1740
+ process.stderr
1797
1741
  );
1798
- return true;
1799
1742
  }
1800
1743
  function uninstallGlobalHooks() {
1801
1744
  const global = gitConfigGet("--global");
@@ -1819,26 +1762,75 @@ function uninstallGlobalHooks() {
1819
1762
  }
1820
1763
  console.log("No prim global git hooks found.");
1821
1764
  }
1765
+ function reportInstall(result, gitRoot) {
1766
+ const { hookName, path } = result;
1767
+ switch (result.outcome) {
1768
+ case "created":
1769
+ console.log(`Installed prim ${hookName} hook at ${path}.`);
1770
+ return;
1771
+ case "updated":
1772
+ console.log(`Updated prim ${hookName} hook at ${path}.`);
1773
+ return;
1774
+ case "unchanged":
1775
+ console.log(`Already current: prim ${hookName} hook at ${path}.`);
1776
+ return;
1777
+ case "external":
1778
+ console.error(
1779
+ `[prim] ${hookName}: Git runs this repository's hooks from ${path}, outside the repository, so prim left it alone. ${externalHookRemedy(hookName, gitRoot)}.`
1780
+ );
1781
+ return;
1782
+ case "kept":
1783
+ console.error(
1784
+ `[prim] ${hookName}: kept the working pre-v1 prim block at ${path}, outside the repository; it still captures. To upgrade it, ${externalHookRemedy(hookName, gitRoot)}.`
1785
+ );
1786
+ return;
1787
+ case "runtime_missing":
1788
+ console.error(
1789
+ `[prim] ${hookName}: kept the existing prim hook at ${path}; it still works, and its replacement would stay inert until the hook runtime is staged.`
1790
+ );
1791
+ return;
1792
+ default:
1793
+ console.error(`[prim] ${hookName}: left ${path} as it was (${result.outcome}).`);
1794
+ }
1795
+ }
1822
1796
  function installHooks(gitRoot, target) {
1823
- if (target === "husky") {
1824
- installToHusky(gitRoot, PRE_COMMIT);
1825
- } else {
1826
- installToDotGit(gitRoot, PRE_COMMIT);
1797
+ let changed = false;
1798
+ if (target !== "effective") {
1799
+ const effectiveDir = resolveEffectiveGitHook(PRE_COMMIT.hookName, gitRoot).hooksDir;
1800
+ const chosenDir = target === "husky" ? resolve(gitRoot, ".husky") : projectHooksDir(gitRoot);
1801
+ if (resolve(effectiveDir) !== chosenDir && resolve(effectiveDir, "..") !== chosenDir) {
1802
+ console.error(
1803
+ `[prim] note: Git runs this repository's hooks from ${effectiveDir}; hooks in ${chosenDir} will not fire unless that directory chains to them.`
1804
+ );
1805
+ }
1827
1806
  }
1828
- const postCommit = ensureEffectivePostCommitHook(gitRoot);
1829
- console.log(
1830
- `${postCommit.changed ? "Installed" : "Refreshed"} effective post-commit hook at ${postCommit.path}.`
1831
- );
1832
- try {
1833
- const postRewrite = ensureEffectivePostRewriteHook(gitRoot);
1834
- console.log(
1835
- `${postRewrite.changed ? "Installed" : "Refreshed"} effective post-rewrite hook at ${postRewrite.path}.`
1836
- );
1837
- } catch (error) {
1838
- const detail = error instanceof Error ? error.message : String(error);
1839
- console.error(`[prim] post-rewrite hook coverage is degraded: ${detail}`);
1807
+ for (const spec of HOOKS) {
1808
+ try {
1809
+ const result = target === "effective" ? ensureEffectiveGitHook(spec.hookName, gitRoot, { context: "explicit" }) : ensureGitHookAtPath(
1810
+ spec.hookName,
1811
+ target === "husky" ? resolve(gitRoot, ".husky", spec.hookName) : resolve(projectHooksDir(gitRoot), spec.hookName),
1812
+ { husky: target === "husky" }
1813
+ );
1814
+ reportInstall(result, gitRoot);
1815
+ if (spec === POST_COMMIT) changed ||= result.changed;
1816
+ if (spec === POST_COMMIT && result.outcome === "external") process.exitCode = 1;
1817
+ } catch (error) {
1818
+ if (spec === POST_COMMIT) throw error;
1819
+ const detail = error instanceof Error ? error.message : String(error);
1820
+ console.error(
1821
+ `[prim] ${spec.hookName} hook coverage is degraded: ${detail}. To wire it by hand, run \`prim hooks snippet ${spec.hookName}\`.`
1822
+ );
1823
+ }
1824
+ }
1825
+ if (repoActiveFlag(gitRoot) === "true") {
1826
+ recordHooksWired(gitRoot, { onlyIfAbsent: !changed });
1840
1827
  }
1841
1828
  }
1829
+ function printManualNote(gitRoot) {
1830
+ console.error(
1831
+ `[prim] prim.gitHooks=manual: left the hook files in ${gitRoot} untouched. Wire each hook with \`prim hooks snippet <hook>\`; \`prim doctor\` reports what it finds.`
1832
+ );
1833
+ }
1842
1834
  function registerHooksCommands(program2) {
1843
1835
  const hooks = program2.command("hooks").description("Manage git hooks");
1844
1836
  hooks.command("install").description(
@@ -1851,20 +1843,47 @@ function registerHooksCommands(program2) {
1851
1843
  ).addOption(
1852
1844
  new Option(
1853
1845
  "--scope <scope>",
1854
- "project (default, this repo) or user (a global core.hooksPath capturing every repo)"
1846
+ "project (default, this repo) or user (prim's global hooks; needs --global-hooks-path or a yes at a terminal to change git's global config)"
1855
1847
  ).choices(["project", "user"])
1856
- ).option("--force", "with --scope user, override a system-level core.hooksPath").action(
1848
+ ).option("--force", "with --scope user, override a system-level core.hooksPath").option(
1849
+ "--global-hooks-path",
1850
+ "with --scope user, change git's global hooks so every repository runs prim's hooks"
1851
+ ).action(
1857
1852
  async (opts, command) => {
1853
+ const globals = command.optsWithGlobals();
1854
+ const nonInteractive = isNonInteractive(globals);
1858
1855
  if (opts.scope === "user") {
1859
- installGlobalHooks({ force: opts.force });
1856
+ const plan = planGlobalHooks({ force: opts.force });
1857
+ const outcome = installGlobalHooks({
1858
+ force: opts.force,
1859
+ machineWide: await consentToMachineWideHooks(
1860
+ plan,
1861
+ opts.globalHooksPath,
1862
+ nonInteractive
1863
+ )
1864
+ });
1865
+ if (outcome === "runtime_missing") {
1866
+ process.exitCode = 1;
1867
+ } else if (opts.globalHooksPath && !INSTALLED_OUTCOMES.has(outcome)) {
1868
+ process.exitCode = EXIT_GLOBAL_HOOKS_NOT_INSTALLED;
1869
+ }
1870
+ return;
1871
+ }
1872
+ if (opts.globalHooksPath) {
1873
+ console.error("[prim] --global-hooks-path applies only with --scope user");
1874
+ process.exitCode = 2;
1860
1875
  return;
1861
1876
  }
1862
- const globals = command.optsWithGlobals();
1863
- const nonInteractive = isNonInteractive(globals);
1864
1877
  const gitRoot = getGitRoot();
1878
+ if (gitHooksMode({ cwd: gitRoot }) === "manual") {
1879
+ printManualNote(gitRoot);
1880
+ return;
1881
+ }
1882
+ stageGitHookRuntime();
1865
1883
  if (opts.target === "husky") return installHooks(gitRoot, "husky");
1866
1884
  if (opts.target === "git-hooks") return installHooks(gitRoot, "git-hooks");
1867
- if (detectHusky(gitRoot)) {
1885
+ const effectiveDir = resolveEffectiveGitHook(PRE_COMMIT.hookName, gitRoot).hooksDir;
1886
+ if (resolve(effectiveDir) === projectHooksDir(gitRoot) && detectHusky(gitRoot)) {
1868
1887
  if (globals.yes) return installHooks(gitRoot, "husky");
1869
1888
  if (nonInteractive) {
1870
1889
  throw new Error(
@@ -1882,10 +1901,25 @@ function registerHooksCommands(program2) {
1882
1901
  } else {
1883
1902
  console.log("Falling back to .git/hooks install.");
1884
1903
  }
1904
+ return installHooks(gitRoot, "git-hooks");
1885
1905
  }
1886
- installHooks(gitRoot, "git-hooks");
1906
+ installHooks(gitRoot, "effective");
1887
1907
  }
1888
1908
  );
1909
+ hooks.command("snippet").description("Print the block that wires one prim git hook (STDOUT), to place by hand").addArgument(new Argument("<hook>", "git hook").choices(MANAGED_GIT_HOOK_NAMES)).action((hook) => {
1910
+ process.stdout.write(`${managedHookBlock(hook)}
1911
+ `);
1912
+ let entrypoint = "<prim config>/prim-git-hook-v1";
1913
+ try {
1914
+ entrypoint = hookRuntimePaths().gitHookEntrypoint;
1915
+ } catch {
1916
+ }
1917
+ process.stderr.write(
1918
+ `[prim] Place this block right after the shebang of your ${hook} hook (in a Husky v8 hook, right after the husky.sh line), before any exit or exec. It runs prim's ${hook} step where prim is installed and enabled, and does nothing elsewhere.${hook === "post-rewrite" ? " It re-arms stdin, so commands after it still read Git's rewrite pairs." : ""}
1919
+ [prim] From a hook manager, run \`${entrypoint} ${hook} "$@"\`${hook === "post-rewrite" ? " with Git's stdin" : ""} instead. Set \`git config prim.gitHooks manual\` so prim leaves your hook files alone.
1920
+ `
1921
+ );
1922
+ });
1889
1923
  hooks.command("uninstall").description(
1890
1924
  "Remove the prim git hooks (.git/hooks, or the global core.hooksPath with --scope user)"
1891
1925
  ).addOption(
@@ -1905,6 +1939,49 @@ function registerHooksCommands(program2) {
1905
1939
  // src/commands/activation.ts
1906
1940
  var CONNECT_PROMPT = "[prim] GitHub repo connection is required to enable repository-specific file attribution, Conflict Gate verification, and commit correlation. Start the GitHub App connection now?";
1907
1941
  var GITHUB_CONNECTION_REQUIRED = "GitHub repo connection is required before using Primitive in this repository. It enables repository-specific file attribution, Conflict Gate verification, and commit correlation. Run `prim github connect` to complete it.";
1942
+ function wireRepositoryHooks(root) {
1943
+ stageGitHookRuntime();
1944
+ refreshOwnedGlobalHooks();
1945
+ const paths = {};
1946
+ let manual = false;
1947
+ let changed = false;
1948
+ for (const hookName of MANAGED_GIT_HOOK_NAMES) {
1949
+ try {
1950
+ const result = ensureEffectiveGitHook(hookName, root, { context: "explicit" });
1951
+ if (result.outcome === "external") {
1952
+ throw new Error(
1953
+ `Git runs this repository's ${hookName} hook from ${result.path}, outside the repository; ${externalHookRemedy(hookName, root)}`
1954
+ );
1955
+ }
1956
+ if (result.outcome === "kept") {
1957
+ process.stderr.write(
1958
+ `[prim] kept the working pre-v1 ${hookName} hook at ${result.path}, outside the repository; to upgrade it, ${externalHookRemedy(hookName, root)}
1959
+ `
1960
+ );
1961
+ }
1962
+ if (result.outcome === "runtime_missing") {
1963
+ process.stderr.write(
1964
+ `[prim] kept the working pre-v1 ${hookName} hook at ${result.path}; it is replaced once the hook runtime is staged
1965
+ `
1966
+ );
1967
+ }
1968
+ paths[hookName] = result.path;
1969
+ manual ||= result.outcome === "manual";
1970
+ if (hookName === "post-commit") changed ||= result.changed;
1971
+ } catch (error) {
1972
+ if (hookName === "post-commit") throw error;
1973
+ const detail = error instanceof Error ? error.message : String(error);
1974
+ process.stderr.write(`[prim] ${hookName} hook coverage is degraded: ${detail}
1975
+ `);
1976
+ }
1977
+ }
1978
+ if (manual) {
1979
+ process.stderr.write(
1980
+ "[prim] prim.gitHooks=manual: left hook files untouched; wire them with `prim hooks snippet <hook>`\n"
1981
+ );
1982
+ }
1983
+ return { paths, changed };
1984
+ }
1908
1985
  async function maybeConnectRepository(root, globals) {
1909
1986
  if (isNonInteractive(globals)) return void 0;
1910
1987
  const approved = Boolean(globals.yes) || await askConfirmation(CONNECT_PROMPT, process.stderr);
@@ -1924,6 +2001,13 @@ async function maybeConnectRepository(root, globals) {
1924
2001
  }
1925
2002
  return void 0;
1926
2003
  }
2004
+ function hookPathFields(paths) {
2005
+ return {
2006
+ ...paths["pre-commit"] ? { preCommitHook: paths["pre-commit"] } : {},
2007
+ ...paths["post-commit"] ? { postCommitHook: paths["post-commit"] } : {},
2008
+ ...paths["post-rewrite"] ? { postRewriteHook: paths["post-rewrite"] } : {}
2009
+ };
2010
+ }
1927
2011
  async function applyActivation(active, globals = {}) {
1928
2012
  const root = gitToplevel();
1929
2013
  if (!root) {
@@ -1936,18 +2020,11 @@ async function applyActivation(active, globals = {}) {
1936
2020
  let phase = active ? "post-commit hook coverage" : "local deactivation";
1937
2021
  try {
1938
2022
  let binding;
1939
- let postCommitHook;
1940
- let postRewriteHook;
2023
+ let hookPaths = {};
2024
+ let hooksChanged = false;
2025
+ const wasActive = repoActiveFlag(root) === "true";
1941
2026
  if (active) {
1942
- refreshOwnedGlobalHooks();
1943
- postCommitHook = ensureEffectivePostCommitHook(root).path;
1944
- try {
1945
- postRewriteHook = ensureEffectivePostRewriteHook(root).path;
1946
- } catch (error) {
1947
- const detail = error instanceof Error ? error.message : String(error);
1948
- process.stderr.write(`[prim] post-rewrite hook coverage is degraded: ${detail}
1949
- `);
1950
- }
2027
+ ({ paths: hookPaths, changed: hooksChanged } = wireRepositoryHooks(root));
1951
2028
  phase = "GitHub repo connection";
1952
2029
  binding = await bindRepository(root);
1953
2030
  }
@@ -1961,8 +2038,7 @@ async function applyActivation(active, globals = {}) {
1961
2038
  repo: root,
1962
2039
  bindingStatus: binding.status,
1963
2040
  repositoryFullName: binding.repositoryFullName,
1964
- ...postCommitHook ? { postCommitHook } : {},
1965
- ...postRewriteHook ? { postRewriteHook } : {}
2041
+ ...hookPathFields(hookPaths)
1966
2042
  });
1967
2043
  process.exitCode = 1;
1968
2044
  return;
@@ -1977,6 +2053,10 @@ async function applyActivation(active, globals = {}) {
1977
2053
  }
1978
2054
  phase = "local activation";
1979
2055
  setRepoActive(root, active);
2056
+ if (!active) clearHooksWired(root);
2057
+ if (active) {
2058
+ recordHooksWired(root, { onlyIfAbsent: wasActive && !hooksChanged });
2059
+ }
1980
2060
  await daemonRequest("statusline_invalidate", {}, { timeoutMs: 250 });
1981
2061
  process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
1982
2062
  `);
@@ -1988,8 +2068,7 @@ async function applyActivation(active, globals = {}) {
1988
2068
  repositoryFullName: binding.repositoryFullName,
1989
2069
  ...binding.status === "connected" ? { repoSyncId: binding.repoSyncId } : {}
1990
2070
  } : {},
1991
- ...postCommitHook ? { postCommitHook } : {},
1992
- ...postRewriteHook ? { postRewriteHook } : {}
2071
+ ...hookPathFields(hookPaths)
1993
2072
  });
1994
2073
  } catch (err) {
1995
2074
  const detail = err instanceof Error ? err.message : String(err);
@@ -2286,7 +2365,7 @@ import {
2286
2365
  writeFileSync as writeFileSync2
2287
2366
  } from "fs";
2288
2367
  import { homedir as homedir3 } from "os";
2289
- import { basename, dirname as dirname2, isAbsolute, join as join4, resolve as resolve2 } from "path";
2368
+ import { basename, dirname, isAbsolute, join as join4, resolve as resolve2 } from "path";
2290
2369
 
2291
2370
  // src/lib/bounded-file.ts
2292
2371
  import { constants, closeSync, fstatSync, lstatSync as lstatSync2, openSync, readSync } from "fs";
@@ -2398,7 +2477,7 @@ function daemonControlPaths(options = {}) {
2398
2477
  };
2399
2478
  }
2400
2479
  function findPackageVersion(entryFile) {
2401
- let dir = dirname2(resolve2(entryFile));
2480
+ let dir = dirname(resolve2(entryFile));
2402
2481
  for (let depth = 0; depth < 6; depth++) {
2403
2482
  const packagePath = join4(dir, "package.json");
2404
2483
  if (existsSync3(packagePath)) {
@@ -2410,7 +2489,7 @@ function findPackageVersion(entryFile) {
2410
2489
  } catch {
2411
2490
  }
2412
2491
  }
2413
- const parent = dirname2(dir);
2492
+ const parent = dirname(dir);
2414
2493
  if (parent === dir) break;
2415
2494
  dir = parent;
2416
2495
  }
@@ -2832,7 +2911,7 @@ function readDaemonLauncherFile(path) {
2832
2911
  function readUsableDaemonConfig(path) {
2833
2912
  const config = readDaemonLauncher(path);
2834
2913
  if (!config) return null;
2835
- const manifest = readRuntimeManifest(join4(dirname2(config.daemonPath), "manifest.json"));
2914
+ const manifest = readRuntimeManifest(join4(dirname(config.daemonPath), "manifest.json"));
2836
2915
  try {
2837
2916
  accessSync(config.nodePath, constants2.X_OK);
2838
2917
  return manifest?.version === config.runtimeVersion && manifest.nodePath === config.nodePath && manifest.daemonSha256 === sha256File(config.daemonPath) ? config : null;
@@ -2926,7 +3005,7 @@ function atomicWrite2(path, content, mode, validate) {
2926
3005
  }
2927
3006
  } catch {
2928
3007
  }
2929
- mkdirSync2(dirname2(path), { recursive: true, mode: RUNTIME_DIR_MODE });
3008
+ mkdirSync2(dirname(path), { recursive: true, mode: RUNTIME_DIR_MODE });
2930
3009
  const temp = `${path}.tmp-${process.pid}-${Date.now()}`;
2931
3010
  writeFileSync2(temp, content, { encoding: "utf8", mode, flag: "wx" });
2932
3011
  try {
@@ -2962,7 +3041,7 @@ function canonicalProspectivePath(path) {
2962
3041
  const suffix = [];
2963
3042
  let ancestor = resolve2(path);
2964
3043
  while (!existsSync3(ancestor)) {
2965
- const parent = dirname2(ancestor);
3044
+ const parent = dirname(ancestor);
2966
3045
  if (parent === ancestor) return resolve2(path);
2967
3046
  suffix.unshift(basename(ancestor));
2968
3047
  ancestor = parent;
@@ -3302,7 +3381,7 @@ async function ensureMacDaemonLocked(options) {
3302
3381
  };
3303
3382
  }
3304
3383
  const servicePaths = launchdPaths(options);
3305
- mkdirSync2(dirname2(servicePaths.logPath), { recursive: true, mode: RUNTIME_DIR_MODE });
3384
+ mkdirSync2(dirname(servicePaths.logPath), { recursive: true, mode: RUNTIME_DIR_MODE });
3306
3385
  writeFileSync2(servicePaths.logPath, "", { mode: RUNTIME_FILE_MODE, flag: "a" });
3307
3386
  chmodSync(servicePaths.logPath, RUNTIME_FILE_MODE);
3308
3387
  const launcher = generateDaemonLauncher(daemonConfig);
@@ -7534,7 +7613,26 @@ async function checkRepositoryBinding() {
7534
7613
  };
7535
7614
  }
7536
7615
  }
7537
- function classifyManagedHook(hookName, inspection) {
7616
+ function managedHookRemedy(inspection) {
7617
+ switch (inspection.reason) {
7618
+ case "missing":
7619
+ case "missing_block":
7620
+ case "stale_block":
7621
+ case "legacy_block":
7622
+ case "unreachable_block":
7623
+ case "misplaced_block":
7624
+ if (inspection.location === "prim") return "run `prim enable` to refresh prim's global hooks";
7625
+ if (inspection.location === "external") {
7626
+ return `outside this repository \u2014 ${externalHookRemedy(inspection.hookName, inspection.gitRoot)}`;
7627
+ }
7628
+ return "run `prim hooks install`";
7629
+ case "entrypoint_missing":
7630
+ return "run `prim enable` to stage the hook runtime";
7631
+ default:
7632
+ return void 0;
7633
+ }
7634
+ }
7635
+ function classifyManagedHook(hookName, inspection, firing) {
7538
7636
  if (inspection.covered) {
7539
7637
  return {
7540
7638
  name: hookName,
@@ -7542,15 +7640,78 @@ function classifyManagedHook(hookName, inspection) {
7542
7640
  detail: `effective and executable \xB7 ${inspection.kind} \xB7 ${inspection.hookPath}`
7543
7641
  };
7544
7642
  }
7643
+ const reason = inspection.reason ?? "uncovered";
7644
+ if (inspection.mode === "manual") {
7645
+ if (hookName === "post-commit" && firing?.state === "fired") {
7646
+ return {
7647
+ name: hookName,
7648
+ status: "ok",
7649
+ detail: `manual (prim.gitHooks=manual) \xB7 ran for the latest commit \xB7 ${inspection.hookPath}`
7650
+ };
7651
+ }
7652
+ return {
7653
+ name: hookName,
7654
+ status: "warn",
7655
+ detail: `manual (prim.gitHooks=manual) \xB7 ${reason} \xB7 wire with \`prim hooks snippet ${hookName}\` \xB7 ${inspection.hookPath}`
7656
+ };
7657
+ }
7658
+ const remedy = managedHookRemedy(inspection);
7659
+ const stillCaptures = reason === "legacy_block" || reason === "misplaced_block";
7545
7660
  return {
7546
7661
  name: hookName,
7547
- status: "fail",
7548
- detail: `${inspection.reason ?? "uncovered"} \xB7 ${inspection.hookPath}`
7662
+ status: hookName === "pre-commit" || stillCaptures ? "warn" : "fail",
7663
+ detail: `${reason}${remedy ? ` \xB7 ${remedy}` : ""} \xB7 ${inspection.hookPath}`
7549
7664
  };
7550
7665
  }
7551
- function checkManagedHook(hookName, inspect) {
7666
+ function minutesAgo(at, now) {
7667
+ const minutes = Math.max(0, Math.round((now - at) / 6e4));
7668
+ return minutes === 0 ? "just now" : `${String(minutes)}m ago`;
7669
+ }
7670
+ function classifyHookFiring(firing, now = Date.now()) {
7671
+ const name = "hook-fired";
7672
+ switch (firing.state) {
7673
+ case "inactive":
7674
+ return { name, status: "ok", detail: "repository not active \u2014 no post-commit run expected" };
7675
+ case "unverified":
7676
+ return {
7677
+ name,
7678
+ status: "ok",
7679
+ detail: "no local commit since the hooks were wired \u2014 the next commit is checked"
7680
+ };
7681
+ case "pending":
7682
+ return {
7683
+ name,
7684
+ status: "ok",
7685
+ detail: `latest commit is under ${String(POST_COMMIT_GRACE_MS / 1e3)}s old \u2014 capture may still be starting`
7686
+ };
7687
+ case "fired":
7688
+ return {
7689
+ name,
7690
+ status: "ok",
7691
+ detail: `post-commit reached prim for the latest commit (${minutesAgo(firing.firedAt, now)})`
7692
+ };
7693
+ case "not_firing":
7694
+ return {
7695
+ name,
7696
+ status: "fail",
7697
+ detail: `post-commit never reached prim for the commit made ${minutesAgo(firing.commitAt, now)}${firing.firedAt === void 0 ? "" : ` (last run ${minutesAgo(firing.firedAt, now)})`} \xB7 likely an exit/exec before prim's block, hooks turned off for that commit (HUSKY=0, a core.hooksPath override), or a git client without prim's environment \xB7 fix the cause, then commit to re-check \xB7 if prim was off for that commit (set with \`git config prim.active\` directly), run \`prim disable\` then \`prim enable\` to restart the check`
7698
+ };
7699
+ }
7700
+ }
7701
+ function inspectFiringHere() {
7702
+ try {
7703
+ const root = gitToplevel();
7704
+ return root ? inspectPostCommitFiring(root) : void 0;
7705
+ } catch {
7706
+ return void 0;
7707
+ }
7708
+ }
7709
+ function checkHookFiring(firing) {
7710
+ return firing ? classifyHookFiring(firing) : { name: "hook-fired", status: "warn", detail: "not a git repository" };
7711
+ }
7712
+ function checkManagedHook(hookName, firing) {
7552
7713
  try {
7553
- return classifyManagedHook(hookName, inspect());
7714
+ return classifyManagedHook(hookName, inspectEffectiveGitHook(hookName), firing);
7554
7715
  } catch (error) {
7555
7716
  const detail = error instanceof Error ? error.message : String(error);
7556
7717
  return {
@@ -7869,15 +8030,9 @@ async function checkFeedbackCapability() {
7869
8030
  return { name: "feedback-api", status: "warn", detail: message.slice(0, 80) };
7870
8031
  }
7871
8032
  }
7872
- function refreshOwnedGlobalHooksForHealth() {
7873
- try {
7874
- refreshOwnedGlobalHooks();
7875
- } catch {
7876
- }
7877
- }
7878
8033
  async function independentChecks() {
7879
- refreshOwnedGlobalHooksForHealth();
7880
8034
  const backend = await checkBackend();
8035
+ const firing = inspectFiringHere();
7881
8036
  return {
7882
8037
  before: [checkAuth()],
7883
8038
  after: [
@@ -7886,8 +8041,10 @@ async function independentChecks() {
7886
8041
  ...checkAgentHooks(),
7887
8042
  checkHookRuntime(),
7888
8043
  await checkRepositoryBinding(),
7889
- checkManagedHook("post-commit", inspectEffectivePostCommitHook),
7890
- checkManagedHook("post-rewrite", inspectEffectivePostRewriteHook),
8044
+ checkManagedHook("pre-commit"),
8045
+ checkManagedHook("post-commit", firing),
8046
+ checkManagedHook("post-rewrite"),
8047
+ checkHookFiring(firing),
7891
8048
  ...backend,
7892
8049
  await checkFeedbackCapability()
7893
8050
  ]
@@ -7936,7 +8093,7 @@ import { join as join9 } from "path";
7936
8093
 
7937
8094
  // src/flusher.ts
7938
8095
  import { createReadStream, renameSync as renameSync3, statSync as statSync3, unlinkSync as unlinkSync3 } from "fs";
7939
- import { dirname as dirname4, join as join8 } from "path";
8096
+ import { dirname as dirname3, join as join8 } from "path";
7940
8097
 
7941
8098
  // src/dead-letter.ts
7942
8099
  import { createHash as createHash3, randomBytes as randomBytes4 } from "crypto";
@@ -7953,12 +8110,12 @@ import {
7953
8110
  rmSync as rmSync3,
7954
8111
  writeFileSync as writeFileSync3
7955
8112
  } from "fs";
7956
- import { dirname as dirname3, join as join7 } from "path";
8113
+ import { dirname as dirname2, join as join7 } from "path";
7957
8114
  var DIRECTORY_MODE = 448;
7958
8115
  var FILE_MODE = 384;
7959
8116
  var DEAD_LETTER_DIRNAME = "dead-letter";
7960
8117
  function deadLetterDirectoryForRotation(flushingPath) {
7961
- return join7(dirname3(flushingPath), DEAD_LETTER_DIRNAME);
8118
+ return join7(dirname2(flushingPath), DEAD_LETTER_DIRNAME);
7962
8119
  }
7963
8120
  function deadLetterPathForRawLine(flushingPath, rawLine) {
7964
8121
  const quarantineId = createHash3("sha256").update(rawLine).digest("hex");
@@ -7968,7 +8125,7 @@ function ensureDeadLetterDirectory(flushingPath, persistDirectory) {
7968
8125
  const directory = deadLetterDirectoryForRotation(flushingPath);
7969
8126
  mkdirSync4(directory, { recursive: true, mode: DIRECTORY_MODE });
7970
8127
  chmodSync2(directory, DIRECTORY_MODE);
7971
- persistDirectory(dirname3(directory));
8128
+ persistDirectory(dirname2(directory));
7972
8129
  return directory;
7973
8130
  }
7974
8131
  function persistDeadLetter(directory, path, quarantineId, record3, persistDirectory) {
@@ -8387,7 +8544,7 @@ async function flushOnce() {
8387
8544
  return inspection.retainedBuckets.length > 0 ? { flushed: total, quarantined, retained: inspection.retainedBuckets } : { flushed: total, quarantined };
8388
8545
  }
8389
8546
  var flushInFlight;
8390
- var FLUSH_LOCK_PATH = join8(dirname4(JOURNAL_DIR), ".flush.lock");
8547
+ var FLUSH_LOCK_PATH = join8(dirname3(JOURNAL_DIR), ".flush.lock");
8391
8548
  var FLUSH_LOCK_TIMEOUT_MS = 250;
8392
8549
  function isFlushLockContended(error) {
8393
8550
  return error instanceof Error && error.message.startsWith("timed out waiting for file lock");
@@ -8735,12 +8892,16 @@ function planSetupSteps(opts) {
8735
8892
  required: true
8736
8893
  });
8737
8894
  }
8738
- steps.push({
8739
- key: "hooks",
8740
- label: "Git hooks",
8741
- args: ["hooks", "install", ...scopeArgs],
8742
- required: true
8743
- });
8895
+ if (opts.scope === "project") {
8896
+ steps.push({ key: "hooks", label: "Git hooks", args: ["hooks", "install"], required: true });
8897
+ } else if (opts.globalHooksPath) {
8898
+ steps.push({
8899
+ key: "hooks",
8900
+ label: "Git hooks (global)",
8901
+ args: ["hooks", "install", "--scope", "user", "--global-hooks-path"],
8902
+ required: true
8903
+ });
8904
+ }
8744
8905
  const skillArgs = ["skill", "install", "--agent", opts.agent, ...scopeArgs];
8745
8906
  steps.push({ key: "skill", label: "Agent skill", args: skillArgs, required: true });
8746
8907
  steps.push({
@@ -8771,7 +8932,44 @@ function planCleanupUninstalls(agent, conflicts) {
8771
8932
  if (conflicts.includes(CONFLICT_SKILL)) steps.push(["skill", "uninstall", "--agent", agent]);
8772
8933
  return steps;
8773
8934
  }
8774
- function detectProjectConflicts(agent, run) {
8935
+ function preCommitRunsPrim(content) {
8936
+ return content.includes("# >>> prim pre-commit hook >>>") || content.includes("prim-pre-commit");
8937
+ }
8938
+ function projectHooksConflict(globalHooksActive, preCommit) {
8939
+ return globalHooksActive && preCommit !== void 0 && preCommitRunsPrim(preCommit);
8940
+ }
8941
+ function enableWiresRepository(root) {
8942
+ if (!root) return void 0;
8943
+ try {
8944
+ if (gitHooksMode({ cwd: root }) === "manual") return void 0;
8945
+ return resolveEffectiveGitHook("post-commit", root).location !== "external";
8946
+ } catch {
8947
+ return void 0;
8948
+ }
8949
+ }
8950
+ function setupGitHooksNote(plan, wiresThisRepository) {
8951
+ if (wiresThisRepository === true && plan.action === "add_to_dir") {
8952
+ return `git hooks \xB7 Git runs this repository's hooks from inside it, so \`prim enable\` wires it; other repositories may run ${plan.global}, which prim edits only with --global-hooks-path, after asking the user`;
8953
+ }
8954
+ if (wiresThisRepository === true && plan.action === "system_declined") {
8955
+ return `git hooks \xB7 Git runs this repository's hooks from inside it, so \`prim enable\` wires it; other repositories may run the system hooks dir ${plan.system}, which prim never edits`;
8956
+ }
8957
+ switch (plan.action) {
8958
+ case "refresh":
8959
+ return "git hooks \xB7 prim's global hooks stay active; `prim enable` refreshes them";
8960
+ case "present_in_dir":
8961
+ return `git hooks \xB7 prim's hooks are already in your global hooks dir ${plan.global}`;
8962
+ case "add_to_dir":
8963
+ return `git hooks \xB7 your global core.hooksPath (${plan.global}) runs every repository's hooks, so \`prim enable\` cannot wire this repository without it; add prim there with --global-hooks-path, after asking the user`;
8964
+ case "system_declined":
8965
+ return `git hooks \xB7 the system core.hooksPath (${plan.system}) runs every repository's hooks, so \`prim enable\` cannot wire this repository: place \`prim hooks snippet <hook>\` there, or, after asking the user, run \`prim hooks install --scope user --global-hooks-path --force\` (its hooks stop firing)`;
8966
+ case "manual":
8967
+ return "git hooks \xB7 prim.gitHooks=manual: prim writes no hook files; wire them with `prim hooks snippet <hook>`";
8968
+ default:
8969
+ return "git hooks \xB7 wired per repository by `prim enable`; git's global hooks are untouched (opt in with --global-hooks-path)";
8970
+ }
8971
+ }
8972
+ function detectProjectConflicts(agent, run, globalHooksActive) {
8775
8973
  const conflicts = [];
8776
8974
  if (agent !== "hermes") {
8777
8975
  try {
@@ -8780,14 +8978,14 @@ function detectProjectConflicts(agent, run) {
8780
8978
  } catch {
8781
8979
  }
8782
8980
  }
8981
+ let preCommit;
8783
8982
  try {
8784
8983
  const root = gitToplevel();
8785
- const preCommit = root && join11(root, ".git", "hooks", "pre-commit");
8786
- if (preCommit && existsSync10(preCommit) && readFileSync7(preCommit, "utf-8").includes("prim-pre-commit")) {
8787
- conflicts.push(CONFLICT_HOOKS);
8788
- }
8984
+ const path = root && join11(root, ".git", "hooks", "pre-commit");
8985
+ if (path && existsSync10(path)) preCommit = readFileSync7(path, "utf-8");
8789
8986
  } catch {
8790
8987
  }
8988
+ if (projectHooksConflict(globalHooksActive, preCommit)) conflicts.push(CONFLICT_HOOKS);
8791
8989
  try {
8792
8990
  const parsed = JSON.parse(
8793
8991
  run(["skill", "status", "--agent", agent, "--json"], true).stdout || "{}"
@@ -8827,8 +9025,11 @@ function registerSetupCommand(program2, dependencies = {}) {
8827
9025
  "Install everything in one shot (auth, session + git hooks, daemon, skill, welcome)"
8828
9026
  ).option("--agent <agent>", "claude, codex, cursor, or hermes (auto-detected when omitted)").option(
8829
9027
  "--scope <scope>",
8830
- "user (default \u2014 install once for every repo) or project (this repo only)",
9028
+ "user (default \u2014 agent integration and skill for every repo; git hooks per repo via enable) or project (this repo only)",
8831
9029
  "user"
9030
+ ).option(
9031
+ "--global-hooks-path",
9032
+ "with the default user scope, also change git's global hooks so prim's hooks run in every repository"
8832
9033
  ).option(
8833
9034
  "--migrate",
8834
9035
  "with the default user scope, remove any project-scoped prim config in this repo (else just warn)"
@@ -8848,6 +9049,11 @@ function registerSetupCommand(program2, dependencies = {}) {
8848
9049
  (dependencies.exit ?? process.exit)(EXIT_USAGE3);
8849
9050
  return;
8850
9051
  }
9052
+ if (opts.globalHooksPath && opts.scope !== "user") {
9053
+ process.stderr.write("[prim] --global-hooks-path applies only with --scope user\n");
9054
+ (dependencies.exit ?? process.exit)(EXIT_USAGE3);
9055
+ return;
9056
+ }
8851
9057
  const agent = agentInput;
8852
9058
  const scope = opts.scope;
8853
9059
  const self = process.argv[1];
@@ -8899,16 +9105,24 @@ function registerSetupCommand(program2, dependencies = {}) {
8899
9105
  note("pre-authorize \xB7 writing prim allow-rule (user scope)\u2026");
8900
9106
  results.preauth = run(["claude", "preauth", "--scope", "user"]).code === 0 ? "ok" : "skipped";
8901
9107
  }
8902
- const setupSteps = planSetupSteps({ agent, daemon: opts.daemon, scope });
9108
+ const setupSteps = planSetupSteps({
9109
+ agent,
9110
+ daemon: opts.daemon,
9111
+ scope,
9112
+ globalHooksPath: opts.globalHooksPath === true
9113
+ });
8903
9114
  for (const step of setupSteps.filter(
8904
9115
  (candidate) => candidate.key !== "enable" && candidate.key !== "health"
8905
9116
  )) {
8906
9117
  note(`${step.label} \xB7 installing\u2026`);
8907
9118
  const { code } = run(step.args);
8908
- results[step.key] = code === 0 ? "ok" : step.required ? "failed" : "skipped";
9119
+ results[step.key] = code === 0 ? "ok" : code === EXIT_GLOBAL_HOOKS_NOT_INSTALLED && step.key === "hooks" ? "skipped" : step.required ? "failed" : "skipped";
9120
+ }
9121
+ if (scope === "user" && !opts.globalHooksPath) {
9122
+ note(setupGitHooksNote(planGlobalHooks(), enableWiresRepository(gitToplevel())));
8909
9123
  }
8910
9124
  if (scope === "user") {
8911
- const conflicts = detectProjectConflicts(agent, run);
9125
+ const conflicts = detectProjectConflicts(agent, run, globalHooksPathIsPrims());
8912
9126
  if (conflicts.length > 0 && opts.migrate) {
8913
9127
  note(`migrate \xB7 removing project-scoped config (${conflicts.join(", ")})\u2026`);
8914
9128
  const ok = planCleanupUninstalls(agent, conflicts).map((args) => run(args).code === 0).every(Boolean);
@@ -8949,12 +9163,12 @@ function registerSetupCommand(program2, dependencies = {}) {
8949
9163
 
8950
9164
  // src/commands/statusline.ts
8951
9165
  import { readFileSync as readFileSync8 } from "fs";
8952
- import { dirname as dirname5, resolve as resolve3 } from "path";
9166
+ import { dirname as dirname4, resolve as resolve3 } from "path";
8953
9167
  import { fileURLToPath } from "url";
8954
9168
  var STATUSLINE_TIMEOUT_MS = 200;
8955
9169
  function readPackageVersion() {
8956
9170
  try {
8957
- const here = dirname5(fileURLToPath(import.meta.url));
9171
+ const here = dirname4(fileURLToPath(import.meta.url));
8958
9172
  const candidates = [
8959
9173
  // Schema-v2 runtimes staged a manifest beside this standalone bundle.
8960
9174
  // Keep resolving it while those legacy launchers transition to schema v3.
@@ -9470,7 +9684,7 @@ import { readFileSync as readFileSync9 } from "fs";
9470
9684
 
9471
9685
  // src/daemon/health.ts
9472
9686
  import { chmodSync as chmodSync3, existsSync as existsSync11, mkdirSync as mkdirSync7, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
9473
- import { dirname as dirname6, join as join12 } from "path";
9687
+ import { dirname as dirname5, join as join12 } from "path";
9474
9688
  var HEARTBEAT_FRESH_MS = 9e4;
9475
9689
  var DAEMON_HEALTH_PATH = join12(primConfigDirectory(), "daemon-health.json");
9476
9690
 
@@ -9599,7 +9813,7 @@ function runStartupBackgroundWork(argv, env, work) {
9599
9813
  }
9600
9814
 
9601
9815
  // src/index.ts
9602
- var __dirname = dirname7(fileURLToPath2(import.meta.url));
9816
+ var __dirname = dirname6(fileURLToPath2(import.meta.url));
9603
9817
  var pkg = JSON.parse(readFileSync10(resolve4(__dirname, "../package.json"), "utf-8"));
9604
9818
  runStartupBackgroundWork(process.argv.slice(2), process.env, {
9605
9819
  notify: () => updateNotifier({ pkg }).notify(),