@primitive.ai/prim 0.1.0-alpha.68 → 0.1.0-alpha.69
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 +39 -1
- package/SKILL.md +1 -1
- package/dist/{chunk-XPVNH3J6.js → chunk-2KW7DVFJ.js} +121 -130
- package/dist/chunk-3EEFL6NJ.js +529 -0
- package/dist/chunk-4A2XB2PC.js +310 -0
- package/dist/{chunk-G5D3FF3D.js → chunk-BQUJ4AFJ.js} +1 -83
- package/dist/chunk-CUE2LKLR.js +155 -0
- package/dist/chunk-FI3LIRCB.js +262 -0
- package/dist/{chunk-CPSE3Y3T.js → chunk-H64F6QHV.js} +27 -8
- package/dist/{chunk-I5BHJQH2.js → chunk-KKP2NYZ3.js} +206 -30
- package/dist/chunk-LEMX3IOG.js +79 -0
- package/dist/{chunk-U6L2RU67.js → chunk-LMRYZL3D.js} +52 -45
- package/dist/{chunk-XUAP572Z.js → chunk-RO3F6JYV.js} +8 -7
- package/dist/{chunk-TDRXHBEX.js → chunk-TJTVHDJ5.js} +41 -18
- package/dist/{chunk-TCDUH7AN.js → chunk-WB7AUZ4C.js} +17 -0
- package/dist/{chunk-UN5O5SYG.js → chunk-YWO7O6QG.js} +14 -53
- package/dist/chunk-Z7YPFWAZ.js +917 -0
- package/dist/{chunk-BDGQEJA5.js → chunk-ZOSNEO5N.js} +2 -2
- package/dist/daemon/server.js +11634 -391
- package/dist/hook-runtime/daemon/server.js +13353 -0
- package/dist/hook-runtime/hooks/post-commit.js +1128 -0
- package/dist/hook-runtime/hooks/post-rewrite.js +1148 -0
- package/dist/hook-runtime/hooks/post-tool-use.js +7586 -0
- package/dist/hook-runtime/hooks/pre-commit.js +1314 -0
- package/dist/hook-runtime/hooks/pre-tool-use.js +7099 -0
- package/dist/hook-runtime/hooks/prim-hook.js +7691 -0
- package/dist/hook-runtime/hooks/session-end.js +495 -0
- package/dist/hook-runtime/hooks/session-start.js +11284 -0
- package/dist/hook-runtime/index.js +42775 -0
- package/dist/hook-runtime/statusline-main.js +765 -0
- package/dist/hooks/post-commit.js +7 -4
- package/dist/hooks/post-rewrite.js +7 -4
- package/dist/hooks/post-tool-use.js +21 -19
- package/dist/hooks/pre-commit.js +4 -3
- package/dist/hooks/pre-tool-use.js +59 -18
- package/dist/hooks/prim-hook.js +39 -56
- package/dist/hooks/session-end.js +3 -2
- package/dist/hooks/session-start.js +15 -12
- package/dist/index.js +15050 -2479
- package/dist/statusline-main.js +477 -89
- package/package.json +11 -5
- package/dist/chunk-K3AYW6VZ.js +0 -300
- package/dist/chunk-RTLFE6EZ.js +0 -518
- package/dist/chunk-UTKQTZHL.js +0 -88
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getClient,
|
|
3
|
-
getSiteUrl
|
|
4
|
-
|
|
3
|
+
getSiteUrl,
|
|
4
|
+
terminalSafeLine
|
|
5
|
+
} from "./chunk-Z7YPFWAZ.js";
|
|
5
6
|
import {
|
|
6
7
|
daemonRequest
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CUE2LKLR.js";
|
|
8
9
|
|
|
9
10
|
// src/daemon/proxy.ts
|
|
10
11
|
var DAEMON_HTTP_TIMEOUT_MS = 1e4;
|
|
@@ -93,7 +94,7 @@ var FILES_PREVIEW_LIMIT = 3;
|
|
|
93
94
|
function formatDecisionsWarning(result) {
|
|
94
95
|
const lines = [];
|
|
95
96
|
if (result.unavailable !== void 0) {
|
|
96
|
-
lines.push(`[prim] decision check not verified \u2014 ${result.unavailable}`);
|
|
97
|
+
lines.push(`[prim] decision check not verified \u2014 ${terminalSafeLine(result.unavailable)}`);
|
|
97
98
|
}
|
|
98
99
|
if (result.decisions.length > 0) {
|
|
99
100
|
lines.push(
|
|
@@ -101,12 +102,12 @@ function formatDecisionsWarning(result) {
|
|
|
101
102
|
);
|
|
102
103
|
for (const d of result.decisions) {
|
|
103
104
|
const statusMark = d.status === "under_review" ? " (under review)" : "";
|
|
104
|
-
const preview = d.matchedFiles.slice(0, FILES_PREVIEW_LIMIT).join(", ");
|
|
105
|
+
const preview = d.matchedFiles.slice(0, FILES_PREVIEW_LIMIT).map(terminalSafeLine).join(", ");
|
|
105
106
|
const overflow = d.matchedFiles.length > FILES_PREVIEW_LIMIT ? ` (+${String(d.matchedFiles.length - FILES_PREVIEW_LIMIT)} more)` : "";
|
|
106
|
-
lines.push(` \xB7 ${d.intent}${statusMark}`);
|
|
107
|
+
lines.push(` \xB7 ${terminalSafeLine(d.intent)}${statusMark}`);
|
|
107
108
|
lines.push(` files: ${preview}${overflow}`);
|
|
108
109
|
if (d.rationale) {
|
|
109
|
-
lines.push(` rationale: ${d.rationale}`);
|
|
110
|
+
lines.push(` rationale: ${terminalSafeLine(d.rationale)}`);
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
}
|
|
@@ -4,6 +4,8 @@ import { createHash } from "crypto";
|
|
|
4
4
|
import { lstatSync, realpathSync } from "fs";
|
|
5
5
|
import { basename, dirname, isAbsolute, relative, resolve, sep } from "path";
|
|
6
6
|
var GIT_TIMEOUT_MS = 1e3;
|
|
7
|
+
var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
|
|
8
|
+
var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
|
|
7
9
|
function gitToplevel(cwd) {
|
|
8
10
|
try {
|
|
9
11
|
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
@@ -38,7 +40,9 @@ function githubRepositoryFullName(cwd) {
|
|
|
38
40
|
const path = match?.[1] ?? match?.[2];
|
|
39
41
|
if (!path) return null;
|
|
40
42
|
const parts = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "").split("/");
|
|
41
|
-
if (parts.length !== 2 ||
|
|
43
|
+
if (parts.length !== 2 || !GITHUB_OWNER_PATTERN.test(parts[0] ?? "") || !GITHUB_REPOSITORY_PATTERN.test(parts[1] ?? "")) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
42
46
|
return `${parts[0]}/${parts[1]}`;
|
|
43
47
|
}
|
|
44
48
|
function canonicalGitRoot(cwd) {
|
|
@@ -186,6 +190,8 @@ import { homedir } from "os";
|
|
|
186
190
|
import { join } from "path";
|
|
187
191
|
var PRIM_ACTIVE_KEY = "prim.active";
|
|
188
192
|
var PRIM_REPO_SYNC_ID_KEY = "prim.repoSyncId";
|
|
193
|
+
var PRIM_REPO_SYNC_REPOSITORY_KEY = "prim.repoSyncRepository";
|
|
194
|
+
var PRIM_REPO_BINDING_STATE_KEY = "prim.repoBindingState";
|
|
189
195
|
var GIT_TIMEOUT_MS2 = 1e3;
|
|
190
196
|
var REPO_SYNC_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
191
197
|
var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
|
|
@@ -218,20 +224,26 @@ function setRepoActive(cwd, active) {
|
|
|
218
224
|
function isValidRepoSyncId(value) {
|
|
219
225
|
return typeof value === "string" && REPO_SYNC_ID_RE.test(value);
|
|
220
226
|
}
|
|
221
|
-
function
|
|
227
|
+
function localGitConfigValue(cwd, key) {
|
|
222
228
|
try {
|
|
223
|
-
const value = execFileSync2("git", ["config", "--local", "--get",
|
|
229
|
+
const value = execFileSync2("git", ["config", "--local", "--get", key], {
|
|
224
230
|
cwd,
|
|
225
231
|
encoding: "utf-8",
|
|
226
232
|
stdio: ["ignore", "pipe", "ignore"],
|
|
227
233
|
timeout: GIT_TIMEOUT_MS2
|
|
228
234
|
}).trim();
|
|
229
|
-
return
|
|
235
|
+
return value || void 0;
|
|
230
236
|
} catch {
|
|
231
237
|
return void 0;
|
|
232
238
|
}
|
|
233
239
|
}
|
|
234
|
-
function
|
|
240
|
+
function repoSyncId(cwd) {
|
|
241
|
+
const value = localGitConfigValue(cwd, PRIM_REPO_SYNC_ID_KEY);
|
|
242
|
+
if (!isValidRepoSyncId(value)) return void 0;
|
|
243
|
+
const repositoryFullName = localGitConfigValue(cwd, PRIM_REPO_SYNC_REPOSITORY_KEY);
|
|
244
|
+
return repositoryFullName === githubRepositoryFullName(cwd) ? value : void 0;
|
|
245
|
+
}
|
|
246
|
+
function setRepoSyncId(cwd, value, repositoryFullName) {
|
|
235
247
|
if (!isValidRepoSyncId(value)) {
|
|
236
248
|
throw new Error("invalid repository binding returned by server");
|
|
237
249
|
}
|
|
@@ -240,20 +252,30 @@ function setRepoSyncId(cwd, value) {
|
|
|
240
252
|
stdio: ["ignore", "ignore", "pipe"],
|
|
241
253
|
timeout: GIT_TIMEOUT_MS2
|
|
242
254
|
});
|
|
255
|
+
execFileSync2("git", ["config", "--local", PRIM_REPO_SYNC_REPOSITORY_KEY, repositoryFullName], {
|
|
256
|
+
cwd,
|
|
257
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
258
|
+
timeout: GIT_TIMEOUT_MS2
|
|
259
|
+
});
|
|
243
260
|
}
|
|
244
|
-
function
|
|
245
|
-
|
|
246
|
-
|
|
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;
|
|
261
|
+
function setRepositoryBindingState(cwd, state) {
|
|
262
|
+
if (state !== "connected" && state !== "unbound" && state !== "invalid") {
|
|
263
|
+
throw new Error("invalid repository binding state");
|
|
256
264
|
}
|
|
265
|
+
execFileSync2("git", ["config", "--local", PRIM_REPO_BINDING_STATE_KEY, state], {
|
|
266
|
+
cwd,
|
|
267
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
268
|
+
timeout: GIT_TIMEOUT_MS2
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function repositoryBindingState(cwd) {
|
|
272
|
+
const rawId = localGitConfigValue(cwd, PRIM_REPO_SYNC_ID_KEY);
|
|
273
|
+
const marker = localGitConfigValue(cwd, PRIM_REPO_BINDING_STATE_KEY);
|
|
274
|
+
if (rawId !== void 0 && !isValidRepoSyncId(rawId)) return "invalid";
|
|
275
|
+
if (marker === "unbound" || marker === "invalid") return marker;
|
|
276
|
+
if (marker === "connected") return repoSyncId(cwd) ? "connected" : "invalid";
|
|
277
|
+
if (marker !== void 0) return "invalid";
|
|
278
|
+
return rawId === void 0 ? "unbound" : repoSyncId(cwd) ? void 0 : "unbound";
|
|
257
279
|
}
|
|
258
280
|
function hasProjectPrimInstall(cwd) {
|
|
259
281
|
const root = gitToplevel(cwd);
|
|
@@ -292,7 +314,8 @@ export {
|
|
|
292
314
|
isValidRepoSyncId,
|
|
293
315
|
repoSyncId,
|
|
294
316
|
setRepoSyncId,
|
|
295
|
-
|
|
317
|
+
setRepositoryBindingState,
|
|
318
|
+
repositoryBindingState,
|
|
296
319
|
isRepoActiveForCapture,
|
|
297
320
|
decisionIngestionStatus
|
|
298
321
|
};
|
|
@@ -9,6 +9,7 @@ function parseAgent(argv) {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// src/hooks/normalize.ts
|
|
12
|
+
import { createHash } from "crypto";
|
|
12
13
|
var HERMES_EVENT_MAP = {
|
|
13
14
|
on_session_start: "SessionStart",
|
|
14
15
|
on_session_end: "SessionEnd",
|
|
@@ -19,6 +20,22 @@ var HERMES_EVENT_MAP = {
|
|
|
19
20
|
subagent_stop: "SubagentStop"
|
|
20
21
|
};
|
|
21
22
|
function normalizeEnvelope(parsed, agent) {
|
|
23
|
+
if (agent === "codex") {
|
|
24
|
+
const event = parsed.hook_event_name;
|
|
25
|
+
if ((event === "PreToolUse" || event === "PostToolUse") && (typeof parsed.tool_use_id !== "string" || parsed.tool_use_id.length === 0)) {
|
|
26
|
+
const stableToolIdentity = JSON.stringify([
|
|
27
|
+
parsed.session_id ?? null,
|
|
28
|
+
parsed.turn_id ?? null,
|
|
29
|
+
parsed.tool_name ?? null,
|
|
30
|
+
parsed.tool_input ?? null
|
|
31
|
+
]);
|
|
32
|
+
return {
|
|
33
|
+
...parsed,
|
|
34
|
+
tool_use_id: `codex:fallback:v1:${createHash("sha256").update(stableToolIdentity).digest("hex")}`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return parsed;
|
|
38
|
+
}
|
|
22
39
|
if (agent !== "hermes") {
|
|
23
40
|
return parsed;
|
|
24
41
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSiteUrl
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Z7YPFWAZ.js";
|
|
4
|
+
import {
|
|
5
|
+
jwtOrganizationId,
|
|
6
|
+
primConfigDirectory,
|
|
7
|
+
resolveAuthCredential
|
|
8
|
+
} from "./chunk-3EEFL6NJ.js";
|
|
4
9
|
|
|
5
10
|
// src/journal.ts
|
|
6
11
|
import {
|
|
@@ -15,9 +20,8 @@ import {
|
|
|
15
20
|
readSync,
|
|
16
21
|
readdirSync
|
|
17
22
|
} from "fs";
|
|
18
|
-
import { homedir } from "os";
|
|
19
23
|
import { dirname, join } from "path";
|
|
20
|
-
var JOURNAL_DIR = join(
|
|
24
|
+
var JOURNAL_DIR = join(primConfigDirectory(), "moves");
|
|
21
25
|
var UNBOUND_BUCKET = "_unbound";
|
|
22
26
|
var JOURNAL_BASENAME = "journal.ndjson";
|
|
23
27
|
var JOURNAL_STATS_SAMPLE_BYTES = 64 * 1024;
|
|
@@ -139,7 +143,9 @@ function sampleJournalFile(path, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
|
|
|
139
143
|
function parseFlushingPid(name) {
|
|
140
144
|
const segments = name.slice(FLUSHING_PREFIX.length).split(".");
|
|
141
145
|
const last = segments.length >= 2 ? segments[segments.length - 1] : void 0;
|
|
142
|
-
|
|
146
|
+
if (last === void 0 || !/^[0-9]+$/.test(last)) return void 0;
|
|
147
|
+
const pid = Number(last);
|
|
148
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
143
149
|
}
|
|
144
150
|
function listFlushingInDir(dir, bucket, options = {}) {
|
|
145
151
|
if (!existsSync(dir)) {
|
|
@@ -253,18 +259,10 @@ function pendingJournalStats() {
|
|
|
253
259
|
|
|
254
260
|
// src/binding.ts
|
|
255
261
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
256
|
-
import { homedir as homedir2 } from "os";
|
|
257
262
|
import { dirname as dirname2, join as join2 } from "path";
|
|
258
|
-
var PRIM_CONFIG_DIR =
|
|
259
|
-
var TOKEN_PATH = join2(PRIM_CONFIG_DIR, "token");
|
|
263
|
+
var PRIM_CONFIG_DIR = primConfigDirectory();
|
|
260
264
|
var SESSIONS_DIR = join2(PRIM_CONFIG_DIR, "sessions");
|
|
261
265
|
var WORKSPACE_FILE = ".prim/workspace.json";
|
|
262
|
-
var JWT_PARTS = 3;
|
|
263
|
-
var BASE64_PAD_4 = 4;
|
|
264
|
-
var ENV_TOKEN_LINE = /^\s*PRIM_TOKEN\s*=\s*(.*)$/m;
|
|
265
|
-
var SURROUNDING_QUOTES = /^["']|["']$/g;
|
|
266
|
-
var BASE64URL_MINUS = /-/g;
|
|
267
|
-
var BASE64URL_UNDERSCORE = /_/g;
|
|
268
266
|
function readJsonSafe(path) {
|
|
269
267
|
if (!existsSync2(path)) {
|
|
270
268
|
return;
|
|
@@ -296,46 +294,9 @@ function fromWorkspaceFile(startDir) {
|
|
|
296
294
|
dir = parent;
|
|
297
295
|
}
|
|
298
296
|
}
|
|
299
|
-
function
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
return fromEnv.trim();
|
|
303
|
-
}
|
|
304
|
-
if (existsSync2(TOKEN_PATH)) {
|
|
305
|
-
return readFileSync2(TOKEN_PATH, "utf-8").trim();
|
|
306
|
-
}
|
|
307
|
-
const envLocal = join2(cwd, ".env.local");
|
|
308
|
-
if (existsSync2(envLocal)) {
|
|
309
|
-
const match = readFileSync2(envLocal, "utf-8").match(ENV_TOKEN_LINE);
|
|
310
|
-
if (match) {
|
|
311
|
-
return match[1].trim().replace(SURROUNDING_QUOTES, "");
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
function base64UrlDecode(s) {
|
|
317
|
-
const padded = s.padEnd(
|
|
318
|
-
s.length + (BASE64_PAD_4 - s.length % BASE64_PAD_4) % BASE64_PAD_4,
|
|
319
|
-
"="
|
|
320
|
-
);
|
|
321
|
-
const normalized = padded.replace(BASE64URL_MINUS, "+").replace(BASE64URL_UNDERSCORE, "/");
|
|
322
|
-
return Buffer.from(normalized, "base64").toString("utf-8");
|
|
323
|
-
}
|
|
324
|
-
function fromDefaultOrg(cwd) {
|
|
325
|
-
const token = readToken(cwd);
|
|
326
|
-
if (!token) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
const parts = token.split(".");
|
|
330
|
-
if (parts.length !== JWT_PARTS) {
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
try {
|
|
334
|
-
const claims = JSON.parse(base64UrlDecode(parts[1]));
|
|
335
|
-
return claims.org_id;
|
|
336
|
-
} catch {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
297
|
+
function fromDefaultOrg(_cwd) {
|
|
298
|
+
const credential = resolveAuthCredential();
|
|
299
|
+
return credential ? jwtOrganizationId(credential.token) : void 0;
|
|
339
300
|
}
|
|
340
301
|
function resolveOrg(args) {
|
|
341
302
|
const fromSession = fromSessionMarker(args.sessionId);
|