@primitive.ai/prim 0.1.0-alpha.63 → 0.1.0-alpha.64

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.
@@ -1,8 +1,3 @@
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
1
  // src/lib/bin-path.ts
7
2
  import { existsSync, readFileSync } from "fs";
8
3
  import { dirname, isAbsolute, join } from "path";
@@ -76,6 +71,9 @@ function commandMatchesBin(command, bin) {
76
71
  }
77
72
 
78
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";
79
77
  var CACHED_BINS = [
80
78
  "prim",
81
79
  "prim-hook",
@@ -649,6 +649,7 @@ if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
649
649
  case "$prim_commit_sha" in *[!0-9a-f]*|"") prim_commit_sha= ;; esac
650
650
  case "\${#prim_commit_sha}" in 40|64) ;; *) prim_commit_sha= ;; esac
651
651
  if [ -n "$prim_commit_sha" ]; then
652
+ prim_commit_observed_file=$(mktemp "\${TMPDIR:-/tmp}/prim-post-commit-observed.XXXXXXXX" 2>/dev/null) || prim_commit_observed_file=
652
653
  prim_commit_branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null) || prim_commit_branch=
653
654
  prim_cache_dir="\${XDG_CACHE_HOME:-$HOME/.cache}/prim/bin"
654
655
  prim_post_commit_ran=0
@@ -656,18 +657,23 @@ if [ "$(git config --get prim.active 2>/dev/null)" = "true" ]; then
656
657
  prim_node=$(cat "$prim_cache_dir/node")
657
658
  prim_entry=$(cat "$prim_cache_dir/prim-post-commit")
658
659
  if [ -x "$prim_node" ] && [ -f "$prim_entry" ]; then
659
- ( trap '' HUP; export PRIM_COMMIT_SHA="$prim_commit_sha" PRIM_COMMIT_BRANCH="$prim_commit_branch"; "$prim_node" "$prim_entry" ) </dev/null >/dev/null 2>&1 &
660
+ ( trap '' HUP; trap 'if [ -n "$prim_commit_observed_file" ]; then rm -f "$prim_commit_observed_file"; fi' 0; export PRIM_COMMIT_SHA="$prim_commit_sha" PRIM_COMMIT_BRANCH="$prim_commit_branch" PRIM_COMMIT_OBSERVED_FILE="$prim_commit_observed_file"; "$prim_node" "$prim_entry" ) </dev/null >/dev/null 2>&1 &
660
661
  prim_post_commit_ran=1
661
662
  fi
662
663
  fi
663
664
  if [ "$prim_post_commit_ran" -eq 0 ]; then
664
665
  if [ -x "./node_modules/.bin/prim-post-commit" ]; then
665
- ( trap '' HUP; export PRIM_COMMIT_SHA="$prim_commit_sha" PRIM_COMMIT_BRANCH="$prim_commit_branch"; ./node_modules/.bin/prim-post-commit ) </dev/null >/dev/null 2>&1 &
666
+ ( trap '' HUP; trap 'if [ -n "$prim_commit_observed_file" ]; then rm -f "$prim_commit_observed_file"; fi' 0; export PRIM_COMMIT_SHA="$prim_commit_sha" PRIM_COMMIT_BRANCH="$prim_commit_branch" PRIM_COMMIT_OBSERVED_FILE="$prim_commit_observed_file"; ./node_modules/.bin/prim-post-commit ) </dev/null >/dev/null 2>&1 &
667
+ prim_post_commit_ran=1
666
668
  elif command -v npx >/dev/null 2>&1; then
667
- ( trap '' HUP; export PRIM_COMMIT_SHA="$prim_commit_sha" PRIM_COMMIT_BRANCH="$prim_commit_branch"; npx --yes -p @primitive.ai/prim@latest prim-post-commit ) </dev/null >/dev/null 2>&1 &
669
+ ( trap '' HUP; trap 'if [ -n "$prim_commit_observed_file" ]; then rm -f "$prim_commit_observed_file"; fi' 0; export PRIM_COMMIT_SHA="$prim_commit_sha" PRIM_COMMIT_BRANCH="$prim_commit_branch" PRIM_COMMIT_OBSERVED_FILE="$prim_commit_observed_file"; npx --yes -p @primitive.ai/prim@latest prim-post-commit ) </dev/null >/dev/null 2>&1 &
670
+ prim_post_commit_ran=1
668
671
  fi
669
672
  fi
670
- unset prim_cache_dir prim_post_commit_ran prim_node prim_entry prim_commit_branch
673
+ if [ "$prim_post_commit_ran" -eq 0 ] && [ -n "$prim_commit_observed_file" ]; then
674
+ rm -f "$prim_commit_observed_file"
675
+ fi
676
+ unset prim_cache_dir prim_post_commit_ran prim_node prim_entry prim_commit_branch prim_commit_observed_file
671
677
  fi
672
678
  unset prim_commit_sha
673
679
  fi
@@ -1211,7 +1217,7 @@ function uninstallPostCommitHookAtPath(hookPath) {
1211
1217
  }
1212
1218
 
1213
1219
  // src/lib/repository-binding.ts
1214
- async function bindRepository(root, options) {
1220
+ async function resolveRepositoryBinding(root, options) {
1215
1221
  const repositoryFullName = githubRepositoryFullName(root);
1216
1222
  if (!repositoryFullName) {
1217
1223
  throw new Error("origin must be a GitHub HTTPS/SSH remote in owner/name form");
@@ -1224,9 +1230,13 @@ async function bindRepository(root, options) {
1224
1230
  if (!isValidRepoSyncId(response.repoSyncId)) {
1225
1231
  throw new Error("server returned no repository binding");
1226
1232
  }
1227
- setRepoSyncId(root, response.repoSyncId);
1228
1233
  return { repoSyncId: response.repoSyncId, repositoryFullName };
1229
1234
  }
1235
+ async function bindRepository(root, options) {
1236
+ const binding = await resolveRepositoryBinding(root, options);
1237
+ setRepoSyncId(root, binding.repoSyncId);
1238
+ return binding;
1239
+ }
1230
1240
 
1231
1241
  export {
1232
1242
  postCommitHookBlock,
@@ -1235,6 +1245,7 @@ export {
1235
1245
  inspectEffectivePostCommitHook,
1236
1246
  uninstallProjectPostCommitHook,
1237
1247
  uninstallPostCommitHookAtPath,
1248
+ resolveRepositoryBinding,
1238
1249
  bindRepository,
1239
1250
  printJson,
1240
1251
  compareSemver,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  analyzeShellTargets,
3
3
  extractFileTargets
4
- } from "./chunk-GF5QII7T.js";
4
+ } from "./chunk-G5D3FF3D.js";
5
5
  import {
6
6
  canonicalRepositoryPath
7
7
  } from "./chunk-XTNMSS4C.js";
@@ -26,7 +26,7 @@ function preserveHookFileMetadata(payload, resolution) {
26
26
  return { ...payload, primitive };
27
27
  }
28
28
  function resolveHookFileRefs(args) {
29
- const command = args.agent === "claude_code" && args.toolName === "Bash" && args.toolInput && typeof args.toolInput === "object" && typeof args.toolInput.command === "string" ? args.toolInput.command : void 0;
29
+ const command = (args.agent === "claude_code" || args.agent === "codex") && args.toolName === "Bash" && args.toolInput && typeof args.toolInput === "object" && typeof args.toolInput.command === "string" ? args.toolInput.command : void 0;
30
30
  const shell = command === void 0 ? void 0 : analyzeShellTargets(command);
31
31
  const shellMutation = shell?.mutation === "none" ? "none" : shell?.coverage === "complete" ? "resolved" : shell ? "unresolved" : void 0;
32
32
  const readPath = args.agent === "claude_code" && args.toolName === "Read" && args.toolInput && typeof args.toolInput === "object" && typeof args.toolInput.file_path === "string" ? [args.toolInput.file_path] : void 0;
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  commitAttributionFromEnvironment,
4
4
  toCommitMove
5
- } from "../chunk-QGVHLP5J.js";
5
+ } from "../chunk-7X7SFOPJ.js";
6
6
  import {
7
7
  appendMove,
8
8
  resolveOrg
@@ -20,12 +20,30 @@ import "../chunk-CPX3BUOC.js";
20
20
 
21
21
  // src/hooks/post-commit.ts
22
22
  import { execFileSync, spawn } from "child_process";
23
- import { readFileSync } from "fs";
24
- import { dirname, join } from "path";
23
+ import { lstatSync, readFileSync } from "fs";
24
+ import { basename, dirname, isAbsolute, join } from "path";
25
25
  import { fileURLToPath } from "url";
26
26
  var here = dirname(fileURLToPath(import.meta.url));
27
27
  var GIT_TIMEOUT_MS = 1e3;
28
28
  var FULL_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u;
29
+ var OBSERVATION_MARKER_RE = /^prim-post-commit-observed\.[A-Za-z0-9]+$/u;
30
+ var MAX_OBSERVATION_MARKER_AGE_MS = 10 * 60 * 1e3;
31
+ var MAX_OBSERVATION_MARKER_FUTURE_SKEW_MS = 5e3;
32
+ function capturedAtFromLauncher(env = process.env, now = Date.now()) {
33
+ const path = env.PRIM_COMMIT_OBSERVED_FILE;
34
+ if (!path || !isAbsolute(path) || !OBSERVATION_MARKER_RE.test(basename(path))) return;
35
+ try {
36
+ const stat = lstatSync(path);
37
+ const capturedAt = Math.trunc(stat.mtimeMs);
38
+ const uid = process.getuid?.();
39
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size !== 0 || stat.nlink !== 1 || (stat.mode & 63) !== 0 || uid !== void 0 && stat.uid !== uid || !Number.isSafeInteger(capturedAt) || capturedAt <= 0 || capturedAt < now - MAX_OBSERVATION_MARKER_AGE_MS || capturedAt > now + MAX_OBSERVATION_MARKER_FUTURE_SKEW_MS) {
40
+ return;
41
+ }
42
+ return capturedAt;
43
+ } catch {
44
+ return;
45
+ }
46
+ }
29
47
  function gitText(args) {
30
48
  try {
31
49
  return execFileSync("git", args, {
@@ -117,6 +135,10 @@ function runPostCommit() {
117
135
  if (!isRepoActiveForCapture(cwd)) {
118
136
  return;
119
137
  }
138
+ const capturedAt = capturedAtFromLauncher();
139
+ if (process.env.PRIM_COMMIT_OBSERVED_FILE !== void 0 && capturedAt === void 0) {
140
+ return;
141
+ }
120
142
  const commit = readCommit();
121
143
  if (commit) {
122
144
  const repository = resolveRepositoryContext(cwd);
@@ -128,7 +150,8 @@ function runPostCommit() {
128
150
  repoFullName: githubRepositoryFullName(cwd) ?? void 0,
129
151
  repoSyncId: repoSyncId(cwd),
130
152
  workspaceId,
131
- attribution
153
+ attribution,
154
+ ...capturedAt === void 0 ? {} : { capturedAt }
132
155
  });
133
156
  const { orgId } = resolveOrg({ sessionId: move.sessionId, cwd });
134
157
  appendMove(move, orgId);
@@ -148,5 +171,6 @@ if (!process.env.VITEST) {
148
171
  process.exit(0);
149
172
  }
150
173
  export {
174
+ capturedAtFromLauncher,
151
175
  runPostCommit
152
176
  };
@@ -7,24 +7,25 @@ import {
7
7
  import {
8
8
  enrichHookPayloadWithFileRefs,
9
9
  preserveHookFileMetadata
10
- } from "../chunk-DA4BMHEB.js";
10
+ } from "../chunk-VYGY3OOX.js";
11
11
  import {
12
12
  postToolInvocationId,
13
- toMove
14
- } from "../chunk-QGVHLP5J.js";
13
+ toMove,
14
+ toolOutcomeFor
15
+ } from "../chunk-7X7SFOPJ.js";
15
16
  import {
16
17
  appendMove,
17
18
  resolveOrg
18
19
  } from "../chunk-HIMWM526.js";
19
20
  import {
20
21
  scrubFromCwd
21
- } from "../chunk-GF5QII7T.js";
22
+ } from "../chunk-G5D3FF3D.js";
22
23
  import {
23
24
  getOrCreateWorkspaceId
24
25
  } from "../chunk-UVIWZWM6.js";
25
26
  import {
26
27
  warmBinCache
27
- } from "../chunk-KBFFVYBC.js";
28
+ } from "../chunk-LNU44OJV.js";
28
29
  import {
29
30
  isRepoActiveForCapture,
30
31
  repoSyncId,
@@ -74,7 +75,7 @@ function isVerdictFooterContext(value) {
74
75
  // src/hooks/post-tool-use.ts
75
76
  var STDIN_TIMEOUT_MS = 1e3;
76
77
  var EDITING_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit", "Bash"]);
77
- var CODEX_EDITING_TOOLS = /* @__PURE__ */ new Set(["apply_patch"]);
78
+ var CODEX_EDITING_TOOLS = /* @__PURE__ */ new Set(["apply_patch", "Bash"]);
78
79
  var HERMES_EDITING_TOOLS = /* @__PURE__ */ new Set(["write_file", "patch"]);
79
80
  function editingToolsFor(agent) {
80
81
  if (agent === "codex") {
@@ -137,17 +138,27 @@ async function main() {
137
138
  emit();
138
139
  return;
139
140
  }
140
- const envelope = parsed;
141
- if (envelope.hook_event_name !== "PostToolUse") {
141
+ let envelope = parsed;
142
+ const isToolResult = envelope.hook_event_name === "PostToolUse" || agent === "claude_code" && envelope.hook_event_name === "PostToolUseFailure";
143
+ const isHermesDenial = agent === "hermes" && envelope.hook_event_name === "post_approval_response" && toolOutcomeFor(parsed, agent) === "prevented";
144
+ if (!isToolResult && !isHermesDenial) {
145
+ emit();
146
+ return;
147
+ }
148
+ const invocationId = postToolInvocationId(parsed, agent);
149
+ if (isHermesDenial && !invocationId) {
142
150
  emit();
143
151
  return;
144
152
  }
145
153
  const toolName = typeof envelope.tool_name === "string" ? envelope.tool_name : "";
146
- const editingTools = editingToolsFor(agent);
147
- if (!editingTools.has(toolName)) {
154
+ if (!isHermesDenial && !editingToolsFor(agent).has(toolName)) {
148
155
  emit();
149
156
  return;
150
157
  }
158
+ if (isHermesDenial && (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) && typeof envelope.extra?.session_key === "string" && envelope.extra.session_key.length > 0) {
159
+ parsed = { ...parsed, session_id: envelope.extra.session_key };
160
+ envelope = parsed;
161
+ }
151
162
  if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) {
152
163
  emit();
153
164
  return;
@@ -170,18 +181,19 @@ async function main() {
170
181
  repository
171
182
  });
172
183
  const { resolution } = enrichment;
173
- if (resolution.shellMutation === "none") {
174
- emit();
175
- return;
176
- }
177
- if (resolution.shellMutation === void 0 && resolution.fileRefs.length === 0) {
178
- emit();
179
- return;
184
+ if (!isHermesDenial) {
185
+ if (resolution.shellMutation === "none") {
186
+ emit();
187
+ return;
188
+ }
189
+ if (resolution.shellMutation === void 0 && resolution.fileRefs.length === 0) {
190
+ emit();
191
+ return;
192
+ }
180
193
  }
181
194
  const enriched = enrichment.parsed;
182
195
  const identity = getOrCreateWorkspaceId(cwd);
183
196
  const workspaceId = identity.status === "ready" ? identity.workspaceId : void 0;
184
- const invocationId = postToolInvocationId(enriched, agent);
185
197
  const base = toMove(enriched, resolveCliVersion(), agent, workspaceId, repository, invocationId);
186
198
  const scrubbed = scrubFromCwd(enriched, cwd);
187
199
  const move = {
@@ -12,10 +12,11 @@ import {
12
12
  failOpenOutput,
13
13
  readHookMode,
14
14
  scrub
15
- } from "../chunk-GF5QII7T.js";
15
+ } from "../chunk-G5D3FF3D.js";
16
16
  import {
17
+ packageVersion,
17
18
  warmBinCache
18
- } from "../chunk-KBFFVYBC.js";
19
+ } from "../chunk-LNU44OJV.js";
19
20
  import {
20
21
  canonicalGitRoot,
21
22
  canonicalRepositoryPath,
@@ -36,6 +37,11 @@ var PREFLIGHT_PROTOCOL_VERSION = 3;
36
37
  var PREFLIGHT_TIMEOUT_MS = 6500;
37
38
  var MAX_PREFLIGHT_PATHS = 32;
38
39
  var MAX_PROPOSAL_BYTES = 6144;
40
+ var MAX_CLIENT_VERSION_CHARS = 32;
41
+ var CLIENT_VERSION_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
42
+ function boundedClientVersion(raw) {
43
+ return typeof raw === "string" && raw.length <= MAX_CLIENT_VERSION_CHARS && CLIENT_VERSION_RE.test(raw) ? raw : "unknown";
44
+ }
39
45
  function resolvePreflightTargets(args) {
40
46
  let rawPaths;
41
47
  let coverage = "complete";
@@ -224,6 +230,8 @@ async function main() {
224
230
  const request = {
225
231
  protocolVersion: PREFLIGHT_PROTOCOL_VERSION,
226
232
  agent,
233
+ clientMode: mode,
234
+ clientVersion: boundedClientVersion(packageVersion()),
227
235
  sessionId,
228
236
  invocationId: callId,
229
237
  repoSyncId: binding,
@@ -2,19 +2,19 @@
2
2
  import {
3
3
  enrichHookPayloadWithFileRefs,
4
4
  preserveHookFileMetadata
5
- } from "../chunk-DA4BMHEB.js";
5
+ } from "../chunk-VYGY3OOX.js";
6
6
  import {
7
7
  postToolInvocationId,
8
8
  shouldFlushAfter,
9
9
  toMove
10
- } from "../chunk-QGVHLP5J.js";
10
+ } from "../chunk-7X7SFOPJ.js";
11
11
  import {
12
12
  appendMove,
13
13
  resolveOrg
14
14
  } from "../chunk-HIMWM526.js";
15
15
  import {
16
16
  scrubFromCwd
17
- } from "../chunk-GF5QII7T.js";
17
+ } from "../chunk-G5D3FF3D.js";
18
18
  import {
19
19
  buildHookOutput,
20
20
  handoffHookOutput
@@ -30,7 +30,7 @@ import {
30
30
  } from "../chunk-UVIWZWM6.js";
31
31
  import {
32
32
  warmBinCache
33
- } from "../chunk-KBFFVYBC.js";
33
+ } from "../chunk-LNU44OJV.js";
34
34
  import {
35
35
  isRepoActiveForCapture,
36
36
  repoSyncId,
@@ -4,7 +4,7 @@ import {
4
4
  ensureEffectivePostCommitHook,
5
5
  hasUsableCodexGuidance,
6
6
  refreshClaudePlugins
7
- } from "../chunk-WNIY6Z6N.js";
7
+ } from "../chunk-MCPHIB4W.js";
8
8
  import {
9
9
  buildHookOutput,
10
10
  handoffHookOutput
@@ -21,12 +21,11 @@ import {
21
21
  import {
22
22
  binFile,
23
23
  warmBinCache
24
- } from "../chunk-KBFFVYBC.js";
24
+ } from "../chunk-LNU44OJV.js";
25
25
  import {
26
26
  gitToplevel,
27
27
  isRepoActiveForCapture,
28
28
  repoActiveFlag,
29
- repoSyncId,
30
29
  setRepoActive
31
30
  } from "../chunk-XTNMSS4C.js";
32
31
  import {
@@ -102,14 +101,12 @@ async function activeProjectRoot(cwd) {
102
101
  } catch {
103
102
  }
104
103
  }
105
- if (!repoSyncId(root)) {
106
- try {
107
- await bindRepository(root, {
108
- signal: AbortSignal.timeout(REPOSITORY_BIND_TIMEOUT_MS),
109
- quietRefresh: true
110
- });
111
- } catch {
112
- }
104
+ try {
105
+ await bindRepository(root, {
106
+ signal: AbortSignal.timeout(REPOSITORY_BIND_TIMEOUT_MS),
107
+ quietRefresh: true
108
+ });
109
+ } catch {
113
110
  }
114
111
  return root;
115
112
  } catch {
package/dist/index.js CHANGED
@@ -32,9 +32,10 @@ import {
32
32
  postCommitHookBlock,
33
33
  printJson,
34
34
  registerSkillCommands,
35
+ resolveRepositoryBinding,
35
36
  uninstallPostCommitHookAtPath,
36
37
  uninstallProjectPostCommitHook
37
- } from "./chunk-WNIY6Z6N.js";
38
+ } from "./chunk-MCPHIB4W.js";
38
39
  import {
39
40
  fetchFeedbackCapability
40
41
  } from "./chunk-KHZNYEUV.js";
@@ -48,7 +49,7 @@ import {
48
49
  packageVersion,
49
50
  pinnedHookCommand,
50
51
  warmBinCache
51
- } from "./chunk-KBFFVYBC.js";
52
+ } from "./chunk-LNU44OJV.js";
52
53
  import {
53
54
  canonicalGitRoot,
54
55
  canonicalRepositoryPath,
@@ -1473,6 +1474,7 @@ var CAPTURE_EVENTS = [
1473
1474
  "UserPromptSubmit",
1474
1475
  "PreToolUse",
1475
1476
  "PostToolUse",
1477
+ "PostToolUseFailure",
1476
1478
  "Stop",
1477
1479
  "SessionEnd",
1478
1480
  "SubagentStop"
@@ -1494,6 +1496,11 @@ var REGISTRATIONS = [
1494
1496
  ),
1495
1497
  makeRegistration("PreToolUse", "Edit|Write|MultiEdit|NotebookEdit|Bash", GATE_BIN),
1496
1498
  makeRegistration("PostToolUse", "Edit|Write|MultiEdit|NotebookEdit|Bash", POST_TOOL_USE_BIN),
1499
+ makeRegistration(
1500
+ "PostToolUseFailure",
1501
+ "Edit|Write|MultiEdit|NotebookEdit|Bash",
1502
+ POST_TOOL_USE_BIN
1503
+ ),
1497
1504
  makeRegistration("SessionStart", "*", SESSION_START_BIN),
1498
1505
  makeDetachedRegistration("SessionEnd", "*", SESSION_END_BIN)
1499
1506
  ];
@@ -1875,6 +1882,8 @@ var GATE_BIN2 = "prim-pre-tool-use";
1875
1882
  var POST_TOOL_USE_BIN2 = "prim-post-tool-use";
1876
1883
  var SESSION_START_BIN2 = "prim-session-start";
1877
1884
  var CODEX_ARGS = "--agent codex";
1885
+ var CODEX_EDIT_MATCHER = "apply_patch";
1886
+ var CODEX_POST_TOOL_MATCHER = "apply_patch|Bash";
1878
1887
  var JSON_INDENT2 = 2;
1879
1888
  var CODEX_CAPTURE_EVENTS = [
1880
1889
  "SessionStart",
@@ -1887,8 +1896,8 @@ var CODEX_CAPTURE_EVENTS = [
1887
1896
  var PRIM_BINS2 = [CAPTURE_BIN2, GATE_BIN2, POST_TOOL_USE_BIN2, SESSION_START_BIN2];
1888
1897
  var CODEX_REGISTRATIONS = [
1889
1898
  ...CODEX_CAPTURE_EVENTS.map((event) => makeRegistration(event, "*", CAPTURE_BIN2, CODEX_ARGS)),
1890
- makeRegistration("PreToolUse", "apply_patch", GATE_BIN2, CODEX_ARGS),
1891
- makeRegistration("PostToolUse", "apply_patch", POST_TOOL_USE_BIN2, CODEX_ARGS),
1899
+ makeRegistration("PreToolUse", CODEX_EDIT_MATCHER, GATE_BIN2, CODEX_ARGS),
1900
+ makeRegistration("PostToolUse", CODEX_POST_TOOL_MATCHER, POST_TOOL_USE_BIN2, CODEX_ARGS),
1892
1901
  makeRegistration("SessionStart", "*", SESSION_START_BIN2, CODEX_ARGS)
1893
1902
  ];
1894
1903
  var USER_SCOPE_PATH2 = join3(homedir3(), ".codex", "hooks.json");
@@ -3908,19 +3917,46 @@ function checkWorkspaceIdentity() {
3908
3917
  };
3909
3918
  }
3910
3919
  }
3911
- function classifyRepositoryBinding(value) {
3912
- return isValidRepoSyncId(value) ? {
3920
+ function classifyRepositoryBinding(value, currentServerValue) {
3921
+ if (!isValidRepoSyncId(value)) {
3922
+ return {
3923
+ name: "repo-binding",
3924
+ status: "fail",
3925
+ detail: "missing or invalid local prim.repoSyncId \u2014 run `prim enable`"
3926
+ };
3927
+ }
3928
+ if (currentServerValue !== void 0 && value !== currentServerValue) {
3929
+ return {
3930
+ name: "repo-binding",
3931
+ status: "fail",
3932
+ detail: "local repository binding is stale for the current origin \u2014 run `prim enable`"
3933
+ };
3934
+ }
3935
+ return {
3913
3936
  name: "repo-binding",
3914
3937
  status: "ok",
3915
- detail: "valid local repository binding ready"
3916
- } : {
3917
- name: "repo-binding",
3918
- status: "fail",
3919
- detail: "missing or invalid local prim.repoSyncId \u2014 run `prim enable`"
3938
+ detail: currentServerValue === void 0 ? "valid local repository binding ready" : "local repository binding matches the current server binding"
3920
3939
  };
3921
3940
  }
3922
- function checkRepositoryBinding() {
3923
- return classifyRepositoryBinding(repoSyncId(process.cwd()));
3941
+ async function checkRepositoryBinding() {
3942
+ const root = process.cwd();
3943
+ const local = repoSyncId(root);
3944
+ const localCheck = classifyRepositoryBinding(local);
3945
+ if (localCheck.status === "fail") return localCheck;
3946
+ try {
3947
+ const current = await resolveRepositoryBinding(root, {
3948
+ signal: AbortSignal.timeout(CONNECTIVITY_TIMEOUT_MS),
3949
+ quietRefresh: true
3950
+ });
3951
+ return classifyRepositoryBinding(local, current.repoSyncId);
3952
+ } catch (error) {
3953
+ const detail = error instanceof Error ? error.message : String(error);
3954
+ return {
3955
+ name: "repo-binding",
3956
+ status: "fail",
3957
+ detail: `could not verify the current repository binding \u2014 ${detail.slice(0, 80)}`
3958
+ };
3959
+ }
3924
3960
  }
3925
3961
  function classifyPostCommitHook(inspection) {
3926
3962
  if (inspection.covered) {
@@ -4067,7 +4103,7 @@ async function collectChecks() {
4067
4103
  checkStranded(),
4068
4104
  checkFeedbackHooks(),
4069
4105
  checkWorkspaceIdentity(),
4070
- checkRepositoryBinding(),
4106
+ await checkRepositoryBinding(),
4071
4107
  checkPostCommitHook(),
4072
4108
  ...backend,
4073
4109
  await checkFeedbackCapability()
@@ -4176,6 +4212,7 @@ var REGISTRATIONS2 = [
4176
4212
  ...CAPTURE_EVENTS2.map((event) => ({ event, bin: CAPTURE_BIN3 })),
4177
4213
  { event: "pre_tool_call", matcher: EDIT_MATCHER, bin: GATE_BIN3, timeout: GATE_TIMEOUT_SECONDS },
4178
4214
  { event: "post_tool_call", matcher: EDIT_MATCHER, bin: POST_TOOL_USE_BIN3 },
4215
+ { event: "post_approval_response", bin: POST_TOOL_USE_BIN3 },
4179
4216
  { event: "on_session_start", bin: SESSION_START_BIN3 },
4180
4217
  { event: "on_session_end", bin: SESSION_END_BIN2 }
4181
4218
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitive.ai/prim",
3
- "version": "0.1.0-alpha.63",
3
+ "version": "0.1.0-alpha.64",
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",