@primitive.ai/prim 0.1.0-alpha.90 → 0.1.0-alpha.91

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.
Files changed (45) hide show
  1. package/README.md +20 -12
  2. package/SKILL.md +6 -6
  3. package/dist/{chunk-LMRYZL3D.js → chunk-5AFRMOPX.js} +1 -1
  4. package/dist/{chunk-RO3F6JYV.js → chunk-5GYQ4J3D.js} +2 -2
  5. package/dist/{chunk-3EEFL6NJ.js → chunk-67VNLLBN.js} +261 -1
  6. package/dist/{chunk-LEMX3IOG.js → chunk-BTNNEH2W.js} +1 -1
  7. package/dist/chunk-EWZMDD44.js +1474 -0
  8. package/dist/{chunk-YWO7O6QG.js → chunk-FBAYCS3X.js} +2 -2
  9. package/dist/{chunk-R26A7TWY.js → chunk-FKXFUUWJ.js} +1 -1
  10. package/dist/{chunk-C7T7B45R.js → chunk-FOLKHXLJ.js} +150 -21
  11. package/dist/chunk-H4UNYFUC.js +196 -0
  12. package/dist/chunk-HU4PW6I6.js +264 -0
  13. package/dist/{chunk-CUE2LKLR.js → chunk-KFXF2V5G.js} +1 -1
  14. package/dist/{chunk-6IWNH776.js → chunk-LFFLHSQO.js} +4 -3
  15. package/dist/{chunk-MAZWKJGL.js → chunk-M6RIJVRE.js} +42 -232
  16. package/dist/{chunk-2JRX7ZR4.js → chunk-NAHHFSLH.js} +16 -5
  17. package/dist/{chunk-FI3LIRCB.js → chunk-OAAP2MY6.js} +2 -2
  18. package/dist/{chunk-Z7YPFWAZ.js → chunk-PCVGBE5B.js} +10 -268
  19. package/dist/{chunk-UZPBB7V4.js → chunk-VW3FDYRT.js} +60 -48
  20. package/dist/chunk-WX52U7Z2.js +112 -0
  21. package/dist/{chunk-PPQYQEIR.js → chunk-ZB4F75SZ.js} +28 -0
  22. package/dist/daemon/server.js +20 -20
  23. package/dist/hook-runtime/daemon/server.js +20 -20
  24. package/dist/hook-runtime/hooks/post-commit.js +20 -20
  25. package/dist/hook-runtime/hooks/post-rewrite.js +20 -20
  26. package/dist/hook-runtime/hooks/post-tool-use.js +651 -132
  27. package/dist/hook-runtime/hooks/pre-tool-use.js +644 -96
  28. package/dist/hook-runtime/hooks/prim-hook.js +612 -96
  29. package/dist/hook-runtime/hooks/session-end.js +610 -27
  30. package/dist/hook-runtime/hooks/session-start.js +1132 -533
  31. package/dist/hook-runtime/index.js +735 -264
  32. package/dist/hook-runtime/statusline-main.js +48 -4
  33. package/dist/hooks/post-commit.js +11 -9
  34. package/dist/hooks/post-rewrite.js +11 -9
  35. package/dist/hooks/post-tool-use.js +73 -48
  36. package/dist/hooks/pre-commit.js +4 -4
  37. package/dist/hooks/pre-tool-use.js +46 -44
  38. package/dist/hooks/prim-hook.js +47 -25
  39. package/dist/hooks/session-end.js +15 -26
  40. package/dist/hooks/session-start.js +59 -32
  41. package/dist/index.js +558 -1436
  42. package/dist/statusline-main.js +48 -4
  43. package/package.json +1 -1
  44. package/dist/chunk-PMRDE2DA.js +0 -310
  45. package/dist/chunk-WB7AUZ4C.js +0 -52
@@ -524,7 +524,7 @@ var PRIM_REPO_SYNC_REPOSITORY_KEY = "prim.repoSyncRepository";
524
524
  var PRIM_REPO_BINDING_STATE_KEY = "prim.repoBindingState";
525
525
  var GIT_TIMEOUT_MS2 = 1e3;
526
526
  var REPO_SYNC_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
527
- var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
527
+ var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json", ".cursor/hooks.json"];
528
528
  var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
529
529
  function repoActiveFlag(cwd) {
530
530
  try {
@@ -731,9 +731,8 @@ function debug(msg) {
731
731
  `);
732
732
  }
733
733
  }
734
- async function renderStatusline() {
734
+ async function renderStatusline(cwd = process.cwd()) {
735
735
  const version = readPackageVersion();
736
- const cwd = process.cwd();
737
736
  let ingestion;
738
737
  const resolveIngestion = () => {
739
738
  ingestion ??= decisionIngestionStatus(cwd);
@@ -754,10 +753,55 @@ async function renderStatusline() {
754
753
  });
755
754
  }
756
755
 
756
+ // src/hooks/hook-stdin.ts
757
+ var DEFAULT_MAX_BYTES = 1024 * 1024;
758
+ function readHookStdin(timeoutMs = 1e3, maxBytes = DEFAULT_MAX_BYTES) {
759
+ return new Promise((resolve4, reject) => {
760
+ const chunks = [];
761
+ let bytes = 0;
762
+ let settled = false;
763
+ const finish = (error) => {
764
+ if (settled) return;
765
+ settled = true;
766
+ clearTimeout(timer);
767
+ process.stdin.removeAllListeners("data");
768
+ process.stdin.removeAllListeners("end");
769
+ process.stdin.removeAllListeners("error");
770
+ if (error) reject(error);
771
+ else resolve4(Buffer.concat(chunks, bytes).toString("utf-8"));
772
+ };
773
+ const timer = setTimeout(() => finish(new Error("stdin read timeout")), timeoutMs);
774
+ process.stdin.on("data", (chunk) => {
775
+ bytes += chunk.length;
776
+ if (bytes > maxBytes) {
777
+ finish(new Error("stdin envelope exceeds size limit"));
778
+ return;
779
+ }
780
+ chunks.push(chunk);
781
+ });
782
+ process.stdin.on("end", () => finish());
783
+ process.stdin.on("error", (error) => finish(error));
784
+ });
785
+ }
786
+
757
787
  // src/statusline-main.ts
788
+ async function inputCwd() {
789
+ try {
790
+ const raw = await readHookStdin(200, 64 * 1024);
791
+ const parsed = JSON.parse(raw);
792
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
793
+ const input = parsed;
794
+ if (typeof input.cwd === "string" && input.cwd.length > 0) return input.cwd;
795
+ if (input.workspace && typeof input.workspace === "object" && !Array.isArray(input.workspace)) {
796
+ const current = input.workspace.current_dir;
797
+ if (typeof current === "string" && current.length > 0) return current;
798
+ }
799
+ } catch {
800
+ }
801
+ }
758
802
  async function main() {
759
803
  try {
760
- process.stdout.write(await renderStatusline());
804
+ process.stdout.write(await renderStatusline(await inputCwd() ?? process.cwd()));
761
805
  } catch {
762
806
  process.stdout.write(`primitive ${readPackageVersion()} (daemon: unavailable)`);
763
807
  }
@@ -2,25 +2,27 @@
2
2
  import {
3
3
  commitAttributionFromEnvironment,
4
4
  toCommitMove
5
- } from "../chunk-2JRX7ZR4.js";
5
+ } from "../chunk-NAHHFSLH.js";
6
6
  import {
7
7
  getOrCreateWorkspaceId
8
8
  } from "../chunk-EZXEWFDG.js";
9
9
  import {
10
10
  appendMove,
11
11
  resolveOrg
12
- } from "../chunk-YWO7O6QG.js";
13
- import "../chunk-FI3LIRCB.js";
14
- import "../chunk-LEMX3IOG.js";
12
+ } from "../chunk-FBAYCS3X.js";
13
+ import "../chunk-OAAP2MY6.js";
14
+ import "../chunk-BTNNEH2W.js";
15
15
  import {
16
16
  cachedCollectScopeAdmits,
17
- githubRepositoryFullName,
18
17
  isRepoActiveForCapture,
19
- repoSyncId,
18
+ repoSyncId
19
+ } from "../chunk-M6RIJVRE.js";
20
+ import "../chunk-PCVGBE5B.js";
21
+ import {
22
+ githubRepositoryFullName,
20
23
  resolveRepositoryContext
21
- } from "../chunk-MAZWKJGL.js";
22
- import "../chunk-Z7YPFWAZ.js";
23
- import "../chunk-3EEFL6NJ.js";
24
+ } from "../chunk-H4UNYFUC.js";
25
+ import "../chunk-67VNLLBN.js";
24
26
 
25
27
  // src/hooks/post-commit.ts
26
28
  import { execFileSync, spawn } from "child_process";
@@ -2,25 +2,27 @@
2
2
  import {
3
3
  canonicalRewritePairs,
4
4
  toRewriteMove
5
- } from "../chunk-2JRX7ZR4.js";
5
+ } from "../chunk-NAHHFSLH.js";
6
6
  import {
7
7
  getOrCreateWorkspaceId
8
8
  } from "../chunk-EZXEWFDG.js";
9
9
  import {
10
10
  appendMove,
11
11
  resolveOrg
12
- } from "../chunk-YWO7O6QG.js";
13
- import "../chunk-FI3LIRCB.js";
14
- import "../chunk-LEMX3IOG.js";
12
+ } from "../chunk-FBAYCS3X.js";
13
+ import "../chunk-OAAP2MY6.js";
14
+ import "../chunk-BTNNEH2W.js";
15
15
  import {
16
16
  cachedCollectScopeAdmits,
17
- githubRepositoryFullName,
18
17
  isRepoActiveForCapture,
19
- repoSyncId,
18
+ repoSyncId
19
+ } from "../chunk-M6RIJVRE.js";
20
+ import "../chunk-PCVGBE5B.js";
21
+ import {
22
+ githubRepositoryFullName,
20
23
  resolveRepositoryContext
21
- } from "../chunk-MAZWKJGL.js";
22
- import "../chunk-Z7YPFWAZ.js";
23
- import "../chunk-3EEFL6NJ.js";
24
+ } from "../chunk-H4UNYFUC.js";
25
+ import "../chunk-67VNLLBN.js";
24
26
 
25
27
  // src/hooks/post-rewrite.ts
26
28
  import { execFileSync, spawn } from "child_process";
@@ -1,53 +1,58 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  requireDurableIngestAcknowledgement
4
- } from "../chunk-R26A7TWY.js";
4
+ } from "../chunk-FKXFUUWJ.js";
5
5
  import {
6
6
  enrichHookPayloadWithFileRefs,
7
7
  preserveHookFileMetadata
8
- } from "../chunk-6IWNH776.js";
8
+ } from "../chunk-LFFLHSQO.js";
9
9
  import {
10
10
  postToolInvocationId,
11
11
  toMove,
12
12
  toolOutcomeFor
13
- } from "../chunk-2JRX7ZR4.js";
13
+ } from "../chunk-NAHHFSLH.js";
14
14
  import {
15
15
  getOrCreateWorkspaceId
16
16
  } from "../chunk-EZXEWFDG.js";
17
17
  import {
18
18
  appendMove,
19
19
  resolveOrg
20
- } from "../chunk-YWO7O6QG.js";
21
- import "../chunk-PPQYQEIR.js";
20
+ } from "../chunk-FBAYCS3X.js";
21
+ import "../chunk-ZB4F75SZ.js";
22
22
  import {
23
23
  appendCodexContext,
24
24
  prepareCodexContext
25
- } from "../chunk-UZPBB7V4.js";
26
- import "../chunk-PMRDE2DA.js";
25
+ } from "../chunk-VW3FDYRT.js";
26
+ import "../chunk-WX52U7Z2.js";
27
27
  import {
28
28
  scrubFromCwd
29
- } from "../chunk-FI3LIRCB.js";
29
+ } from "../chunk-OAAP2MY6.js";
30
30
  import {
31
31
  bold,
32
32
  color
33
- } from "../chunk-LEMX3IOG.js";
33
+ } from "../chunk-BTNNEH2W.js";
34
34
  import {
35
35
  cachedCollectScopeAdmits,
36
- currentBranch,
37
36
  isRepoActiveForCapture,
38
- repoSyncId,
39
- resolveRepositoryContext
40
- } from "../chunk-MAZWKJGL.js";
37
+ repoSyncId
38
+ } from "../chunk-M6RIJVRE.js";
41
39
  import {
42
40
  getClient,
43
41
  terminalSafeLine
44
- } from "../chunk-Z7YPFWAZ.js";
42
+ } from "../chunk-PCVGBE5B.js";
45
43
  import {
46
44
  normalizeEnvelope,
47
- parseAgent
48
- } from "../chunk-WB7AUZ4C.js";
49
- import "../chunk-CUE2LKLR.js";
50
- import "../chunk-3EEFL6NJ.js";
45
+ parseAgent,
46
+ readHookStdin,
47
+ shouldSuppressImportedCursorHandler
48
+ } from "../chunk-HU4PW6I6.js";
49
+ import "../chunk-EWZMDD44.js";
50
+ import {
51
+ currentBranch,
52
+ resolveRepositoryContext
53
+ } from "../chunk-H4UNYFUC.js";
54
+ import "../chunk-KFXF2V5G.js";
55
+ import "../chunk-67VNLLBN.js";
51
56
 
52
57
  // src/hooks/post-tool-use.ts
53
58
  import { readFileSync } from "fs";
@@ -87,6 +92,7 @@ var STDIN_TIMEOUT_MS = 1e3;
87
92
  var EDITING_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit", "Bash"]);
88
93
  var CODEX_EDITING_TOOLS = /* @__PURE__ */ new Set(["apply_patch", "Bash"]);
89
94
  var HERMES_EDITING_TOOLS = /* @__PURE__ */ new Set(["write_file", "patch"]);
95
+ var CURSOR_EDITING_TOOLS = /* @__PURE__ */ new Set(["Write", "Delete", "Shell"]);
90
96
  function editingToolsFor(agent) {
91
97
  if (agent === "codex") {
92
98
  return CODEX_EDITING_TOOLS;
@@ -94,6 +100,9 @@ function editingToolsFor(agent) {
94
100
  if (agent === "hermes") {
95
101
  return HERMES_EDITING_TOOLS;
96
102
  }
103
+ if (agent === "cursor") {
104
+ return CURSOR_EDITING_TOOLS;
105
+ }
97
106
  return EDITING_TOOLS;
98
107
  }
99
108
  var here = dirname(fileURLToPath(import.meta.url));
@@ -105,23 +114,6 @@ function resolveCliVersion() {
105
114
  return "unknown";
106
115
  }
107
116
  }
108
- function readStdin() {
109
- return new Promise((resolve, reject) => {
110
- const chunks = [];
111
- const timer = setTimeout(() => {
112
- reject(new Error("stdin read timeout"));
113
- }, STDIN_TIMEOUT_MS);
114
- process.stdin.on("data", (chunk) => chunks.push(chunk));
115
- process.stdin.on("end", () => {
116
- clearTimeout(timer);
117
- resolve(Buffer.concat(chunks).toString("utf-8"));
118
- });
119
- process.stdin.on("error", (err) => {
120
- clearTimeout(timer);
121
- reject(err);
122
- });
123
- });
124
- }
125
117
  async function emit(output = {}) {
126
118
  return await new Promise((resolve) => {
127
119
  try {
@@ -136,6 +128,30 @@ async function emitWithAcknowledgment(output, acknowledge) {
136
128
  const handedOff = await emit(output);
137
129
  await acknowledge?.(handedOff);
138
130
  }
131
+ async function emitCursorContext(envelope) {
132
+ if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0 || typeof envelope.cwd !== "string" || envelope.cwd.length === 0) {
133
+ await emit();
134
+ return;
135
+ }
136
+ try {
137
+ const context = await prepareCodexContext({
138
+ cwd: envelope.cwd,
139
+ sessionId: envelope.session_id,
140
+ includeDigest: true,
141
+ namespace: "cursor"
142
+ });
143
+ await emitWithAcknowledgment(
144
+ context.context ? { additional_context: context.context } : {},
145
+ context.acknowledge
146
+ );
147
+ } catch {
148
+ await emit();
149
+ }
150
+ }
151
+ async function finish(agent, envelope) {
152
+ if (agent === "cursor" && envelope) await emitCursorContext(envelope);
153
+ else await emit();
154
+ }
139
155
  function debug(msg) {
140
156
  if (process.env.PRIM_HOOK_VERBOSE === "1") {
141
157
  process.stderr.write(`[prim-post-tool-use] ${msg}
@@ -146,33 +162,38 @@ async function main() {
146
162
  const agent = parseAgent(process.argv);
147
163
  let raw;
148
164
  try {
149
- raw = await readStdin();
165
+ raw = await readHookStdin(STDIN_TIMEOUT_MS);
150
166
  } catch {
151
167
  await emit();
152
168
  return;
153
169
  }
154
170
  let parsed;
155
171
  try {
156
- parsed = normalizeEnvelope(JSON.parse(raw), agent);
172
+ const incoming = JSON.parse(raw);
173
+ if (agent !== "cursor" && shouldSuppressImportedCursorHandler(incoming, "prim-post-tool-use")) {
174
+ await emit();
175
+ return;
176
+ }
177
+ parsed = normalizeEnvelope(incoming, agent);
157
178
  } catch {
158
179
  await emit();
159
180
  return;
160
181
  }
161
182
  let envelope = parsed;
162
- const isToolResult = envelope.hook_event_name === "PostToolUse" || agent === "claude_code" && envelope.hook_event_name === "PostToolUseFailure";
183
+ const isToolResult = envelope.hook_event_name === "PostToolUse" || (agent === "claude_code" || agent === "cursor") && envelope.hook_event_name === "PostToolUseFailure";
163
184
  const isHermesDenial = agent === "hermes" && envelope.hook_event_name === "post_approval_response" && toolOutcomeFor(parsed, agent) === "prevented";
164
185
  if (!isToolResult && !isHermesDenial) {
165
- await emit();
186
+ await finish(agent, envelope);
166
187
  return;
167
188
  }
168
189
  const invocationId = postToolInvocationId(parsed, agent);
169
190
  if (isHermesDenial && !invocationId) {
170
- await emit();
191
+ await finish(agent, envelope);
171
192
  return;
172
193
  }
173
194
  const toolName = typeof envelope.tool_name === "string" ? envelope.tool_name : "";
174
195
  if (!isHermesDenial && !editingToolsFor(agent).has(toolName)) {
175
- await emit();
196
+ await finish(agent, envelope);
176
197
  return;
177
198
  }
178
199
  if (isHermesDenial && (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) && typeof envelope.extra?.session_key === "string" && envelope.extra.session_key.length > 0) {
@@ -180,17 +201,17 @@ async function main() {
180
201
  envelope = parsed;
181
202
  }
182
203
  if (typeof envelope.session_id !== "string" || envelope.session_id.length === 0) {
183
- await emit();
204
+ await finish(agent, envelope);
184
205
  return;
185
206
  }
186
207
  const cwd = parsed.cwd ?? process.cwd();
187
208
  if (!isRepoActiveForCapture(cwd)) {
188
- await emit();
209
+ await finish(agent, envelope);
189
210
  return;
190
211
  }
191
212
  const resolvedRepository = resolveRepositoryContext(cwd);
192
213
  if (!resolvedRepository) {
193
- await emit();
214
+ await finish(agent, envelope);
194
215
  return;
195
216
  }
196
217
  const repository = { ...resolvedRepository, repoSyncId: repoSyncId(cwd) };
@@ -203,23 +224,27 @@ async function main() {
203
224
  const { resolution } = enrichment;
204
225
  if (!isHermesDenial) {
205
226
  if (resolution.shellMutation === "none") {
206
- await emit();
227
+ await finish(agent, envelope);
207
228
  return;
208
229
  }
209
230
  if (resolution.shellMutation === void 0 && resolution.fileRefs.length === 0) {
210
- await emit();
231
+ await finish(agent, envelope);
211
232
  return;
212
233
  }
213
234
  }
214
235
  const pathsComplete = !resolution.targetsIncomplete && !resolution.targetsTruncated && resolution.rejected.length === 0 && resolution.shellMutation !== "unresolved";
215
236
  const hasPathEvidence = resolution.fileRefs.length > 0 || resolution.rejected.length > 0 || resolution.targetsIncomplete || resolution.targetsTruncated || resolution.shellMutation === "unresolved";
237
+ if (agent === "cursor" && (resolution.rejected.length > 0 || resolution.targetsIncomplete || resolution.targetsTruncated || resolution.shellMutation === "unresolved")) {
238
+ await finish(agent, envelope);
239
+ return;
240
+ }
216
241
  if (!cachedCollectScopeAdmits(cwd, {
217
242
  repository: resolvedRepository.repoFullName,
218
243
  branch: currentBranch(cwd),
219
244
  agent,
220
245
  ...isHermesDenial || !hasPathEvidence ? {} : { paths: resolution.fileRefs, pathsComplete }
221
246
  })) {
222
- await emit();
247
+ await finish(agent, envelope);
223
248
  return;
224
249
  }
225
250
  const enriched = enrichment.parsed;
@@ -256,7 +281,7 @@ async function main() {
256
281
  } catch {
257
282
  }
258
283
  }
259
- await emit();
284
+ await finish(agent, envelope);
260
285
  }
261
286
  main().catch(async () => {
262
287
  await emit();
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  checkAffectedDecisions,
4
4
  formatDecisionsWarning
5
- } from "../chunk-RO3F6JYV.js";
6
- import "../chunk-Z7YPFWAZ.js";
7
- import "../chunk-CUE2LKLR.js";
8
- import "../chunk-3EEFL6NJ.js";
5
+ } from "../chunk-5GYQ4J3D.js";
6
+ import "../chunk-PCVGBE5B.js";
7
+ import "../chunk-KFXF2V5G.js";
8
+ import "../chunk-67VNLLBN.js";
9
9
 
10
10
  // src/hooks/pre-commit.ts
11
11
  import { execSync } from "child_process";
@@ -2,53 +2,60 @@
2
2
  import {
3
3
  analyzeShellTargets,
4
4
  buildCodexOutput,
5
+ buildCursorOutput,
5
6
  buildHermesOutput,
6
7
  buildHookOutput,
7
8
  demoteForMode,
8
9
  extractFileTargets,
9
10
  failOpenCodex,
11
+ failOpenCursor,
10
12
  failOpenHermes,
11
13
  failOpenOutput,
12
14
  readHookMode
13
- } from "../chunk-PPQYQEIR.js";
15
+ } from "../chunk-ZB4F75SZ.js";
14
16
  import {
15
17
  appendCodexContext,
16
18
  hasVisibleCodexMessage,
17
19
  prepareCodexContext
18
- } from "../chunk-UZPBB7V4.js";
19
- import {
20
- packageVersion
21
- } from "../chunk-PMRDE2DA.js";
20
+ } from "../chunk-VW3FDYRT.js";
21
+ import "../chunk-WX52U7Z2.js";
22
22
  import {
23
23
  DEFAULT_RULES,
24
24
  scrub
25
- } from "../chunk-FI3LIRCB.js";
25
+ } from "../chunk-OAAP2MY6.js";
26
26
  import {
27
27
  boundedHealthError
28
- } from "../chunk-LEMX3IOG.js";
28
+ } from "../chunk-BTNNEH2W.js";
29
29
  import {
30
30
  cachedCollectScopeAdmits,
31
- canonicalGitRoot,
32
- canonicalRepositoryPath,
33
- currentBranch,
34
- githubRepositoryFullName,
35
31
  isRepoActive,
36
32
  repoSyncId
37
- } from "../chunk-MAZWKJGL.js";
33
+ } from "../chunk-M6RIJVRE.js";
38
34
  import {
39
35
  getClient
40
- } from "../chunk-Z7YPFWAZ.js";
36
+ } from "../chunk-PCVGBE5B.js";
41
37
  import {
42
38
  normalizeEnvelope,
43
- parseAgent
44
- } from "../chunk-WB7AUZ4C.js";
45
- import "../chunk-CUE2LKLR.js";
46
- import "../chunk-3EEFL6NJ.js";
39
+ parseAgent,
40
+ readHookStdin,
41
+ shouldSuppressImportedCursorHandler
42
+ } from "../chunk-HU4PW6I6.js";
43
+ import {
44
+ packageVersion
45
+ } from "../chunk-EWZMDD44.js";
46
+ import {
47
+ canonicalGitRoot,
48
+ canonicalRepositoryPath,
49
+ currentBranch,
50
+ githubRepositoryFullName
51
+ } from "../chunk-H4UNYFUC.js";
52
+ import "../chunk-KFXF2V5G.js";
53
+ import "../chunk-67VNLLBN.js";
47
54
 
48
55
  // src/hooks/preflight-v3.ts
49
- import { Buffer as Buffer2 } from "buffer";
56
+ import { Buffer } from "buffer";
50
57
  var PREFLIGHT_PROTOCOL_VERSION = 3;
51
- var PREFLIGHT_TIMEOUT_MS = 9500;
58
+ var PREFLIGHT_TIMEOUT_MS = 6500;
52
59
  var MAX_PREFLIGHT_PATHS = 32;
53
60
  var MAX_PROPOSAL_BYTES = 6144;
54
61
  var MAX_CLIENT_VERSION_CHARS = 32;
@@ -64,7 +71,8 @@ function resolvePreflightTargets(args) {
64
71
  let coverage = "complete";
65
72
  let mutation = "present";
66
73
  let definite = false;
67
- if ((args.agent === "claude_code" || args.agent === "codex") && args.toolName === "Bash") {
74
+ const shellTool = (args.agent === "claude_code" || args.agent === "codex") && args.toolName === "Bash" || args.agent === "cursor" && args.toolName === "Shell";
75
+ if (shellTool) {
68
76
  const command = typeof args.toolInput === "string" ? args.toolInput : args.toolInput && typeof args.toolInput === "object" ? args.toolInput.command : void 0;
69
77
  if (typeof command !== "string") {
70
78
  return { paths: [], coverage: "unverified", mutation: "present" };
@@ -97,7 +105,7 @@ function resolvePreflightTargets(args) {
97
105
  return { paths: resolved, coverage, mutation };
98
106
  }
99
107
  function truncateUtf8(value, maxBytes) {
100
- const encoded = Buffer2.from(value);
108
+ const encoded = Buffer.from(value);
101
109
  if (encoded.byteLength <= maxBytes) return value;
102
110
  return encoded.subarray(0, maxBytes).toString("utf8").replace(/\uFFFD$/u, "");
103
111
  }
@@ -174,23 +182,6 @@ function unverifiedResult(message) {
174
182
 
175
183
  // src/hooks/pre-tool-use.ts
176
184
  var STDIN_TIMEOUT_MS = 1e3;
177
- async function readStdin() {
178
- return new Promise((resolve, reject) => {
179
- const chunks = [];
180
- const timer = setTimeout(() => {
181
- reject(new Error("stdin read timeout"));
182
- }, STDIN_TIMEOUT_MS);
183
- process.stdin.on("data", (chunk) => chunks.push(chunk));
184
- process.stdin.on("end", () => {
185
- clearTimeout(timer);
186
- resolve(Buffer.concat(chunks).toString("utf-8"));
187
- });
188
- process.stdin.on("error", (err) => {
189
- clearTimeout(timer);
190
- reject(err);
191
- });
192
- });
193
- }
194
185
  var agent = parseAgent(process.argv);
195
186
  async function emit(output) {
196
187
  return await new Promise((resolve) => {
@@ -208,6 +199,7 @@ async function emitWithAcknowledgment(output, acknowledge) {
208
199
  }
209
200
  function failOpen() {
210
201
  if (agent === "hermes") return failOpenHermes();
202
+ if (agent === "cursor") return failOpenCursor();
211
203
  return agent === "codex" ? failOpenCodex() : failOpenOutput();
212
204
  }
213
205
  function invocationId(envelope) {
@@ -232,6 +224,10 @@ async function emitUnverified(message, envelope) {
232
224
  process.stderr.write(`[primitive] ${message}
233
225
  `);
234
226
  await emit(failOpenHermes());
227
+ } else if (agent === "cursor") {
228
+ process.stderr.write(`[primitive] ${message}
229
+ `);
230
+ await emit(buildCursorOutput("allow", [result]));
235
231
  } else {
236
232
  let output = agent === "codex" ? buildCodexOutput("allow", [result]) : buildHookOutput("allow", [result]);
237
233
  if (agent === "codex" && typeof envelope?.session_id === "string" && envelope.session_id) {
@@ -253,17 +249,19 @@ async function emitUnverified(message, envelope) {
253
249
  async function main() {
254
250
  let raw;
255
251
  try {
256
- raw = await readStdin();
252
+ raw = await readHookStdin(STDIN_TIMEOUT_MS);
257
253
  } catch {
258
254
  await emit(failOpen());
259
255
  return;
260
256
  }
261
257
  let envelope;
262
258
  try {
263
- envelope = normalizeEnvelope(
264
- JSON.parse(raw),
265
- agent
266
- );
259
+ const incoming = JSON.parse(raw);
260
+ if (agent !== "cursor" && shouldSuppressImportedCursorHandler(incoming, "prim-pre-tool-use")) {
261
+ await emit(failOpenCursor());
262
+ return;
263
+ }
264
+ envelope = normalizeEnvelope(incoming, agent);
267
265
  } catch {
268
266
  await emit(failOpen());
269
267
  return;
@@ -298,6 +296,10 @@ async function main() {
298
296
  await emit(failOpen());
299
297
  return;
300
298
  }
299
+ if (agent === "cursor" && targets.coverage === "unverified") {
300
+ await emitUnverified("tool targets were ambiguous or outside the active repository", envelope);
301
+ return;
302
+ }
301
303
  const binding = repoSyncId(cwd);
302
304
  const branch = currentBranch(cwd);
303
305
  const sessionId = envelope.session_id;
@@ -350,7 +352,7 @@ async function main() {
350
352
  if (advisory) process.stderr.write(`${advisory}
351
353
  `);
352
354
  }
353
- let output = agent === "hermes" ? buildHermesOutput(aggregate, [result]) : agent === "codex" ? buildCodexOutput(aggregate, [result]) : buildHookOutput(aggregate, [result]);
355
+ let output = agent === "hermes" ? buildHermesOutput(aggregate, [result]) : agent === "cursor" ? buildCursorOutput(aggregate, [result]) : agent === "codex" ? buildCodexOutput(aggregate, [result]) : buildHookOutput(aggregate, [result]);
354
356
  if (agent === "codex" && hasVisibleCodexMessage(output) && typeof sessionId === "string" && sessionId.length > 0) {
355
357
  try {
356
358
  const context = await prepareCodexContext({ cwd, sessionId, includeDigest: false });