@primitive.ai/prim 0.1.0-alpha.93 → 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.
- package/README.md +57 -12
- package/SKILL.md +5 -2
- package/dist/{chunk-UKUAAAXE.js → chunk-27ADT6FQ.js} +2 -2
- package/dist/{chunk-OTHKG76J.js → chunk-35FMRWJH.js} +1 -1
- package/dist/{chunk-DXJQR4QI.js → chunk-4GHANPWQ.js} +2 -2
- package/dist/{chunk-DCJN6LZH.js → chunk-7KABYYWG.js} +747 -430
- package/dist/{chunk-LFFLHSQO.js → chunk-CU7RW7XU.js} +63 -6
- package/dist/{chunk-H4UNYFUC.js → chunk-DHOQGHKQ.js} +184 -1
- package/dist/{chunk-2GP5UFV7.js → chunk-DWRSEC34.js} +5 -3
- package/dist/{chunk-36TWVNBE.js → chunk-FVDBULGZ.js} +9 -11
- package/dist/{chunk-G2VMA2QO.js → chunk-LY6NJ7KL.js} +6 -4
- package/dist/{chunk-ZB4F75SZ.js → chunk-MXVCP33E.js} +5 -1
- package/dist/{chunk-TYAHC25F.js → chunk-QCBB5HFX.js} +1 -1
- package/dist/{chunk-C3VAA5CG.js → chunk-RK4ZS3HG.js} +2 -2
- package/dist/{chunk-ZU563PQI.js → chunk-ROLYLXJS.js} +2 -2
- package/dist/{chunk-OH3EZXYX.js → chunk-UO2U6IKF.js} +1 -1
- package/dist/{chunk-K7D2EYBS.js → chunk-URLLSALP.js} +2 -2
- package/dist/chunk-XEDJ5N3M.js +177 -0
- package/dist/{chunk-X64HTHUF.js → chunk-Y5W4TZM7.js} +202 -10
- package/dist/{chunk-ICM2XCXS.js → chunk-YYE2NLWX.js} +2 -2
- package/dist/{chunk-DSWT6BCC.js → chunk-Z6NPNLOT.js} +825 -665
- package/dist/daemon/server.js +836 -540
- package/dist/hook-runtime/daemon/server.js +836 -540
- package/dist/hook-runtime/hooks/post-commit.js +978 -566
- package/dist/hook-runtime/hooks/post-rewrite.js +836 -540
- package/dist/hook-runtime/hooks/post-tool-use.js +1215 -796
- package/dist/hook-runtime/hooks/pre-tool-use.js +2100 -1741
- package/dist/hook-runtime/hooks/prim-hook.js +1312 -861
- package/dist/hook-runtime/hooks/session-end.js +232 -134
- package/dist/hook-runtime/hooks/session-start.js +1799 -935
- package/dist/hook-runtime/index.js +3628 -2461
- package/dist/hooks/post-commit.js +14 -10
- package/dist/hooks/post-rewrite.js +10 -10
- package/dist/hooks/post-tool-use.js +20 -20
- package/dist/hooks/pre-commit.js +4 -4
- package/dist/hooks/pre-tool-use.js +29 -17
- package/dist/hooks/prim-hook.js +22 -18
- package/dist/hooks/session-end.js +11 -5
- package/dist/hooks/session-start.js +46 -28
- package/dist/index.js +623 -390
- package/package.json +1 -1
|
@@ -1,18 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getOrCreateWorkspaceId
|
|
3
|
+
} from "./chunk-EZXEWFDG.js";
|
|
1
4
|
import {
|
|
2
5
|
analyzeShellTargets,
|
|
3
6
|
extractFileTargets
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MXVCP33E.js";
|
|
8
|
+
import {
|
|
9
|
+
scrubEnvironmentPaths
|
|
10
|
+
} from "./chunk-URLLSALP.js";
|
|
5
11
|
import {
|
|
6
|
-
|
|
7
|
-
} from "./chunk-
|
|
12
|
+
cachedCollectScopeAdmits
|
|
13
|
+
} from "./chunk-Z6NPNLOT.js";
|
|
14
|
+
import {
|
|
15
|
+
canonicalRepositoryPath,
|
|
16
|
+
currentBranch,
|
|
17
|
+
isRepoActiveForCapture,
|
|
18
|
+
relatedWorktreeTarget,
|
|
19
|
+
repoSyncId
|
|
20
|
+
} from "./chunk-DHOQGHKQ.js";
|
|
8
21
|
|
|
9
22
|
// src/hooks/file-refs.ts
|
|
10
23
|
var MAX_PREFLIGHT_FILE_TARGETS = 25;
|
|
11
24
|
function primitiveMetadataForResolution(resolution) {
|
|
12
|
-
const shouldAttach = resolution.fileRefs.length > 0 || resolution.rejected.length > 0 || resolution.targetsIncomplete || resolution.targetsTruncated || resolution.shellMutation !== void 0;
|
|
25
|
+
const shouldAttach = resolution.fileRefs.length > 0 || (resolution.targetCheckouts?.length ?? 0) > 0 || resolution.rejected.length > 0 || resolution.targetsIncomplete || resolution.targetsTruncated || resolution.shellMutation !== void 0;
|
|
13
26
|
if (!shouldAttach) return;
|
|
14
27
|
return {
|
|
15
28
|
fileRefs: [...resolution.fileRefs],
|
|
29
|
+
...resolution.targetCheckouts ? { targetCheckouts: resolution.targetCheckouts } : {},
|
|
16
30
|
...resolution.rejected.length > 0 || resolution.targetsIncomplete ? { fileRefsIncomplete: true } : {},
|
|
17
31
|
...resolution.targetsTruncated ? { fileRefsTruncated: true } : {},
|
|
18
32
|
...resolution.shellMutation !== void 0 ? { shellMutation: resolution.shellMutation } : {}
|
|
@@ -37,13 +51,55 @@ function resolveHookFileRefs(args) {
|
|
|
37
51
|
const targetsTruncated = uniqueRawPaths.length > MAX_PREFLIGHT_FILE_TARGETS;
|
|
38
52
|
const fileRefs = /* @__PURE__ */ new Set();
|
|
39
53
|
const rejected = [];
|
|
54
|
+
const targets = /* @__PURE__ */ new Map();
|
|
40
55
|
for (const path of uniqueRawPaths.slice(0, MAX_PREFLIGHT_FILE_TARGETS)) {
|
|
41
56
|
const canonical = canonicalRepositoryPath(path, args.repository, args.cwd);
|
|
42
57
|
if (canonical.ok) fileRefs.add(canonical.file);
|
|
43
|
-
else
|
|
58
|
+
else {
|
|
59
|
+
const target = args.captureTargets && canonical.reason === "outside_repository" ? relatedWorktreeTarget(path, args.cwd, args.repository) : null;
|
|
60
|
+
const resolved = target ? canonicalRepositoryPath(path, target, args.cwd) : null;
|
|
61
|
+
if (target && resolved?.ok && isRepoActiveForCapture(target.repoRoot) && repoSyncId(target.repoRoot) === args.repository.repoSyncId && args.repository.repoSyncId && cachedCollectScopeAdmits(target.repoRoot, {
|
|
62
|
+
repository: target.repoFullName,
|
|
63
|
+
branch: currentBranch(target.repoRoot),
|
|
64
|
+
agent: args.agent,
|
|
65
|
+
paths: [resolved.file],
|
|
66
|
+
pathsComplete: true
|
|
67
|
+
})) {
|
|
68
|
+
const group = targets.get(target.repoRoot) ?? { repository: target, fileRefs: [] };
|
|
69
|
+
group.fileRefs.push(resolved.file);
|
|
70
|
+
targets.set(target.repoRoot, group);
|
|
71
|
+
} else rejected.push({ path, reason: canonical.reason });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const targetCheckouts = [];
|
|
75
|
+
if (targets.size > 0) {
|
|
76
|
+
if (fileRefs.size > 0)
|
|
77
|
+
targets.set(args.repository.repoRoot, {
|
|
78
|
+
repository: args.repository,
|
|
79
|
+
fileRefs: [...fileRefs]
|
|
80
|
+
});
|
|
81
|
+
for (const { repository, fileRefs: paths } of targets.values()) {
|
|
82
|
+
const identity = getOrCreateWorkspaceId(repository.repoRoot);
|
|
83
|
+
const binding = repoSyncId(repository.repoRoot);
|
|
84
|
+
if (identity.status !== "ready" || !binding) {
|
|
85
|
+
rejected.push({ path: repository.repoRoot, reason: "invalid_path" });
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const root = scrubEnvironmentPaths({
|
|
89
|
+
cwd: repository.repoRoot,
|
|
90
|
+
gitRoot: repository.repoRoot
|
|
91
|
+
}).gitRoot;
|
|
92
|
+
targetCheckouts.push({
|
|
93
|
+
gitRoot: root,
|
|
94
|
+
workspaceId: identity.workspaceId,
|
|
95
|
+
repoSyncId: binding,
|
|
96
|
+
fileRefs: paths
|
|
97
|
+
});
|
|
98
|
+
}
|
|
44
99
|
}
|
|
45
100
|
return {
|
|
46
101
|
fileRefs: [...fileRefs],
|
|
102
|
+
...targetCheckouts.length > 0 ? { targetCheckouts } : {},
|
|
47
103
|
rejected,
|
|
48
104
|
targetsIncomplete: nativeTargets?.complete === false,
|
|
49
105
|
targetsTruncated,
|
|
@@ -57,7 +113,8 @@ function enrichHookPayloadWithFileRefs(args) {
|
|
|
57
113
|
toolInput: args.parsed.tool_input,
|
|
58
114
|
agent: args.agent,
|
|
59
115
|
cwd: args.cwd,
|
|
60
|
-
repository: args.repository
|
|
116
|
+
repository: args.repository,
|
|
117
|
+
captureTargets: true
|
|
61
118
|
});
|
|
62
119
|
const primitive = primitiveMetadataForResolution(resolution);
|
|
63
120
|
if (!primitive) return { parsed: args.parsed, resolution };
|
|
@@ -18,6 +18,24 @@ function gitToplevel(cwd) {
|
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
function mainWorktreeRoot(cwd) {
|
|
22
|
+
try {
|
|
23
|
+
const [gitDir, commonDir] = execFileSync(
|
|
24
|
+
"git",
|
|
25
|
+
["rev-parse", "--git-dir", "--git-common-dir"],
|
|
26
|
+
{
|
|
27
|
+
cwd,
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
30
|
+
timeout: GIT_TIMEOUT_MS
|
|
31
|
+
}
|
|
32
|
+
).trim().split("\n").map((value) => resolve(cwd ?? process.cwd(), value));
|
|
33
|
+
if (gitDir === void 0 || commonDir === void 0 || gitDir === commonDir) return null;
|
|
34
|
+
return basename(commonDir) === ".git" ? dirname(commonDir) : null;
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
21
39
|
function gitValue(cwd, args) {
|
|
22
40
|
try {
|
|
23
41
|
const value = execFileSync("git", args, {
|
|
@@ -56,6 +74,31 @@ function canonicalGitRoot(cwd) {
|
|
|
56
74
|
return null;
|
|
57
75
|
}
|
|
58
76
|
}
|
|
77
|
+
function relatedWorktreeTarget(path, cwd, origin) {
|
|
78
|
+
let parent = dirname(resolve(cwd, path));
|
|
79
|
+
while (true) {
|
|
80
|
+
try {
|
|
81
|
+
realpathSync.native(parent);
|
|
82
|
+
break;
|
|
83
|
+
} catch {
|
|
84
|
+
const next = dirname(parent);
|
|
85
|
+
if (next === parent) return null;
|
|
86
|
+
parent = next;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const target = resolveRepositoryContext(parent);
|
|
90
|
+
if (!target || !target.repoKey || target.repoKey !== origin.repoKey) return null;
|
|
91
|
+
const common = (root) => {
|
|
92
|
+
const value = gitValue(root, ["rev-parse", "--git-common-dir"]);
|
|
93
|
+
try {
|
|
94
|
+
return value ? realpathSync.native(resolve(root, value)) : void 0;
|
|
95
|
+
} catch {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const originCommon = common(origin.repoRoot);
|
|
100
|
+
return originCommon && originCommon === common(target.repoRoot) ? target : null;
|
|
101
|
+
}
|
|
59
102
|
function normalizeOriginRemote(remote) {
|
|
60
103
|
const value = remote.trim();
|
|
61
104
|
if (!value) return void 0;
|
|
@@ -186,11 +229,151 @@ function canonicalRepositoryPath(filePath, contextOrCwd, baseDirOrRoot) {
|
|
|
186
229
|
);
|
|
187
230
|
}
|
|
188
231
|
|
|
232
|
+
// src/lib/activation.ts
|
|
233
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
234
|
+
import { existsSync, readFileSync } from "fs";
|
|
235
|
+
import { homedir } from "os";
|
|
236
|
+
import { join } from "path";
|
|
237
|
+
var PRIM_ACTIVE_KEY = "prim.active";
|
|
238
|
+
var PRIM_REPO_SYNC_ID_KEY = "prim.repoSyncId";
|
|
239
|
+
var PRIM_REPO_SYNC_REPOSITORY_KEY = "prim.repoSyncRepository";
|
|
240
|
+
var PRIM_REPO_BINDING_STATE_KEY = "prim.repoBindingState";
|
|
241
|
+
var GIT_TIMEOUT_MS2 = 1e3;
|
|
242
|
+
var REPO_SYNC_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
243
|
+
var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json", ".cursor/hooks.json"];
|
|
244
|
+
var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
|
|
245
|
+
function repoActiveFlag(cwd) {
|
|
246
|
+
try {
|
|
247
|
+
const value = execFileSync2("git", ["config", "--get", PRIM_ACTIVE_KEY], {
|
|
248
|
+
cwd,
|
|
249
|
+
encoding: "utf-8",
|
|
250
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
251
|
+
timeout: GIT_TIMEOUT_MS2
|
|
252
|
+
}).trim();
|
|
253
|
+
if (value === "true") return "true";
|
|
254
|
+
if (value === "false") return "false";
|
|
255
|
+
return void 0;
|
|
256
|
+
} catch {
|
|
257
|
+
return void 0;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function isRepoActive(cwd) {
|
|
261
|
+
return repoActiveFlag(cwd) === "true";
|
|
262
|
+
}
|
|
263
|
+
function setRepoActive(cwd, active) {
|
|
264
|
+
execFileSync2("git", ["config", "--local", PRIM_ACTIVE_KEY, active ? "true" : "false"], {
|
|
265
|
+
cwd,
|
|
266
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
267
|
+
timeout: GIT_TIMEOUT_MS2
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function isValidRepoSyncId(value) {
|
|
271
|
+
return typeof value === "string" && REPO_SYNC_ID_RE.test(value);
|
|
272
|
+
}
|
|
273
|
+
function localGitConfigValue(cwd, key) {
|
|
274
|
+
try {
|
|
275
|
+
const value = execFileSync2("git", ["config", "--local", "--get", key], {
|
|
276
|
+
cwd,
|
|
277
|
+
encoding: "utf-8",
|
|
278
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
279
|
+
timeout: GIT_TIMEOUT_MS2
|
|
280
|
+
}).trim();
|
|
281
|
+
return value || void 0;
|
|
282
|
+
} catch {
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function setLocalGitConfigValue(cwd, key, value) {
|
|
287
|
+
execFileSync2("git", ["config", "--local", key, value], {
|
|
288
|
+
cwd,
|
|
289
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
290
|
+
timeout: GIT_TIMEOUT_MS2
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function repoSyncId(cwd) {
|
|
294
|
+
const value = localGitConfigValue(cwd, PRIM_REPO_SYNC_ID_KEY);
|
|
295
|
+
if (!isValidRepoSyncId(value)) return void 0;
|
|
296
|
+
const repositoryFullName = localGitConfigValue(cwd, PRIM_REPO_SYNC_REPOSITORY_KEY);
|
|
297
|
+
return repositoryFullName === githubRepositoryFullName(cwd) ? value : void 0;
|
|
298
|
+
}
|
|
299
|
+
function setRepoSyncId(cwd, value, repositoryFullName) {
|
|
300
|
+
if (!isValidRepoSyncId(value)) {
|
|
301
|
+
throw new Error("invalid repository binding returned by server");
|
|
302
|
+
}
|
|
303
|
+
execFileSync2("git", ["config", "--local", PRIM_REPO_SYNC_ID_KEY, value], {
|
|
304
|
+
cwd,
|
|
305
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
306
|
+
timeout: GIT_TIMEOUT_MS2
|
|
307
|
+
});
|
|
308
|
+
execFileSync2("git", ["config", "--local", PRIM_REPO_SYNC_REPOSITORY_KEY, repositoryFullName], {
|
|
309
|
+
cwd,
|
|
310
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
311
|
+
timeout: GIT_TIMEOUT_MS2
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
function setRepositoryBindingState(cwd, state) {
|
|
315
|
+
if (state !== "connected" && state !== "unbound" && state !== "invalid") {
|
|
316
|
+
throw new Error("invalid repository binding state");
|
|
317
|
+
}
|
|
318
|
+
execFileSync2("git", ["config", "--local", PRIM_REPO_BINDING_STATE_KEY, state], {
|
|
319
|
+
cwd,
|
|
320
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
321
|
+
timeout: GIT_TIMEOUT_MS2
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function repositoryBindingState(cwd) {
|
|
325
|
+
const rawId = localGitConfigValue(cwd, PRIM_REPO_SYNC_ID_KEY);
|
|
326
|
+
const marker = localGitConfigValue(cwd, PRIM_REPO_BINDING_STATE_KEY);
|
|
327
|
+
if (rawId !== void 0 && !isValidRepoSyncId(rawId)) return "invalid";
|
|
328
|
+
if (marker === "unbound" || marker === "invalid") return marker;
|
|
329
|
+
if (marker === "connected") return repoSyncId(cwd) ? "connected" : "invalid";
|
|
330
|
+
if (marker !== void 0) return "invalid";
|
|
331
|
+
return rawId === void 0 ? "unbound" : repoSyncId(cwd) ? void 0 : "unbound";
|
|
332
|
+
}
|
|
333
|
+
function hasProjectPrimInstall(cwd) {
|
|
334
|
+
const root = gitToplevel(cwd);
|
|
335
|
+
if (root === null || root === homedir()) return false;
|
|
336
|
+
for (const rel of PROJECT_INSTALL_FILES) {
|
|
337
|
+
const path = join(root, rel);
|
|
338
|
+
if (existsSync(path)) {
|
|
339
|
+
try {
|
|
340
|
+
const content = readFileSync(path, "utf-8");
|
|
341
|
+
if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
function isRepoActiveForCapture(cwd) {
|
|
349
|
+
const flag = repoActiveFlag(cwd);
|
|
350
|
+
if (flag === "true") return true;
|
|
351
|
+
if (flag === "false") return false;
|
|
352
|
+
return hasProjectPrimInstall(cwd);
|
|
353
|
+
}
|
|
354
|
+
function decisionIngestionStatus(cwd) {
|
|
355
|
+
return isRepoActiveForCapture(cwd) ? "enabled" : "disabled";
|
|
356
|
+
}
|
|
357
|
+
|
|
189
358
|
export {
|
|
190
359
|
gitToplevel,
|
|
360
|
+
mainWorktreeRoot,
|
|
191
361
|
currentBranch,
|
|
192
362
|
githubRepositoryFullName,
|
|
193
363
|
canonicalGitRoot,
|
|
364
|
+
relatedWorktreeTarget,
|
|
194
365
|
resolveRepositoryContext,
|
|
195
|
-
canonicalRepositoryPath
|
|
366
|
+
canonicalRepositoryPath,
|
|
367
|
+
repoActiveFlag,
|
|
368
|
+
isRepoActive,
|
|
369
|
+
setRepoActive,
|
|
370
|
+
isValidRepoSyncId,
|
|
371
|
+
localGitConfigValue,
|
|
372
|
+
setLocalGitConfigValue,
|
|
373
|
+
repoSyncId,
|
|
374
|
+
setRepoSyncId,
|
|
375
|
+
setRepositoryBindingState,
|
|
376
|
+
repositoryBindingState,
|
|
377
|
+
isRepoActiveForCapture,
|
|
378
|
+
decisionIngestionStatus
|
|
196
379
|
};
|
|
@@ -24,6 +24,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
mod
|
|
25
25
|
));
|
|
26
26
|
|
|
27
|
+
// src/lib/credentials.ts
|
|
28
|
+
import { createHash } from "crypto";
|
|
29
|
+
import { readFileSync } from "fs";
|
|
30
|
+
import { join as join2 } from "path";
|
|
31
|
+
|
|
27
32
|
// src/lib/paths.ts
|
|
28
33
|
import { homedir } from "os";
|
|
29
34
|
import { isAbsolute, join, normalize } from "path";
|
|
@@ -353,9 +358,6 @@ async function pollWorkosDeviceAuthorization(configuration, authorization, optio
|
|
|
353
358
|
}
|
|
354
359
|
|
|
355
360
|
// src/lib/credentials.ts
|
|
356
|
-
import { createHash } from "crypto";
|
|
357
|
-
import { readFileSync } from "fs";
|
|
358
|
-
import { join as join2 } from "path";
|
|
359
361
|
var CONFIG_DIRECTORY = primConfigDirectory();
|
|
360
362
|
var TOKEN_FILE_PATH = join2(CONFIG_DIRECTORY, "token");
|
|
361
363
|
var REFRESH_TOKEN_PATH = join2(CONFIG_DIRECTORY, "refresh_token");
|
|
@@ -1,30 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatStatusline
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
decisionIngestionStatus,
|
|
6
|
-
repositoryBindingState
|
|
7
|
-
} from "./chunk-DSWT6BCC.js";
|
|
3
|
+
} from "./chunk-ROLYLXJS.js";
|
|
8
4
|
import {
|
|
9
5
|
getSiteUrl,
|
|
10
6
|
isSessionEnded,
|
|
11
7
|
terminalSafeText
|
|
12
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LY6NJ7KL.js";
|
|
13
9
|
import {
|
|
14
10
|
packageVersion
|
|
15
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-Y5W4TZM7.js";
|
|
16
12
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
decisionIngestionStatus,
|
|
14
|
+
gitToplevel,
|
|
15
|
+
repositoryBindingState
|
|
16
|
+
} from "./chunk-DHOQGHKQ.js";
|
|
19
17
|
import {
|
|
20
18
|
daemonPrincipalsMatch,
|
|
21
19
|
daemonRequest,
|
|
22
20
|
resolveDaemonPrincipal
|
|
23
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-35FMRWJH.js";
|
|
24
22
|
import {
|
|
25
23
|
primConfigDirectory,
|
|
26
24
|
withFileLock
|
|
27
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-DWRSEC34.js";
|
|
28
26
|
|
|
29
27
|
// src/hooks/codex-context.ts
|
|
30
28
|
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
@@ -19,7 +19,12 @@ import {
|
|
|
19
19
|
resolveAuthCredential,
|
|
20
20
|
withFileLock,
|
|
21
21
|
workosConnectTokenEndpoint
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-DWRSEC34.js";
|
|
23
|
+
|
|
24
|
+
// src/client.ts
|
|
25
|
+
import { createHash } from "crypto";
|
|
26
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync } from "fs";
|
|
27
|
+
import { dirname } from "path";
|
|
23
28
|
|
|
24
29
|
// src/lib/terminal-safe.ts
|
|
25
30
|
function replaceIsolatedSurrogates(value) {
|
|
@@ -67,9 +72,6 @@ function isTerminalSafeText(value) {
|
|
|
67
72
|
var stripControlChars = terminalSafeText;
|
|
68
73
|
|
|
69
74
|
// src/client.ts
|
|
70
|
-
import { createHash } from "crypto";
|
|
71
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync } from "fs";
|
|
72
|
-
import { dirname } from "path";
|
|
73
75
|
var CONFIG_DIR_MODE = 448;
|
|
74
76
|
var CREDENTIAL_FILE_MODE = 384;
|
|
75
77
|
var REFRESH_THRESHOLD_MS = 6e4;
|
|
@@ -253,7 +253,7 @@ var COMMAND_OPTIONS = {
|
|
|
253
253
|
longOptionalValues: set("--color")
|
|
254
254
|
},
|
|
255
255
|
grep: {
|
|
256
|
-
shortFlags: "
|
|
256
|
+
shortFlags: "EFGHIPRTUVabcdhiLlnoqrsuvwxyZz",
|
|
257
257
|
shortValues: "ABCDefm",
|
|
258
258
|
longFlags: set(
|
|
259
259
|
"--basic-regexp --binary-files-without-match --byte-offset --count --extended-regexp --files-with-matches --files-without-match --fixed-regexp --fixed-strings --help --ignore-case --initial-tab --invert-match --line-buffered --line-number --line-regexp --null --null-data --only-matching --perl-regexp --quiet --recursive --silent --unix-byte-offsets --version --with-filename --word-regexp"
|
|
@@ -264,12 +264,14 @@ var COMMAND_OPTIONS = {
|
|
|
264
264
|
longOptionalValues: set("--color")
|
|
265
265
|
},
|
|
266
266
|
head: {
|
|
267
|
+
numericCount: true,
|
|
267
268
|
shortFlags: "qvz",
|
|
268
269
|
shortValues: "cn",
|
|
269
270
|
longFlags: set("--help --quiet --verbose --version --zero-terminated"),
|
|
270
271
|
longValues: set("--bytes --lines")
|
|
271
272
|
},
|
|
272
273
|
tail: {
|
|
274
|
+
numericCount: true,
|
|
273
275
|
shortFlags: "Fqsvz",
|
|
274
276
|
shortValues: "cn",
|
|
275
277
|
longFlags: set("--help --quiet --retry --silent --verbose --version --zero-terminated"),
|
|
@@ -294,6 +296,7 @@ var COMMAND_OPTIONS = {
|
|
|
294
296
|
}
|
|
295
297
|
};
|
|
296
298
|
var GIT_INSPECTION_OPTIONS = {
|
|
299
|
+
numericCount: true,
|
|
297
300
|
shortFlags: "abcdfhiklmnprstuvwz",
|
|
298
301
|
shortValues: "ABCGLMSU",
|
|
299
302
|
longFlags: set(
|
|
@@ -436,6 +439,7 @@ function parseOptions(args, spec) {
|
|
|
436
439
|
optionsEnded = true;
|
|
437
440
|
continue;
|
|
438
441
|
}
|
|
442
|
+
if (spec.numericCount && /^-\d+$/.test(arg)) continue;
|
|
439
443
|
if (arg.startsWith("--")) {
|
|
440
444
|
const separator = arg.indexOf("=");
|
|
441
445
|
const option = separator === -1 ? arg : arg.slice(0, separator);
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
cursorHooksPath,
|
|
3
3
|
hasExactCursorHandler,
|
|
4
4
|
readCursorHooks
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-Y5W4TZM7.js";
|
|
6
6
|
import {
|
|
7
7
|
resolveRepositoryContext
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DHOQGHKQ.js";
|
|
9
9
|
|
|
10
10
|
// src/hooks/agent.ts
|
|
11
11
|
function parseAgent(argv) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
boundedHealthError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Z6NPNLOT.js";
|
|
4
4
|
import {
|
|
5
5
|
terminalSafeLine
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LY6NJ7KL.js";
|
|
7
7
|
|
|
8
8
|
// src/hooks/redact.ts
|
|
9
9
|
import { existsSync, readFileSync, statSync } from "fs";
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
repoActiveFlag
|
|
3
|
+
} from "./chunk-DHOQGHKQ.js";
|
|
4
|
+
import {
|
|
5
|
+
atomicWriteFile
|
|
6
|
+
} from "./chunk-DWRSEC34.js";
|
|
7
|
+
|
|
8
|
+
// src/lib/commit-heartbeat.ts
|
|
9
|
+
import { execFileSync } from "child_process";
|
|
10
|
+
import { mkdirSync, readFileSync, readdirSync, unlinkSync } from "fs";
|
|
11
|
+
import { dirname, join, resolve } from "path";
|
|
12
|
+
var FIRED_DIR = "prim/post-commit-fired";
|
|
13
|
+
var WIRED_PATH = "prim/git-hooks-wired";
|
|
14
|
+
var GIT_TIMEOUT_MS = 1e3;
|
|
15
|
+
var MAX_STAMP_BYTES = 1024;
|
|
16
|
+
var REFLOG_SCAN_LIMIT = 50;
|
|
17
|
+
var MAX_FIRED_STAMPS = 256;
|
|
18
|
+
var PRUNE_SLACK = 64;
|
|
19
|
+
var FULL_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u;
|
|
20
|
+
var REFLOG_LINE_RE = /^([0-9a-f]{40}|[0-9a-f]{64})\tHEAD@\{([0-9]+)\}\t(.*)$/u;
|
|
21
|
+
var COMMIT_SUBJECT_RE = /^commit(?: \([^)]*\))?:/u;
|
|
22
|
+
var REFLOG_RESOLUTION_MS = 1e3;
|
|
23
|
+
var POST_COMMIT_GRACE_MS = 6e4;
|
|
24
|
+
function commonPath(cwd, relative) {
|
|
25
|
+
const value = execFileSync("git", ["rev-parse", "--git-common-dir"], {
|
|
26
|
+
cwd,
|
|
27
|
+
encoding: "utf8",
|
|
28
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
29
|
+
timeout: GIT_TIMEOUT_MS
|
|
30
|
+
}).replace(/\r?\n$/u, "");
|
|
31
|
+
if (value === "" || /[\0\r\n]/u.test(value)) throw new Error("Git returned an unsafe path");
|
|
32
|
+
return join(resolve(cwd, value), relative);
|
|
33
|
+
}
|
|
34
|
+
function gitPath(cwd, relative) {
|
|
35
|
+
const value = execFileSync("git", ["rev-parse", "--git-path", relative], {
|
|
36
|
+
cwd,
|
|
37
|
+
encoding: "utf8",
|
|
38
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
39
|
+
timeout: GIT_TIMEOUT_MS
|
|
40
|
+
}).replace(/\r?\n$/u, "");
|
|
41
|
+
if (value === "" || /[\0\r\n]/u.test(value)) throw new Error("Git returned an unsafe path");
|
|
42
|
+
return resolve(cwd, value);
|
|
43
|
+
}
|
|
44
|
+
function ensurePrivateParent(path) {
|
|
45
|
+
mkdirSync(dirname(dirname(path)), { recursive: true, mode: 448 });
|
|
46
|
+
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
47
|
+
}
|
|
48
|
+
function writeStamp(path, at) {
|
|
49
|
+
ensurePrivateParent(path);
|
|
50
|
+
atomicWriteFile(path, `${JSON.stringify({ at })}
|
|
51
|
+
`, { mode: 384 });
|
|
52
|
+
}
|
|
53
|
+
function readStampAt(path) {
|
|
54
|
+
try {
|
|
55
|
+
const raw = readFileSync(path, "utf8");
|
|
56
|
+
if (raw.length > MAX_STAMP_BYTES) return void 0;
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
return typeof parsed.at === "number" && Number.isSafeInteger(parsed.at) && parsed.at > 0 ? parsed.at : void 0;
|
|
59
|
+
} catch {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function recordConversationCaptured(cwd, agent) {
|
|
64
|
+
try {
|
|
65
|
+
writeStamp(gitPath(cwd, `prim/conversation-captured/${agent}`), Date.now());
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function lastConversationCaptured(cwd, agent) {
|
|
70
|
+
try {
|
|
71
|
+
return readStampAt(gitPath(cwd, `prim/conversation-captured/${agent}`));
|
|
72
|
+
} catch {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function readFiredStamps(cwd) {
|
|
77
|
+
try {
|
|
78
|
+
const dir = gitPath(cwd, FIRED_DIR);
|
|
79
|
+
return readdirSync(dir).flatMap((sha) => {
|
|
80
|
+
if (!FULL_SHA_RE.test(sha)) return [];
|
|
81
|
+
const at = readStampAt(join(dir, sha));
|
|
82
|
+
return at === void 0 ? [] : [{ sha, at }];
|
|
83
|
+
});
|
|
84
|
+
} catch {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function recordPostCommitFired(cwd, sha, now = Date.now()) {
|
|
89
|
+
if (!sha || !FULL_SHA_RE.test(sha)) return;
|
|
90
|
+
try {
|
|
91
|
+
const dir = gitPath(cwd, FIRED_DIR);
|
|
92
|
+
writeStamp(join(dir, sha), now);
|
|
93
|
+
const names = readdirSync(dir).filter((name) => FULL_SHA_RE.test(name));
|
|
94
|
+
if (names.length <= MAX_FIRED_STAMPS + PRUNE_SLACK) return;
|
|
95
|
+
const stale = names.map((name) => ({ name, at: readStampAt(join(dir, name)) ?? 0 })).sort((left, right) => right.at - left.at || left.name.localeCompare(right.name)).slice(MAX_FIRED_STAMPS);
|
|
96
|
+
for (const { name } of stale) unlinkSync(join(dir, name));
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function recordHooksWired(cwd, options = {}) {
|
|
101
|
+
try {
|
|
102
|
+
const path = commonPath(cwd, WIRED_PATH);
|
|
103
|
+
if (options.onlyIfAbsent && readStampAt(path) !== void 0) return;
|
|
104
|
+
writeStamp(path, options.now ?? Date.now());
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function clearHooksWired(cwd) {
|
|
109
|
+
try {
|
|
110
|
+
unlinkSync(commonPath(cwd, WIRED_PATH));
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function latestLocalCommit(cwd) {
|
|
115
|
+
let output;
|
|
116
|
+
try {
|
|
117
|
+
output = execFileSync(
|
|
118
|
+
"git",
|
|
119
|
+
[
|
|
120
|
+
"log",
|
|
121
|
+
"-g",
|
|
122
|
+
"--no-show-signature",
|
|
123
|
+
"-n",
|
|
124
|
+
String(REFLOG_SCAN_LIMIT),
|
|
125
|
+
"--date=unix",
|
|
126
|
+
"--format=%H%x09%gd%x09%gs",
|
|
127
|
+
"HEAD"
|
|
128
|
+
],
|
|
129
|
+
{ cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: GIT_TIMEOUT_MS }
|
|
130
|
+
);
|
|
131
|
+
} catch {
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
for (const line of output.split("\n")) {
|
|
135
|
+
const match = REFLOG_LINE_RE.exec(line);
|
|
136
|
+
if (match?.[1] && match[2] && COMMIT_SUBJECT_RE.test(match[3] ?? "")) {
|
|
137
|
+
return { sha: match[1], at: Number(match[2]) * 1e3 };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
function inspectPostCommitFiring(cwd, now = Date.now()) {
|
|
143
|
+
if (repoActiveFlag(cwd) !== "true") return { state: "inactive" };
|
|
144
|
+
const commit = latestLocalCommit(cwd);
|
|
145
|
+
if (!commit) return { state: "unverified" };
|
|
146
|
+
const stamps = readFiredStamps(cwd);
|
|
147
|
+
const own = stamps.find((stamp) => stamp.sha === commit.sha);
|
|
148
|
+
if (own) return { state: "fired", commitAt: commit.at, firedAt: own.at };
|
|
149
|
+
if (now - commit.at < POST_COMMIT_GRACE_MS) return { state: "pending", commitAt: commit.at };
|
|
150
|
+
const lastFiredAt = stamps.reduce(
|
|
151
|
+
(latest, stamp) => latest === void 0 || stamp.at > latest ? stamp.at : latest,
|
|
152
|
+
void 0
|
|
153
|
+
);
|
|
154
|
+
let wiredAt;
|
|
155
|
+
try {
|
|
156
|
+
wiredAt = readStampAt(commonPath(cwd, WIRED_PATH));
|
|
157
|
+
} catch {
|
|
158
|
+
wiredAt = void 0;
|
|
159
|
+
}
|
|
160
|
+
if (wiredAt === void 0) return { state: "unverified" };
|
|
161
|
+
const firedSinceWired = lastFiredAt !== void 0 && lastFiredAt >= wiredAt;
|
|
162
|
+
const judged = commit.at > wiredAt || firedSinceWired && commit.at + REFLOG_RESOLUTION_MS > lastFiredAt;
|
|
163
|
+
if (judged) {
|
|
164
|
+
return lastFiredAt === void 0 ? { state: "not_firing", commitAt: commit.at } : { state: "not_firing", commitAt: commit.at, firedAt: lastFiredAt };
|
|
165
|
+
}
|
|
166
|
+
return { state: "unverified" };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
POST_COMMIT_GRACE_MS,
|
|
171
|
+
recordConversationCaptured,
|
|
172
|
+
lastConversationCaptured,
|
|
173
|
+
recordPostCommitFired,
|
|
174
|
+
recordHooksWired,
|
|
175
|
+
clearHooksWired,
|
|
176
|
+
inspectPostCommitFiring
|
|
177
|
+
};
|