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

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.
package/README.md CHANGED
@@ -71,12 +71,13 @@ so the agent runs it directly — instead of fetching a doc and executing a comm
71
71
  it, which an auto-mode safety classifier blocks as untrusted-origin code:
72
72
 
73
73
  ```text
74
- Install the Primitive CLI: run `npx --yes @primitive.ai/prim@latest setup` and surface its output. It's an agent-first CLI — drive it yourself, don't hand the command back to me. The only thing I'll do is click "Authorize" in the browser if it asks. When it finishes, show me the welcome.
74
+ Install the Primitive CLI and activate passive decision capture for this repository: run `npx --yes @primitive.ai/prim@latest setup` and surface its output. Drive it yourself; I'll only click Authorize in the browser. This request authorizes setup's built-in activation of this repository and an idempotent retry of that step, but not activation in another repository or creation of a Decision. If enable or health fails, surface the actual error; do not claim fresh activation approval is required. Then show me the welcome.
75
75
  ```
76
76
 
77
- That single command runs the whole install in one process (auth → session hooks →
78
- daemon → git hooks → skill → welcome). The detailed step-by-step the agent follows —
79
- and the manual fallback — live in [`setup.md`](./setup.md).
77
+ That single command runs the whole install and current-repository activation in one
78
+ process (auth → session hooks → daemon → git hooks → skill → welcome). The detailed
79
+ step-by-step the agent follows — and the manual fallback — live in
80
+ [`setup.md`](./setup.md).
80
81
 
81
82
  ## Commands
82
83
 
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-UVIWZWM6.js";
4
4
  import {
5
5
  isValidRepoSyncId
6
- } from "./chunk-XTNMSS4C.js";
6
+ } from "./chunk-TDRXHBEX.js";
7
7
 
8
8
  // src/hooks/prim-hook-core.ts
9
9
  import { createHash, randomUUID } from "crypto";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-G5D3FF3D.js";
5
5
  import {
6
6
  canonicalRepositoryPath
7
- } from "./chunk-XTNMSS4C.js";
7
+ } from "./chunk-TDRXHBEX.js";
8
8
 
9
9
  // src/hooks/file-refs.ts
10
10
  var MAX_PREFLIGHT_FILE_TARGETS = 25;
@@ -1,10 +1,12 @@
1
1
  import {
2
+ clearRepoSyncId,
2
3
  gitToplevel,
3
4
  githubRepositoryFullName,
4
5
  isValidRepoSyncId,
5
6
  setRepoSyncId
6
- } from "./chunk-XTNMSS4C.js";
7
+ } from "./chunk-TDRXHBEX.js";
7
8
  import {
9
+ HttpError,
8
10
  getClient,
9
11
  withFileLock
10
12
  } from "./chunk-CPX3BUOC.js";
@@ -1222,19 +1224,31 @@ async function resolveRepositoryBinding(root, options) {
1222
1224
  if (!repositoryFullName) {
1223
1225
  throw new Error("origin must be a GitHub HTTPS/SSH remote in owner/name form");
1224
1226
  }
1225
- const response = await getClient().post(
1226
- "/api/cli/repositories/bind",
1227
- { repositoryFullName },
1228
- options
1229
- );
1227
+ let response;
1228
+ try {
1229
+ response = await getClient().post(
1230
+ "/api/cli/repositories/bind",
1231
+ { repositoryFullName },
1232
+ options
1233
+ );
1234
+ } catch (error) {
1235
+ if (error instanceof HttpError && error.status === 404) {
1236
+ return { status: "pending", repositoryFullName };
1237
+ }
1238
+ throw error;
1239
+ }
1230
1240
  if (!isValidRepoSyncId(response.repoSyncId)) {
1231
1241
  throw new Error("server returned no repository binding");
1232
1242
  }
1233
- return { repoSyncId: response.repoSyncId, repositoryFullName };
1243
+ return { status: "connected", repoSyncId: response.repoSyncId, repositoryFullName };
1234
1244
  }
1235
1245
  async function bindRepository(root, options) {
1236
1246
  const binding = await resolveRepositoryBinding(root, options);
1237
- setRepoSyncId(root, binding.repoSyncId);
1247
+ if (binding.status === "connected") {
1248
+ setRepoSyncId(root, binding.repoSyncId);
1249
+ } else {
1250
+ clearRepoSyncId(root);
1251
+ }
1238
1252
  return binding;
1239
1253
  }
1240
1254
 
@@ -241,6 +241,20 @@ function setRepoSyncId(cwd, value) {
241
241
  timeout: GIT_TIMEOUT_MS2
242
242
  });
243
243
  }
244
+ function clearRepoSyncId(cwd) {
245
+ try {
246
+ execFileSync2("git", ["config", "--local", "--unset-all", PRIM_REPO_SYNC_ID_KEY], {
247
+ cwd,
248
+ stdio: ["ignore", "ignore", "pipe"],
249
+ timeout: GIT_TIMEOUT_MS2
250
+ });
251
+ } catch (error) {
252
+ if (typeof error === "object" && error !== null && "status" in error && error.status === 5) {
253
+ return;
254
+ }
255
+ throw error;
256
+ }
257
+ }
244
258
  function hasProjectPrimInstall(cwd) {
245
259
  const root = gitToplevel(cwd);
246
260
  if (root === null || root === homedir()) return false;
@@ -278,6 +292,7 @@ export {
278
292
  isValidRepoSyncId,
279
293
  repoSyncId,
280
294
  setRepoSyncId,
295
+ clearRepoSyncId,
281
296
  isRepoActiveForCapture,
282
297
  decisionIngestionStatus
283
298
  };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  commitAttributionFromEnvironment,
4
4
  toCommitMove
5
- } from "../chunk-7X7SFOPJ.js";
5
+ } from "../chunk-5CKL5EYQ.js";
6
6
  import {
7
7
  appendMove,
8
8
  resolveOrg
@@ -15,7 +15,7 @@ import {
15
15
  isRepoActiveForCapture,
16
16
  repoSyncId,
17
17
  resolveRepositoryContext
18
- } from "../chunk-XTNMSS4C.js";
18
+ } from "../chunk-TDRXHBEX.js";
19
19
  import "../chunk-CPX3BUOC.js";
20
20
 
21
21
  // src/hooks/post-commit.ts
@@ -7,12 +7,12 @@ import {
7
7
  import {
8
8
  enrichHookPayloadWithFileRefs,
9
9
  preserveHookFileMetadata
10
- } from "../chunk-VYGY3OOX.js";
10
+ } from "../chunk-BDGQEJA5.js";
11
11
  import {
12
12
  postToolInvocationId,
13
13
  toMove,
14
14
  toolOutcomeFor
15
- } from "../chunk-7X7SFOPJ.js";
15
+ } from "../chunk-5CKL5EYQ.js";
16
16
  import {
17
17
  appendMove,
18
18
  resolveOrg
@@ -30,7 +30,7 @@ import {
30
30
  isRepoActiveForCapture,
31
31
  repoSyncId,
32
32
  resolveRepositoryContext
33
- } from "../chunk-XTNMSS4C.js";
33
+ } from "../chunk-TDRXHBEX.js";
34
34
  import {
35
35
  getClient
36
36
  } from "../chunk-CPX3BUOC.js";
@@ -22,7 +22,7 @@ import {
22
22
  canonicalRepositoryPath,
23
23
  isRepoActive,
24
24
  repoSyncId
25
- } from "../chunk-XTNMSS4C.js";
25
+ } from "../chunk-TDRXHBEX.js";
26
26
  import {
27
27
  getClient
28
28
  } from "../chunk-CPX3BUOC.js";
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  enrichHookPayloadWithFileRefs,
4
4
  preserveHookFileMetadata
5
- } from "../chunk-VYGY3OOX.js";
5
+ } from "../chunk-BDGQEJA5.js";
6
6
  import {
7
7
  postToolInvocationId,
8
8
  shouldFlushAfter,
9
9
  toMove
10
- } from "../chunk-7X7SFOPJ.js";
10
+ } from "../chunk-5CKL5EYQ.js";
11
11
  import {
12
12
  appendMove,
13
13
  resolveOrg
@@ -35,7 +35,7 @@ import {
35
35
  isRepoActiveForCapture,
36
36
  repoSyncId,
37
37
  resolveRepositoryContext
38
- } from "../chunk-XTNMSS4C.js";
38
+ } from "../chunk-TDRXHBEX.js";
39
39
  import "../chunk-CPX3BUOC.js";
40
40
  import {
41
41
  normalizeEnvelope,
@@ -4,7 +4,7 @@ import {
4
4
  ensureEffectivePostCommitHook,
5
5
  hasUsableCodexGuidance,
6
6
  refreshClaudePlugins
7
- } from "../chunk-MCPHIB4W.js";
7
+ } from "../chunk-CPHJUQNE.js";
8
8
  import {
9
9
  buildHookOutput,
10
10
  handoffHookOutput
@@ -27,7 +27,7 @@ import {
27
27
  isRepoActiveForCapture,
28
28
  repoActiveFlag,
29
29
  setRepoActive
30
- } from "../chunk-XTNMSS4C.js";
30
+ } from "../chunk-TDRXHBEX.js";
31
31
  import {
32
32
  getSiteUrl,
33
33
  isSessionEnded
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  resolveRepositoryBinding,
36
36
  uninstallPostCommitHookAtPath,
37
37
  uninstallProjectPostCommitHook
38
- } from "./chunk-MCPHIB4W.js";
38
+ } from "./chunk-CPHJUQNE.js";
39
39
  import {
40
40
  fetchFeedbackCapability
41
41
  } from "./chunk-KHZNYEUV.js";
@@ -59,7 +59,7 @@ import {
59
59
  isValidRepoSyncId,
60
60
  repoSyncId,
61
61
  setRepoActive
62
- } from "./chunk-XTNMSS4C.js";
62
+ } from "./chunk-TDRXHBEX.js";
63
63
  import {
64
64
  HttpError,
65
65
  REFRESH_TOKEN_PATH,
@@ -97,22 +97,48 @@ async function applyActivation(active) {
97
97
  );
98
98
  process.exit(1);
99
99
  }
100
+ let phase = active ? "post-commit hook coverage" : "local deactivation";
100
101
  try {
101
- let bound;
102
+ let binding;
102
103
  let postCommitHook;
103
104
  if (active) {
104
105
  postCommitHook = ensureEffectivePostCommitHook(root).path;
105
- bound = await bindRepository(root);
106
+ phase = "repository binding";
107
+ binding = await bindRepository(root);
108
+ phase = "local activation";
106
109
  }
107
110
  setRepoActive(root, active);
108
111
  await daemonRequest("statusline_invalidate", {}, { timeoutMs: 250 });
109
- process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
112
+ if (binding?.status === "pending") {
113
+ process.stderr.write(`[prim] Prim is enabled locally in ${root}
110
114
  `);
111
- printJson({ active, repo: root, ...bound, ...postCommitHook ? { postCommitHook } : {} });
115
+ process.stderr.write(
116
+ `[prim] repository ${binding.repositoryFullName} is not connected to Primitive; Moves still ingest into the team graph without repository-specific file attribution, Conflict Gate verification, or commit correlation
117
+ `
118
+ );
119
+ process.stderr.write(
120
+ `[prim] ask an organization owner or administrator to grant Primitive's GitHub App access to this repository through Primitive's GitHub App onboarding; binding retries automatically at the next agent SessionStart
121
+ `
122
+ );
123
+ } else {
124
+ process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
125
+ `);
126
+ }
127
+ printJson({
128
+ active,
129
+ repo: root,
130
+ ...binding ? {
131
+ bindingStatus: binding.status,
132
+ repositoryFullName: binding.repositoryFullName,
133
+ ...binding.status === "connected" ? { repoSyncId: binding.repoSyncId } : {}
134
+ } : {},
135
+ ...postCommitHook ? { postCommitHook } : {}
136
+ });
112
137
  } catch (err) {
113
138
  const detail = err instanceof Error ? err.message : String(err);
114
139
  process.stderr.write(
115
- `[prim] failed to ${active ? "cover, bind, and enable" : "disable"} prim: ${detail}
140
+ active ? `[prim] failed to enable prim during ${phase}: ${detail}
141
+ ` : `[prim] failed to disable prim: ${detail}
116
142
  `
117
143
  );
118
144
  process.exit(1);
@@ -3917,7 +3943,28 @@ function checkWorkspaceIdentity() {
3917
3943
  };
3918
3944
  }
3919
3945
  }
3920
- function classifyRepositoryBinding(value, currentServerValue) {
3946
+ function classifyRepositoryBinding(value, current, active) {
3947
+ if (current.status === "pending") {
3948
+ if (value !== void 0) {
3949
+ return {
3950
+ name: "repo-binding",
3951
+ status: "fail",
3952
+ detail: `local repository binding is stale for ${current.repositoryFullName} \u2014 run \`prim enable\``
3953
+ };
3954
+ }
3955
+ if (!active) {
3956
+ return {
3957
+ name: "repo-binding",
3958
+ status: "fail",
3959
+ detail: "passive capture is inactive \u2014 run `prim enable`"
3960
+ };
3961
+ }
3962
+ return {
3963
+ name: "repo-binding",
3964
+ status: "warn",
3965
+ detail: `${current.repositoryFullName} is not connected to Primitive \u2014 local capture is active, but file scoping, Conflict Gate verification, and commit correlation are unavailable; ask an organization owner/admin to grant the Primitive GitHub App access (retried automatically next SessionStart)`
3966
+ };
3967
+ }
3921
3968
  if (!isValidRepoSyncId(value)) {
3922
3969
  return {
3923
3970
  name: "repo-binding",
@@ -3925,7 +3972,7 @@ function classifyRepositoryBinding(value, currentServerValue) {
3925
3972
  detail: "missing or invalid local prim.repoSyncId \u2014 run `prim enable`"
3926
3973
  };
3927
3974
  }
3928
- if (currentServerValue !== void 0 && value !== currentServerValue) {
3975
+ if (value !== current.repoSyncId) {
3929
3976
  return {
3930
3977
  name: "repo-binding",
3931
3978
  status: "fail",
@@ -3935,26 +3982,25 @@ function classifyRepositoryBinding(value, currentServerValue) {
3935
3982
  return {
3936
3983
  name: "repo-binding",
3937
3984
  status: "ok",
3938
- detail: currentServerValue === void 0 ? "valid local repository binding ready" : "local repository binding matches the current server binding"
3985
+ detail: "local repository binding matches the current server binding"
3939
3986
  };
3940
3987
  }
3941
3988
  async function checkRepositoryBinding() {
3942
3989
  const root = process.cwd();
3943
3990
  const local = repoSyncId(root);
3944
- const localCheck = classifyRepositoryBinding(local);
3945
- if (localCheck.status === "fail") return localCheck;
3946
3991
  try {
3947
3992
  const current = await resolveRepositoryBinding(root, {
3948
3993
  signal: AbortSignal.timeout(CONNECTIVITY_TIMEOUT_MS),
3949
3994
  quietRefresh: true
3950
3995
  });
3951
- return classifyRepositoryBinding(local, current.repoSyncId);
3996
+ return classifyRepositoryBinding(local, current, isRepoActiveForCapture(root));
3952
3997
  } catch (error) {
3953
- const detail = error instanceof Error ? error.message : String(error);
3998
+ const message = error instanceof Error ? error.message : String(error);
3999
+ const detail = boundedHealthError(`could not verify the current repository binding \u2014 ${message}`) ?? "could not verify the current repository binding";
3954
4000
  return {
3955
4001
  name: "repo-binding",
3956
4002
  status: "fail",
3957
- detail: `could not verify the current repository binding \u2014 ${detail.slice(0, 80)}`
4003
+ detail
3958
4004
  };
3959
4005
  }
3960
4006
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitive.ai/prim",
3
- "version": "0.1.0-alpha.64",
3
+ "version": "0.1.0-alpha.65",
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",