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

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.
@@ -1,34 +1,126 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- PREFLIGHT_PROTOCOL_VERSION,
3
+ DEFAULT_RULES,
4
+ analyzeShellTargets,
4
5
  buildCodexOutput,
5
6
  buildHermesOutput,
6
7
  buildHookOutput,
7
8
  demoteForMode,
9
+ extractFileTargets,
8
10
  failOpenCodex,
9
11
  failOpenHermes,
10
12
  failOpenOutput,
11
- proposalFor,
12
13
  readHookMode,
13
- requestPreflight,
14
- resolvePreflightTargets,
15
- resultForPreflight,
16
- unverifiedResult
17
- } from "../chunk-B4SHJYIN.js";
18
- import "../chunk-GUXBUBQ7.js";
14
+ scrub
15
+ } from "../chunk-GF5QII7T.js";
19
16
  import {
20
17
  warmBinCache
21
- } from "../chunk-YIMUIPMD.js";
18
+ } from "../chunk-KBFFVYBC.js";
22
19
  import {
20
+ canonicalGitRoot,
21
+ canonicalRepositoryPath,
23
22
  isRepoActive,
24
23
  repoSyncId
25
- } from "../chunk-LDQZASIF.js";
26
- import "../chunk-I3HIPAIR.js";
24
+ } from "../chunk-XTNMSS4C.js";
25
+ import {
26
+ getClient
27
+ } from "../chunk-I3HIPAIR.js";
27
28
  import {
28
29
  normalizeEnvelope,
29
30
  parseAgent
30
31
  } from "../chunk-TCDUH7AN.js";
31
32
 
33
+ // src/hooks/preflight-v3.ts
34
+ import { Buffer as Buffer2 } from "buffer";
35
+ var PREFLIGHT_PROTOCOL_VERSION = 3;
36
+ var PREFLIGHT_TIMEOUT_MS = 6500;
37
+ var MAX_PREFLIGHT_PATHS = 32;
38
+ var MAX_PROPOSAL_BYTES = 6144;
39
+ function resolvePreflightTargets(args) {
40
+ let rawPaths;
41
+ let coverage = "complete";
42
+ let mutation = "present";
43
+ if (args.agent === "claude_code" && args.toolName === "Bash") {
44
+ const command = args.toolInput && typeof args.toolInput === "object" ? args.toolInput.command : void 0;
45
+ if (typeof command !== "string") {
46
+ return { paths: [], coverage: "unverified", mutation: "present" };
47
+ }
48
+ const shell = analyzeShellTargets(command);
49
+ rawPaths = shell.paths;
50
+ coverage = shell.coverage;
51
+ mutation = shell.mutation;
52
+ } else {
53
+ const extracted = extractFileTargets(args.toolName, args.toolInput, args.agent);
54
+ if (!extracted) return { paths: [], coverage: "complete", mutation: "none" };
55
+ rawPaths = extracted.paths;
56
+ if (!extracted.complete) coverage = "unverified";
57
+ }
58
+ const root = canonicalGitRoot(args.cwd);
59
+ const paths = /* @__PURE__ */ new Set();
60
+ for (const rawPath of rawPaths) {
61
+ const canonical = canonicalRepositoryPath(rawPath, args.cwd, root);
62
+ if (canonical) paths.add(canonical);
63
+ else coverage = "unverified";
64
+ }
65
+ const bounded = [...paths];
66
+ if (bounded.length > MAX_PREFLIGHT_PATHS) coverage = "unverified";
67
+ return { paths: bounded.slice(0, MAX_PREFLIGHT_PATHS), coverage, mutation };
68
+ }
69
+ function truncateUtf8(value, maxBytes) {
70
+ const encoded = Buffer2.from(value);
71
+ if (encoded.byteLength <= maxBytes) return value;
72
+ return encoded.subarray(0, maxBytes).toString("utf8").replace(/\uFFFD$/u, "");
73
+ }
74
+ function proposalFor(toolInput) {
75
+ const redacted = scrub(toolInput, DEFAULT_RULES);
76
+ return truncateUtf8(JSON.stringify(redacted) ?? "", MAX_PROPOSAL_BYTES);
77
+ }
78
+ function parsePreflightResponse(value) {
79
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
80
+ const response = value;
81
+ const verdicts = /* @__PURE__ */ new Set(["allow", "warn", "ask", "block", "unavailable"]);
82
+ if (response.protocolVersion !== PREFLIGHT_PROTOCOL_VERSION || typeof response.verdict !== "string" || !verdicts.has(response.verdict) || typeof response.reasonCode !== "string" || typeof response.message !== "string" || !Array.isArray(response.conflicts) || !Array.isArray(response.bypassed)) {
83
+ return null;
84
+ }
85
+ return response;
86
+ }
87
+ async function requestPreflight(request, signal = AbortSignal.timeout(PREFLIGHT_TIMEOUT_MS)) {
88
+ const response = await getClient().post("/api/cli/decisions/conflict-check", request, {
89
+ signal,
90
+ quietRefresh: true
91
+ });
92
+ return parsePreflightResponse(response);
93
+ }
94
+ function resultForPreflight(response) {
95
+ const verdict = response.verdict === "block" ? "deny" : response.verdict;
96
+ const safe = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
97
+ const directives = response.conflicts.flatMap((value) => {
98
+ if (!value || typeof value !== "object") return [];
99
+ const { decisionId, shortId } = value;
100
+ const id = typeof shortId === "string" && safe.test(shortId) ? `dec_${shortId}` : typeof decisionId === "string" && safe.test(decisionId) ? decisionId : void 0;
101
+ return id ? [`To reconcile, run: prim reconcile ${id}`] : [];
102
+ });
103
+ const reason = [response.message, ...new Set(directives)].filter(Boolean).join("\n");
104
+ return {
105
+ verdict,
106
+ conflicts: response.conflicts,
107
+ reason,
108
+ additionalContext: ["warn", "ask", "block"].includes(response.verdict) ? reason : "",
109
+ truncated: false,
110
+ unavailable: response.verdict === "unavailable" ? response.message || response.reasonCode : void 0
111
+ };
112
+ }
113
+ function unverifiedResult(message) {
114
+ return {
115
+ verdict: "unavailable",
116
+ conflicts: [],
117
+ reason: "",
118
+ additionalContext: "",
119
+ truncated: false,
120
+ unavailable: message
121
+ };
122
+ }
123
+
32
124
  // src/hooks/pre-tool-use.ts
33
125
  var STDIN_TIMEOUT_MS = 1e3;
34
126
  async function readStdin() {
@@ -1,16 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ enrichHookPayloadWithFileRefs,
4
+ preserveHookFileMetadata
5
+ } from "../chunk-DA4BMHEB.js";
2
6
  import {
3
7
  postToolInvocationId,
4
8
  shouldFlushAfter,
5
9
  toMove
6
- } from "../chunk-YDBZTFW5.js";
10
+ } from "../chunk-QGVHLP5J.js";
7
11
  import {
8
12
  appendMove,
9
13
  resolveOrg
10
14
  } from "../chunk-S4LFRVGD.js";
11
15
  import {
12
16
  scrubFromCwd
13
- } from "../chunk-GUXBUBQ7.js";
17
+ } from "../chunk-GF5QII7T.js";
14
18
  import {
15
19
  buildHookOutput,
16
20
  handoffHookOutput
@@ -23,13 +27,15 @@ import {
23
27
  } from "../chunk-R4CUX6FY.js";
24
28
  import {
25
29
  getOrCreateWorkspaceId
26
- } from "../chunk-IMAIBPUC.js";
30
+ } from "../chunk-UVIWZWM6.js";
27
31
  import {
28
32
  warmBinCache
29
- } from "../chunk-YIMUIPMD.js";
33
+ } from "../chunk-KBFFVYBC.js";
30
34
  import {
31
- isRepoActiveForCapture
32
- } from "../chunk-LDQZASIF.js";
35
+ isRepoActiveForCapture,
36
+ repoSyncId,
37
+ resolveRepositoryContext
38
+ } from "../chunk-XTNMSS4C.js";
33
39
  import "../chunk-I3HIPAIR.js";
34
40
  import {
35
41
  normalizeEnvelope,
@@ -90,10 +96,25 @@ async function main() {
90
96
  }
91
97
  const identity = getOrCreateWorkspaceId(cwd);
92
98
  const workspaceId = identity.status === "ready" ? identity.workspaceId : void 0;
99
+ const resolvedRepository = resolveRepositoryContext(cwd);
100
+ const repository = resolvedRepository ? { ...resolvedRepository, repoSyncId: repoSyncId(cwd) } : null;
93
101
  try {
94
- const invocationId = postToolInvocationId(parsed, agent);
95
- const base = toMove(parsed, resolveCliVersion(), agent, workspaceId, invocationId);
96
- const move = { ...base, payload: scrubFromCwd(parsed, cwd) };
102
+ const enrichment = repository ? enrichHookPayloadWithFileRefs({ parsed, agent, cwd, repository }) : void 0;
103
+ const enriched = enrichment?.parsed ?? parsed;
104
+ const invocationId = postToolInvocationId(enriched, agent);
105
+ const base = toMove(
106
+ enriched,
107
+ resolveCliVersion(),
108
+ agent,
109
+ workspaceId,
110
+ repository,
111
+ invocationId
112
+ );
113
+ const scrubbed = scrubFromCwd(enriched, cwd);
114
+ const move = {
115
+ ...base,
116
+ payload: enrichment ? preserveHookFileMetadata(scrubbed, enrichment.resolution) : scrubbed
117
+ };
97
118
  const { orgId } = resolveOrg({ sessionId: move.sessionId, cwd: move.env.cwd });
98
119
  appendMove(move, orgId);
99
120
  if (shouldFlushAfter(move.eventType)) {
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  bindRepository,
4
+ ensureEffectivePostCommitHook,
4
5
  hasUsableCodexGuidance,
5
6
  refreshClaudePlugins
6
- } from "../chunk-N4R7EXZT.js";
7
+ } from "../chunk-LOGW54W5.js";
7
8
  import {
8
9
  buildHookOutput,
9
10
  handoffHookOutput
@@ -16,16 +17,18 @@ import {
16
17
  } from "../chunk-R4CUX6FY.js";
17
18
  import {
18
19
  getOrCreateWorkspaceId
19
- } from "../chunk-IMAIBPUC.js";
20
+ } from "../chunk-UVIWZWM6.js";
20
21
  import {
21
22
  binFile,
22
23
  warmBinCache
23
- } from "../chunk-YIMUIPMD.js";
24
+ } from "../chunk-KBFFVYBC.js";
24
25
  import {
25
26
  gitToplevel,
26
27
  isRepoActiveForCapture,
27
- repoSyncId
28
- } from "../chunk-LDQZASIF.js";
28
+ repoActiveFlag,
29
+ repoSyncId,
30
+ setRepoActive
31
+ } from "../chunk-XTNMSS4C.js";
29
32
  import {
30
33
  getSiteUrl,
31
34
  isSessionEnded
@@ -81,6 +84,20 @@ async function activeProjectRoot(cwd) {
81
84
  try {
82
85
  const root = gitToplevel(cwd);
83
86
  if (!root || !isRepoActiveForCapture(cwd)) return null;
87
+ let shellGateActive = repoActiveFlag(root) === "true";
88
+ if (!shellGateActive) {
89
+ try {
90
+ setRepoActive(root, true);
91
+ shellGateActive = true;
92
+ } catch {
93
+ }
94
+ }
95
+ if (shellGateActive) {
96
+ try {
97
+ ensureEffectivePostCommitHook(root);
98
+ } catch {
99
+ }
100
+ }
84
101
  if (!repoSyncId(root)) {
85
102
  try {
86
103
  await bindRepository(root, {