@primitive.ai/prim 0.1.0-alpha.36 → 0.1.0-alpha.37

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.
@@ -0,0 +1,113 @@
1
+ // src/lib/bin-cache.ts
2
+ import { chmodSync, mkdirSync, renameSync, writeFileSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join as join2 } from "path";
5
+
6
+ // src/lib/bin-path.ts
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { dirname, isAbsolute, join } from "path";
9
+ import { fileURLToPath } from "url";
10
+ var PKG_NAME = "@primitive.ai/prim";
11
+ var ROOT_WALK_LIMIT = 6;
12
+ var NPX_FALLBACK = `npx --yes -p ${PKG_NAME}@latest`;
13
+ var BIN_CACHE_DIR_SH = "${XDG_CACHE_HOME:-$HOME/.cache}/prim/bin";
14
+ var BIN_CACHE_TTL_MIN_DEFAULT = 1440;
15
+ var resolvedRoot;
16
+ function locateRoot() {
17
+ if (resolvedRoot !== void 0) {
18
+ return resolvedRoot;
19
+ }
20
+ let dir = dirname(fileURLToPath(import.meta.url));
21
+ for (let depth = 0; depth < ROOT_WALK_LIMIT; depth++) {
22
+ const manifestPath = join(dir, "package.json");
23
+ if (existsSync(manifestPath)) {
24
+ try {
25
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
26
+ if (manifest.name === PKG_NAME && manifest.bin) {
27
+ resolvedRoot = { dir, bin: manifest.bin };
28
+ return resolvedRoot;
29
+ }
30
+ } catch {
31
+ }
32
+ }
33
+ const parent = dirname(dir);
34
+ if (parent === dir) {
35
+ break;
36
+ }
37
+ dir = parent;
38
+ }
39
+ resolvedRoot = null;
40
+ return resolvedRoot;
41
+ }
42
+ function binFile(bin) {
43
+ const root = locateRoot();
44
+ const rel = root?.bin[bin];
45
+ if (!root || !rel) {
46
+ return null;
47
+ }
48
+ return isAbsolute(rel) ? rel : join(root.dir, rel);
49
+ }
50
+ function hookShimCommand(bin, args = "", opts = {}) {
51
+ const invoke = (cmd) => args ? `${cmd} ${args}` : cmd;
52
+ const ladder = `if command -v ${bin} >/dev/null 2>&1; then ${invoke(bin)}; elif [ -f "./node_modules/.bin/${bin}" ]; then ${invoke(`./node_modules/.bin/${bin}`)}; else ${invoke(`${NPX_FALLBACK} ${bin}`)}; fi`;
53
+ if (opts.cacheRead === false) {
54
+ return ladder;
55
+ }
56
+ const execArgs = args ? ` ${args}` : "";
57
+ const cacheBranch = `d="${BIN_CACHE_DIR_SH}"; if [ "\${PRIM_BIN_CACHE:-1}" != "0" ] && [ -f "$d/${bin}" ] && [ -f "$d/node" ] && [ -n "$(find "$d/${bin}" -mmin "-\${PRIM_BIN_CACHE_TTL_MIN:-${BIN_CACHE_TTL_MIN_DEFAULT}}" 2>/dev/null)" ]; then n=$(cat "$d/node"); p=$(cat "$d/${bin}"); if [ -x "$n" ] && [ -f "$p" ]; then export PRIM_BIN_CACHE_HIT=1; exec "$n" "$p"${execArgs}; fi; fi; `;
58
+ return cacheBranch + ladder;
59
+ }
60
+ function detachedHookShimCommand(bin, args = "") {
61
+ return `payload=$(cat); { trap '' HUP; export npm_config_fetch_retries=2 npm_config_fetch_retry_mintimeout=10000 npm_config_fetch_retry_maxtimeout=10000 npm_config_fetch_timeout=60000; printf '%s' "$payload" | { ${hookShimCommand(bin, args, { cacheRead: false })}; }; } </dev/null >/dev/null 2>&1 &`;
62
+ }
63
+ function commandMatchesBin(command, bin) {
64
+ if (!command) {
65
+ return false;
66
+ }
67
+ const c = command.trim();
68
+ if (c === bin || c.startsWith(`${bin} `)) {
69
+ return true;
70
+ }
71
+ return c.includes(`command -v ${bin} `);
72
+ }
73
+
74
+ // src/lib/bin-cache.ts
75
+ var CACHED_BINS = ["prim", "prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
76
+ function binCacheDir() {
77
+ const base = process.env.XDG_CACHE_HOME || join2(homedir(), ".cache");
78
+ return join2(base, "prim", "bin");
79
+ }
80
+ function writeAtomic(path, content) {
81
+ const tmp = `${path}.${process.pid}.tmp`;
82
+ writeFileSync(tmp, content, { mode: 384 });
83
+ renameSync(tmp, path);
84
+ }
85
+ function warmBinCache() {
86
+ try {
87
+ if (process.env.PRIM_BIN_CACHE_HIT) {
88
+ return;
89
+ }
90
+ if (process.env.PRIM_BIN_CACHE === "0") {
91
+ return;
92
+ }
93
+ const dir = binCacheDir();
94
+ mkdirSync(dir, { recursive: true, mode: 448 });
95
+ chmodSync(dir, 448);
96
+ writeAtomic(join2(dir, "node"), process.execPath);
97
+ for (const bin of CACHED_BINS) {
98
+ const file = binFile(bin);
99
+ if (file) {
100
+ writeAtomic(join2(dir, bin), file);
101
+ }
102
+ }
103
+ } catch {
104
+ }
105
+ }
106
+
107
+ export {
108
+ binFile,
109
+ hookShimCommand,
110
+ detachedHookShimCommand,
111
+ commandMatchesBin,
112
+ warmBinCache
113
+ };
@@ -12,6 +12,9 @@ import {
12
12
  import {
13
13
  isRepoActiveForCapture
14
14
  } from "../chunk-LUPD2JSH.js";
15
+ import {
16
+ warmBinCache
17
+ } from "../chunk-HSZPTVKU.js";
15
18
  import {
16
19
  getClient
17
20
  } from "../chunk-26VA3ADF.js";
@@ -100,6 +103,7 @@ async function ingestMove(move) {
100
103
  );
101
104
  }
102
105
  async function main() {
106
+ warmBinCache();
103
107
  const agent = parseAgent(process.argv);
104
108
  let raw;
105
109
  try {
@@ -2,6 +2,9 @@
2
2
  import {
3
3
  isRepoActiveForCapture
4
4
  } from "../chunk-LUPD2JSH.js";
5
+ import {
6
+ warmBinCache
7
+ } from "../chunk-HSZPTVKU.js";
5
8
  import {
6
9
  getClient,
7
10
  getSiteUrl
@@ -262,6 +265,7 @@ async function checkOneFile(file) {
262
265
  );
263
266
  }
264
267
  async function main() {
268
+ warmBinCache();
265
269
  let raw;
266
270
  try {
267
271
  raw = await readStdin();
@@ -13,6 +13,9 @@ import {
13
13
  import {
14
14
  isRepoActiveForCapture
15
15
  } from "../chunk-LUPD2JSH.js";
16
+ import {
17
+ warmBinCache
18
+ } from "../chunk-HSZPTVKU.js";
16
19
  import "../chunk-26VA3ADF.js";
17
20
  import {
18
21
  normalizeEnvelope,
@@ -41,6 +44,7 @@ function spawnBackgroundFlush() {
41
44
  }).unref();
42
45
  }
43
46
  try {
47
+ warmBinCache();
44
48
  const agent = parseAgent(process.argv);
45
49
  const raw = readFileSync(0, "utf-8");
46
50
  const parsed = normalizeEnvelope(JSON.parse(raw), agent);
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ warmBinCache
4
+ } from "../chunk-HSZPTVKU.js";
2
5
  import {
3
6
  getSiteUrl
4
7
  } from "../chunk-26VA3ADF.js";
@@ -45,6 +48,7 @@ function emit(additionalContext) {
45
48
  `);
46
49
  }
47
50
  async function main() {
51
+ warmBinCache();
48
52
  const agent = parseAgent(process.argv);
49
53
  let raw;
50
54
  try {
package/dist/index.js CHANGED
@@ -25,6 +25,13 @@ import {
25
25
  gitToplevel,
26
26
  setRepoActive
27
27
  } from "./chunk-LUPD2JSH.js";
28
+ import {
29
+ binFile,
30
+ commandMatchesBin,
31
+ detachedHookShimCommand,
32
+ hookShimCommand,
33
+ warmBinCache
34
+ } from "./chunk-HSZPTVKU.js";
28
35
  import {
29
36
  HttpError,
30
37
  REFRESH_TOKEN_PATH,
@@ -42,9 +49,9 @@ import {
42
49
  } from "./chunk-UTKQTZHL.js";
43
50
 
44
51
  // src/index.ts
45
- import { readFileSync as readFileSync12 } from "fs";
46
- import { dirname as dirname9, resolve as resolve5 } from "path";
47
- import { fileURLToPath as fileURLToPath5 } from "url";
52
+ import { readFileSync as readFileSync11 } from "fs";
53
+ import { dirname as dirname8, resolve as resolve5 } from "path";
54
+ import { fileURLToPath as fileURLToPath4 } from "url";
48
55
  import { Command } from "commander";
49
56
  import updateNotifier from "update-notifier";
50
57
 
@@ -529,78 +536,16 @@ async function exchangeCode(siteUrl, code, codeVerifier, redirectUri) {
529
536
  // src/commands/claude-install.ts
530
537
  import {
531
538
  closeSync,
532
- existsSync as existsSync3,
539
+ existsSync as existsSync2,
533
540
  fsyncSync,
534
541
  mkdirSync as mkdirSync2,
535
542
  openSync,
536
- readFileSync as readFileSync3,
543
+ readFileSync as readFileSync2,
537
544
  renameSync,
538
545
  writeFileSync as writeFileSync2
539
546
  } from "fs";
540
547
  import { homedir } from "os";
541
- import { dirname as dirname3, join as join2 } from "path";
542
-
543
- // src/lib/bin-path.ts
544
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
545
- import { dirname as dirname2, isAbsolute, join } from "path";
546
- import { fileURLToPath } from "url";
547
- var PKG_NAME = "@primitive.ai/prim";
548
- var ROOT_WALK_LIMIT = 6;
549
- var NPX_FALLBACK = `npx --yes -p ${PKG_NAME}@latest`;
550
- var resolvedRoot;
551
- function locateRoot() {
552
- if (resolvedRoot !== void 0) {
553
- return resolvedRoot;
554
- }
555
- let dir = dirname2(fileURLToPath(import.meta.url));
556
- for (let depth = 0; depth < ROOT_WALK_LIMIT; depth++) {
557
- const manifestPath = join(dir, "package.json");
558
- if (existsSync2(manifestPath)) {
559
- try {
560
- const manifest = JSON.parse(readFileSync2(manifestPath, "utf-8"));
561
- if (manifest.name === PKG_NAME && manifest.bin) {
562
- resolvedRoot = { dir, bin: manifest.bin };
563
- return resolvedRoot;
564
- }
565
- } catch {
566
- }
567
- }
568
- const parent = dirname2(dir);
569
- if (parent === dir) {
570
- break;
571
- }
572
- dir = parent;
573
- }
574
- resolvedRoot = null;
575
- return resolvedRoot;
576
- }
577
- function binFile(bin) {
578
- const root = locateRoot();
579
- const rel = root?.bin[bin];
580
- if (!root || !rel) {
581
- return null;
582
- }
583
- return isAbsolute(rel) ? rel : join(root.dir, rel);
584
- }
585
- function hookShimCommand(bin, args = "") {
586
- const invoke = (cmd) => args ? `${cmd} ${args}` : cmd;
587
- return `if command -v ${bin} >/dev/null 2>&1; then ${invoke(bin)}; elif [ -f "./node_modules/.bin/${bin}" ]; then ${invoke(`./node_modules/.bin/${bin}`)}; else ${invoke(`${NPX_FALLBACK} ${bin}`)}; fi`;
588
- }
589
- function detachedHookShimCommand(bin, args = "") {
590
- return `payload=$(cat); { trap '' HUP; export npm_config_fetch_retries=2 npm_config_fetch_retry_mintimeout=10000 npm_config_fetch_retry_maxtimeout=10000 npm_config_fetch_timeout=60000; printf '%s' "$payload" | { ${hookShimCommand(bin, args)}; }; } </dev/null >/dev/null 2>&1 &`;
591
- }
592
- function commandMatchesBin(command, bin) {
593
- if (!command) {
594
- return false;
595
- }
596
- const c = command.trim();
597
- if (c === bin || c.startsWith(`${bin} `)) {
598
- return true;
599
- }
600
- return c.includes(`command -v ${bin} `);
601
- }
602
-
603
- // src/commands/claude-install.ts
548
+ import { dirname as dirname2, join } from "path";
604
549
  var CAPTURE_BIN = "prim-hook";
605
550
  var GATE_BIN = "prim-pre-tool-use";
606
551
  var POST_TOOL_USE_BIN = "prim-post-tool-use";
@@ -618,11 +563,11 @@ var PRIM_BINS = [
618
563
  SESSION_END_BIN
619
564
  ];
620
565
  var JSON_INDENT = 2;
621
- var USER_SCOPE_PATH = join2(homedir(), ".claude", "settings.json");
566
+ var USER_SCOPE_PATH = join(homedir(), ".claude", "settings.json");
622
567
  function projectRoot() {
623
568
  return gitToplevel() ?? process.cwd();
624
569
  }
625
- var projectScopePath = () => join2(projectRoot(), ".claude", "settings.json");
570
+ var projectScopePath = () => join(projectRoot(), ".claude", "settings.json");
626
571
  var CAPTURE_EVENTS = [
627
572
  "SessionStart",
628
573
  "UserPromptSubmit",
@@ -632,8 +577,8 @@ var CAPTURE_EVENTS = [
632
577
  "SessionEnd",
633
578
  "SubagentStop"
634
579
  ];
635
- function makeRegistration(event, matcher, bin, args = "") {
636
- return { event, matcher, bin, command: hookShimCommand(bin, args) };
580
+ function makeRegistration(event, matcher, bin, args = "", opts = {}) {
581
+ return { event, matcher, bin, command: hookShimCommand(bin, args, opts) };
637
582
  }
638
583
  function makeDetachedRegistration(event, matcher, bin, args = "") {
639
584
  return { event, matcher, bin, command: detachedHookShimCommand(bin, args) };
@@ -644,7 +589,9 @@ var REGISTRATIONS = [
644
589
  ),
645
590
  makeRegistration("PreToolUse", "Edit|Write|MultiEdit", GATE_BIN),
646
591
  makeRegistration("PostToolUse", "Edit|Write|MultiEdit", POST_TOOL_USE_BIN),
647
- makeRegistration("SessionStart", "*", SESSION_START_BIN),
592
+ // Bare ladder (no branch-0): SessionStart must re-resolve @latest each session
593
+ // to refresh the bin cache the other hooks read. See lib/bin-cache.ts.
594
+ makeRegistration("SessionStart", "*", SESSION_START_BIN, "", { cacheRead: false }),
648
595
  makeDetachedRegistration("SessionEnd", "*", SESSION_END_BIN)
649
596
  ];
650
597
  var PRIM_PERMISSION_RULE = "Bash(npx --yes @primitive.ai/prim*)";
@@ -662,10 +609,10 @@ function settingsPathFor(scope) {
662
609
  return scope === "user" ? USER_SCOPE_PATH : projectScopePath();
663
610
  }
664
611
  function readSettings(path) {
665
- if (!existsSync3(path)) {
612
+ if (!existsSync2(path)) {
666
613
  return {};
667
614
  }
668
- const raw = readFileSync3(path, "utf-8");
615
+ const raw = readFileSync2(path, "utf-8");
669
616
  try {
670
617
  return JSON.parse(raw);
671
618
  } catch (err) {
@@ -806,8 +753,8 @@ function isGateInstalled(settings) {
806
753
  return (settings.hooks?.PreToolUse ?? []).some((e) => entryHasCommand(e, GATE_BIN));
807
754
  }
808
755
  function atomicWrite(path, content) {
809
- const dir = dirname3(path);
810
- if (!existsSync3(dir)) {
756
+ const dir = dirname2(path);
757
+ if (!existsSync2(dir)) {
811
758
  mkdirSync2(dir, { recursive: true });
812
759
  }
813
760
  const tmp = `${path}.tmp.${String(Date.now())}`;
@@ -955,7 +902,7 @@ ${line("project", result.project)}`);
955
902
 
956
903
  // src/commands/codex-install.ts
957
904
  import { homedir as homedir2 } from "os";
958
- import { join as join3 } from "path";
905
+ import { join as join2 } from "path";
959
906
  var CAPTURE_BIN2 = "prim-hook";
960
907
  var GATE_BIN2 = "prim-pre-tool-use";
961
908
  var POST_TOOL_USE_BIN2 = "prim-post-tool-use";
@@ -975,10 +922,12 @@ var CODEX_REGISTRATIONS = [
975
922
  ...CODEX_CAPTURE_EVENTS.map((event) => makeRegistration(event, "*", CAPTURE_BIN2, CODEX_ARGS)),
976
923
  makeRegistration("PreToolUse", "apply_patch", GATE_BIN2, CODEX_ARGS),
977
924
  makeRegistration("PostToolUse", "apply_patch", POST_TOOL_USE_BIN2, CODEX_ARGS),
978
- makeRegistration("SessionStart", "*", SESSION_START_BIN2, CODEX_ARGS)
925
+ // Bare ladder (no branch-0): SessionStart re-resolves @latest each session to
926
+ // refresh the bin cache the other hooks read. See lib/bin-cache.ts.
927
+ makeRegistration("SessionStart", "*", SESSION_START_BIN2, CODEX_ARGS, { cacheRead: false })
979
928
  ];
980
- var USER_SCOPE_PATH2 = join3(homedir2(), ".codex", "hooks.json");
981
- var projectScopePath2 = () => join3(projectRoot(), ".codex", "hooks.json");
929
+ var USER_SCOPE_PATH2 = join2(homedir2(), ".codex", "hooks.json");
930
+ var projectScopePath2 = () => join2(projectRoot(), ".codex", "hooks.json");
982
931
  function settingsPathFor2(scope) {
983
932
  return scope === "user" ? USER_SCOPE_PATH2 : projectScopePath2();
984
933
  }
@@ -1102,9 +1051,9 @@ ${line("project", result.project)}`);
1102
1051
 
1103
1052
  // src/commands/daemon.ts
1104
1053
  import { spawn } from "child_process";
1105
- import { closeSync as closeSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync4, unlinkSync } from "fs";
1054
+ import { closeSync as closeSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync3, unlinkSync } from "fs";
1106
1055
  import { homedir as homedir3 } from "os";
1107
- import { join as join4 } from "path";
1056
+ import { join as join3 } from "path";
1108
1057
 
1109
1058
  // src/lib/presence.ts
1110
1059
  function formatTeammates(names, cap) {
@@ -1122,10 +1071,10 @@ function formatTeammates(names, cap) {
1122
1071
 
1123
1072
  // src/commands/daemon.ts
1124
1073
  var DAEMON_BIN = "prim-daemon-server";
1125
- var CONFIG_DIR = join4(homedir3(), ".config", "prim");
1126
- var PID_PATH = join4(CONFIG_DIR, "daemon.pid");
1127
- var SOCK_PATH = join4(CONFIG_DIR, "sock");
1128
- var LOG_PATH = join4(CONFIG_DIR, "daemon.log");
1074
+ var CONFIG_DIR = join3(homedir3(), ".config", "prim");
1075
+ var PID_PATH = join3(CONFIG_DIR, "daemon.pid");
1076
+ var SOCK_PATH = join3(CONFIG_DIR, "sock");
1077
+ var LOG_PATH = join3(CONFIG_DIR, "daemon.log");
1129
1078
  var CONFIG_DIR_MODE = 448;
1130
1079
  var LOG_FILE_MODE = 384;
1131
1080
  var STOP_TIMEOUT_MS = 5e3;
@@ -1138,10 +1087,10 @@ var EXIT_OK2 = 0;
1138
1087
  var EXIT_NOT_RUNNING = 2;
1139
1088
  var EXIT_BOOTING = 3;
1140
1089
  function readPidfile() {
1141
- if (!existsSync4(PID_PATH)) {
1090
+ if (!existsSync3(PID_PATH)) {
1142
1091
  return null;
1143
1092
  }
1144
- const raw = readFileSync4(PID_PATH, "utf-8").trim();
1093
+ const raw = readFileSync3(PID_PATH, "utf-8").trim();
1145
1094
  const pid = Number(raw);
1146
1095
  if (!Number.isInteger(pid) || pid <= 0) {
1147
1096
  return null;
@@ -1177,10 +1126,10 @@ function spawnDaemon(options) {
1177
1126
  return file ? spawn(process.execPath, [file], options) : spawn(DAEMON_BIN, [], options);
1178
1127
  }
1179
1128
  function openDaemonLog(configDir = CONFIG_DIR) {
1180
- if (!existsSync4(configDir)) {
1129
+ if (!existsSync3(configDir)) {
1181
1130
  mkdirSync3(configDir, { recursive: true, mode: CONFIG_DIR_MODE });
1182
1131
  }
1183
- return openSync2(join4(configDir, "daemon.log"), "a", LOG_FILE_MODE);
1132
+ return openSync2(join3(configDir, "daemon.log"), "a", LOG_FILE_MODE);
1184
1133
  }
1185
1134
  async function waitForReady() {
1186
1135
  const deadline = Date.now() + READY_TIMEOUT_MS;
@@ -2171,7 +2120,7 @@ function registerDecisionsCommands(program2) {
2171
2120
  }
2172
2121
 
2173
2122
  // src/commands/doctor.ts
2174
- import { existsSync as existsSync5 } from "fs";
2123
+ import { existsSync as existsSync4 } from "fs";
2175
2124
  var DAEMON_PROBE_TIMEOUT_MS = 500;
2176
2125
  var CONNECTIVITY_TIMEOUT_MS = 3e3;
2177
2126
  var MS_PER_SECOND = 1e3;
@@ -2189,7 +2138,7 @@ function checkAuth() {
2189
2138
  return { name: "auth", status: "fail", detail: "no token \u2014 run `prim auth login`" };
2190
2139
  }
2191
2140
  const expiresAt = getTokenExpiresAt();
2192
- const hasRefresh = existsSync5(REFRESH_TOKEN_PATH);
2141
+ const hasRefresh = existsSync4(REFRESH_TOKEN_PATH);
2193
2142
  if (expiresAt !== void 0 && Date.now() >= expiresAt) {
2194
2143
  return hasRefresh ? { name: "auth", status: "warn", detail: "access token expired (refresh available)" } : {
2195
2144
  name: "auth",
@@ -2289,17 +2238,17 @@ function registerDoctorCommands(program2) {
2289
2238
  import {
2290
2239
  chmodSync,
2291
2240
  closeSync as closeSync3,
2292
- existsSync as existsSync6,
2241
+ existsSync as existsSync5,
2293
2242
  fsyncSync as fsyncSync2,
2294
2243
  mkdirSync as mkdirSync4,
2295
2244
  openSync as openSync3,
2296
- readFileSync as readFileSync5,
2245
+ readFileSync as readFileSync4,
2297
2246
  renameSync as renameSync2,
2298
2247
  rmSync as rmSync2,
2299
2248
  writeFileSync as writeFileSync3
2300
2249
  } from "fs";
2301
2250
  import { homedir as homedir4 } from "os";
2302
- import { dirname as dirname4, join as join5 } from "path";
2251
+ import { dirname as dirname3, join as join4 } from "path";
2303
2252
  import { Document, parseDocument, stringify } from "yaml";
2304
2253
  var CAPTURE_BIN3 = "prim-hook";
2305
2254
  var GATE_BIN3 = "prim-pre-tool-use";
@@ -2318,13 +2267,13 @@ var PRIM_BINS3 = [
2318
2267
  SESSION_END_BIN2
2319
2268
  ];
2320
2269
  function hermesHome() {
2321
- return process.env.HERMES_HOME ?? join5(homedir4(), ".hermes");
2270
+ return process.env.HERMES_HOME ?? join4(homedir4(), ".hermes");
2322
2271
  }
2323
2272
  function configPath() {
2324
- return join5(hermesHome(), "config.yaml");
2273
+ return join4(hermesHome(), "config.yaml");
2325
2274
  }
2326
2275
  function shimPath() {
2327
- return join5(hermesHome(), "agent-hooks", "prim-shim.sh");
2276
+ return join4(hermesHome(), "agent-hooks", "prim-shim.sh");
2328
2277
  }
2329
2278
  var SHIM_SCRIPT = `#!/bin/sh
2330
2279
  # prim Hermes hook shim \u2014 managed by \`prim hermes install\`. Hermes runs hooks
@@ -2413,10 +2362,10 @@ function isCaptureInstalled(hooks) {
2413
2362
  );
2414
2363
  }
2415
2364
  function readDoc(path) {
2416
- if (!existsSync6(path)) {
2365
+ if (!existsSync5(path)) {
2417
2366
  return new Document();
2418
2367
  }
2419
- const raw = readFileSync5(path, "utf-8");
2368
+ const raw = readFileSync4(path, "utf-8");
2420
2369
  return raw.trim().length === 0 ? new Document() : parseDocument(raw);
2421
2370
  }
2422
2371
  function readHooks(doc) {
@@ -2488,8 +2437,8 @@ function setAutoAccept(raw) {
2488
2437
  `;
2489
2438
  }
2490
2439
  function atomicWriteFile(path, content) {
2491
- const dir = dirname4(path);
2492
- if (!existsSync6(dir)) {
2440
+ const dir = dirname3(path);
2441
+ if (!existsSync5(dir)) {
2493
2442
  mkdirSync4(dir, { recursive: true });
2494
2443
  }
2495
2444
  const tmp = `${path}.tmp.${String(process.pid)}`;
@@ -2513,8 +2462,8 @@ function assertMergeValid(before, after) {
2513
2462
  }
2514
2463
  function writeShim() {
2515
2464
  const path = shimPath();
2516
- const dir = dirname4(path);
2517
- if (!existsSync6(dir)) {
2465
+ const dir = dirname3(path);
2466
+ if (!existsSync5(dir)) {
2518
2467
  mkdirSync4(dir, { recursive: true });
2519
2468
  }
2520
2469
  writeFileSync3(path, SHIM_SCRIPT, "utf-8");
@@ -2534,7 +2483,7 @@ function readHooksFromRaw(raw) {
2534
2483
  }
2535
2484
  function performInstall3(opts) {
2536
2485
  const path = configPath();
2537
- const raw = existsSync6(path) ? readFileSync5(path, "utf-8") : "";
2486
+ const raw = existsSync5(path) ? readFileSync4(path, "utf-8") : "";
2538
2487
  const existing = readHooksFromRaw(raw);
2539
2488
  const desired = applyInstall3(existing, opts.force);
2540
2489
  let next = raw;
@@ -2560,11 +2509,11 @@ function performInstall3(opts) {
2560
2509
  }
2561
2510
  function performUninstall3() {
2562
2511
  const path = configPath();
2563
- if (!existsSync6(path)) {
2512
+ if (!existsSync5(path)) {
2564
2513
  removeShim();
2565
2514
  return { path, gate: false, capture: false, autoAccept: false, changed: false };
2566
2515
  }
2567
- const raw = readFileSync5(path, "utf-8");
2516
+ const raw = readFileSync4(path, "utf-8");
2568
2517
  const existing = readHooksFromRaw(raw);
2569
2518
  const remaining = applyUninstall3(existing);
2570
2519
  const next = JSON.stringify(existing) === JSON.stringify(remaining) ? raw : spliceHooks(raw, remaining);
@@ -2584,7 +2533,7 @@ function performUninstall3() {
2584
2533
  }
2585
2534
  function performStatus3() {
2586
2535
  const path = configPath();
2587
- const hooks = existsSync6(path) ? readHooks(readDoc(path)) : {};
2536
+ const hooks = existsSync5(path) ? readHooks(readDoc(path)) : {};
2588
2537
  return { path, gate: isGateInstalled3(hooks), capture: isCaptureInstalled(hooks) };
2589
2538
  }
2590
2539
  var TRUST_NOTICE2 = "[prim] Hermes requires hook consent: it prompts once per (event, command) on a TTY and records approval in ~/.hermes/shell-hooks-allowlist.json. To pre-approve non-interactively, re-run with --auto-accept (sets hooks_auto_accept: true), export HERMES_ACCEPT_HOOKS=1, or start Hermes with `hermes --accept-hooks chat`. Until approved, the hooks will not fire.";
@@ -2629,9 +2578,9 @@ function registerHermesCommands(program2) {
2629
2578
 
2630
2579
  // src/commands/hooks.ts
2631
2580
  import { execFileSync } from "child_process";
2632
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2581
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2633
2582
  import { homedir as homedir5 } from "os";
2634
- import { dirname as dirname5, join as join6, resolve } from "path";
2583
+ import { dirname as dirname4, join as join5, resolve } from "path";
2635
2584
  import { Option } from "commander";
2636
2585
  var PRE_COMMIT = { hookName: "pre-commit", binName: "prim-pre-commit" };
2637
2586
  var POST_COMMIT = { hookName: "post-commit", binName: "prim-post-commit" };
@@ -2680,15 +2629,15 @@ ${gatedShim(spec.binName)}
2680
2629
  ${end}`;
2681
2630
  }
2682
2631
  function mergePrimBlock(hookPath, block, binName) {
2683
- if (existsSync7(hookPath)) {
2684
- const existing = readFileSync6(hookPath, "utf-8");
2632
+ if (existsSync6(hookPath)) {
2633
+ const existing = readFileSync5(hookPath, "utf-8");
2685
2634
  if (containsPrimHook(existing, binName)) return false;
2686
2635
  const separator = existing.endsWith("\n") ? "\n" : "\n\n";
2687
2636
  writeFileSync4(hookPath, `${existing}${separator}${block}
2688
2637
  `, { mode: 493 });
2689
2638
  return true;
2690
2639
  }
2691
- mkdirSync5(dirname5(hookPath), { recursive: true });
2640
+ mkdirSync5(dirname4(hookPath), { recursive: true });
2692
2641
  writeFileSync4(hookPath, `#!/bin/sh
2693
2642
  # ${PRIM_CREATED_MARK}
2694
2643
 
@@ -2705,13 +2654,13 @@ function getGitRoot() {
2705
2654
  }
2706
2655
  function detectHusky(gitRoot) {
2707
2656
  const huskyDir = resolve(gitRoot, ".husky");
2708
- if (!existsSync7(huskyDir)) return false;
2709
- if (existsSync7(resolve(huskyDir, "_"))) return true;
2710
- if (existsSync7(resolve(huskyDir, "pre-commit"))) return true;
2657
+ if (!existsSync6(huskyDir)) return false;
2658
+ if (existsSync6(resolve(huskyDir, "_"))) return true;
2659
+ if (existsSync6(resolve(huskyDir, "pre-commit"))) return true;
2711
2660
  const pkgPath = resolve(gitRoot, "package.json");
2712
- if (existsSync7(pkgPath)) {
2661
+ if (existsSync6(pkgPath)) {
2713
2662
  try {
2714
- const pkg2 = JSON.parse(readFileSync6(pkgPath, "utf-8"));
2663
+ const pkg2 = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2715
2664
  const scripts = pkg2.scripts ?? {};
2716
2665
  if (/husky/i.test(scripts.prepare ?? "") || /husky/i.test(scripts.postinstall ?? "")) {
2717
2666
  return true;
@@ -2738,7 +2687,7 @@ async function askConfirmation(question) {
2738
2687
  }
2739
2688
  function installToHusky(gitRoot, spec = PRE_COMMIT) {
2740
2689
  const hookPath = resolve(gitRoot, ".husky", spec.hookName);
2741
- const existed = existsSync7(hookPath);
2690
+ const existed = existsSync6(hookPath);
2742
2691
  const wrote = mergePrimBlock(hookPath, huskyBlock(spec), spec.binName);
2743
2692
  if (!wrote) {
2744
2693
  console.log(`Prim ${spec.hookName} hook is already installed in .husky/${spec.hookName}.`);
@@ -2751,11 +2700,11 @@ function installToHusky(gitRoot, spec = PRE_COMMIT) {
2751
2700
  function installToDotGit(gitRoot, spec = PRE_COMMIT) {
2752
2701
  const hooksDir = resolve(gitRoot, ".git", "hooks");
2753
2702
  const hookPath = resolve(hooksDir, spec.hookName);
2754
- if (!existsSync7(hooksDir)) {
2703
+ if (!existsSync6(hooksDir)) {
2755
2704
  mkdirSync5(hooksDir, { recursive: true });
2756
2705
  }
2757
- if (existsSync7(hookPath)) {
2758
- const existing = readFileSync6(hookPath, "utf-8");
2706
+ if (existsSync6(hookPath)) {
2707
+ const existing = readFileSync5(hookPath, "utf-8");
2759
2708
  if (containsPrimHook(existing, spec.binName)) {
2760
2709
  console.log(`Prim ${spec.hookName} hook is already installed at ${hookPath}.`);
2761
2710
  return;
@@ -2767,9 +2716,9 @@ function installToDotGit(gitRoot, spec = PRE_COMMIT) {
2767
2716
  writeFileSync4(hookPath, dotGitScript(spec), { mode: 493 });
2768
2717
  console.log(`Installed ${spec.hookName} hook at ${hookPath}`);
2769
2718
  }
2770
- var PRIM_GIT_HOOKS_DIR = join6(homedir5(), ".config", "prim", "git-hooks");
2719
+ var PRIM_GIT_HOOKS_DIR = join5(homedir5(), ".config", "prim", "git-hooks");
2771
2720
  function expandTilde(p) {
2772
- return p.startsWith("~/") ? join6(homedir5(), p.slice(2)) : p;
2721
+ return p.startsWith("~/") ? join5(homedir5(), p.slice(2)) : p;
2773
2722
  }
2774
2723
  function isOurHooksDir(value) {
2775
2724
  return value !== "" && expandTilde(value) === PRIM_GIT_HOOKS_DIR;
@@ -2834,7 +2783,7 @@ function ownedHookNames() {
2834
2783
  return [...HOOKS.map((s) => s.hookName), ...PASSTHROUGH_HOOKS];
2835
2784
  }
2836
2785
  function writeOwnHooks() {
2837
- if (!existsSync7(PRIM_GIT_HOOKS_DIR)) {
2786
+ if (!existsSync6(PRIM_GIT_HOOKS_DIR)) {
2838
2787
  mkdirSync5(PRIM_GIT_HOOKS_DIR, { recursive: true });
2839
2788
  }
2840
2789
  for (const spec of HOOKS) {
@@ -2850,8 +2799,8 @@ function appendPrimBlock(hookPath, spec) {
2850
2799
  mergePrimBlock(hookPath, gatedBlock(spec), spec.binName);
2851
2800
  }
2852
2801
  function stripPrimBlock(hookPath, spec) {
2853
- if (!existsSync7(hookPath)) return;
2854
- const existing = readFileSync6(hookPath, "utf-8");
2802
+ if (!existsSync6(hookPath)) return;
2803
+ const existing = readFileSync5(hookPath, "utf-8");
2855
2804
  const primCreated = existing.includes(PRIM_CREATED_MARK);
2856
2805
  const { start, end } = blockMarkers(spec);
2857
2806
  const s = existing.indexOf(start);
@@ -2906,7 +2855,7 @@ function uninstallGlobalHooks() {
2906
2855
  if (isOurHooksDir(global)) {
2907
2856
  for (const name of ownedHookNames()) {
2908
2857
  const p = resolve(PRIM_GIT_HOOKS_DIR, name);
2909
- if (existsSync7(p)) unlinkSync2(p);
2858
+ if (existsSync6(p)) unlinkSync2(p);
2910
2859
  }
2911
2860
  execFileSync("git", ["config", "--global", "--unset", "core.hooksPath"]);
2912
2861
  console.log("Removed prim global git hooks and unset core.hooksPath.");
@@ -2996,11 +2945,11 @@ function registerHooksCommands(program2) {
2996
2945
  const gitRoot = getGitRoot();
2997
2946
  for (const spec of HOOKS) {
2998
2947
  const hookPath = resolve(gitRoot, ".git", "hooks", spec.hookName);
2999
- if (!existsSync7(hookPath)) {
2948
+ if (!existsSync6(hookPath)) {
3000
2949
  console.log(`No ${spec.hookName} hook found.`);
3001
2950
  continue;
3002
2951
  }
3003
- if (containsPrimHook(readFileSync6(hookPath, "utf-8"), spec.binName)) {
2952
+ if (containsPrimHook(readFileSync5(hookPath, "utf-8"), spec.binName)) {
3004
2953
  unlinkSync2(hookPath);
3005
2954
  console.log(`Removed ${spec.hookName} hook at ${hookPath}`);
3006
2955
  } else {
@@ -3011,8 +2960,8 @@ function registerHooksCommands(program2) {
3011
2960
  }
3012
2961
 
3013
2962
  // src/commands/moves.ts
3014
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
3015
- import { join as join7 } from "path";
2963
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
2964
+ import { join as join6 } from "path";
3016
2965
 
3017
2966
  // src/flusher.ts
3018
2967
  import { renameSync as renameSync3, unlinkSync as unlinkSync3 } from "fs";
@@ -3171,19 +3120,19 @@ function registerMovesCommands(program2) {
3171
3120
  }
3172
3121
  });
3173
3122
  moves.command("bind").description("Pin the current directory to an org via .prim/workspace.json").requiredOption("--orgId <orgId>", "Convex organization id").action((opts) => {
3174
- const dir = join7(process.cwd(), ".prim");
3175
- if (!existsSync8(dir)) {
3123
+ const dir = join6(process.cwd(), ".prim");
3124
+ if (!existsSync7(dir)) {
3176
3125
  mkdirSync6(dir, { recursive: true, mode: DIR_MODE });
3177
3126
  }
3178
- const file = join7(process.cwd(), WORKSPACE_FILE);
3127
+ const file = join6(process.cwd(), WORKSPACE_FILE);
3179
3128
  writeFileSync5(file, JSON.stringify({ orgId: opts.orgId, boundAt: Date.now() }, null, 2), {
3180
3129
  mode: FILE_MODE2
3181
3130
  });
3182
3131
  console.log(`[prim] bound ${process.cwd()} to org ${opts.orgId}`);
3183
3132
  });
3184
3133
  moves.command("drop").description("Remove the .prim/workspace.json binding from the cwd").action(() => {
3185
- const file = join7(process.cwd(), WORKSPACE_FILE);
3186
- if (!existsSync8(file)) {
3134
+ const file = join6(process.cwd(), WORKSPACE_FILE);
3135
+ if (!existsSync7(file)) {
3187
3136
  console.log("[prim] no workspace binding in cwd");
3188
3137
  return;
3189
3138
  }
@@ -3276,23 +3225,23 @@ function registerReconcileCommands(program2) {
3276
3225
 
3277
3226
  // src/commands/session.ts
3278
3227
  import {
3279
- existsSync as existsSync9,
3228
+ existsSync as existsSync8,
3280
3229
  mkdirSync as mkdirSync7,
3281
- readFileSync as readFileSync7,
3230
+ readFileSync as readFileSync6,
3282
3231
  readdirSync,
3283
3232
  unlinkSync as unlinkSync5,
3284
3233
  writeFileSync as writeFileSync6
3285
3234
  } from "fs";
3286
- import { join as join8 } from "path";
3235
+ import { join as join7 } from "path";
3287
3236
  var DIR_MODE2 = 448;
3288
3237
  var FILE_MODE3 = 384;
3289
3238
  function ensureDir() {
3290
- if (!existsSync9(SESSIONS_DIR)) {
3239
+ if (!existsSync8(SESSIONS_DIR)) {
3291
3240
  mkdirSync7(SESSIONS_DIR, { recursive: true, mode: DIR_MODE2 });
3292
3241
  }
3293
3242
  }
3294
3243
  function markerPath(sessionId) {
3295
- return join8(SESSIONS_DIR, `${sessionId}.json`);
3244
+ return join7(SESSIONS_DIR, `${sessionId}.json`);
3296
3245
  }
3297
3246
  function registerSessionCommands(program2) {
3298
3247
  const session = program2.command("session").description("Decision Event Pipeline \u2014 session binding markers");
@@ -3308,7 +3257,7 @@ function registerSessionCommands(program2) {
3308
3257
  console.log(`[prim] session ${sessionId} bound to org ${opts.orgId}`);
3309
3258
  });
3310
3259
  session.command("list").description("List active session markers").action(() => {
3311
- if (!existsSync9(SESSIONS_DIR)) {
3260
+ if (!existsSync8(SESSIONS_DIR)) {
3312
3261
  console.log("[prim] no session markers");
3313
3262
  return;
3314
3263
  }
@@ -3320,7 +3269,7 @@ function registerSessionCommands(program2) {
3320
3269
  for (const f of files) {
3321
3270
  const sessionId = f.replace(/\.json$/, "");
3322
3271
  try {
3323
- const m = JSON.parse(readFileSync7(join8(SESSIONS_DIR, f), "utf-8"));
3272
+ const m = JSON.parse(readFileSync6(join7(SESSIONS_DIR, f), "utf-8"));
3324
3273
  console.log(`${sessionId} org=${m.orgId}`);
3325
3274
  } catch {
3326
3275
  }
@@ -3328,7 +3277,7 @@ function registerSessionCommands(program2) {
3328
3277
  });
3329
3278
  session.command("drop <sessionId>").description("Remove a session marker").action((sessionId) => {
3330
3279
  const p = markerPath(sessionId);
3331
- if (!existsSync9(p)) {
3280
+ if (!existsSync8(p)) {
3332
3281
  console.log(`[prim] no marker for session ${sessionId}`);
3333
3282
  return;
3334
3283
  }
@@ -3339,8 +3288,8 @@ function registerSessionCommands(program2) {
3339
3288
 
3340
3289
  // src/commands/setup.ts
3341
3290
  import { spawnSync } from "child_process";
3342
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
3343
- import { join as join9 } from "path";
3291
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
3292
+ import { join as join8 } from "path";
3344
3293
  var EXIT_INCOMPLETE = 1;
3345
3294
  var EXIT_USAGE3 = 2;
3346
3295
  var SESSION_LABELS = {
@@ -3403,8 +3352,8 @@ function detectProjectConflicts(agent, run) {
3403
3352
  }
3404
3353
  try {
3405
3354
  const root = gitToplevel();
3406
- const preCommit = root && join9(root, ".git", "hooks", "pre-commit");
3407
- if (preCommit && existsSync10(preCommit) && readFileSync8(preCommit, "utf-8").includes("prim-pre-commit")) {
3355
+ const preCommit = root && join8(root, ".git", "hooks", "pre-commit");
3356
+ if (preCommit && existsSync9(preCommit) && readFileSync7(preCommit, "utf-8").includes("prim-pre-commit")) {
3408
3357
  conflicts.push(CONFLICT_HOOKS);
3409
3358
  }
3410
3359
  } catch {
@@ -3522,39 +3471,39 @@ function registerSetupCommand(program2) {
3522
3471
  // src/commands/skill.ts
3523
3472
  import {
3524
3473
  closeSync as closeSync4,
3525
- existsSync as existsSync12,
3474
+ existsSync as existsSync11,
3526
3475
  fsyncSync as fsyncSync3,
3527
3476
  openSync as openSync4,
3528
- readFileSync as readFileSync10,
3477
+ readFileSync as readFileSync9,
3529
3478
  renameSync as renameSync4,
3530
3479
  writeFileSync as writeFileSync7
3531
3480
  } from "fs";
3532
3481
  import { homedir as homedir7 } from "os";
3533
- import { dirname as dirname7, join as join11, resolve as resolve3 } from "path";
3534
- import { fileURLToPath as fileURLToPath3 } from "url";
3482
+ import { dirname as dirname6, join as join10, resolve as resolve3 } from "path";
3483
+ import { fileURLToPath as fileURLToPath2 } from "url";
3535
3484
  import { createPatch } from "diff";
3536
3485
 
3537
3486
  // src/commands/claude-plugin.ts
3538
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9, readdirSync as readdirSync2, rmSync as rmSync3, rmdirSync } from "fs";
3487
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync8, readdirSync as readdirSync2, rmSync as rmSync3, rmdirSync } from "fs";
3539
3488
  import { homedir as homedir6 } from "os";
3540
- import { dirname as dirname6, join as join10, resolve as resolve2 } from "path";
3541
- import { fileURLToPath as fileURLToPath2 } from "url";
3542
- var __dirname = dirname6(fileURLToPath2(import.meta.url));
3489
+ import { dirname as dirname5, join as join9, resolve as resolve2 } from "path";
3490
+ import { fileURLToPath } from "url";
3491
+ var __dirname = dirname5(fileURLToPath(import.meta.url));
3543
3492
  var PLUGIN_DESCRIPTION = "Primitive decision-graph guidance for the prim CLI \u2014 a model-invoked skill installed by prim skill install.";
3544
3493
  function resolvePluginDir(cwd, scope) {
3545
3494
  if (scope && scope !== "user" && scope !== "project") {
3546
3495
  console.error(`Unknown --scope "${scope}" (expected user or project)`);
3547
3496
  return null;
3548
3497
  }
3549
- const base = scope === "user" ? join10(homedir6(), ".claude") : join10(gitToplevel(cwd) ?? cwd, ".claude");
3550
- return join10(base, "skills", "prim");
3498
+ const base = scope === "user" ? join9(homedir6(), ".claude") : join9(gitToplevel(cwd) ?? cwd, ".claude");
3499
+ return join9(base, "skills", "prim");
3551
3500
  }
3552
3501
  function packageVersion() {
3553
3502
  let dir = __dirname;
3554
- while (dir !== dirname6(dir)) {
3503
+ while (dir !== dirname5(dir)) {
3555
3504
  const p = resolve2(dir, "package.json");
3556
- if (existsSync11(p)) return JSON.parse(readFileSync9(p, "utf-8")).version;
3557
- dir = dirname6(dir);
3505
+ if (existsSync10(p)) return JSON.parse(readFileSync8(p, "utf-8")).version;
3506
+ dir = dirname5(dir);
3558
3507
  }
3559
3508
  return "0.0.0";
3560
3509
  }
@@ -3564,12 +3513,12 @@ function pluginManifest() {
3564
3513
  `;
3565
3514
  }
3566
3515
  function removeDirIfEmpty(dir) {
3567
- if (existsSync11(dir) && readdirSync2(dir).length === 0) rmdirSync(dir);
3516
+ if (existsSync10(dir) && readdirSync2(dir).length === 0) rmdirSync(dir);
3568
3517
  }
3569
3518
  function pluginPaths(dir) {
3570
3519
  return {
3571
- manifestPath: join10(dir, ".claude-plugin", "plugin.json"),
3572
- skillPath: join10(dir, "SKILL.md")
3520
+ manifestPath: join9(dir, ".claude-plugin", "plugin.json"),
3521
+ skillPath: join9(dir, "SKILL.md")
3573
3522
  };
3574
3523
  }
3575
3524
  function installClaudePlugin(cwd, opts) {
@@ -3578,8 +3527,8 @@ function installClaudePlugin(cwd, opts) {
3578
3527
  const { manifestPath, skillPath } = pluginPaths(dir);
3579
3528
  const manifest = pluginManifest();
3580
3529
  const skill = loadSkill();
3581
- const manifestCurrent = existsSync11(manifestPath) ? readFileSync9(manifestPath, "utf-8") : null;
3582
- const skillCurrent = existsSync11(skillPath) ? readFileSync9(skillPath, "utf-8") : null;
3530
+ const manifestCurrent = existsSync10(manifestPath) ? readFileSync8(manifestPath, "utf-8") : null;
3531
+ const skillCurrent = existsSync10(skillPath) ? readFileSync8(skillPath, "utf-8") : null;
3583
3532
  if (manifestCurrent === manifest && skillCurrent === skill) {
3584
3533
  console.log("No changes \u2014 prim skill plugin already up to date.");
3585
3534
  return 0;
@@ -3588,7 +3537,7 @@ function installClaudePlugin(cwd, opts) {
3588
3537
  console.log(`Would write plugin to ${dir} (.claude-plugin/plugin.json + SKILL.md)`);
3589
3538
  return 0;
3590
3539
  }
3591
- mkdirSync8(join10(dir, ".claude-plugin"), { recursive: true });
3540
+ mkdirSync8(join9(dir, ".claude-plugin"), { recursive: true });
3592
3541
  atomicWrite2(manifestPath, manifest);
3593
3542
  atomicWrite2(skillPath, skill);
3594
3543
  console.log(`Installed prim skill plugin at ${dir}`);
@@ -3602,13 +3551,13 @@ function uninstallClaudePlugin(cwd, opts) {
3602
3551
  const dir = resolvePluginDir(cwd, opts.scope);
3603
3552
  if (dir === null) return 1;
3604
3553
  const { manifestPath, skillPath } = pluginPaths(dir);
3605
- if (!existsSync11(manifestPath) && !existsSync11(skillPath)) {
3554
+ if (!existsSync10(manifestPath) && !existsSync10(skillPath)) {
3606
3555
  console.log(`prim skill plugin not present at ${dir}`);
3607
3556
  return 0;
3608
3557
  }
3609
3558
  rmSync3(manifestPath, { force: true });
3610
3559
  rmSync3(skillPath, { force: true });
3611
- removeDirIfEmpty(join10(dir, ".claude-plugin"));
3560
+ removeDirIfEmpty(join9(dir, ".claude-plugin"));
3612
3561
  removeDirIfEmpty(dir);
3613
3562
  console.log(`Removed prim skill plugin from ${dir}`);
3614
3563
  return 0;
@@ -3617,7 +3566,7 @@ function statusClaudePlugin(cwd, opts) {
3617
3566
  const dir = resolvePluginDir(cwd, opts.scope);
3618
3567
  if (dir === null) return 1;
3619
3568
  const { skillPath } = pluginPaths(dir);
3620
- const installed = existsSync11(skillPath);
3569
+ const installed = existsSync10(skillPath);
3621
3570
  if (opts.json) {
3622
3571
  printJson({ installed, target: dir });
3623
3572
  return installed ? 0 : 1;
@@ -3631,7 +3580,7 @@ function statusClaudePlugin(cwd, opts) {
3631
3580
  }
3632
3581
 
3633
3582
  // src/commands/skill.ts
3634
- var __dirname2 = dirname7(fileURLToPath3(import.meta.url));
3583
+ var __dirname2 = dirname6(fileURLToPath2(import.meta.url));
3635
3584
  var SKILL_BEGIN = "<!-- BEGIN PRIM SKILL v1 -->";
3636
3585
  var SKILL_END = "<!-- END PRIM SKILL v1 -->";
3637
3586
  var TARGET_CANDIDATES = [
@@ -3649,24 +3598,24 @@ var AGENT_TARGET = {
3649
3598
  hermes: ".hermes.md"
3650
3599
  };
3651
3600
  function userTargetFor(agent) {
3652
- if (agent === "claude") return join11(homedir7(), ".claude", "CLAUDE.md");
3653
- if (agent === "codex") return join11(homedir7(), ".codex", "AGENTS.md");
3601
+ if (agent === "claude") return join10(homedir7(), ".claude", "CLAUDE.md");
3602
+ if (agent === "codex") return join10(homedir7(), ".codex", "AGENTS.md");
3654
3603
  if (agent === "hermes") {
3655
- return join11(process.env.HERMES_HOME ?? join11(homedir7(), ".hermes"), ".hermes.md");
3604
+ return join10(process.env.HERMES_HOME ?? join10(homedir7(), ".hermes"), ".hermes.md");
3656
3605
  }
3657
3606
  return null;
3658
3607
  }
3659
3608
  function loadSkill() {
3660
3609
  let dir = __dirname2;
3661
- while (dir !== dirname7(dir)) {
3610
+ while (dir !== dirname6(dir)) {
3662
3611
  const p = resolve3(dir, "SKILL.md");
3663
- if (existsSync12(p)) return readFileSync10(p, "utf-8");
3664
- dir = dirname7(dir);
3612
+ if (existsSync11(p)) return readFileSync9(p, "utf-8");
3613
+ dir = dirname6(dir);
3665
3614
  }
3666
3615
  throw new Error("SKILL.md not found in package");
3667
3616
  }
3668
3617
  function detectTargets(cwd) {
3669
- return TARGET_CANDIDATES.filter((p) => existsSync12(resolve3(cwd, p)));
3618
+ return TARGET_CANDIDATES.filter((p) => existsSync11(resolve3(cwd, p)));
3670
3619
  }
3671
3620
  function detectNewline(content) {
3672
3621
  return content.includes("\r\n") ? "\r\n" : "\n";
@@ -3736,7 +3685,7 @@ function runInstall(cwd, opts) {
3736
3685
  if (opts.agent === "claude" && !opts.target) return installClaudePlugin(cwd, opts);
3737
3686
  const target = resolveTarget(cwd, opts);
3738
3687
  if (target === null) return 1;
3739
- const existing = existsSync12(target) ? readFileSync10(target, "utf-8") : "";
3688
+ const existing = existsSync11(target) ? readFileSync9(target, "utf-8") : "";
3740
3689
  const eol = existing ? detectNewline(existing) : "\n";
3741
3690
  const block = composeBlock(loadSkill(), eol);
3742
3691
  const next = applyBlock(existing, block, eol);
@@ -3756,11 +3705,11 @@ function runUninstall(cwd, opts) {
3756
3705
  if (opts.agent === "claude" && !opts.target) return uninstallClaudePlugin(cwd, opts);
3757
3706
  const target = resolveTarget(cwd, opts);
3758
3707
  if (target === null) return 1;
3759
- if (!existsSync12(target)) {
3708
+ if (!existsSync11(target)) {
3760
3709
  console.log(`Skill block not present at ${target}`);
3761
3710
  return 0;
3762
3711
  }
3763
- const existing = readFileSync10(target, "utf-8");
3712
+ const existing = readFileSync9(target, "utf-8");
3764
3713
  const next = removeBlock(existing);
3765
3714
  if (next === null) {
3766
3715
  console.log(`Skill block not present at ${target}`);
@@ -3774,10 +3723,10 @@ function runStatus(cwd, opts) {
3774
3723
  if (opts.agent === "claude" && !opts.target) return statusClaudePlugin(cwd, opts);
3775
3724
  const target = resolveTarget(cwd, opts);
3776
3725
  if (target === null) return 1;
3777
- const fileExists = existsSync12(target);
3726
+ const fileExists = existsSync11(target);
3778
3727
  let installed = false;
3779
3728
  if (fileExists) {
3780
- const content = readFileSync10(target, "utf-8");
3729
+ const content = readFileSync9(target, "utf-8");
3781
3730
  installed = content.includes(SKILL_BEGIN) && content.includes(SKILL_END);
3782
3731
  }
3783
3732
  if (opts.json) {
@@ -3814,18 +3763,18 @@ function registerSkillCommands(program2) {
3814
3763
  }
3815
3764
 
3816
3765
  // src/commands/statusline.ts
3817
- import { readFileSync as readFileSync11 } from "fs";
3818
- import { dirname as dirname8, resolve as resolve4 } from "path";
3819
- import { fileURLToPath as fileURLToPath4 } from "url";
3766
+ import { readFileSync as readFileSync10 } from "fs";
3767
+ import { dirname as dirname7, resolve as resolve4 } from "path";
3768
+ import { fileURLToPath as fileURLToPath3 } from "url";
3820
3769
  var STATUSLINE_TIMEOUT_MS = 200;
3821
3770
  var STATUSLINE_NAME_CAP = 3;
3822
3771
  function readPackageVersion() {
3823
3772
  try {
3824
- const here = dirname8(fileURLToPath4(import.meta.url));
3773
+ const here = dirname7(fileURLToPath3(import.meta.url));
3825
3774
  const candidates = [resolve4(here, "../../package.json"), resolve4(here, "../package.json")];
3826
3775
  for (const path of candidates) {
3827
3776
  try {
3828
- const pkg2 = JSON.parse(readFileSync11(path, "utf-8"));
3777
+ const pkg2 = JSON.parse(readFileSync10(path, "utf-8"));
3829
3778
  if (pkg2.version) {
3830
3779
  return pkg2.version;
3831
3780
  }
@@ -3874,6 +3823,7 @@ async function renderStatusline() {
3874
3823
  function registerStatuslineCommands(program2) {
3875
3824
  program2.command("statusline").description("Render the Claude Code statusLine for the prim companion daemon").action(async () => {
3876
3825
  try {
3826
+ warmBinCache();
3877
3827
  const line = await renderStatusline();
3878
3828
  process.stdout.write(line);
3879
3829
  } catch {
@@ -3993,8 +3943,8 @@ function registerWelcomeCommand(program2, deps = { getClient }) {
3993
3943
  }
3994
3944
 
3995
3945
  // src/index.ts
3996
- var __dirname3 = dirname9(fileURLToPath5(import.meta.url));
3997
- var pkg = JSON.parse(readFileSync12(resolve5(__dirname3, "../package.json"), "utf-8"));
3946
+ var __dirname3 = dirname8(fileURLToPath4(import.meta.url));
3947
+ var pkg = JSON.parse(readFileSync11(resolve5(__dirname3, "../package.json"), "utf-8"));
3998
3948
  updateNotifier({ pkg }).notify();
3999
3949
  var program = new Command();
4000
3950
  program.name("prim").description("CLI for Primitive's decision graph").version(pkg.version).option("-y, --yes", "auto-confirm prompts").option(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitive.ai/prim",
3
- "version": "0.1.0-alpha.36",
3
+ "version": "0.1.0-alpha.37",
4
4
  "description": "CLI for Primitive's decision graph — passive decision capture, conflict gate, and team presence",
5
5
  "type": "module",
6
6
  "license": "MIT",