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

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.
@@ -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-CPSE3Y3T.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
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ canonicalRewritePairs,
4
+ toRewriteMove
5
+ } from "../chunk-CPSE3Y3T.js";
6
+ import {
7
+ appendMove,
8
+ resolveOrg
9
+ } from "../chunk-HIMWM526.js";
10
+ import {
11
+ getOrCreateWorkspaceId
12
+ } from "../chunk-UVIWZWM6.js";
13
+ import {
14
+ githubRepositoryFullName,
15
+ isRepoActiveForCapture,
16
+ repoSyncId,
17
+ resolveRepositoryContext
18
+ } from "../chunk-TDRXHBEX.js";
19
+ import "../chunk-CPX3BUOC.js";
20
+
21
+ // src/hooks/post-rewrite.ts
22
+ import { execFileSync, spawn } from "child_process";
23
+ import {
24
+ constants,
25
+ closeSync,
26
+ fstatSync,
27
+ lstatSync,
28
+ openSync,
29
+ readFileSync,
30
+ unlinkSync
31
+ } from "fs";
32
+ import { basename, dirname, isAbsolute, join } from "path";
33
+ import { fileURLToPath } from "url";
34
+ var here = dirname(fileURLToPath(import.meta.url));
35
+ var GIT_TIMEOUT_MS = 1e3;
36
+ var MAX_PAIRS_FILE_BYTES = 1048576;
37
+ var MAX_PAIRS_FILE_AGE_MS = 10 * 60 * 1e3;
38
+ var MAX_PAIRS_FILE_FUTURE_SKEW_MS = 5e3;
39
+ var PAIRS_FILE_RE = /^prim-post-rewrite-pairs\.[A-Za-z0-9]+$/u;
40
+ var FULL_SHA_SOURCE = "(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})";
41
+ var REWRITE_PAIR_RE = new RegExp(
42
+ `^(${FULL_SHA_SOURCE}) (${FULL_SHA_SOURCE})(?: ([^\\s]+))?$`,
43
+ "u"
44
+ );
45
+ function parseRewritePairs(value) {
46
+ if (value.length === 0) return [];
47
+ const body = value.endsWith("\n") ? value.slice(0, -1) : value;
48
+ if (body.length === 0) return [];
49
+ const parsed = [];
50
+ for (const line of body.split("\n")) {
51
+ const match = REWRITE_PAIR_RE.exec(line);
52
+ if (!match?.[1] || !match[2]) return;
53
+ parsed.push({ oldSha: match[1], newSha: match[2] });
54
+ }
55
+ return canonicalRewritePairs(parsed) ?? void 0;
56
+ }
57
+ function privatePairsFileStat(stat, now) {
58
+ const capturedAt = Math.trunc(stat.mtimeMs);
59
+ const uid = process.getuid?.();
60
+ return stat.isFile() && !stat.isSymbolicLink() && stat.size <= MAX_PAIRS_FILE_BYTES && stat.nlink === 1 && (stat.mode & 511) === 384 && (uid === void 0 || stat.uid === uid) && Number.isSafeInteger(capturedAt) && capturedAt > 0 && capturedAt >= now - MAX_PAIRS_FILE_AGE_MS && capturedAt <= now + MAX_PAIRS_FILE_FUTURE_SKEW_MS;
61
+ }
62
+ function rewritePairsFromLauncher(env = process.env, now = Date.now()) {
63
+ const path = env.PRIM_REWRITE_PAIRS_FILE;
64
+ if (!path || !isAbsolute(path) || !PAIRS_FILE_RE.test(basename(path))) return;
65
+ let fd;
66
+ try {
67
+ const pathStat = lstatSync(path);
68
+ if (!privatePairsFileStat(pathStat, now)) return;
69
+ fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
70
+ const openStat = fstatSync(fd);
71
+ if (!privatePairsFileStat(openStat, now) || openStat.dev !== pathStat.dev || openStat.ino !== pathStat.ino) {
72
+ return;
73
+ }
74
+ const bytes = readFileSync(fd);
75
+ if (bytes.length !== openStat.size) return;
76
+ let text;
77
+ try {
78
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
79
+ } catch {
80
+ return;
81
+ }
82
+ const pairs = parseRewritePairs(text);
83
+ if (!pairs) return;
84
+ return {
85
+ path,
86
+ dev: openStat.dev,
87
+ ino: openStat.ino,
88
+ capturedAt: Math.trunc(openStat.mtimeMs),
89
+ pairs
90
+ };
91
+ } catch {
92
+ return;
93
+ } finally {
94
+ if (fd !== void 0) {
95
+ try {
96
+ closeSync(fd);
97
+ } catch {
98
+ }
99
+ }
100
+ }
101
+ }
102
+ function unlinkSnapshot(snapshot) {
103
+ try {
104
+ const current = lstatSync(snapshot.path);
105
+ if (current.isFile() && !current.isSymbolicLink() && current.dev === snapshot.dev && current.ino === snapshot.ino) {
106
+ unlinkSync(snapshot.path);
107
+ }
108
+ } catch {
109
+ }
110
+ }
111
+ function gitText(args) {
112
+ try {
113
+ return execFileSync("git", args, {
114
+ encoding: "utf-8",
115
+ stdio: ["ignore", "pipe", "ignore"],
116
+ timeout: GIT_TIMEOUT_MS
117
+ }).trim();
118
+ } catch {
119
+ return;
120
+ }
121
+ }
122
+ function validSource(value) {
123
+ return value === "amend" || value === "rebase";
124
+ }
125
+ function validBranch(value) {
126
+ return value && value === value.trim() && ![...value].some((char) => char < " " || char === "\x7F") ? value : void 0;
127
+ }
128
+ function resolveCliVersion() {
129
+ try {
130
+ const pkg = JSON.parse(readFileSync(join(here, "..", "..", "package.json"), "utf-8"));
131
+ return pkg.version ?? "unknown";
132
+ } catch {
133
+ return "unknown";
134
+ }
135
+ }
136
+ function spawnBackgroundFlush() {
137
+ const entry = join(here, "..", "index.js");
138
+ spawn(process.execPath, [entry, "moves", "flush"], {
139
+ detached: true,
140
+ stdio: "ignore"
141
+ }).unref();
142
+ }
143
+ function runPostRewrite() {
144
+ const snapshot = rewritePairsFromLauncher();
145
+ if (!snapshot) return;
146
+ try {
147
+ const source = process.env.PRIM_REWRITE_SOURCE;
148
+ if (!validSource(source) || snapshot.pairs.length === 0) return;
149
+ const cwd = gitText(["rev-parse", "--show-toplevel"]) ?? process.cwd();
150
+ if (!isRepoActiveForCapture(cwd)) return;
151
+ const repository = resolveRepositoryContext(cwd);
152
+ const identity = getOrCreateWorkspaceId(cwd);
153
+ const workspaceId = identity.status === "ready" ? identity.workspaceId : void 0;
154
+ const moves = toRewriteMove(
155
+ {
156
+ source,
157
+ pairs: snapshot.pairs,
158
+ branch: validBranch(process.env.PRIM_REWRITE_BRANCH)
159
+ },
160
+ resolveCliVersion(),
161
+ cwd,
162
+ {
163
+ repository,
164
+ repoFullName: githubRepositoryFullName(cwd) ?? void 0,
165
+ repoSyncId: repoSyncId(cwd),
166
+ workspaceId,
167
+ capturedAt: snapshot.capturedAt
168
+ }
169
+ );
170
+ if (moves.length === 0) return;
171
+ const { orgId } = resolveOrg({ sessionId: "", cwd });
172
+ for (const move of moves) appendMove(move, orgId);
173
+ spawnBackgroundFlush();
174
+ } finally {
175
+ unlinkSnapshot(snapshot);
176
+ }
177
+ }
178
+ if (!process.env.VITEST) {
179
+ try {
180
+ runPostRewrite();
181
+ } catch (error) {
182
+ if (process.env.PRIM_HOOK_DEBUG) {
183
+ const detail = error instanceof Error ? error.message : String(error);
184
+ process.stderr.write(`[prim-post-rewrite] capture failed: ${detail}
185
+ `);
186
+ }
187
+ }
188
+ process.exit(0);
189
+ }
190
+ export {
191
+ parseRewritePairs,
192
+ rewritePairsFromLauncher,
193
+ runPostRewrite
194
+ };
@@ -7,30 +7,30 @@ 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-CPSE3Y3T.js";
16
16
  import {
17
17
  appendMove,
18
18
  resolveOrg
19
19
  } from "../chunk-HIMWM526.js";
20
- import {
21
- scrubFromCwd
22
- } from "../chunk-G5D3FF3D.js";
23
20
  import {
24
21
  getOrCreateWorkspaceId
25
22
  } from "../chunk-UVIWZWM6.js";
23
+ import {
24
+ scrubFromCwd
25
+ } from "../chunk-G5D3FF3D.js";
26
26
  import {
27
27
  warmBinCache
28
- } from "../chunk-LNU44OJV.js";
28
+ } from "../chunk-CZ24W5QU.js";
29
29
  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";
@@ -16,13 +16,13 @@ import {
16
16
  import {
17
17
  packageVersion,
18
18
  warmBinCache
19
- } from "../chunk-LNU44OJV.js";
19
+ } from "../chunk-CZ24W5QU.js";
20
20
  import {
21
21
  canonicalGitRoot,
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";
@@ -1,41 +1,41 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ buildHookOutput,
4
+ handoffHookOutput
5
+ } from "../chunk-4GLVJLEQ.js";
6
+ import {
7
+ FEEDBACK_DEADLINE_MS,
8
+ acknowledgeDecisionFeedback,
9
+ leaseDecisionFeedback,
10
+ renderFeedback
11
+ } from "../chunk-KHZNYEUV.js";
2
12
  import {
3
13
  enrichHookPayloadWithFileRefs,
4
14
  preserveHookFileMetadata
5
- } from "../chunk-VYGY3OOX.js";
15
+ } from "../chunk-BDGQEJA5.js";
6
16
  import {
7
17
  postToolInvocationId,
8
18
  shouldFlushAfter,
9
19
  toMove
10
- } from "../chunk-7X7SFOPJ.js";
20
+ } from "../chunk-CPSE3Y3T.js";
11
21
  import {
12
22
  appendMove,
13
23
  resolveOrg
14
24
  } from "../chunk-HIMWM526.js";
15
- import {
16
- scrubFromCwd
17
- } from "../chunk-G5D3FF3D.js";
18
- import {
19
- buildHookOutput,
20
- handoffHookOutput
21
- } from "../chunk-4GLVJLEQ.js";
22
- import {
23
- FEEDBACK_DEADLINE_MS,
24
- acknowledgeDecisionFeedback,
25
- leaseDecisionFeedback,
26
- renderFeedback
27
- } from "../chunk-KHZNYEUV.js";
28
25
  import {
29
26
  getOrCreateWorkspaceId
30
27
  } from "../chunk-UVIWZWM6.js";
28
+ import {
29
+ scrubFromCwd
30
+ } from "../chunk-G5D3FF3D.js";
31
31
  import {
32
32
  warmBinCache
33
- } from "../chunk-LNU44OJV.js";
33
+ } from "../chunk-CZ24W5QU.js";
34
34
  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,
@@ -2,9 +2,10 @@
2
2
  import {
3
3
  bindRepository,
4
4
  ensureEffectivePostCommitHook,
5
+ ensureEffectivePostRewriteHook,
5
6
  hasUsableCodexGuidance,
6
7
  refreshClaudePlugins
7
- } from "../chunk-MCPHIB4W.js";
8
+ } from "../chunk-DR66XM5L.js";
8
9
  import {
9
10
  buildHookOutput,
10
11
  handoffHookOutput
@@ -21,13 +22,13 @@ import {
21
22
  import {
22
23
  binFile,
23
24
  warmBinCache
24
- } from "../chunk-LNU44OJV.js";
25
+ } from "../chunk-CZ24W5QU.js";
25
26
  import {
26
27
  gitToplevel,
27
28
  isRepoActiveForCapture,
28
29
  repoActiveFlag,
29
30
  setRepoActive
30
- } from "../chunk-XTNMSS4C.js";
31
+ } from "../chunk-TDRXHBEX.js";
31
32
  import {
32
33
  getSiteUrl,
33
34
  isSessionEnded
@@ -100,6 +101,10 @@ async function activeProjectRoot(cwd) {
100
101
  ensureEffectivePostCommitHook(root);
101
102
  } catch {
102
103
  }
104
+ try {
105
+ ensureEffectivePostRewriteHook(root);
106
+ } catch {
107
+ }
103
108
  }
104
109
  try {
105
110
  await bindRepository(root, {