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

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
+ };
@@ -47,6 +47,7 @@ var activeSessionId = process.env.PRIM_DAEMON_SESSION_ID ?? `daemon-${process.pi
47
47
  var lastHeartbeatAt;
48
48
  var lastOnlineCount;
49
49
  var lastOnlineNames;
50
+ var lastOnlineTeammates;
50
51
  var lastOkAtLocal;
51
52
  var heartbeatTimer;
52
53
  var tokenCheckTimer;
@@ -92,6 +93,7 @@ async function sendHeartbeat() {
92
93
  }
93
94
  lastOnlineCount = typeof result.onlineCount === "number" ? result.onlineCount : void 0;
94
95
  lastOnlineNames = Array.isArray(result.onlineNames) ? result.onlineNames : void 0;
96
+ lastOnlineTeammates = Array.isArray(result.onlineTeammates) ? result.onlineTeammates : void 0;
95
97
  }
96
98
  } catch (err) {
97
99
  process.stderr.write(
@@ -148,6 +150,7 @@ function handleStatusSnapshot(params) {
148
150
  ...base,
149
151
  onlineCount: void 0,
150
152
  onlineNames: void 0,
153
+ onlineTeammates: void 0,
151
154
  presenceStale: false,
152
155
  envMismatch: true
153
156
  };
@@ -156,10 +159,11 @@ function handleStatusSnapshot(params) {
156
159
  const presenceStale = lastOkAtLocal !== void 0 && !presenceFresh;
157
160
  return {
158
161
  ...base,
159
- // Withhold a frozen count/names once they're no longer fresh; the
162
+ // Withhold a frozen count/names/teammates once they're no longer fresh; the
160
163
  // statusline shows "presence: stale" rather than a confident, wrong list.
161
164
  onlineCount: presenceFresh ? lastOnlineCount : void 0,
162
165
  onlineNames: presenceFresh ? lastOnlineNames : void 0,
166
+ onlineTeammates: presenceFresh ? lastOnlineTeammates : void 0,
163
167
  presenceStale
164
168
  };
165
169
  }
@@ -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,30 +1051,42 @@ ${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
- function formatTeammates(names, cap) {
1111
- if (names === void 0) {
1059
+ function formatLabeled(labels, cap) {
1060
+ if (labels === void 0) {
1112
1061
  return "\u2014";
1113
1062
  }
1114
- if (names.length === 0) {
1063
+ if (labels.length === 0) {
1115
1064
  return "just you";
1116
1065
  }
1117
- if (names.length <= cap) {
1118
- return names.join(", ");
1066
+ if (labels.length <= cap) {
1067
+ return labels.join(", ");
1119
1068
  }
1120
- return `${names.slice(0, cap).join(", ")} +${String(names.length - cap)}`;
1069
+ return `${labels.slice(0, cap).join(", ")} +${String(labels.length - cap)}`;
1070
+ }
1071
+ function formatTeammates(names, cap) {
1072
+ return formatLabeled(names, cap);
1073
+ }
1074
+ function formatTeammatesWithArea(teammates, cap) {
1075
+ return formatLabeled(
1076
+ teammates?.map((t) => {
1077
+ const area = t.area?.trim();
1078
+ return area ? `${t.name} - ${area}` : t.name;
1079
+ }),
1080
+ cap
1081
+ );
1121
1082
  }
1122
1083
 
1123
1084
  // src/commands/daemon.ts
1124
1085
  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");
1086
+ var CONFIG_DIR = join3(homedir3(), ".config", "prim");
1087
+ var PID_PATH = join3(CONFIG_DIR, "daemon.pid");
1088
+ var SOCK_PATH = join3(CONFIG_DIR, "sock");
1089
+ var LOG_PATH = join3(CONFIG_DIR, "daemon.log");
1129
1090
  var CONFIG_DIR_MODE = 448;
1130
1091
  var LOG_FILE_MODE = 384;
1131
1092
  var STOP_TIMEOUT_MS = 5e3;
@@ -1138,10 +1099,10 @@ var EXIT_OK2 = 0;
1138
1099
  var EXIT_NOT_RUNNING = 2;
1139
1100
  var EXIT_BOOTING = 3;
1140
1101
  function readPidfile() {
1141
- if (!existsSync4(PID_PATH)) {
1102
+ if (!existsSync3(PID_PATH)) {
1142
1103
  return null;
1143
1104
  }
1144
- const raw = readFileSync4(PID_PATH, "utf-8").trim();
1105
+ const raw = readFileSync3(PID_PATH, "utf-8").trim();
1145
1106
  const pid = Number(raw);
1146
1107
  if (!Number.isInteger(pid) || pid <= 0) {
1147
1108
  return null;
@@ -1177,10 +1138,10 @@ function spawnDaemon(options) {
1177
1138
  return file ? spawn(process.execPath, [file], options) : spawn(DAEMON_BIN, [], options);
1178
1139
  }
1179
1140
  function openDaemonLog(configDir = CONFIG_DIR) {
1180
- if (!existsSync4(configDir)) {
1141
+ if (!existsSync3(configDir)) {
1181
1142
  mkdirSync3(configDir, { recursive: true, mode: CONFIG_DIR_MODE });
1182
1143
  }
1183
- return openSync2(join4(configDir, "daemon.log"), "a", LOG_FILE_MODE);
1144
+ return openSync2(join3(configDir, "daemon.log"), "a", LOG_FILE_MODE);
1184
1145
  }
1185
1146
  async function waitForReady() {
1186
1147
  const deadline = Date.now() + READY_TIMEOUT_MS;
@@ -2171,7 +2132,7 @@ function registerDecisionsCommands(program2) {
2171
2132
  }
2172
2133
 
2173
2134
  // src/commands/doctor.ts
2174
- import { existsSync as existsSync5 } from "fs";
2135
+ import { existsSync as existsSync4 } from "fs";
2175
2136
  var DAEMON_PROBE_TIMEOUT_MS = 500;
2176
2137
  var CONNECTIVITY_TIMEOUT_MS = 3e3;
2177
2138
  var MS_PER_SECOND = 1e3;
@@ -2189,7 +2150,7 @@ function checkAuth() {
2189
2150
  return { name: "auth", status: "fail", detail: "no token \u2014 run `prim auth login`" };
2190
2151
  }
2191
2152
  const expiresAt = getTokenExpiresAt();
2192
- const hasRefresh = existsSync5(REFRESH_TOKEN_PATH);
2153
+ const hasRefresh = existsSync4(REFRESH_TOKEN_PATH);
2193
2154
  if (expiresAt !== void 0 && Date.now() >= expiresAt) {
2194
2155
  return hasRefresh ? { name: "auth", status: "warn", detail: "access token expired (refresh available)" } : {
2195
2156
  name: "auth",
@@ -2289,17 +2250,17 @@ function registerDoctorCommands(program2) {
2289
2250
  import {
2290
2251
  chmodSync,
2291
2252
  closeSync as closeSync3,
2292
- existsSync as existsSync6,
2253
+ existsSync as existsSync5,
2293
2254
  fsyncSync as fsyncSync2,
2294
2255
  mkdirSync as mkdirSync4,
2295
2256
  openSync as openSync3,
2296
- readFileSync as readFileSync5,
2257
+ readFileSync as readFileSync4,
2297
2258
  renameSync as renameSync2,
2298
2259
  rmSync as rmSync2,
2299
2260
  writeFileSync as writeFileSync3
2300
2261
  } from "fs";
2301
2262
  import { homedir as homedir4 } from "os";
2302
- import { dirname as dirname4, join as join5 } from "path";
2263
+ import { dirname as dirname3, join as join4 } from "path";
2303
2264
  import { Document, parseDocument, stringify } from "yaml";
2304
2265
  var CAPTURE_BIN3 = "prim-hook";
2305
2266
  var GATE_BIN3 = "prim-pre-tool-use";
@@ -2318,13 +2279,13 @@ var PRIM_BINS3 = [
2318
2279
  SESSION_END_BIN2
2319
2280
  ];
2320
2281
  function hermesHome() {
2321
- return process.env.HERMES_HOME ?? join5(homedir4(), ".hermes");
2282
+ return process.env.HERMES_HOME ?? join4(homedir4(), ".hermes");
2322
2283
  }
2323
2284
  function configPath() {
2324
- return join5(hermesHome(), "config.yaml");
2285
+ return join4(hermesHome(), "config.yaml");
2325
2286
  }
2326
2287
  function shimPath() {
2327
- return join5(hermesHome(), "agent-hooks", "prim-shim.sh");
2288
+ return join4(hermesHome(), "agent-hooks", "prim-shim.sh");
2328
2289
  }
2329
2290
  var SHIM_SCRIPT = `#!/bin/sh
2330
2291
  # prim Hermes hook shim \u2014 managed by \`prim hermes install\`. Hermes runs hooks
@@ -2413,10 +2374,10 @@ function isCaptureInstalled(hooks) {
2413
2374
  );
2414
2375
  }
2415
2376
  function readDoc(path) {
2416
- if (!existsSync6(path)) {
2377
+ if (!existsSync5(path)) {
2417
2378
  return new Document();
2418
2379
  }
2419
- const raw = readFileSync5(path, "utf-8");
2380
+ const raw = readFileSync4(path, "utf-8");
2420
2381
  return raw.trim().length === 0 ? new Document() : parseDocument(raw);
2421
2382
  }
2422
2383
  function readHooks(doc) {
@@ -2488,8 +2449,8 @@ function setAutoAccept(raw) {
2488
2449
  `;
2489
2450
  }
2490
2451
  function atomicWriteFile(path, content) {
2491
- const dir = dirname4(path);
2492
- if (!existsSync6(dir)) {
2452
+ const dir = dirname3(path);
2453
+ if (!existsSync5(dir)) {
2493
2454
  mkdirSync4(dir, { recursive: true });
2494
2455
  }
2495
2456
  const tmp = `${path}.tmp.${String(process.pid)}`;
@@ -2513,8 +2474,8 @@ function assertMergeValid(before, after) {
2513
2474
  }
2514
2475
  function writeShim() {
2515
2476
  const path = shimPath();
2516
- const dir = dirname4(path);
2517
- if (!existsSync6(dir)) {
2477
+ const dir = dirname3(path);
2478
+ if (!existsSync5(dir)) {
2518
2479
  mkdirSync4(dir, { recursive: true });
2519
2480
  }
2520
2481
  writeFileSync3(path, SHIM_SCRIPT, "utf-8");
@@ -2534,7 +2495,7 @@ function readHooksFromRaw(raw) {
2534
2495
  }
2535
2496
  function performInstall3(opts) {
2536
2497
  const path = configPath();
2537
- const raw = existsSync6(path) ? readFileSync5(path, "utf-8") : "";
2498
+ const raw = existsSync5(path) ? readFileSync4(path, "utf-8") : "";
2538
2499
  const existing = readHooksFromRaw(raw);
2539
2500
  const desired = applyInstall3(existing, opts.force);
2540
2501
  let next = raw;
@@ -2560,11 +2521,11 @@ function performInstall3(opts) {
2560
2521
  }
2561
2522
  function performUninstall3() {
2562
2523
  const path = configPath();
2563
- if (!existsSync6(path)) {
2524
+ if (!existsSync5(path)) {
2564
2525
  removeShim();
2565
2526
  return { path, gate: false, capture: false, autoAccept: false, changed: false };
2566
2527
  }
2567
- const raw = readFileSync5(path, "utf-8");
2528
+ const raw = readFileSync4(path, "utf-8");
2568
2529
  const existing = readHooksFromRaw(raw);
2569
2530
  const remaining = applyUninstall3(existing);
2570
2531
  const next = JSON.stringify(existing) === JSON.stringify(remaining) ? raw : spliceHooks(raw, remaining);
@@ -2584,7 +2545,7 @@ function performUninstall3() {
2584
2545
  }
2585
2546
  function performStatus3() {
2586
2547
  const path = configPath();
2587
- const hooks = existsSync6(path) ? readHooks(readDoc(path)) : {};
2548
+ const hooks = existsSync5(path) ? readHooks(readDoc(path)) : {};
2588
2549
  return { path, gate: isGateInstalled3(hooks), capture: isCaptureInstalled(hooks) };
2589
2550
  }
2590
2551
  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 +2590,9 @@ function registerHermesCommands(program2) {
2629
2590
 
2630
2591
  // src/commands/hooks.ts
2631
2592
  import { execFileSync } from "child_process";
2632
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2593
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2633
2594
  import { homedir as homedir5 } from "os";
2634
- import { dirname as dirname5, join as join6, resolve } from "path";
2595
+ import { dirname as dirname4, join as join5, resolve } from "path";
2635
2596
  import { Option } from "commander";
2636
2597
  var PRE_COMMIT = { hookName: "pre-commit", binName: "prim-pre-commit" };
2637
2598
  var POST_COMMIT = { hookName: "post-commit", binName: "prim-post-commit" };
@@ -2680,15 +2641,15 @@ ${gatedShim(spec.binName)}
2680
2641
  ${end}`;
2681
2642
  }
2682
2643
  function mergePrimBlock(hookPath, block, binName) {
2683
- if (existsSync7(hookPath)) {
2684
- const existing = readFileSync6(hookPath, "utf-8");
2644
+ if (existsSync6(hookPath)) {
2645
+ const existing = readFileSync5(hookPath, "utf-8");
2685
2646
  if (containsPrimHook(existing, binName)) return false;
2686
2647
  const separator = existing.endsWith("\n") ? "\n" : "\n\n";
2687
2648
  writeFileSync4(hookPath, `${existing}${separator}${block}
2688
2649
  `, { mode: 493 });
2689
2650
  return true;
2690
2651
  }
2691
- mkdirSync5(dirname5(hookPath), { recursive: true });
2652
+ mkdirSync5(dirname4(hookPath), { recursive: true });
2692
2653
  writeFileSync4(hookPath, `#!/bin/sh
2693
2654
  # ${PRIM_CREATED_MARK}
2694
2655
 
@@ -2705,13 +2666,13 @@ function getGitRoot() {
2705
2666
  }
2706
2667
  function detectHusky(gitRoot) {
2707
2668
  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;
2669
+ if (!existsSync6(huskyDir)) return false;
2670
+ if (existsSync6(resolve(huskyDir, "_"))) return true;
2671
+ if (existsSync6(resolve(huskyDir, "pre-commit"))) return true;
2711
2672
  const pkgPath = resolve(gitRoot, "package.json");
2712
- if (existsSync7(pkgPath)) {
2673
+ if (existsSync6(pkgPath)) {
2713
2674
  try {
2714
- const pkg2 = JSON.parse(readFileSync6(pkgPath, "utf-8"));
2675
+ const pkg2 = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2715
2676
  const scripts = pkg2.scripts ?? {};
2716
2677
  if (/husky/i.test(scripts.prepare ?? "") || /husky/i.test(scripts.postinstall ?? "")) {
2717
2678
  return true;
@@ -2738,7 +2699,7 @@ async function askConfirmation(question) {
2738
2699
  }
2739
2700
  function installToHusky(gitRoot, spec = PRE_COMMIT) {
2740
2701
  const hookPath = resolve(gitRoot, ".husky", spec.hookName);
2741
- const existed = existsSync7(hookPath);
2702
+ const existed = existsSync6(hookPath);
2742
2703
  const wrote = mergePrimBlock(hookPath, huskyBlock(spec), spec.binName);
2743
2704
  if (!wrote) {
2744
2705
  console.log(`Prim ${spec.hookName} hook is already installed in .husky/${spec.hookName}.`);
@@ -2751,11 +2712,11 @@ function installToHusky(gitRoot, spec = PRE_COMMIT) {
2751
2712
  function installToDotGit(gitRoot, spec = PRE_COMMIT) {
2752
2713
  const hooksDir = resolve(gitRoot, ".git", "hooks");
2753
2714
  const hookPath = resolve(hooksDir, spec.hookName);
2754
- if (!existsSync7(hooksDir)) {
2715
+ if (!existsSync6(hooksDir)) {
2755
2716
  mkdirSync5(hooksDir, { recursive: true });
2756
2717
  }
2757
- if (existsSync7(hookPath)) {
2758
- const existing = readFileSync6(hookPath, "utf-8");
2718
+ if (existsSync6(hookPath)) {
2719
+ const existing = readFileSync5(hookPath, "utf-8");
2759
2720
  if (containsPrimHook(existing, spec.binName)) {
2760
2721
  console.log(`Prim ${spec.hookName} hook is already installed at ${hookPath}.`);
2761
2722
  return;
@@ -2767,9 +2728,9 @@ function installToDotGit(gitRoot, spec = PRE_COMMIT) {
2767
2728
  writeFileSync4(hookPath, dotGitScript(spec), { mode: 493 });
2768
2729
  console.log(`Installed ${spec.hookName} hook at ${hookPath}`);
2769
2730
  }
2770
- var PRIM_GIT_HOOKS_DIR = join6(homedir5(), ".config", "prim", "git-hooks");
2731
+ var PRIM_GIT_HOOKS_DIR = join5(homedir5(), ".config", "prim", "git-hooks");
2771
2732
  function expandTilde(p) {
2772
- return p.startsWith("~/") ? join6(homedir5(), p.slice(2)) : p;
2733
+ return p.startsWith("~/") ? join5(homedir5(), p.slice(2)) : p;
2773
2734
  }
2774
2735
  function isOurHooksDir(value) {
2775
2736
  return value !== "" && expandTilde(value) === PRIM_GIT_HOOKS_DIR;
@@ -2834,7 +2795,7 @@ function ownedHookNames() {
2834
2795
  return [...HOOKS.map((s) => s.hookName), ...PASSTHROUGH_HOOKS];
2835
2796
  }
2836
2797
  function writeOwnHooks() {
2837
- if (!existsSync7(PRIM_GIT_HOOKS_DIR)) {
2798
+ if (!existsSync6(PRIM_GIT_HOOKS_DIR)) {
2838
2799
  mkdirSync5(PRIM_GIT_HOOKS_DIR, { recursive: true });
2839
2800
  }
2840
2801
  for (const spec of HOOKS) {
@@ -2850,8 +2811,8 @@ function appendPrimBlock(hookPath, spec) {
2850
2811
  mergePrimBlock(hookPath, gatedBlock(spec), spec.binName);
2851
2812
  }
2852
2813
  function stripPrimBlock(hookPath, spec) {
2853
- if (!existsSync7(hookPath)) return;
2854
- const existing = readFileSync6(hookPath, "utf-8");
2814
+ if (!existsSync6(hookPath)) return;
2815
+ const existing = readFileSync5(hookPath, "utf-8");
2855
2816
  const primCreated = existing.includes(PRIM_CREATED_MARK);
2856
2817
  const { start, end } = blockMarkers(spec);
2857
2818
  const s = existing.indexOf(start);
@@ -2906,7 +2867,7 @@ function uninstallGlobalHooks() {
2906
2867
  if (isOurHooksDir(global)) {
2907
2868
  for (const name of ownedHookNames()) {
2908
2869
  const p = resolve(PRIM_GIT_HOOKS_DIR, name);
2909
- if (existsSync7(p)) unlinkSync2(p);
2870
+ if (existsSync6(p)) unlinkSync2(p);
2910
2871
  }
2911
2872
  execFileSync("git", ["config", "--global", "--unset", "core.hooksPath"]);
2912
2873
  console.log("Removed prim global git hooks and unset core.hooksPath.");
@@ -2996,11 +2957,11 @@ function registerHooksCommands(program2) {
2996
2957
  const gitRoot = getGitRoot();
2997
2958
  for (const spec of HOOKS) {
2998
2959
  const hookPath = resolve(gitRoot, ".git", "hooks", spec.hookName);
2999
- if (!existsSync7(hookPath)) {
2960
+ if (!existsSync6(hookPath)) {
3000
2961
  console.log(`No ${spec.hookName} hook found.`);
3001
2962
  continue;
3002
2963
  }
3003
- if (containsPrimHook(readFileSync6(hookPath, "utf-8"), spec.binName)) {
2964
+ if (containsPrimHook(readFileSync5(hookPath, "utf-8"), spec.binName)) {
3004
2965
  unlinkSync2(hookPath);
3005
2966
  console.log(`Removed ${spec.hookName} hook at ${hookPath}`);
3006
2967
  } else {
@@ -3011,8 +2972,8 @@ function registerHooksCommands(program2) {
3011
2972
  }
3012
2973
 
3013
2974
  // 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";
2975
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
2976
+ import { join as join6 } from "path";
3016
2977
 
3017
2978
  // src/flusher.ts
3018
2979
  import { renameSync as renameSync3, unlinkSync as unlinkSync3 } from "fs";
@@ -3171,19 +3132,19 @@ function registerMovesCommands(program2) {
3171
3132
  }
3172
3133
  });
3173
3134
  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)) {
3135
+ const dir = join6(process.cwd(), ".prim");
3136
+ if (!existsSync7(dir)) {
3176
3137
  mkdirSync6(dir, { recursive: true, mode: DIR_MODE });
3177
3138
  }
3178
- const file = join7(process.cwd(), WORKSPACE_FILE);
3139
+ const file = join6(process.cwd(), WORKSPACE_FILE);
3179
3140
  writeFileSync5(file, JSON.stringify({ orgId: opts.orgId, boundAt: Date.now() }, null, 2), {
3180
3141
  mode: FILE_MODE2
3181
3142
  });
3182
3143
  console.log(`[prim] bound ${process.cwd()} to org ${opts.orgId}`);
3183
3144
  });
3184
3145
  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)) {
3146
+ const file = join6(process.cwd(), WORKSPACE_FILE);
3147
+ if (!existsSync7(file)) {
3187
3148
  console.log("[prim] no workspace binding in cwd");
3188
3149
  return;
3189
3150
  }
@@ -3276,23 +3237,23 @@ function registerReconcileCommands(program2) {
3276
3237
 
3277
3238
  // src/commands/session.ts
3278
3239
  import {
3279
- existsSync as existsSync9,
3240
+ existsSync as existsSync8,
3280
3241
  mkdirSync as mkdirSync7,
3281
- readFileSync as readFileSync7,
3242
+ readFileSync as readFileSync6,
3282
3243
  readdirSync,
3283
3244
  unlinkSync as unlinkSync5,
3284
3245
  writeFileSync as writeFileSync6
3285
3246
  } from "fs";
3286
- import { join as join8 } from "path";
3247
+ import { join as join7 } from "path";
3287
3248
  var DIR_MODE2 = 448;
3288
3249
  var FILE_MODE3 = 384;
3289
3250
  function ensureDir() {
3290
- if (!existsSync9(SESSIONS_DIR)) {
3251
+ if (!existsSync8(SESSIONS_DIR)) {
3291
3252
  mkdirSync7(SESSIONS_DIR, { recursive: true, mode: DIR_MODE2 });
3292
3253
  }
3293
3254
  }
3294
3255
  function markerPath(sessionId) {
3295
- return join8(SESSIONS_DIR, `${sessionId}.json`);
3256
+ return join7(SESSIONS_DIR, `${sessionId}.json`);
3296
3257
  }
3297
3258
  function registerSessionCommands(program2) {
3298
3259
  const session = program2.command("session").description("Decision Event Pipeline \u2014 session binding markers");
@@ -3308,7 +3269,7 @@ function registerSessionCommands(program2) {
3308
3269
  console.log(`[prim] session ${sessionId} bound to org ${opts.orgId}`);
3309
3270
  });
3310
3271
  session.command("list").description("List active session markers").action(() => {
3311
- if (!existsSync9(SESSIONS_DIR)) {
3272
+ if (!existsSync8(SESSIONS_DIR)) {
3312
3273
  console.log("[prim] no session markers");
3313
3274
  return;
3314
3275
  }
@@ -3320,7 +3281,7 @@ function registerSessionCommands(program2) {
3320
3281
  for (const f of files) {
3321
3282
  const sessionId = f.replace(/\.json$/, "");
3322
3283
  try {
3323
- const m = JSON.parse(readFileSync7(join8(SESSIONS_DIR, f), "utf-8"));
3284
+ const m = JSON.parse(readFileSync6(join7(SESSIONS_DIR, f), "utf-8"));
3324
3285
  console.log(`${sessionId} org=${m.orgId}`);
3325
3286
  } catch {
3326
3287
  }
@@ -3328,7 +3289,7 @@ function registerSessionCommands(program2) {
3328
3289
  });
3329
3290
  session.command("drop <sessionId>").description("Remove a session marker").action((sessionId) => {
3330
3291
  const p = markerPath(sessionId);
3331
- if (!existsSync9(p)) {
3292
+ if (!existsSync8(p)) {
3332
3293
  console.log(`[prim] no marker for session ${sessionId}`);
3333
3294
  return;
3334
3295
  }
@@ -3339,8 +3300,8 @@ function registerSessionCommands(program2) {
3339
3300
 
3340
3301
  // src/commands/setup.ts
3341
3302
  import { spawnSync } from "child_process";
3342
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
3343
- import { join as join9 } from "path";
3303
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
3304
+ import { join as join8 } from "path";
3344
3305
  var EXIT_INCOMPLETE = 1;
3345
3306
  var EXIT_USAGE3 = 2;
3346
3307
  var SESSION_LABELS = {
@@ -3403,8 +3364,8 @@ function detectProjectConflicts(agent, run) {
3403
3364
  }
3404
3365
  try {
3405
3366
  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")) {
3367
+ const preCommit = root && join8(root, ".git", "hooks", "pre-commit");
3368
+ if (preCommit && existsSync9(preCommit) && readFileSync7(preCommit, "utf-8").includes("prim-pre-commit")) {
3408
3369
  conflicts.push(CONFLICT_HOOKS);
3409
3370
  }
3410
3371
  } catch {
@@ -3522,39 +3483,39 @@ function registerSetupCommand(program2) {
3522
3483
  // src/commands/skill.ts
3523
3484
  import {
3524
3485
  closeSync as closeSync4,
3525
- existsSync as existsSync12,
3486
+ existsSync as existsSync11,
3526
3487
  fsyncSync as fsyncSync3,
3527
3488
  openSync as openSync4,
3528
- readFileSync as readFileSync10,
3489
+ readFileSync as readFileSync9,
3529
3490
  renameSync as renameSync4,
3530
3491
  writeFileSync as writeFileSync7
3531
3492
  } from "fs";
3532
3493
  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";
3494
+ import { dirname as dirname6, join as join10, resolve as resolve3 } from "path";
3495
+ import { fileURLToPath as fileURLToPath2 } from "url";
3535
3496
  import { createPatch } from "diff";
3536
3497
 
3537
3498
  // 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";
3499
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync8, readdirSync as readdirSync2, rmSync as rmSync3, rmdirSync } from "fs";
3539
3500
  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));
3501
+ import { dirname as dirname5, join as join9, resolve as resolve2 } from "path";
3502
+ import { fileURLToPath } from "url";
3503
+ var __dirname = dirname5(fileURLToPath(import.meta.url));
3543
3504
  var PLUGIN_DESCRIPTION = "Primitive decision-graph guidance for the prim CLI \u2014 a model-invoked skill installed by prim skill install.";
3544
3505
  function resolvePluginDir(cwd, scope) {
3545
3506
  if (scope && scope !== "user" && scope !== "project") {
3546
3507
  console.error(`Unknown --scope "${scope}" (expected user or project)`);
3547
3508
  return null;
3548
3509
  }
3549
- const base = scope === "user" ? join10(homedir6(), ".claude") : join10(gitToplevel(cwd) ?? cwd, ".claude");
3550
- return join10(base, "skills", "prim");
3510
+ const base = scope === "user" ? join9(homedir6(), ".claude") : join9(gitToplevel(cwd) ?? cwd, ".claude");
3511
+ return join9(base, "skills", "prim");
3551
3512
  }
3552
3513
  function packageVersion() {
3553
3514
  let dir = __dirname;
3554
- while (dir !== dirname6(dir)) {
3515
+ while (dir !== dirname5(dir)) {
3555
3516
  const p = resolve2(dir, "package.json");
3556
- if (existsSync11(p)) return JSON.parse(readFileSync9(p, "utf-8")).version;
3557
- dir = dirname6(dir);
3517
+ if (existsSync10(p)) return JSON.parse(readFileSync8(p, "utf-8")).version;
3518
+ dir = dirname5(dir);
3558
3519
  }
3559
3520
  return "0.0.0";
3560
3521
  }
@@ -3564,12 +3525,12 @@ function pluginManifest() {
3564
3525
  `;
3565
3526
  }
3566
3527
  function removeDirIfEmpty(dir) {
3567
- if (existsSync11(dir) && readdirSync2(dir).length === 0) rmdirSync(dir);
3528
+ if (existsSync10(dir) && readdirSync2(dir).length === 0) rmdirSync(dir);
3568
3529
  }
3569
3530
  function pluginPaths(dir) {
3570
3531
  return {
3571
- manifestPath: join10(dir, ".claude-plugin", "plugin.json"),
3572
- skillPath: join10(dir, "SKILL.md")
3532
+ manifestPath: join9(dir, ".claude-plugin", "plugin.json"),
3533
+ skillPath: join9(dir, "SKILL.md")
3573
3534
  };
3574
3535
  }
3575
3536
  function installClaudePlugin(cwd, opts) {
@@ -3578,8 +3539,8 @@ function installClaudePlugin(cwd, opts) {
3578
3539
  const { manifestPath, skillPath } = pluginPaths(dir);
3579
3540
  const manifest = pluginManifest();
3580
3541
  const skill = loadSkill();
3581
- const manifestCurrent = existsSync11(manifestPath) ? readFileSync9(manifestPath, "utf-8") : null;
3582
- const skillCurrent = existsSync11(skillPath) ? readFileSync9(skillPath, "utf-8") : null;
3542
+ const manifestCurrent = existsSync10(manifestPath) ? readFileSync8(manifestPath, "utf-8") : null;
3543
+ const skillCurrent = existsSync10(skillPath) ? readFileSync8(skillPath, "utf-8") : null;
3583
3544
  if (manifestCurrent === manifest && skillCurrent === skill) {
3584
3545
  console.log("No changes \u2014 prim skill plugin already up to date.");
3585
3546
  return 0;
@@ -3588,7 +3549,7 @@ function installClaudePlugin(cwd, opts) {
3588
3549
  console.log(`Would write plugin to ${dir} (.claude-plugin/plugin.json + SKILL.md)`);
3589
3550
  return 0;
3590
3551
  }
3591
- mkdirSync8(join10(dir, ".claude-plugin"), { recursive: true });
3552
+ mkdirSync8(join9(dir, ".claude-plugin"), { recursive: true });
3592
3553
  atomicWrite2(manifestPath, manifest);
3593
3554
  atomicWrite2(skillPath, skill);
3594
3555
  console.log(`Installed prim skill plugin at ${dir}`);
@@ -3602,13 +3563,13 @@ function uninstallClaudePlugin(cwd, opts) {
3602
3563
  const dir = resolvePluginDir(cwd, opts.scope);
3603
3564
  if (dir === null) return 1;
3604
3565
  const { manifestPath, skillPath } = pluginPaths(dir);
3605
- if (!existsSync11(manifestPath) && !existsSync11(skillPath)) {
3566
+ if (!existsSync10(manifestPath) && !existsSync10(skillPath)) {
3606
3567
  console.log(`prim skill plugin not present at ${dir}`);
3607
3568
  return 0;
3608
3569
  }
3609
3570
  rmSync3(manifestPath, { force: true });
3610
3571
  rmSync3(skillPath, { force: true });
3611
- removeDirIfEmpty(join10(dir, ".claude-plugin"));
3572
+ removeDirIfEmpty(join9(dir, ".claude-plugin"));
3612
3573
  removeDirIfEmpty(dir);
3613
3574
  console.log(`Removed prim skill plugin from ${dir}`);
3614
3575
  return 0;
@@ -3617,7 +3578,7 @@ function statusClaudePlugin(cwd, opts) {
3617
3578
  const dir = resolvePluginDir(cwd, opts.scope);
3618
3579
  if (dir === null) return 1;
3619
3580
  const { skillPath } = pluginPaths(dir);
3620
- const installed = existsSync11(skillPath);
3581
+ const installed = existsSync10(skillPath);
3621
3582
  if (opts.json) {
3622
3583
  printJson({ installed, target: dir });
3623
3584
  return installed ? 0 : 1;
@@ -3631,7 +3592,7 @@ function statusClaudePlugin(cwd, opts) {
3631
3592
  }
3632
3593
 
3633
3594
  // src/commands/skill.ts
3634
- var __dirname2 = dirname7(fileURLToPath3(import.meta.url));
3595
+ var __dirname2 = dirname6(fileURLToPath2(import.meta.url));
3635
3596
  var SKILL_BEGIN = "<!-- BEGIN PRIM SKILL v1 -->";
3636
3597
  var SKILL_END = "<!-- END PRIM SKILL v1 -->";
3637
3598
  var TARGET_CANDIDATES = [
@@ -3649,24 +3610,24 @@ var AGENT_TARGET = {
3649
3610
  hermes: ".hermes.md"
3650
3611
  };
3651
3612
  function userTargetFor(agent) {
3652
- if (agent === "claude") return join11(homedir7(), ".claude", "CLAUDE.md");
3653
- if (agent === "codex") return join11(homedir7(), ".codex", "AGENTS.md");
3613
+ if (agent === "claude") return join10(homedir7(), ".claude", "CLAUDE.md");
3614
+ if (agent === "codex") return join10(homedir7(), ".codex", "AGENTS.md");
3654
3615
  if (agent === "hermes") {
3655
- return join11(process.env.HERMES_HOME ?? join11(homedir7(), ".hermes"), ".hermes.md");
3616
+ return join10(process.env.HERMES_HOME ?? join10(homedir7(), ".hermes"), ".hermes.md");
3656
3617
  }
3657
3618
  return null;
3658
3619
  }
3659
3620
  function loadSkill() {
3660
3621
  let dir = __dirname2;
3661
- while (dir !== dirname7(dir)) {
3622
+ while (dir !== dirname6(dir)) {
3662
3623
  const p = resolve3(dir, "SKILL.md");
3663
- if (existsSync12(p)) return readFileSync10(p, "utf-8");
3664
- dir = dirname7(dir);
3624
+ if (existsSync11(p)) return readFileSync9(p, "utf-8");
3625
+ dir = dirname6(dir);
3665
3626
  }
3666
3627
  throw new Error("SKILL.md not found in package");
3667
3628
  }
3668
3629
  function detectTargets(cwd) {
3669
- return TARGET_CANDIDATES.filter((p) => existsSync12(resolve3(cwd, p)));
3630
+ return TARGET_CANDIDATES.filter((p) => existsSync11(resolve3(cwd, p)));
3670
3631
  }
3671
3632
  function detectNewline(content) {
3672
3633
  return content.includes("\r\n") ? "\r\n" : "\n";
@@ -3736,7 +3697,7 @@ function runInstall(cwd, opts) {
3736
3697
  if (opts.agent === "claude" && !opts.target) return installClaudePlugin(cwd, opts);
3737
3698
  const target = resolveTarget(cwd, opts);
3738
3699
  if (target === null) return 1;
3739
- const existing = existsSync12(target) ? readFileSync10(target, "utf-8") : "";
3700
+ const existing = existsSync11(target) ? readFileSync9(target, "utf-8") : "";
3740
3701
  const eol = existing ? detectNewline(existing) : "\n";
3741
3702
  const block = composeBlock(loadSkill(), eol);
3742
3703
  const next = applyBlock(existing, block, eol);
@@ -3756,11 +3717,11 @@ function runUninstall(cwd, opts) {
3756
3717
  if (opts.agent === "claude" && !opts.target) return uninstallClaudePlugin(cwd, opts);
3757
3718
  const target = resolveTarget(cwd, opts);
3758
3719
  if (target === null) return 1;
3759
- if (!existsSync12(target)) {
3720
+ if (!existsSync11(target)) {
3760
3721
  console.log(`Skill block not present at ${target}`);
3761
3722
  return 0;
3762
3723
  }
3763
- const existing = readFileSync10(target, "utf-8");
3724
+ const existing = readFileSync9(target, "utf-8");
3764
3725
  const next = removeBlock(existing);
3765
3726
  if (next === null) {
3766
3727
  console.log(`Skill block not present at ${target}`);
@@ -3774,10 +3735,10 @@ function runStatus(cwd, opts) {
3774
3735
  if (opts.agent === "claude" && !opts.target) return statusClaudePlugin(cwd, opts);
3775
3736
  const target = resolveTarget(cwd, opts);
3776
3737
  if (target === null) return 1;
3777
- const fileExists = existsSync12(target);
3738
+ const fileExists = existsSync11(target);
3778
3739
  let installed = false;
3779
3740
  if (fileExists) {
3780
- const content = readFileSync10(target, "utf-8");
3741
+ const content = readFileSync9(target, "utf-8");
3781
3742
  installed = content.includes(SKILL_BEGIN) && content.includes(SKILL_END);
3782
3743
  }
3783
3744
  if (opts.json) {
@@ -3814,18 +3775,18 @@ function registerSkillCommands(program2) {
3814
3775
  }
3815
3776
 
3816
3777
  // 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";
3778
+ import { readFileSync as readFileSync10 } from "fs";
3779
+ import { dirname as dirname7, resolve as resolve4 } from "path";
3780
+ import { fileURLToPath as fileURLToPath3 } from "url";
3820
3781
  var STATUSLINE_TIMEOUT_MS = 200;
3821
3782
  var STATUSLINE_NAME_CAP = 3;
3822
3783
  function readPackageVersion() {
3823
3784
  try {
3824
- const here = dirname8(fileURLToPath4(import.meta.url));
3785
+ const here = dirname7(fileURLToPath3(import.meta.url));
3825
3786
  const candidates = [resolve4(here, "../../package.json"), resolve4(here, "../package.json")];
3826
3787
  for (const path of candidates) {
3827
3788
  try {
3828
- const pkg2 = JSON.parse(readFileSync11(path, "utf-8"));
3789
+ const pkg2 = JSON.parse(readFileSync10(path, "utf-8"));
3829
3790
  if (pkg2.version) {
3830
3791
  return pkg2.version;
3831
3792
  }
@@ -3862,7 +3823,9 @@ async function renderStatusline() {
3862
3823
  return `primitive ${version} (daemon: live \xB7 presence: stale)`;
3863
3824
  }
3864
3825
  let team;
3865
- if (snapshot.onlineNames !== void 0) {
3826
+ if (snapshot.onlineTeammates !== void 0) {
3827
+ team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
3828
+ } else if (snapshot.onlineNames !== void 0) {
3866
3829
  team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
3867
3830
  } else if (typeof snapshot.onlineCount === "number") {
3868
3831
  team = `team: ${String(snapshot.onlineCount)} online`;
@@ -3874,6 +3837,7 @@ async function renderStatusline() {
3874
3837
  function registerStatuslineCommands(program2) {
3875
3838
  program2.command("statusline").description("Render the Claude Code statusLine for the prim companion daemon").action(async () => {
3876
3839
  try {
3840
+ warmBinCache();
3877
3841
  const line = await renderStatusline();
3878
3842
  process.stdout.write(line);
3879
3843
  } catch {
@@ -3993,8 +3957,8 @@ function registerWelcomeCommand(program2, deps = { getClient }) {
3993
3957
  }
3994
3958
 
3995
3959
  // src/index.ts
3996
- var __dirname3 = dirname9(fileURLToPath5(import.meta.url));
3997
- var pkg = JSON.parse(readFileSync12(resolve5(__dirname3, "../package.json"), "utf-8"));
3960
+ var __dirname3 = dirname8(fileURLToPath4(import.meta.url));
3961
+ var pkg = JSON.parse(readFileSync11(resolve5(__dirname3, "../package.json"), "utf-8"));
3998
3962
  updateNotifier({ pkg }).notify();
3999
3963
  var program = new Command();
4000
3964
  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.38",
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",