@primitive.ai/prim 0.1.0-alpha.94 → 0.1.0-alpha.96

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.
Files changed (40) hide show
  1. package/README.md +7 -4
  2. package/dist/{chunk-UKUAAAXE.js → chunk-27ADT6FQ.js} +2 -2
  3. package/dist/{chunk-OTHKG76J.js → chunk-35FMRWJH.js} +1 -1
  4. package/dist/{chunk-DXJQR4QI.js → chunk-4GHANPWQ.js} +2 -2
  5. package/dist/{chunk-WUXFLE7X.js → chunk-7KABYYWG.js} +64 -66
  6. package/dist/{chunk-LFFLHSQO.js → chunk-CU7RW7XU.js} +63 -6
  7. package/dist/{chunk-H4UNYFUC.js → chunk-DHOQGHKQ.js} +184 -1
  8. package/dist/{chunk-2GP5UFV7.js → chunk-DWRSEC34.js} +5 -3
  9. package/dist/{chunk-O7YAW7G6.js → chunk-FVDBULGZ.js} +9 -11
  10. package/dist/{chunk-G2VMA2QO.js → chunk-LY6NJ7KL.js} +6 -4
  11. package/dist/{chunk-ZB4F75SZ.js → chunk-MXVCP33E.js} +5 -1
  12. package/dist/{chunk-TYAHC25F.js → chunk-QCBB5HFX.js} +1 -1
  13. package/dist/{chunk-IMK4JKTA.js → chunk-RK4ZS3HG.js} +2 -2
  14. package/dist/{chunk-ZU563PQI.js → chunk-ROLYLXJS.js} +2 -2
  15. package/dist/{chunk-OH3EZXYX.js → chunk-UO2U6IKF.js} +1 -1
  16. package/dist/{chunk-K7D2EYBS.js → chunk-URLLSALP.js} +2 -2
  17. package/dist/{chunk-FXF4JWK2.js → chunk-XEDJ5N3M.js} +17 -2
  18. package/dist/{chunk-AXQQMX2U.js → chunk-Y5W4TZM7.js} +2 -2
  19. package/dist/{chunk-ICM2XCXS.js → chunk-YYE2NLWX.js} +2 -2
  20. package/dist/{chunk-DSWT6BCC.js → chunk-Z6NPNLOT.js} +825 -665
  21. package/dist/daemon/server.js +836 -540
  22. package/dist/hook-runtime/daemon/server.js +836 -540
  23. package/dist/hook-runtime/hooks/post-commit.js +836 -540
  24. package/dist/hook-runtime/hooks/post-rewrite.js +836 -540
  25. package/dist/hook-runtime/hooks/post-tool-use.js +1160 -791
  26. package/dist/hook-runtime/hooks/pre-tool-use.js +2060 -1751
  27. package/dist/hook-runtime/hooks/prim-hook.js +1257 -856
  28. package/dist/hook-runtime/hooks/session-end.js +179 -129
  29. package/dist/hook-runtime/hooks/session-start.js +938 -640
  30. package/dist/hook-runtime/index.js +892 -554
  31. package/dist/hooks/post-commit.js +11 -11
  32. package/dist/hooks/post-rewrite.js +10 -10
  33. package/dist/hooks/post-tool-use.js +20 -20
  34. package/dist/hooks/pre-commit.js +4 -4
  35. package/dist/hooks/pre-tool-use.js +29 -17
  36. package/dist/hooks/prim-hook.js +22 -18
  37. package/dist/hooks/session-end.js +11 -5
  38. package/dist/hooks/session-start.js +26 -22
  39. package/dist/index.js +79 -60
  40. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19,24 +19,25 @@ import {
19
19
  resolveRepositoryBinding,
20
20
  uninstallGitHookAtPath,
21
21
  uninstallProjectGitHook
22
- } from "./chunk-WUXFLE7X.js";
23
- import {
24
- POST_COMMIT_GRACE_MS,
25
- clearHooksWired,
26
- inspectPostCommitFiring,
27
- recordHooksWired
28
- } from "./chunk-FXF4JWK2.js";
22
+ } from "./chunk-7KABYYWG.js";
29
23
  import {
30
24
  requireDurableIngestAcknowledgement
31
- } from "./chunk-TYAHC25F.js";
25
+ } from "./chunk-QCBB5HFX.js";
32
26
  import {
33
27
  checkAffectedDecisions,
34
28
  daemonOrDirectGet,
35
29
  formatDecisionsWarning
36
- } from "./chunk-UKUAAAXE.js";
30
+ } from "./chunk-27ADT6FQ.js";
37
31
  import {
38
32
  fetchFeedbackCapability
39
- } from "./chunk-OH3EZXYX.js";
33
+ } from "./chunk-UO2U6IKF.js";
34
+ import {
35
+ POST_COMMIT_GRACE_MS,
36
+ clearHooksWired,
37
+ inspectPostCommitFiring,
38
+ lastConversationCaptured,
39
+ recordHooksWired
40
+ } from "./chunk-XEDJ5N3M.js";
40
41
  import {
41
42
  isUnattended,
42
43
  unattendedEnv
@@ -55,7 +56,7 @@ import {
55
56
  rotationIdentity,
56
57
  sweepOrphanedDrainProgress,
57
58
  writeDrainCheckpoint
58
- } from "./chunk-DXJQR4QI.js";
59
+ } from "./chunk-4GHANPWQ.js";
59
60
  import {
60
61
  MS_PER_SECOND,
61
62
  deliveryBacklogState,
@@ -64,13 +65,12 @@ import {
64
65
  formatStatusline,
65
66
  formatTeammates,
66
67
  repositoryBindingDiagnosticLabel
67
- } from "./chunk-ZU563PQI.js";
68
+ } from "./chunk-ROLYLXJS.js";
68
69
  import {
69
70
  bold,
70
71
  boundedHealthError,
71
72
  color,
72
73
  colorForArea,
73
- decisionIngestionStatus,
74
74
  dim,
75
75
  fetchAndCacheCollectScope,
76
76
  isCliErrorResponse,
@@ -83,14 +83,8 @@ import {
83
83
  isGitHubInstallIntentStartResponse,
84
84
  isGitHubInstallIntentStatusResponse,
85
85
  isMoveIngestRequest,
86
- isRepoActiveForCapture,
87
- isValidRepoSyncId,
88
- repoActiveFlag,
89
- repoSyncId,
90
- repositoryBindingState,
91
- setRepoActive,
92
86
  stripAnsi
93
- } from "./chunk-DSWT6BCC.js";
87
+ } from "./chunk-Z6NPNLOT.js";
94
88
  import {
95
89
  CliManagementResponseError,
96
90
  HttpError,
@@ -107,7 +101,7 @@ import {
107
101
  setStoredToken,
108
102
  stripControlChars,
109
103
  terminalSafeLine
110
- } from "./chunk-G2VMA2QO.js";
104
+ } from "./chunk-LY6NJ7KL.js";
111
105
  import {
112
106
  MANAGED_GIT_HOOK_NAMES,
113
107
  assertOwnedHookRuntime,
@@ -139,16 +133,24 @@ import {
139
133
  stableNodePath,
140
134
  stageHookRuntime,
141
135
  stripCommand
142
- } from "./chunk-AXQQMX2U.js";
136
+ } from "./chunk-Y5W4TZM7.js";
143
137
  import {
144
138
  canonicalGitRoot,
145
139
  canonicalRepositoryPath,
146
- gitToplevel
147
- } from "./chunk-H4UNYFUC.js";
140
+ decisionIngestionStatus,
141
+ gitToplevel,
142
+ isRepoActiveForCapture,
143
+ isValidRepoSyncId,
144
+ mainWorktreeRoot,
145
+ repoActiveFlag,
146
+ repoSyncId,
147
+ repositoryBindingState,
148
+ setRepoActive
149
+ } from "./chunk-DHOQGHKQ.js";
148
150
  import {
149
151
  daemonIsLive,
150
152
  daemonRequest
151
- } from "./chunk-OTHKG76J.js";
153
+ } from "./chunk-35FMRWJH.js";
152
154
  import {
153
155
  REFRESH_TOKEN_PATH,
154
156
  TOKEN_FILE_PATH,
@@ -163,7 +165,7 @@ import {
163
165
  syncDirectory,
164
166
  withFileLock,
165
167
  withFileLockSync
166
- } from "./chunk-2GP5UFV7.js";
168
+ } from "./chunk-DWRSEC34.js";
167
169
 
168
170
  // src/index.ts
169
171
  import { readFileSync as readFileSync10 } from "fs";
@@ -2221,22 +2223,32 @@ function performStatus3() {
2221
2223
  };
2222
2224
  return { user: statusFor(USER_SCOPE_PATH), project: statusFor(projectScopePath()) };
2223
2225
  }
2226
+ function uncoveredWorktreeHooks(status) {
2227
+ if (status.user.present || status.project.present) return null;
2228
+ const main = mainWorktreeRoot();
2229
+ if (main === null) return null;
2230
+ const path = join2(main, ".codex", "hooks.json");
2231
+ return hasAnyHookRegistration(readSettings(path)) ? path : null;
2232
+ }
2233
+ function uncoveredWorktreeAdvice(path) {
2234
+ return `prim Codex hooks in ${path} don't load in this worktree \u2014 run \`prim codex install\`, then \`prim codex uninstall --scope project\` in that checkout`;
2235
+ }
2224
2236
  function resolveScope(input) {
2225
- if (input === void 0 || input === "project") {
2226
- return "project";
2227
- }
2228
- if (input === "user") {
2237
+ if (input === void 0 || input === "user") {
2229
2238
  return "user";
2230
2239
  }
2240
+ if (input === "project") {
2241
+ return "project";
2242
+ }
2231
2243
  console.error(`[prim] unknown --scope "${input}" (expected: user or project)`);
2232
2244
  process.exit(1);
2233
2245
  }
2234
2246
  var TRUST_NOTICE = "[prim] Codex requires hook trust: run `/hooks` in Codex to review and trust these hooks (or start Codex with --dangerously-bypass-hook-trust). Until trusted, the hooks will not fire.";
2235
2247
  function registerCodexCommands(program2) {
2236
2248
  const codex = program2.command("codex").description("Manage the prim Codex integration (capture, gate, ingest, presence)");
2237
- codex.command("install").description("Register the prim hooks in Codex's hooks.json (project scope by default)").option(
2249
+ codex.command("install").description("Register the prim hooks in Codex's hooks.json (user scope by default)").option(
2238
2250
  "--scope <scope>",
2239
- "project (default, the repo's .codex/hooks.json) or user (~/.codex/hooks.json)"
2251
+ "user (default, ~/.codex/hooks.json) or project (this checkout's .codex/hooks.json)"
2240
2252
  ).option("--force", "Replace any drifted prim hook entries").action((opts) => {
2241
2253
  const scope = resolveScope(opts.scope);
2242
2254
  const result = performInstall(scope, opts.force ?? false);
@@ -2250,7 +2262,7 @@ function registerCodexCommands(program2) {
2250
2262
  });
2251
2263
  codex.command("uninstall").description("Remove all prim hooks from Codex's hooks.json").option(
2252
2264
  "--scope <scope>",
2253
- "project (default, the repo's .codex/hooks.json) or user (~/.codex/hooks.json)"
2265
+ "user (default, ~/.codex/hooks.json) or project (this checkout's .codex/hooks.json)"
2254
2266
  ).action((opts) => {
2255
2267
  const scope = resolveScope(opts.scope);
2256
2268
  const result = performUninstall(scope);
@@ -2267,6 +2279,9 @@ function registerCodexCommands(program2) {
2267
2279
  const line = (label, s) => `[prim] ${label}: gate ${mark(s.gate)} \xB7 capture ${mark(s.capture)} (${s.path})`;
2268
2280
  console.error(`${line("user", result.user)}
2269
2281
  ${line("project", result.project)}`);
2282
+ const uncovered = uncoveredWorktreeHooks(result);
2283
+ if (uncovered !== null)
2284
+ console.error(`[prim] warning: ${uncoveredWorktreeAdvice(uncovered)}`);
2270
2285
  console.log(JSON.stringify(result, null, JSON_INDENT));
2271
2286
  });
2272
2287
  }
@@ -4879,8 +4894,6 @@ var DECISION_NOT_FOUND_MESSAGE = "Decision not found";
4879
4894
  var REPLACEMENT_NOT_FOUND_MESSAGE = "Replacement decision not found";
4880
4895
  var SELF_REPLACEMENT_MESSAGE = "A decision cannot supersede itself";
4881
4896
  var OLD_SERVER_NOT_FOUND_MESSAGE = "Not found";
4882
- var FEATURE_DISABLED_MESSAGE = "feature_disabled";
4883
- var FEATURE_CHECK_UNAVAILABLE_MESSAGE = "feature_check_unavailable";
4884
4897
  var STAGE_NAME = "(?:draft|provisional|adopted|superseded|abandoned)";
4885
4898
  var IMMUTABLE_MESSAGE = new RegExp(
4886
4899
  `^An ${STAGE_NAME} decision is immutable \u2014 supersede it to change it$`,
@@ -4957,12 +4970,6 @@ function classifyHttpError2(error) {
4957
4970
  if (error.status === 403 && message === NOT_AUTHOR_MESSAGE) {
4958
4971
  return failure2("not_author", DECISION_LIFECYCLE_EXIT.rejected, error.status);
4959
4972
  }
4960
- if (error.status === 403 && message === FEATURE_DISABLED_MESSAGE) {
4961
- return failure2("feature_disabled", DECISION_LIFECYCLE_EXIT.rejected, error.status);
4962
- }
4963
- if (error.status === 503 && message === FEATURE_CHECK_UNAVAILABLE_MESSAGE) {
4964
- return failure2("feature_check_unavailable", DECISION_LIFECYCLE_EXIT.server, error.status);
4965
- }
4966
4973
  if (error.status === 409 && message === AMBIGUOUS_MESSAGE) {
4967
4974
  return failure2("ambiguous_identifier", DECISION_LIFECYCLE_EXIT.rejected, error.status);
4968
4975
  }
@@ -5022,10 +5029,6 @@ function formatFailureHuman(operation, result) {
5022
5029
  return `[prim] ${verb} rejected: Decision not found.`;
5023
5030
  case "replacement_not_found":
5024
5031
  return "[prim] supersede rejected: replacement Decision not found.";
5025
- case "feature_disabled":
5026
- return `[prim] ${verb} rejected: manual lifecycle actions are not enabled for this account.`;
5027
- case "feature_check_unavailable":
5028
- return `[prim] ${verb} unavailable: the server could not verify feature availability; retry shortly.`;
5029
5032
  case "unsupported_server":
5030
5033
  return `[prim] ${verb} unavailable: this Primitive server does not support this Decision lifecycle operation; upgrade the server before retrying.`;
5031
5034
  case "rejected":
@@ -7748,9 +7751,16 @@ function classifyClaudeHooks(statuses) {
7748
7751
  }
7749
7752
  return { name: "feedback-hooks", status: "ok", detail: "complete Claude lifecycle ready" };
7750
7753
  }
7751
- function classifyCodexHooks(statuses) {
7754
+ function classifyCodexHooks(statuses, uncoveredWorktree = null, capturedAt) {
7752
7755
  const installed = statuses.filter((status) => status.present);
7753
7756
  if (installed.length === 0) {
7757
+ if (uncoveredWorktree !== null) {
7758
+ return {
7759
+ name: "codex-hooks",
7760
+ status: "warn",
7761
+ detail: uncoveredWorktreeAdvice(uncoveredWorktree)
7762
+ };
7763
+ }
7754
7764
  return { name: "codex-hooks", status: "ok", detail: "not installed" };
7755
7765
  }
7756
7766
  if (installed.some((status) => !status.complete)) {
@@ -7762,8 +7772,8 @@ function classifyCodexHooks(statuses) {
7762
7772
  }
7763
7773
  return {
7764
7774
  name: "codex-hooks",
7765
- status: "warn",
7766
- detail: "installed; Codex trust is not machine-readable \u2014 verify with `/hooks`"
7775
+ status: capturedAt === void 0 ? "warn" : "ok",
7776
+ detail: capturedAt === void 0 ? "installed; conversation capture unverified; Codex trust unknown \u2014 verify with `/hooks`" : `installed; conversation capture observed ${new Date(capturedAt).toISOString()}; current trust unknown`
7767
7777
  };
7768
7778
  }
7769
7779
  function classifyCursorHooks(statuses) {
@@ -7815,7 +7825,13 @@ function checkAgentHooks() {
7815
7825
  const checks = [];
7816
7826
  try {
7817
7827
  const status = performStatus3();
7818
- checks.push(classifyCodexHooks([status.project, status.user]));
7828
+ checks.push(
7829
+ classifyCodexHooks(
7830
+ [status.project, status.user],
7831
+ uncoveredWorktreeHooks(status),
7832
+ lastConversationCaptured(process.cwd(), "codex")
7833
+ )
7834
+ );
7819
7835
  } catch (error) {
7820
7836
  const detail = boundedHealthError(error instanceof Error ? error.message : String(error));
7821
7837
  checks.push({
@@ -7967,7 +7983,7 @@ function parseMovesStatus(value) {
7967
7983
  }
7968
7984
  const status = value;
7969
7985
  const nullableNumber = (candidate) => candidate === null || typeof candidate === "number" && Number.isFinite(candidate);
7970
- if (status.captureState !== "enabled" && status.captureState !== "disabled" || !nullableNumber(status.latestIngestAt) || !nullableNumber(status.latestClassificationAt) || !nullableNumber(status.highWaterMark) || typeof status.pendingSessionCount !== "number" || !Number.isInteger(status.pendingSessionCount) || status.pendingSessionCount < 0 || typeof status.sampled !== "boolean") {
7986
+ if (status.captureState !== "enabled" || !nullableNumber(status.latestIngestAt) || !nullableNumber(status.latestClassificationAt) || !nullableNumber(status.highWaterMark) || typeof status.pendingSessionCount !== "number" || !Number.isInteger(status.pendingSessionCount) || status.pendingSessionCount < 0 || typeof status.sampled !== "boolean") {
7971
7987
  throw new Error("moves status returned an invalid response");
7972
7988
  }
7973
7989
  if (status.oldestPendingAt !== void 0 && !nullableNumber(status.oldestPendingAt) || status.oldestPendingAgeMs !== void 0 && !nullableNumber(status.oldestPendingAgeMs) || typeof status.oldestPendingAgeMs === "number" && status.oldestPendingAgeMs < 0 || status.pendingCommitCorrelationCount !== void 0 && (!Number.isInteger(status.pendingCommitCorrelationCount) || status.pendingCommitCorrelationCount < 0)) {
@@ -7976,10 +7992,10 @@ function parseMovesStatus(value) {
7976
7992
  return status;
7977
7993
  }
7978
7994
  function classifyMovesStatus(status) {
7979
- const capture = status.captureState === "enabled" ? { name: "capture", status: "ok", detail: "enabled; ingest endpoint durable" } : {
7995
+ const capture = {
7980
7996
  name: "capture",
7981
- status: "fail",
7982
- detail: "disabled for the current organization; local Moves are retained"
7997
+ status: "ok",
7998
+ detail: "enabled; ingest endpoint durable"
7983
7999
  };
7984
8000
  const correlation = status.pendingCommitCorrelationCount === void 0 ? void 0 : status.pendingCommitCorrelationCount > 0 ? {
7985
8001
  name: "commit-correlation",
@@ -8868,7 +8884,7 @@ var SESSION_LABELS = {
8868
8884
  };
8869
8885
  function planSetupSteps(opts) {
8870
8886
  const scopeArgs = opts.scope === "user" ? ["--scope", "user"] : [];
8871
- const sessionArgs = opts.agent === "hermes" ? [opts.agent, "install"] : [opts.agent, "install", ...scopeArgs];
8887
+ const sessionArgs = opts.agent === "hermes" ? [opts.agent, "install"] : [opts.agent, "install", "--scope", opts.scope];
8872
8888
  const steps = [
8873
8889
  {
8874
8890
  key: "session",
@@ -9652,14 +9668,17 @@ function startDriftHeal(options) {
9652
9668
  if (!runsConfigRoot()) return false;
9653
9669
  const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
9654
9670
  if (!primEntry) return false;
9655
- const attempt = {
9656
- attemptedAt: (options.nowMs ?? Date.now)(),
9657
- fromVersion: launcher.runtimeVersion,
9658
- toVersion
9659
- };
9660
9671
  const claimed = withFileLockSync(
9661
9672
  daemonDriftHealClaimPath(paths),
9662
- () => claimAttempt(daemonDriftHealMarkerPath(paths), attempt),
9673
+ () => (
9674
+ // Sample after acquiring the claim: an earlier timestamp from a delayed
9675
+ // command must not make the previous winner look like a clock rollback.
9676
+ claimAttempt(daemonDriftHealMarkerPath(paths), {
9677
+ attemptedAt: (options.nowMs ?? Date.now)(),
9678
+ fromVersion: launcher.runtimeVersion,
9679
+ toVersion
9680
+ })
9681
+ ),
9663
9682
  { timeoutMs: 0, sleep: claimHeld }
9664
9683
  );
9665
9684
  if (!claimed) return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitive.ai/prim",
3
- "version": "0.1.0-alpha.94",
3
+ "version": "0.1.0-alpha.96",
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",