@primitive.ai/prim 0.1.0-alpha.67 → 0.1.0-alpha.68

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.
@@ -28,9 +28,12 @@ import {
28
28
  import {
29
29
  scrubFromCwd
30
30
  } from "../chunk-G5D3FF3D.js";
31
+ import {
32
+ prepareCodexContext
33
+ } from "../chunk-I5BHJQH2.js";
31
34
  import {
32
35
  warmBinCache
33
- } from "../chunk-CZ24W5QU.js";
36
+ } from "../chunk-K3AYW6VZ.js";
34
37
  import {
35
38
  isRepoActiveForCapture,
36
39
  repoSyncId,
@@ -41,12 +44,65 @@ import {
41
44
  normalizeEnvelope,
42
45
  parseAgent
43
46
  } from "../chunk-TCDUH7AN.js";
47
+ import "../chunk-UTKQTZHL.js";
44
48
 
45
49
  // src/hooks/prim-hook.ts
46
50
  import { spawn } from "child_process";
47
51
  import { readFileSync } from "fs";
48
52
  import { dirname, join } from "path";
49
53
  import { fileURLToPath } from "url";
54
+
55
+ // src/hooks/codex-message-context.ts
56
+ var defaultDeps = { prepare: prepareCodexContext };
57
+ var STOP_BACKSTOP_INSTRUCTION = "A new Primitive Decision arrived during this turn. Incorporate it into the active task and proactively tell the user about it before finishing.";
58
+ async function processCodexMessageContext(envelope, deps = defaultDeps) {
59
+ const event = envelope.hook_event_name;
60
+ if (event !== "UserPromptSubmit" && event !== "Stop") return { output: {} };
61
+ if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) {
62
+ return { output: {} };
63
+ }
64
+ if (event === "Stop" && envelope.stop_hook_active === true) return { output: {} };
65
+ let prepared;
66
+ try {
67
+ prepared = await deps.prepare({
68
+ cwd: typeof envelope.cwd === "string" ? envelope.cwd : process.cwd(),
69
+ sessionId: envelope.session_id
70
+ });
71
+ } catch {
72
+ return { output: {} };
73
+ }
74
+ if (event === "UserPromptSubmit") {
75
+ return {
76
+ output: prepared.context ? {
77
+ hookSpecificOutput: {
78
+ hookEventName: "UserPromptSubmit",
79
+ additionalContext: prepared.context
80
+ }
81
+ } : {},
82
+ // Even an empty prompt response is a successful handoff. Committing it
83
+ // advances verified feed state and report dedup without inventing output.
84
+ acknowledge: async () => {
85
+ await prepared.acknowledge(true);
86
+ }
87
+ };
88
+ }
89
+ if (!prepared.decisionDigest) {
90
+ return { output: {} };
91
+ }
92
+ return {
93
+ output: {
94
+ decision: "block",
95
+ reason: `${prepared.context ?? prepared.decisionDigest}
96
+
97
+ ${STOP_BACKSTOP_INSTRUCTION}`
98
+ },
99
+ acknowledge: async () => {
100
+ await prepared.acknowledge(true);
101
+ }
102
+ };
103
+ }
104
+
105
+ // src/hooks/prim-hook.ts
50
106
  var here = dirname(fileURLToPath(import.meta.url));
51
107
  var outputAttempted = false;
52
108
  function emitOutput(output, acknowledge) {
@@ -90,8 +146,9 @@ async function main() {
90
146
  }
91
147
  const cwd = parsed.cwd ?? process.cwd();
92
148
  const isClaudeStop = agent === "claude_code" && parsed.hook_event_name === "Stop";
149
+ const isCodexContextEvent = agent === "codex" && (parsed.hook_event_name === "UserPromptSubmit" || parsed.hook_event_name === "Stop");
93
150
  if (!isRepoActiveForCapture(cwd)) {
94
- if (isClaudeStop) await emitOutput(buildHookOutput({}));
151
+ if (isClaudeStop || isCodexContextEvent) await emitOutput(buildHookOutput({}));
95
152
  return;
96
153
  }
97
154
  const identity = getOrCreateWorkspaceId(cwd);
@@ -123,6 +180,11 @@ async function main() {
123
180
  } catch (error) {
124
181
  debug("capture", error);
125
182
  }
183
+ if (isCodexContextEvent) {
184
+ const result = await processCodexMessageContext(parsed);
185
+ await emitOutput(result.output, result.acknowledge);
186
+ return;
187
+ }
126
188
  if (!isClaudeStop) return;
127
189
  const sessionId = typeof parsed.session_id === "string" ? parsed.session_id : "";
128
190
  if (!workspaceId || !sessionId) {
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- daemonRequest
4
- } from "../chunk-UTKQTZHL.js";
5
2
  import {
6
3
  normalizeEnvelope,
7
4
  parseAgent
8
5
  } from "../chunk-TCDUH7AN.js";
6
+ import {
7
+ daemonRequest
8
+ } from "../chunk-UTKQTZHL.js";
9
9
 
10
10
  // src/hooks/session-end.ts
11
11
  var STDIN_TIMEOUT_MS = 1e3;
@@ -19,10 +19,13 @@ import {
19
19
  import {
20
20
  getOrCreateWorkspaceId
21
21
  } from "../chunk-UVIWZWM6.js";
22
+ import {
23
+ prepareCodexContext
24
+ } from "../chunk-I5BHJQH2.js";
22
25
  import {
23
26
  binFile,
24
27
  warmBinCache
25
- } from "../chunk-CZ24W5QU.js";
28
+ } from "../chunk-K3AYW6VZ.js";
26
29
  import {
27
30
  gitToplevel,
28
31
  isRepoActiveForCapture,
@@ -30,16 +33,15 @@ import {
30
33
  setRepoActive
31
34
  } from "../chunk-TDRXHBEX.js";
32
35
  import {
33
- getSiteUrl,
34
36
  isSessionEnded
35
37
  } from "../chunk-RTLFE6EZ.js";
36
- import {
37
- daemonRequest
38
- } from "../chunk-UTKQTZHL.js";
39
38
  import {
40
39
  normalizeEnvelope,
41
40
  parseAgent
42
41
  } from "../chunk-TCDUH7AN.js";
42
+ import {
43
+ daemonRequest
44
+ } from "../chunk-UTKQTZHL.js";
43
45
 
44
46
  // src/daemon/self-heal.ts
45
47
  import { spawn } from "child_process";
@@ -68,7 +70,6 @@ function kickDaemonEnsure(options = {}) {
68
70
  // src/hooks/reauth-notice.ts
69
71
  var REAUTH_NOTICE = "[prim] authentication ended \u2014 run `prim auth login` to resume decision capture";
70
72
  function reauthNoticeFields(agent) {
71
- if (agent === "codex") return { additionalContext: REAUTH_NOTICE };
72
73
  if (agent === "claude_code") return { systemMessage: REAUTH_NOTICE };
73
74
  return;
74
75
  }
@@ -156,7 +157,7 @@ async function processSessionStart(raw, agent, feedbackSignal) {
156
157
  } catch {
157
158
  }
158
159
  }
159
- if (isSessionEnded()) {
160
+ if (isSessionEnded() && agent !== "codex") {
160
161
  const notice = reauthNoticeFields(agent);
161
162
  if (notice) {
162
163
  return {
@@ -165,14 +166,16 @@ async function processSessionStart(raw, agent, feedbackSignal) {
165
166
  }
166
167
  }
167
168
  if (agent === "codex") {
168
- const snapshot = await daemonRequest(
169
- "status_snapshot",
170
- // callerEnv: a cross-env daemon withholds onlineCount, so a prod Codex
171
- // session never gets a staging daemon's team count injected.
172
- { callerEnv: getSiteUrl() },
173
- { timeoutMs: DAEMON_TIMEOUT_MS }
174
- );
175
- const presence = snapshot && !snapshot.presenceStale && typeof snapshot.onlineCount === "number" ? `[prim] team: ${snapshot.onlineCount} online` : void 0;
169
+ let context;
170
+ try {
171
+ context = await prepareCodexContext({
172
+ cwd,
173
+ sessionId: envelope.session_id,
174
+ startup: true,
175
+ includeDigest: false
176
+ });
177
+ } catch {
178
+ }
176
179
  projectRoot = await activeProjectRoot(cwd);
177
180
  active = projectRoot !== null;
178
181
  let proactive = false;
@@ -180,8 +183,13 @@ async function processSessionStart(raw, agent, feedbackSignal) {
180
183
  proactive = projectRoot !== null && active && hasUsableCodexGuidance(projectRoot);
181
184
  } catch {
182
185
  }
183
- const additionalContext = [proactive ? CODEX_PRIM_REMINDER : void 0, presence].filter((value) => value !== void 0).join("\n\n");
184
- return { output: buildHookOutput({ additionalContext }) };
186
+ const additionalContext = [proactive ? CODEX_PRIM_REMINDER : void 0, context?.context].filter((value) => value !== void 0).join("\n\n");
187
+ return {
188
+ output: buildHookOutput({ additionalContext }),
189
+ acknowledge: async () => {
190
+ await context?.acknowledge(true);
191
+ }
192
+ };
185
193
  }
186
194
  if (agent === "claude_code") {
187
195
  const additionalContext = active && skillState.installed > 0 ? PRIM_SKILL_REMINDER : void 0;
package/dist/index.js CHANGED
@@ -19,15 +19,8 @@ import {
19
19
  uninstallProjectPostRewriteHook
20
20
  } from "./chunk-XPVNH3J6.js";
21
21
  import {
22
- bold,
23
- boundedHealthError,
24
- color,
25
- colorForArea,
26
- dim,
27
- requireDurableIngestAcknowledgement,
28
- stripAnsi,
29
- stripControlChars
30
- } from "./chunk-JQFN6LBO.js";
22
+ requireDurableIngestAcknowledgement
23
+ } from "./chunk-XGADQPCI.js";
31
24
  import {
32
25
  checkAffectedDecisions,
33
26
  daemonOrDirectGet,
@@ -50,12 +43,21 @@ import {
50
43
  } from "./chunk-UVIWZWM6.js";
51
44
  import {
52
45
  binFile,
46
+ bold,
47
+ boundedHealthError,
48
+ color,
49
+ colorForArea,
53
50
  commandMatchesBin,
54
51
  detachedHookShimCommand,
52
+ dim,
53
+ formatStatusline,
54
+ formatTeammates,
55
55
  packageVersion,
56
56
  pinnedHookCommand,
57
+ stripAnsi,
58
+ stripControlChars,
57
59
  warmBinCache
58
- } from "./chunk-CZ24W5QU.js";
60
+ } from "./chunk-K3AYW6VZ.js";
59
61
  import {
60
62
  canonicalGitRoot,
61
63
  canonicalRepositoryPath,
@@ -2131,53 +2133,6 @@ async function runLatestDaemonBootstrap(ensureCurrent, options = {}) {
2131
2133
  }
2132
2134
  }
2133
2135
 
2134
- // src/lib/presence.ts
2135
- var DECISION_ORIGIN = "https://app.getprimitive.ai";
2136
- function validDecisionUrl(value) {
2137
- if (typeof value !== "string" || stripControlChars(value) !== value) {
2138
- return void 0;
2139
- }
2140
- try {
2141
- const url = new URL(value);
2142
- if (url.origin !== DECISION_ORIGIN || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !/^\/decisions\/[^/]+$/u.test(url.pathname)) {
2143
- return void 0;
2144
- }
2145
- return url.href;
2146
- } catch {
2147
- return void 0;
2148
- }
2149
- }
2150
- function decisionLink(label, decisionUrl) {
2151
- const url = validDecisionUrl(decisionUrl);
2152
- return url ? `\x1B]8;;${url}\x07\x1B[34;4m${label}\x1B[0m\x1B]8;;\x07` : label;
2153
- }
2154
- function formatLabeled(labels, cap) {
2155
- if (labels === void 0) {
2156
- return "\u2014";
2157
- }
2158
- if (labels.length === 0) {
2159
- return "just you";
2160
- }
2161
- if (labels.length <= cap) {
2162
- return labels.join(", ");
2163
- }
2164
- return `${labels.slice(0, cap).join(", ")} +${String(labels.length - cap)}`;
2165
- }
2166
- function formatTeammates(names, cap) {
2167
- return formatLabeled(names?.map(stripControlChars), cap);
2168
- }
2169
- function formatTeammatesWithArea(teammates, cap) {
2170
- return formatLabeled(
2171
- teammates?.map((t) => {
2172
- const name = stripControlChars(t.name);
2173
- const area = stripControlChars(t.area ?? "").trim();
2174
- const label = area ? `${name} - ${area}` : name;
2175
- return decisionLink(label, t.decisionUrl);
2176
- }),
2177
- cap
2178
- );
2179
- }
2180
-
2181
2136
  // src/commands/daemon.ts
2182
2137
  var DAEMON_BIN = "prim-daemon-server";
2183
2138
  var CONFIG_DIR = join5(homedir5(), ".config", "prim");
@@ -3077,7 +3032,7 @@ async function fetchRecent(args, deps = defaultDeps2) {
3077
3032
  "decisions_recent",
3078
3033
  `/api/cli/decisions/recent?${params.toString()}`,
3079
3034
  client,
3080
- RECENT_TIMEOUT_MS
3035
+ deps.timeoutMs ?? RECENT_TIMEOUT_MS
3081
3036
  );
3082
3037
  if (args.author !== void 0 && res.author === void 0 && res.unavailable === void 0) {
3083
3038
  return {
@@ -6244,48 +6199,6 @@ function registerSetupCommand(program2, dependencies = {}) {
6244
6199
  import { readFileSync as readFileSync9 } from "fs";
6245
6200
  import { dirname as dirname6, resolve as resolve3 } from "path";
6246
6201
  import { fileURLToPath } from "url";
6247
-
6248
- // src/lib/statusline-render.ts
6249
- var STATUSLINE_NAME_CAP = 3;
6250
- function formatStatusline(version, snapshot, resolveIngestionStatus) {
6251
- if (!snapshot) {
6252
- return `primitive ${version} (daemon: down)`;
6253
- }
6254
- if (snapshot.healthy === false) {
6255
- if (snapshot.needsReauth) {
6256
- return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
6257
- }
6258
- if (snapshot.ingestion?.healthy === false) {
6259
- const pending = snapshot.ingestion.pendingCount;
6260
- const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
6261
- return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""})`;
6262
- }
6263
- if (snapshot.heartbeat?.healthy === false) {
6264
- return `primitive ${version} (daemon: degraded \xB7 presence: unavailable)`;
6265
- }
6266
- return `primitive ${version} (daemon: starting)`;
6267
- }
6268
- const ingestionStatus = resolveIngestionStatus();
6269
- if (snapshot.envMismatch) {
6270
- return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: other env)`;
6271
- }
6272
- if (snapshot.presenceStale) {
6273
- return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: stale)`;
6274
- }
6275
- let team;
6276
- if (snapshot.onlineTeammates !== void 0) {
6277
- team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
6278
- } else if (snapshot.onlineNames !== void 0) {
6279
- team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
6280
- } else if (typeof snapshot.onlineCount === "number") {
6281
- team = `team: ${String(snapshot.onlineCount)} online`;
6282
- } else {
6283
- team = "team: \u2014";
6284
- }
6285
- return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 ${team})`;
6286
- }
6287
-
6288
- // src/commands/statusline.ts
6289
6202
  var STATUSLINE_TIMEOUT_MS = 200;
6290
6203
  function readPackageVersion() {
6291
6204
  try {
@@ -245,9 +245,12 @@ function validDecisionUrl(value) {
245
245
  return void 0;
246
246
  }
247
247
  }
248
- function decisionLink(label, decisionUrl) {
248
+ function decisionLink(label, decisionUrl, plain) {
249
249
  const url = validDecisionUrl(decisionUrl);
250
- return url ? `\x1B]8;;${url}\x07\x1B[34;4m${label}\x1B[0m\x1B]8;;\x07` : label;
250
+ if (plain || !url) {
251
+ return label;
252
+ }
253
+ return `\x1B]8;;${url}\x07\x1B[34;4m${label}\x1B[0m\x1B]8;;\x07`;
251
254
  }
252
255
  function formatLabeled(labels, cap) {
253
256
  if (labels === void 0) {
@@ -264,13 +267,13 @@ function formatLabeled(labels, cap) {
264
267
  function formatTeammates(names, cap) {
265
268
  return formatLabeled(names?.map(stripControlChars), cap);
266
269
  }
267
- function formatTeammatesWithArea(teammates, cap) {
270
+ function formatTeammatesWithArea(teammates, cap, plainLinks = false) {
268
271
  return formatLabeled(
269
272
  teammates?.map((t) => {
270
273
  const name = stripControlChars(t.name);
271
274
  const area = stripControlChars(t.area ?? "").trim();
272
275
  const label = area ? `${name} - ${area}` : name;
273
- return decisionLink(label, t.decisionUrl);
276
+ return decisionLink(label, t.decisionUrl, plainLinks);
274
277
  }),
275
278
  cap
276
279
  );
@@ -278,25 +281,26 @@ function formatTeammatesWithArea(teammates, cap) {
278
281
 
279
282
  // src/lib/statusline-render.ts
280
283
  var STATUSLINE_NAME_CAP = 3;
281
- function formatStatusline(version, snapshot, resolveIngestionStatus) {
284
+ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {}) {
285
+ const ingestionSuffix = (status) => status === void 0 ? "" : ` \xB7 Decision ingestion ${status}`;
286
+ const ingestionStatus = snapshot === null || snapshot.healthy === false ? options.includeIngestionWhenUnavailable ? resolveIngestionStatus() : void 0 : resolveIngestionStatus();
282
287
  if (!snapshot) {
283
- return `primitive ${version} (daemon: down)`;
288
+ return `primitive ${version} (daemon: down${ingestionSuffix(ingestionStatus)})`;
284
289
  }
285
290
  if (snapshot.healthy === false) {
286
291
  if (snapshot.needsReauth) {
287
- return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
292
+ return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`${ingestionSuffix(ingestionStatus)})`;
288
293
  }
289
294
  if (snapshot.ingestion?.healthy === false) {
290
295
  const pending = snapshot.ingestion.pendingCount;
291
296
  const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
292
- return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""})`;
297
+ return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${ingestionSuffix(ingestionStatus)})`;
293
298
  }
294
299
  if (snapshot.heartbeat?.healthy === false) {
295
- return `primitive ${version} (daemon: degraded \xB7 presence: unavailable)`;
300
+ return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${ingestionSuffix(ingestionStatus)})`;
296
301
  }
297
- return `primitive ${version} (daemon: starting)`;
302
+ return `primitive ${version} (daemon: starting${ingestionSuffix(ingestionStatus)})`;
298
303
  }
299
- const ingestionStatus = resolveIngestionStatus();
300
304
  if (snapshot.envMismatch) {
301
305
  return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: other env)`;
302
306
  }
@@ -305,7 +309,7 @@ function formatStatusline(version, snapshot, resolveIngestionStatus) {
305
309
  }
306
310
  let team;
307
311
  if (snapshot.onlineTeammates !== void 0) {
308
- team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
312
+ team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP, options.plainLinks === true)}`;
309
313
  } else if (snapshot.onlineNames !== void 0) {
310
314
  team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
311
315
  } else if (typeof snapshot.onlineCount === "number") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitive.ai/prim",
3
- "version": "0.1.0-alpha.67",
3
+ "version": "0.1.0-alpha.68",
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",
@@ -1,123 +0,0 @@
1
- // src/lib/bin-path.ts
2
- import { existsSync, readFileSync } from "fs";
3
- import { dirname, isAbsolute, join } from "path";
4
- import { fileURLToPath } from "url";
5
- var PKG_NAME = "@primitive.ai/prim";
6
- var ROOT_WALK_LIMIT = 6;
7
- var NPX_FALLBACK = `npx --yes -p ${PKG_NAME}@latest`;
8
- var resolvedRoot;
9
- function locateRoot() {
10
- if (resolvedRoot !== void 0) {
11
- return resolvedRoot;
12
- }
13
- let dir = dirname(fileURLToPath(import.meta.url));
14
- for (let depth = 0; depth < ROOT_WALK_LIMIT; depth++) {
15
- const manifestPath = join(dir, "package.json");
16
- if (existsSync(manifestPath)) {
17
- try {
18
- const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
19
- if (manifest.name === PKG_NAME && manifest.bin) {
20
- resolvedRoot = { dir, version: manifest.version, bin: manifest.bin };
21
- return resolvedRoot;
22
- }
23
- } catch {
24
- }
25
- }
26
- const parent = dirname(dir);
27
- if (parent === dir) {
28
- break;
29
- }
30
- dir = parent;
31
- }
32
- resolvedRoot = null;
33
- return resolvedRoot;
34
- }
35
- function binFile(bin) {
36
- const root = locateRoot();
37
- const rel = root?.bin[bin];
38
- if (!root || !rel) {
39
- return null;
40
- }
41
- return isAbsolute(rel) ? rel : join(root.dir, rel);
42
- }
43
- function packageVersion() {
44
- return locateRoot()?.version ?? null;
45
- }
46
- function shellQuote(value) {
47
- return `'${value.replaceAll("'", `'"'"'`)}'`;
48
- }
49
- function pinnedHookCommand(bin, args = "") {
50
- const version = packageVersion();
51
- if (!version) throw new Error("cannot determine Primitive package version");
52
- const suffix = args ? ` ${args}` : "";
53
- const fallback = `npx --yes -p ${PKG_NAME}@${version} ${bin}${suffix}`;
54
- const file = binFile(bin);
55
- if (!file) return fallback;
56
- return `if [ -x ${shellQuote(process.execPath)} ] && [ -f ${shellQuote(file)} ]; then ${shellQuote(process.execPath)} ${shellQuote(file)}${suffix}; else ${fallback}; fi`;
57
- }
58
- function detachedHookShimCommand(bin, args = "") {
59
- 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" | { ${pinnedHookCommand(bin, args)}; }; } </dev/null >/dev/null 2>&1 &`;
60
- }
61
- function commandMatchesBin(command, bin) {
62
- if (!command) {
63
- return false;
64
- }
65
- const c = command.trim();
66
- if (c === bin || c.startsWith(`${bin} `)) {
67
- return true;
68
- }
69
- const exactBin = new RegExp(`(?:^|\\s)${bin.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|;|$)`);
70
- return c.includes(`command -v ${bin} `) || c.includes(`-p ${PKG_NAME}@`) && exactBin.test(c);
71
- }
72
-
73
- // src/lib/bin-cache.ts
74
- import { chmodSync, mkdirSync, renameSync, writeFileSync } from "fs";
75
- import { homedir } from "os";
76
- import { join as join2 } from "path";
77
- var CACHED_BINS = [
78
- "prim",
79
- "prim-hook",
80
- "prim-pre-tool-use",
81
- "prim-post-tool-use",
82
- "prim-post-commit",
83
- "prim-post-rewrite"
84
- ];
85
- function binCacheDir() {
86
- const base = process.env.XDG_CACHE_HOME || join2(homedir(), ".cache");
87
- return join2(base, "prim", "bin");
88
- }
89
- function writeAtomic(path, content) {
90
- const tmp = `${path}.${process.pid}.tmp`;
91
- writeFileSync(tmp, content, { mode: 384 });
92
- renameSync(tmp, path);
93
- }
94
- function warmBinCache() {
95
- try {
96
- if (process.env.PRIM_BIN_CACHE_HIT) {
97
- return;
98
- }
99
- if (process.env.PRIM_BIN_CACHE === "0") {
100
- return;
101
- }
102
- const dir = binCacheDir();
103
- mkdirSync(dir, { recursive: true, mode: 448 });
104
- chmodSync(dir, 448);
105
- writeAtomic(join2(dir, "node"), process.execPath);
106
- for (const bin of CACHED_BINS) {
107
- const file = binFile(bin);
108
- if (file) {
109
- writeAtomic(join2(dir, bin), file);
110
- }
111
- }
112
- } catch {
113
- }
114
- }
115
-
116
- export {
117
- binFile,
118
- packageVersion,
119
- pinnedHookCommand,
120
- detachedHookShimCommand,
121
- commandMatchesBin,
122
- warmBinCache
123
- };
@@ -1,110 +0,0 @@
1
- // src/lib/ansi.ts
2
- var ANSI_CODES = {
3
- yellow: "\x1B[33m",
4
- magenta: "\x1B[35m",
5
- blue: "\x1B[34m",
6
- cyan: "\x1B[36m",
7
- green: "\x1B[32m",
8
- red: "\x1B[31m",
9
- // 256-color approximation of the orange trigger marker (xterm 208).
10
- orange: "\x1B[38;5;208m",
11
- gray: "\x1B[90m"
12
- };
13
- var ANSI_RESET = "\x1B[0m";
14
- var ANSI_DIM = "\x1B[2m";
15
- var ANSI_BOLD = "\x1B[1m";
16
- function supportsColor() {
17
- if (process.env.NO_COLOR !== void 0 && process.env.NO_COLOR !== "") {
18
- return false;
19
- }
20
- return process.stderr.isTTY === true;
21
- }
22
- function color(text, c) {
23
- if (!supportsColor()) {
24
- return text;
25
- }
26
- return `${ANSI_CODES[c]}${text}${ANSI_RESET}`;
27
- }
28
- function dim(text) {
29
- if (!supportsColor()) {
30
- return text;
31
- }
32
- return `${ANSI_DIM}${text}${ANSI_RESET}`;
33
- }
34
- function bold(text) {
35
- if (!supportsColor()) {
36
- return text;
37
- }
38
- return `${ANSI_BOLD}${text}${ANSI_RESET}`;
39
- }
40
- var AREA_COLORS = {
41
- auth: "yellow",
42
- data: "magenta",
43
- mobile: "blue",
44
- infra: "cyan",
45
- ui: "green",
46
- billing: "orange",
47
- api: "blue",
48
- docs: "gray",
49
- testing: "gray"
50
- };
51
- function colorForArea(area) {
52
- if (!area) {
53
- return "gray";
54
- }
55
- return AREA_COLORS[area] ?? "gray";
56
- }
57
- function stripControlChars(text) {
58
- let out = "";
59
- for (const ch of text) {
60
- const code = ch.codePointAt(0) ?? 0;
61
- if (code > 31 && !(code >= 127 && code <= 159)) {
62
- out += ch;
63
- }
64
- }
65
- return out;
66
- }
67
- var MAX_HEALTH_ERROR_LENGTH = 240;
68
- function boundedHealthError(value) {
69
- if (!value) return void 0;
70
- const clean = stripControlChars(value).replace(/\s+/g, " ").trim();
71
- if (!clean) return void 0;
72
- return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
73
- }
74
- function stripAnsi(text) {
75
- return text.replace(/\u001b\[[0-9;]*m/g, "");
76
- }
77
-
78
- // src/ingest-response.ts
79
- var IngestAcknowledgementError = class extends Error {
80
- constructor(message) {
81
- super(message);
82
- this.name = "IngestAcknowledgementError";
83
- }
84
- };
85
- function requireDurableIngestAcknowledgement(value, expected) {
86
- if (typeof value !== "object" || value === null) {
87
- throw new IngestAcknowledgementError("ingest response was not an object");
88
- }
89
- const response = value;
90
- if (response.disposition !== "persisted") {
91
- throw new IngestAcknowledgementError("ingest response did not confirm persistence");
92
- }
93
- if (response.acknowledged !== expected) {
94
- throw new IngestAcknowledgementError(
95
- `ingest acknowledged ${String(response.acknowledged)} of ${String(expected)} moves`
96
- );
97
- }
98
- return value;
99
- }
100
-
101
- export {
102
- color,
103
- dim,
104
- bold,
105
- colorForArea,
106
- stripControlChars,
107
- boundedHealthError,
108
- stripAnsi,
109
- requireDurableIngestAcknowledgement
110
- };