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

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.
@@ -26,10 +26,10 @@ import { dirname, join } from "path";
26
26
 
27
27
  // src/client.ts
28
28
  var DEFAULT_API_URL = "https://api.getprimitive.ai";
29
- function loadEnvFile() {
29
+ function loadEnvFile(cwd = process.cwd()) {
30
30
  const envVars = {};
31
31
  for (const file of [".env.local", ".env"]) {
32
- const filePath = resolve(process.cwd(), file);
32
+ const filePath = resolve(cwd, file);
33
33
  if (!existsSync2(filePath)) continue;
34
34
  const content = readFileSync2(filePath, "utf-8");
35
35
  for (const line of content.split("\n")) {
@@ -48,8 +48,11 @@ var TOKEN_EXPIRES_PATH = join2(dirname2(TOKEN_FILE_PATH), "token_expires_at");
48
48
  var TERMINAL_REFRESH_PATH = join2(dirname2(TOKEN_FILE_PATH), "refresh_terminal");
49
49
  var CREDENTIAL_LOCK_PATH = join2(dirname2(TOKEN_FILE_PATH), "credentials.lock");
50
50
  function getSiteUrl() {
51
- if (process.env.PRIM_API_URL) return process.env.PRIM_API_URL;
52
- return loadEnvFile().PRIM_API_URL ?? DEFAULT_API_URL;
51
+ return getSiteUrlForCwd(process.cwd(), process.env.PRIM_API_URL);
52
+ }
53
+ function getSiteUrlForCwd(cwd, primApiUrl) {
54
+ if (primApiUrl) return primApiUrl;
55
+ return loadEnvFile(cwd).PRIM_API_URL ?? DEFAULT_API_URL;
53
56
  }
54
57
 
55
58
  // src/daemon/client.ts
@@ -273,15 +276,54 @@ function formatTeammatesWithArea(teammates, cap) {
273
276
  );
274
277
  }
275
278
 
279
+ // src/lib/statusline-render.ts
280
+ var STATUSLINE_NAME_CAP = 3;
281
+ function formatStatusline(version, snapshot, resolveIngestionStatus) {
282
+ if (!snapshot) {
283
+ return `primitive ${version} (daemon: down)`;
284
+ }
285
+ if (snapshot.healthy === false) {
286
+ if (snapshot.needsReauth) {
287
+ return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
288
+ }
289
+ if (snapshot.ingestion?.healthy === false) {
290
+ const pending = snapshot.ingestion.pendingCount;
291
+ const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
292
+ return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""})`;
293
+ }
294
+ if (snapshot.heartbeat?.healthy === false) {
295
+ return `primitive ${version} (daemon: degraded \xB7 presence: unavailable)`;
296
+ }
297
+ return `primitive ${version} (daemon: starting)`;
298
+ }
299
+ const ingestionStatus = resolveIngestionStatus();
300
+ if (snapshot.envMismatch) {
301
+ return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: other env)`;
302
+ }
303
+ if (snapshot.presenceStale) {
304
+ return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: stale)`;
305
+ }
306
+ let team;
307
+ if (snapshot.onlineTeammates !== void 0) {
308
+ team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
309
+ } else if (snapshot.onlineNames !== void 0) {
310
+ team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
311
+ } else if (typeof snapshot.onlineCount === "number") {
312
+ team = `team: ${String(snapshot.onlineCount)} online`;
313
+ } else {
314
+ team = "team: \u2014";
315
+ }
316
+ return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 ${team})`;
317
+ }
318
+
276
319
  // src/commands/statusline.ts
277
320
  var STATUSLINE_TIMEOUT_MS = 200;
278
- var STATUSLINE_NAME_CAP = 3;
279
321
  function readPackageVersion() {
280
322
  try {
281
323
  const here = dirname5(fileURLToPath2(import.meta.url));
282
324
  const candidates = [
283
- // The supervised runtime stages a tiny manifest beside the standalone
284
- // statusline bundle, so it remains versioned without loading the package.
325
+ // Schema-v2 runtimes staged a manifest beside this standalone bundle.
326
+ // Keep resolving it while those legacy launchers transition to schema v3.
285
327
  resolve3(here, "manifest.json"),
286
328
  resolve3(here, "../../package.json"),
287
329
  resolve3(here, "../package.json")
@@ -316,40 +358,8 @@ async function renderStatusline() {
316
358
  );
317
359
  if (!snapshot) {
318
360
  debug("daemon snapshot missing");
319
- return `primitive ${version} (daemon: down)`;
320
- }
321
- if (snapshot.healthy === false) {
322
- if (snapshot.needsReauth) {
323
- return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
324
- }
325
- if (snapshot.ingestion?.healthy === false) {
326
- const pending = snapshot.ingestion.pendingCount;
327
- const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
328
- return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""})`;
329
- }
330
- if (snapshot.heartbeat?.healthy === false) {
331
- return `primitive ${version} (daemon: degraded \xB7 presence: unavailable)`;
332
- }
333
- return `primitive ${version} (daemon: starting)`;
334
361
  }
335
- const ingestionStatus = decisionIngestionStatus(process.cwd());
336
- if (snapshot.envMismatch) {
337
- return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: other env)`;
338
- }
339
- if (snapshot.presenceStale) {
340
- return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: stale)`;
341
- }
342
- let team;
343
- if (snapshot.onlineTeammates !== void 0) {
344
- team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
345
- } else if (snapshot.onlineNames !== void 0) {
346
- team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
347
- } else if (typeof snapshot.onlineCount === "number") {
348
- team = `team: ${String(snapshot.onlineCount)} online`;
349
- } else {
350
- team = "team: \u2014";
351
- }
352
- return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 ${team})`;
362
+ return formatStatusline(version, snapshot, () => decisionIngestionStatus(process.cwd()));
353
363
  }
354
364
 
355
365
  // src/statusline-main.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitive.ai/prim",
3
- "version": "0.1.0-alpha.62",
3
+ "version": "0.1.0-alpha.64",
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",
@@ -1,411 +0,0 @@
1
- // src/hooks/pre-tool-use-scoring.ts
2
- import { isAbsolute, relative, sep } from "path";
3
- function anyUnverified(results) {
4
- return results.some((r) => r.verdict === "unavailable" || r.truncated);
5
- }
6
- function unverifiedNote(results) {
7
- const causes = [];
8
- const unavailable = results.find((r) => r.verdict === "unavailable");
9
- if (unavailable) {
10
- causes.push(
11
- unavailable.unavailable ? `decision check skipped \u2014 ${unavailable.unavailable}` : "decision check skipped \u2014 not verified"
12
- );
13
- }
14
- if (results.some((r) => r.truncated)) {
15
- causes.push("decision check partial \u2014 conflict set truncated (per-file cap hit)");
16
- }
17
- return causes.map((c) => `[primitive] ${c}`).join("\n");
18
- }
19
- function blockingReason(results) {
20
- const parts = results.filter((result) => result.verdict === "ask" || result.verdict === "deny").flatMap((result) => [result.reason, result.additionalContext]).filter((value) => value.length > 0);
21
- return [...new Set(parts)].join("\n\n") || "[primitive] conflict detected (no detail available)";
22
- }
23
- function advisoryNote(aggregate, results) {
24
- const details = results.flatMap((result) => [
25
- ...aggregate === "warn" ? [result.reason] : [],
26
- result.additionalContext
27
- ]);
28
- if (anyUnverified(results)) details.push(unverifiedNote(results));
29
- return [...new Set(details.filter((value) => value.length > 0))].join("\n\n");
30
- }
31
- function buildHookOutput(aggregate, results) {
32
- if (aggregate === "deny" || aggregate === "ask") {
33
- return {
34
- hookSpecificOutput: {
35
- hookEventName: "PreToolUse",
36
- permissionDecision: aggregate,
37
- permissionDecisionReason: blockingReason(results)
38
- }
39
- };
40
- }
41
- const notes = advisoryNote(aggregate, results);
42
- if (notes.length > 0) {
43
- return {
44
- systemMessage: notes,
45
- hookSpecificOutput: {
46
- hookEventName: "PreToolUse",
47
- permissionDecision: "allow",
48
- additionalContext: notes
49
- }
50
- };
51
- }
52
- return {
53
- hookSpecificOutput: {
54
- hookEventName: "PreToolUse",
55
- permissionDecision: "allow"
56
- }
57
- };
58
- }
59
- function buildCodexOutput(aggregate, results) {
60
- if (aggregate === "deny" || aggregate === "ask") {
61
- return {
62
- hookSpecificOutput: {
63
- hookEventName: "PreToolUse",
64
- permissionDecision: "deny",
65
- permissionDecisionReason: blockingReason(results)
66
- }
67
- };
68
- }
69
- const notes = advisoryNote(aggregate, results);
70
- return notes ? {
71
- systemMessage: notes,
72
- hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: notes }
73
- } : {};
74
- }
75
- function failOpenOutput() {
76
- return {
77
- hookSpecificOutput: {
78
- hookEventName: "PreToolUse",
79
- permissionDecision: "allow"
80
- }
81
- };
82
- }
83
- function failOpenCodex() {
84
- return {};
85
- }
86
- function buildHermesOutput(aggregate, results) {
87
- if (aggregate !== "deny" && aggregate !== "ask") {
88
- return {};
89
- }
90
- const reason = results.filter((r) => r.verdict === "deny" || r.verdict === "ask").map((r) => r.reason).filter((s) => s.length > 0).join("\n\n");
91
- const directive = results.map((r) => r.additionalContext).filter((s) => s.length > 0 && s !== reason).join("\n");
92
- const message = [reason, directive].filter((s) => s.length > 0).join("\n\n") || "[primitive] conflict detected (no detail available)";
93
- return { action: "block", message };
94
- }
95
- function failOpenHermes() {
96
- return {};
97
- }
98
- var SUPPORTED_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
99
- var APPLY_PATCH_FILE_RE = /^\*\*\* (?:Update|Add|Delete) File: (?<path>.+)$/;
100
- var MOVE_FILE_RE = /^\*\*\* Move File:\s*(?<src>.+?)\s*->\s*(?<dst>.+?)\s*$/;
101
- var MOVE_TO_RE = /^\*\*\* Move to: (?<dst>.+)$/;
102
- var LINE_SPLIT_RE = /\r?\n/;
103
- function parseApplyPatchTargets(command) {
104
- const paths = /* @__PURE__ */ new Set();
105
- let activeUpdate = false;
106
- let complete = true;
107
- for (const line of command.split(LINE_SPLIT_RE)) {
108
- const path = APPLY_PATCH_FILE_RE.exec(line)?.groups?.path;
109
- if (path) {
110
- paths.add(path);
111
- activeUpdate = line.startsWith("*** Update File:");
112
- continue;
113
- }
114
- const moveTo = MOVE_TO_RE.exec(line)?.groups?.dst;
115
- if (moveTo) {
116
- if (activeUpdate) paths.add(moveTo);
117
- else complete = false;
118
- activeUpdate = false;
119
- continue;
120
- }
121
- const move = MOVE_FILE_RE.exec(line)?.groups;
122
- if (move?.src) {
123
- paths.add(move.src);
124
- if (move.dst) {
125
- paths.add(move.dst);
126
- }
127
- activeUpdate = false;
128
- continue;
129
- }
130
- if (line === "*** Begin Patch" || line === "*** End Patch") activeUpdate = false;
131
- else if (line.startsWith("*** ")) complete = false;
132
- }
133
- return { paths: [...paths], complete: complete && paths.size > 0 };
134
- }
135
- function extractCodexFileTargets(toolName, toolInput) {
136
- if (toolName !== "apply_patch") {
137
- return null;
138
- }
139
- if (!toolInput || typeof toolInput !== "object") {
140
- return { paths: [], complete: false };
141
- }
142
- const command = toolInput.command;
143
- return typeof command === "string" ? parseApplyPatchTargets(command) : { paths: [], complete: false };
144
- }
145
- var HERMES_EDITING_TOOLS = /* @__PURE__ */ new Set(["write_file", "patch"]);
146
- function extractHermesFileTargets(toolName, toolInput) {
147
- if (!HERMES_EDITING_TOOLS.has(toolName)) {
148
- return null;
149
- }
150
- if (!toolInput || typeof toolInput !== "object") {
151
- return { paths: [], complete: false };
152
- }
153
- const input = toolInput;
154
- if (toolName === "patch" && input.mode === "patch") {
155
- return typeof input.patch === "string" ? parseApplyPatchTargets(input.patch) : { paths: [], complete: false };
156
- }
157
- const paths = typeof input.path === "string" && input.path.length > 0 ? [input.path] : [];
158
- const mode = input.mode;
159
- return { paths, complete: paths.length > 0 && (mode === void 0 || mode === "replace") };
160
- }
161
- function extractFileTargets(toolName, toolInput, agent = "claude_code") {
162
- if (agent === "codex") {
163
- return extractCodexFileTargets(toolName, toolInput);
164
- }
165
- if (agent === "hermes") {
166
- return extractHermesFileTargets(toolName, toolInput);
167
- }
168
- if (!SUPPORTED_TOOLS.has(toolName)) {
169
- return null;
170
- }
171
- if (!toolInput || typeof toolInput !== "object") {
172
- return { paths: [], complete: false };
173
- }
174
- const input = toolInput;
175
- if (toolName === "NotebookEdit") {
176
- const paths2 = typeof input.notebook_path === "string" && input.notebook_path.length > 0 ? [input.notebook_path] : [];
177
- return { paths: paths2, complete: paths2.length > 0 };
178
- }
179
- const paths = typeof input.file_path === "string" && input.file_path.length > 0 ? [input.file_path] : [];
180
- return { paths, complete: paths.length > 0 };
181
- }
182
- function readHookMode(env) {
183
- if (env.PRIM_BYPASS === "1" || env.PRIM_BYPASS === "true") {
184
- return "off";
185
- }
186
- const mode = env.PRIM_HOOK_MODE;
187
- if (mode === "off" || mode === "warn") {
188
- return mode;
189
- }
190
- return "block";
191
- }
192
- function demoteForMode(verdict, mode) {
193
- if (mode === "off") {
194
- return "allow";
195
- }
196
- if (mode === "warn" && (verdict === "ask" || verdict === "deny")) {
197
- return "warn";
198
- }
199
- return verdict;
200
- }
201
-
202
- // src/hooks/redact.ts
203
- import { existsSync, readFileSync } from "fs";
204
- import { join } from "path";
205
- var DEFAULT_RULES = [
206
- { pattern: /Bearer\s+[A-Za-z0-9._\-]+/g, reason: "bearer-token" },
207
- { pattern: /sk-[A-Za-z0-9]{32,}/g, reason: "sk-api-key" },
208
- {
209
- pattern: /-----BEGIN [A-Z ]+PRIVATE KEY-----[\s\S]+?-----END [A-Z ]+PRIVATE KEY-----/g,
210
- reason: "private-key"
211
- },
212
- { pattern: /xox[abprs]-[A-Za-z0-9-]{10,}/g, reason: "slack-token" },
213
- { pattern: /ghp_[A-Za-z0-9]{36,}/g, reason: "github-pat" }
214
- ];
215
- var MAX_SCRUB_LEN = 256e3;
216
- var WORKSPACE_FILE = ".prim/redaction.json";
217
- function debugRedaction(message, err) {
218
- if (!process.env.PRIM_HOOK_DEBUG) {
219
- return;
220
- }
221
- const detail = err instanceof Error ? err.message : String(err);
222
- process.stderr.write(`[prim-hook] redaction: ${message}: ${detail}
223
- `);
224
- }
225
- function loadWorkspaceRules(cwd) {
226
- const path = join(cwd, WORKSPACE_FILE);
227
- if (!existsSync(path)) {
228
- return [];
229
- }
230
- let cfg;
231
- try {
232
- cfg = JSON.parse(readFileSync(path, "utf-8"));
233
- } catch (err) {
234
- debugRedaction(`ignoring unparseable ${WORKSPACE_FILE}`, err);
235
- return [];
236
- }
237
- if (!cfg.rules) {
238
- return [];
239
- }
240
- const compiled = [];
241
- for (const r of cfg.rules) {
242
- try {
243
- compiled.push({ pattern: new RegExp(r.pattern, r.flags ?? "g"), reason: r.reason });
244
- } catch (err) {
245
- debugRedaction(`skipping invalid redaction rule "${r.reason}"`, err);
246
- }
247
- }
248
- return compiled;
249
- }
250
- function applyRules(input, rules) {
251
- if (input.length > MAX_SCRUB_LEN) {
252
- return "<REDACTED:oversized>";
253
- }
254
- let out = input;
255
- for (const rule of rules) {
256
- out = out.replace(rule.pattern, `<REDACTED:${rule.reason}>`);
257
- }
258
- return out;
259
- }
260
- function scrub(value, rules = DEFAULT_RULES) {
261
- if (typeof value === "string") {
262
- return applyRules(value, rules);
263
- }
264
- if (Array.isArray(value)) {
265
- return value.map((v) => scrub(v, rules));
266
- }
267
- if (value && typeof value === "object") {
268
- const out = {};
269
- for (const [k, v] of Object.entries(value)) {
270
- out[k] = scrub(v, rules);
271
- }
272
- return out;
273
- }
274
- return value;
275
- }
276
- function scrubFromCwd(value, cwd) {
277
- const rules = [...DEFAULT_RULES, ...loadWorkspaceRules(cwd)];
278
- return scrub(value, rules);
279
- }
280
-
281
- // src/hooks/shell-targets.ts
282
- import { parse } from "unbash";
283
- var OUTPUT_REDIRECTS = /* @__PURE__ */ new Set([">", ">>", ">|", "&>", "&>>"]);
284
- var READ_ONLY_COMMANDS = new Set(": cat echo false printf pwd test true [".split(" "));
285
- var SINKS = /* @__PURE__ */ new Set(["/dev/null", "/dev/stdout", "/dev/stderr"]);
286
- var STATIC_PARTS = /* @__PURE__ */ new Set(["Literal", "SingleQuoted", "AnsiCQuoted"]);
287
- function staticPart(part) {
288
- return STATIC_PARTS.has(part.type) || part.type === "DoubleQuoted" && part.parts.every(staticPart);
289
- }
290
- function staticWord(word) {
291
- if (!word || !word.value || word.value.includes("\0") || word.value.includes("\n")) return;
292
- if (word.parts ? !word.parts.every(staticPart) : word.value.startsWith("~") || /[*?\[$`]/.test(word.text))
293
- return;
294
- return word.value;
295
- }
296
- function unknown(state) {
297
- state.complete = false;
298
- state.mutation = true;
299
- }
300
- function recordTarget(word, state) {
301
- const path = staticWord(word);
302
- if (path === "-" || path && (SINKS.has(path) || path.startsWith("/dev/fd/"))) return;
303
- state.mutation = true;
304
- if (path) state.paths.add(path);
305
- else state.complete = false;
306
- }
307
- function visitRedirect(redirect, state) {
308
- const target = staticWord(redirect.target);
309
- if (redirect.operator === ">&" && (target === "-" || /^\d+$/.test(target ?? ""))) return;
310
- if (OUTPUT_REDIRECTS.has(redirect.operator) || redirect.operator === ">&")
311
- recordTarget(redirect.target, state);
312
- else unknown(state);
313
- }
314
- function operands(words, short = "", long = /* @__PURE__ */ new Set()) {
315
- const result = [];
316
- let optionsEnded = false;
317
- for (const word of words) {
318
- const value = staticWord(word);
319
- if (value === void 0) return;
320
- if (optionsEnded || value === "-" || !value.startsWith("-")) result.push(value);
321
- else if (value === "--") optionsEnded = true;
322
- else if (value.startsWith("--") ? !long.has(value) : [...value.slice(1)].some((c) => !short.includes(c)))
323
- return;
324
- }
325
- return result;
326
- }
327
- function visitCommand(command, state) {
328
- for (const redirect of command.redirects) visitRedirect(redirect, state);
329
- const name = staticWord(command.name);
330
- if (!name || name.includes("/") || command.suffix.some((word) => !staticWord(word))) {
331
- unknown(state);
332
- } else if (!READ_ONLY_COMMANDS.has(name)) {
333
- state.mutation = true;
334
- let targets;
335
- if (name === "tee") {
336
- targets = operands(command.suffix, "ai", /* @__PURE__ */ new Set(["--append", "--ignore-interrupts"]));
337
- } else if (name === "touch") {
338
- targets = operands(command.suffix);
339
- } else if (name === "rm") {
340
- targets = operands(
341
- command.suffix,
342
- "fivI",
343
- /* @__PURE__ */ new Set(["--force", "--interactive", "--interactive=once", "--verbose"])
344
- );
345
- } else if (name === "cp" || name === "mv") {
346
- const pair = operands(command.suffix);
347
- if (pair?.length === 2) targets = name === "cp" ? [pair[1]] : pair;
348
- }
349
- if (!targets?.length) state.complete = false;
350
- else for (const target of targets) state.paths.add(target);
351
- }
352
- visitNested([command.name, command.prefix, command.suffix, command.redirects], state);
353
- }
354
- var CONTAINERS = /* @__PURE__ */ new Set(["Script", "Pipeline", "AndOr", "CompoundList"]);
355
- function visitNested(value, state) {
356
- if (Array.isArray(value)) {
357
- for (const child of value) visitNested(child, state);
358
- } else if (value && typeof value === "object") {
359
- const child = value;
360
- if (child.type === "Command") {
361
- visitCommand(child, state);
362
- } else if (child.type === "Statement") {
363
- visitStatement(child, state);
364
- } else if ("parts" in child) {
365
- for (const part of value.parts ?? []) visitNested(part, state);
366
- } else {
367
- const node = typeof child.type === "string" && "pos" in child && "end" in child;
368
- if (node && !CONTAINERS.has(child.type)) unknown(state);
369
- if (child.type === "Function") return;
370
- for (const nested of Object.values(child)) visitNested(nested, state);
371
- }
372
- }
373
- }
374
- function visitStatement(statement, state) {
375
- if (statement.background) unknown(state);
376
- for (const redirect of statement.redirects) visitRedirect(redirect, state);
377
- visitNested([statement.redirects, statement.command], state);
378
- }
379
- function analyzeShellTargets(source) {
380
- if (!source.trim())
381
- return { paths: [], coverage: "complete", mutation: "none" };
382
- try {
383
- const script = parse(source);
384
- const state = { paths: /* @__PURE__ */ new Set(), complete: !script.errors?.length, mutation: false };
385
- for (const statement of script.commands) visitStatement(statement, state);
386
- if (script.errors?.length) state.mutation = true;
387
- return {
388
- paths: [...state.paths],
389
- coverage: state.complete ? "complete" : "unverified",
390
- mutation: state.mutation ? "present" : "none"
391
- };
392
- } catch {
393
- return { paths: [], coverage: "unverified", mutation: "present" };
394
- }
395
- }
396
-
397
- export {
398
- buildHookOutput,
399
- buildCodexOutput,
400
- failOpenOutput,
401
- failOpenCodex,
402
- buildHermesOutput,
403
- failOpenHermes,
404
- extractFileTargets,
405
- readHookMode,
406
- demoteForMode,
407
- analyzeShellTargets,
408
- DEFAULT_RULES,
409
- scrub,
410
- scrubFromCwd
411
- };