@holdpoint/engine-cursor 0.1.0-alpha.10 → 0.1.0-alpha.11

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/dist/index.d.ts CHANGED
@@ -1,11 +1,419 @@
1
1
  import { HoldpointConfig } from '@holdpoint/types';
2
+ import { TranslateHookInputOptions } from '@holdpoint/sdk';
2
3
 
4
+ declare const HOLDPOINT_CURSOR_HOOK_MARKER = "HOLDPOINT_MANAGED=cursor";
5
+ /**
6
+ * Generate project-level Cursor hooks.
7
+ *
8
+ * Cursor project hooks live at `.cursor/hooks.json`, run from the project root,
9
+ * and can observe, inject context, or auto-continue the agent loop. Holdpoint
10
+ * uses a single generated script so hook behavior stays in sync with
11
+ * `checks.yaml` after every `holdpoint update`.
12
+ */
13
+ declare function buildHooksJson(config: HoldpointConfig): string;
14
+ /**
15
+ * Generate `.cursor/holdpoint-hook.mjs`.
16
+ *
17
+ * The script speaks Cursor's native hook protocol:
18
+ * - `sessionStart` returns `additional_context` when session_context_files exist.
19
+ * - `stop` and completed `subagentStop` run Holdpoint checks and return a
20
+ * `followup_message` on failure so Cursor keeps iterating.
21
+ * - all other hooks are used for Live telemetry and emit either an allow
22
+ * response or no output, depending on that hook's schema.
23
+ */
24
+ declare function buildCheckScript(): string;
25
+ /**
26
+ * Generate a standalone context-injection script for sessionStart tests.
27
+ * Cursor uses the same generated dispatcher for context, telemetry, and gates.
28
+ */
29
+ declare function buildContextScript(): string;
3
30
  /**
4
31
  * Generate .cursorrules additions from a HoldpointConfig.
5
32
  *
6
- * Cursor has no programmatic hooks, so we inject natural-language rules that
7
- * instruct the agent to run and respect Holdpoint checks before completing work.
33
+ * Cursor now enforces Holdpoint through `.cursor/hooks.json`; this rules block
34
+ * remains useful context for the agent and for Cursor cloud cases where not all
35
+ * local hook stages are available.
8
36
  */
9
37
  declare function buildEngine(config: HoldpointConfig): string;
10
38
 
11
- export { buildEngine };
39
+ declare const adapter: {
40
+ id: string;
41
+ displayName: string;
42
+ capabilities: {
43
+ can_stream: boolean;
44
+ };
45
+ generateBridgeCommand(): string;
46
+ translateHookInput(raw: unknown, options?: TranslateHookInputOptions): {
47
+ v: 1;
48
+ id: string;
49
+ ts: number;
50
+ engine: string;
51
+ session_id: string;
52
+ project_hash: string;
53
+ cwd: string;
54
+ type: "session_start";
55
+ payload: {
56
+ source?: "startup" | "resume" | undefined;
57
+ tools_available?: string[] | undefined;
58
+ };
59
+ seq?: number | undefined;
60
+ caps?: {
61
+ can_stream?: boolean | undefined;
62
+ can_control?: boolean | undefined;
63
+ can_modify_context?: boolean | undefined;
64
+ can_register_tools?: boolean | undefined;
65
+ control_online?: boolean | undefined;
66
+ } | undefined;
67
+ } | {
68
+ v: 1;
69
+ id: string;
70
+ ts: number;
71
+ engine: string;
72
+ session_id: string;
73
+ project_hash: string;
74
+ cwd: string;
75
+ type: "session_end";
76
+ payload: {
77
+ reason?: "user" | "completed" | "error" | undefined;
78
+ turn_count?: number | undefined;
79
+ };
80
+ seq?: number | undefined;
81
+ caps?: {
82
+ can_stream?: boolean | undefined;
83
+ can_control?: boolean | undefined;
84
+ can_modify_context?: boolean | undefined;
85
+ can_register_tools?: boolean | undefined;
86
+ control_online?: boolean | undefined;
87
+ } | undefined;
88
+ } | {
89
+ v: 1;
90
+ id: string;
91
+ ts: number;
92
+ engine: string;
93
+ session_id: string;
94
+ project_hash: string;
95
+ cwd: string;
96
+ type: "prompt_submit";
97
+ payload: {
98
+ prompt: string;
99
+ truncated_at?: number | undefined;
100
+ };
101
+ seq?: number | undefined;
102
+ caps?: {
103
+ can_stream?: boolean | undefined;
104
+ can_control?: boolean | undefined;
105
+ can_modify_context?: boolean | undefined;
106
+ can_register_tools?: boolean | undefined;
107
+ control_online?: boolean | undefined;
108
+ } | undefined;
109
+ } | {
110
+ v: 1;
111
+ id: string;
112
+ ts: number;
113
+ engine: string;
114
+ session_id: string;
115
+ project_hash: string;
116
+ cwd: string;
117
+ type: "tool_pre";
118
+ payload: {
119
+ tool_name: string;
120
+ tool_use_id: string;
121
+ tool_input: Record<string, unknown>;
122
+ write_targets?: string[] | undefined;
123
+ };
124
+ seq?: number | undefined;
125
+ caps?: {
126
+ can_stream?: boolean | undefined;
127
+ can_control?: boolean | undefined;
128
+ can_modify_context?: boolean | undefined;
129
+ can_register_tools?: boolean | undefined;
130
+ control_online?: boolean | undefined;
131
+ } | undefined;
132
+ } | {
133
+ v: 1;
134
+ id: string;
135
+ ts: number;
136
+ engine: string;
137
+ session_id: string;
138
+ project_hash: string;
139
+ cwd: string;
140
+ type: "tool_post";
141
+ payload: {
142
+ tool_name: string;
143
+ tool_use_id: string;
144
+ success: boolean;
145
+ duration_ms: number;
146
+ output_summary?: string | undefined;
147
+ write_targets?: string[] | undefined;
148
+ };
149
+ seq?: number | undefined;
150
+ caps?: {
151
+ can_stream?: boolean | undefined;
152
+ can_control?: boolean | undefined;
153
+ can_modify_context?: boolean | undefined;
154
+ can_register_tools?: boolean | undefined;
155
+ control_online?: boolean | undefined;
156
+ } | undefined;
157
+ } | {
158
+ v: 1;
159
+ id: string;
160
+ ts: number;
161
+ engine: string;
162
+ session_id: string;
163
+ project_hash: string;
164
+ cwd: string;
165
+ type: "tool_failure";
166
+ payload: {
167
+ tool_name: string;
168
+ tool_use_id: string;
169
+ error: string;
170
+ };
171
+ seq?: number | undefined;
172
+ caps?: {
173
+ can_stream?: boolean | undefined;
174
+ can_control?: boolean | undefined;
175
+ can_modify_context?: boolean | undefined;
176
+ can_register_tools?: boolean | undefined;
177
+ control_online?: boolean | undefined;
178
+ } | undefined;
179
+ } | {
180
+ v: 1;
181
+ id: string;
182
+ ts: number;
183
+ engine: string;
184
+ session_id: string;
185
+ project_hash: string;
186
+ cwd: string;
187
+ type: "notification";
188
+ payload: {
189
+ kind: "permission_prompt" | "idle" | "auth_success" | "elicitation";
190
+ message: string;
191
+ };
192
+ seq?: number | undefined;
193
+ caps?: {
194
+ can_stream?: boolean | undefined;
195
+ can_control?: boolean | undefined;
196
+ can_modify_context?: boolean | undefined;
197
+ can_register_tools?: boolean | undefined;
198
+ control_online?: boolean | undefined;
199
+ } | undefined;
200
+ } | {
201
+ v: 1;
202
+ id: string;
203
+ ts: number;
204
+ engine: string;
205
+ session_id: string;
206
+ project_hash: string;
207
+ cwd: string;
208
+ type: "stop_block";
209
+ payload: {
210
+ reason: string;
211
+ failing_checks: string[];
212
+ };
213
+ seq?: number | undefined;
214
+ caps?: {
215
+ can_stream?: boolean | undefined;
216
+ can_control?: boolean | undefined;
217
+ can_modify_context?: boolean | undefined;
218
+ can_register_tools?: boolean | undefined;
219
+ control_online?: boolean | undefined;
220
+ } | undefined;
221
+ } | {
222
+ v: 1;
223
+ id: string;
224
+ ts: number;
225
+ engine: string;
226
+ session_id: string;
227
+ project_hash: string;
228
+ cwd: string;
229
+ type: "stop_pass";
230
+ payload: {
231
+ duration_ms: number;
232
+ };
233
+ seq?: number | undefined;
234
+ caps?: {
235
+ can_stream?: boolean | undefined;
236
+ can_control?: boolean | undefined;
237
+ can_modify_context?: boolean | undefined;
238
+ can_register_tools?: boolean | undefined;
239
+ control_online?: boolean | undefined;
240
+ } | undefined;
241
+ } | {
242
+ v: 1;
243
+ id: string;
244
+ ts: number;
245
+ engine: string;
246
+ session_id: string;
247
+ project_hash: string;
248
+ cwd: string;
249
+ type: "check_run";
250
+ payload: {
251
+ check_id: string;
252
+ label: string;
253
+ status: "pass" | "fail" | "skip";
254
+ duration_ms: number;
255
+ output?: string | undefined;
256
+ };
257
+ seq?: number | undefined;
258
+ caps?: {
259
+ can_stream?: boolean | undefined;
260
+ can_control?: boolean | undefined;
261
+ can_modify_context?: boolean | undefined;
262
+ can_register_tools?: boolean | undefined;
263
+ control_online?: boolean | undefined;
264
+ } | undefined;
265
+ } | {
266
+ v: 1;
267
+ id: string;
268
+ ts: number;
269
+ engine: string;
270
+ session_id: string;
271
+ project_hash: string;
272
+ cwd: string;
273
+ type: "permission_pending";
274
+ payload: {
275
+ request_id: string;
276
+ permission_kind: "shell" | "write" | "mcp" | "read" | "url" | "custom-tool" | "memory" | "hook";
277
+ tool_call_id?: string | undefined;
278
+ tool_name?: string | undefined;
279
+ title?: string | undefined;
280
+ details?: string | undefined;
281
+ };
282
+ seq?: number | undefined;
283
+ caps?: {
284
+ can_stream?: boolean | undefined;
285
+ can_control?: boolean | undefined;
286
+ can_modify_context?: boolean | undefined;
287
+ can_register_tools?: boolean | undefined;
288
+ control_online?: boolean | undefined;
289
+ } | undefined;
290
+ } | {
291
+ v: 1;
292
+ id: string;
293
+ ts: number;
294
+ engine: string;
295
+ session_id: string;
296
+ project_hash: string;
297
+ cwd: string;
298
+ type: "permission_resolved";
299
+ payload: {
300
+ request_id: string;
301
+ outcome: "timeout" | "approved" | "denied" | "resolved_by_hook" | "session_ended";
302
+ reason?: string | undefined;
303
+ };
304
+ seq?: number | undefined;
305
+ caps?: {
306
+ can_stream?: boolean | undefined;
307
+ can_control?: boolean | undefined;
308
+ can_modify_context?: boolean | undefined;
309
+ can_register_tools?: boolean | undefined;
310
+ control_online?: boolean | undefined;
311
+ } | undefined;
312
+ } | {
313
+ v: 1;
314
+ id: string;
315
+ ts: number;
316
+ engine: string;
317
+ session_id: string;
318
+ project_hash: string;
319
+ cwd: string;
320
+ type: "conflict";
321
+ payload: {
322
+ kind: "file_write" | "lock_held";
323
+ file_path: string;
324
+ holder: {
325
+ engine: string;
326
+ session_id: string;
327
+ };
328
+ requester: {
329
+ engine: string;
330
+ session_id: string;
331
+ };
332
+ };
333
+ seq?: number | undefined;
334
+ caps?: {
335
+ can_stream?: boolean | undefined;
336
+ can_control?: boolean | undefined;
337
+ can_modify_context?: boolean | undefined;
338
+ can_register_tools?: boolean | undefined;
339
+ control_online?: boolean | undefined;
340
+ } | undefined;
341
+ } | {
342
+ v: 1;
343
+ id: string;
344
+ ts: number;
345
+ engine: string;
346
+ session_id: string;
347
+ project_hash: string;
348
+ cwd: string;
349
+ type: "control";
350
+ payload: {
351
+ command: "approve_pending";
352
+ args: {
353
+ request_id: string;
354
+ };
355
+ actor: "user";
356
+ actor_session?: string | undefined;
357
+ } | {
358
+ command: "deny_pending";
359
+ args: {
360
+ request_id: string;
361
+ reason?: string | undefined;
362
+ };
363
+ actor: "user";
364
+ actor_session?: string | undefined;
365
+ } | {
366
+ command: "inject_context";
367
+ args: {
368
+ text: string;
369
+ };
370
+ actor: "user";
371
+ actor_session?: string | undefined;
372
+ } | {
373
+ command: "trigger_tool";
374
+ args: {
375
+ tool_name: string;
376
+ input?: Record<string, unknown> | undefined;
377
+ };
378
+ actor: "user";
379
+ actor_session?: string | undefined;
380
+ };
381
+ seq?: number | undefined;
382
+ caps?: {
383
+ can_stream?: boolean | undefined;
384
+ can_control?: boolean | undefined;
385
+ can_modify_context?: boolean | undefined;
386
+ can_register_tools?: boolean | undefined;
387
+ control_online?: boolean | undefined;
388
+ } | undefined;
389
+ } | {
390
+ v: 1;
391
+ id: string;
392
+ ts: number;
393
+ engine: string;
394
+ session_id: string;
395
+ project_hash: string;
396
+ cwd: string;
397
+ type: "meta";
398
+ payload: {
399
+ [x: string]: unknown;
400
+ kind: string;
401
+ };
402
+ seq?: number | undefined;
403
+ caps?: {
404
+ can_stream?: boolean | undefined;
405
+ can_control?: boolean | undefined;
406
+ can_modify_context?: boolean | undefined;
407
+ can_register_tools?: boolean | undefined;
408
+ control_online?: boolean | undefined;
409
+ } | undefined;
410
+ } | null;
411
+ };
412
+
413
+ declare const manifest: {
414
+ readonly manifestVersion: 1;
415
+ readonly id: "cursor";
416
+ readonly displayName: "Cursor";
417
+ };
418
+
419
+ export { HOLDPOINT_CURSOR_HOOK_MARKER, adapter, buildCheckScript, buildContextScript, buildEngine, buildHooksJson, manifest };
package/dist/index.js CHANGED
@@ -1,14 +1,235 @@
1
1
  // src/engine.ts
2
+ var HOLDPOINT_CURSOR_HOOK_MARKER = "HOLDPOINT_MANAGED=cursor";
3
+ var HOOK_COMMAND = `node .cursor/holdpoint-hook.mjs # ${HOLDPOINT_CURSOR_HOOK_MARKER}`;
4
+ function hook(options = {}) {
5
+ return {
6
+ command: HOOK_COMMAND,
7
+ ...options
8
+ };
9
+ }
10
+ function buildHooksJson(config) {
11
+ const hooks = {
12
+ beforeSubmitPrompt: [hook({ timeout: 30 })],
13
+ preToolUse: [hook({ timeout: 30, matcher: "Shell|Read|Write|Grep|Task|MCP:.*" })],
14
+ postToolUse: [hook({ timeout: 30 })],
15
+ postToolUseFailure: [hook({ timeout: 30 })],
16
+ beforeShellExecution: [hook({ timeout: 30 })],
17
+ afterShellExecution: [hook({ timeout: 30 })],
18
+ beforeMCPExecution: [hook({ timeout: 30 })],
19
+ afterMCPExecution: [hook({ timeout: 30 })],
20
+ beforeReadFile: [hook({ timeout: 30 })],
21
+ afterFileEdit: [hook({ timeout: 30 })],
22
+ subagentStart: [hook({ timeout: 30 })],
23
+ subagentStop: [hook({ timeout: 600, loop_limit: 5 })],
24
+ preCompact: [hook({ timeout: 30 })],
25
+ afterAgentResponse: [hook({ timeout: 30 })],
26
+ stop: [hook({ timeout: 600, loop_limit: 5 })]
27
+ };
28
+ if (config.session_context_files?.length) {
29
+ hooks.sessionStart = [hook({ timeout: 30 })];
30
+ }
31
+ return JSON.stringify({ version: 1, hooks }, null, 2) + "\n";
32
+ }
33
+ function buildCheckScript() {
34
+ return `#!/usr/bin/env node
35
+ // AUTO-GENERATED by Holdpoint \u2014 do not edit. Re-generate: npx holdpoint update
36
+ import { execSync } from "node:child_process";
37
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
38
+ import { isAbsolute, join, relative, resolve } from "node:path";
39
+
40
+ const CHECK_COMMAND = "node_modules/.bin/holdpoint check --staged";
41
+ const LIVE_COMMAND = "node_modules/.bin/holdpoint event --engine cursor --from-hook";
42
+ const MAX_CONTEXT_CHARS = 100_000;
43
+ const MAX_CHECK_OUTPUT_CHARS = 60_000;
44
+ const CHECK_MAX_BUFFER_BYTES = 1024 * 1024 * 10;
45
+
46
+ function readInput() {
47
+ try {
48
+ const raw = readFileSync(0, "utf8").trim();
49
+ return raw ? JSON.parse(raw) : {};
50
+ } catch {
51
+ return {};
52
+ }
53
+ }
54
+
55
+ function resolveRepoRoot(cwd = process.cwd()) {
56
+ try {
57
+ return execSync("git rev-parse --show-toplevel", {
58
+ cwd,
59
+ encoding: "utf8",
60
+ stdio: ["pipe", "pipe", "ignore"],
61
+ }).trim();
62
+ } catch {
63
+ return cwd;
64
+ }
65
+ }
66
+
67
+ function eventName(input) {
68
+ return String(input && input.hook_event_name ? input.hook_event_name : "");
69
+ }
70
+
71
+ function truncateText(value, maxChars) {
72
+ const text = String(value || "");
73
+ if (text.length <= maxChars) return { text, truncated: false, originalLength: text.length };
74
+ return {
75
+ text: text.slice(0, maxChars) + "\\n\\n[Holdpoint output truncated to " + maxChars + " chars.]",
76
+ truncated: true,
77
+ originalLength: text.length,
78
+ };
79
+ }
80
+
81
+ function isPathInsideRoot(repoRoot, absPath) {
82
+ const rel = relative(repoRoot, absPath);
83
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
84
+ }
85
+
86
+ function sendLiveEvent(input) {
87
+ try {
88
+ execSync(LIVE_COMMAND, {
89
+ input: JSON.stringify(input),
90
+ encoding: "utf8",
91
+ stdio: ["pipe", "ignore", "ignore"],
92
+ });
93
+ } catch {
94
+ // Live telemetry is best-effort and must never break Cursor's hook flow.
95
+ }
96
+ }
97
+
98
+ function readSessionContext(repoRoot) {
99
+ const configPath = join(repoRoot, ".github/holdpoint/generated/checks.immutable.json");
100
+ if (!existsSync(configPath)) return undefined;
101
+ try {
102
+ const config = JSON.parse(readFileSync(configPath, "utf8"));
103
+ const files = Array.isArray(config.session_context_files) ? config.session_context_files : [];
104
+ const parts = [];
105
+ for (const file of files) {
106
+ if (typeof file !== "string" || !file.trim()) continue;
107
+ const abs = resolve(repoRoot, file);
108
+ if (!isPathInsideRoot(repoRoot, abs) || !existsSync(abs)) continue;
109
+ try {
110
+ parts.push("<!-- " + file + " -->\\n" + readFileSync(abs, "utf8"));
111
+ } catch {}
112
+ }
113
+ if (parts.length === 0) return undefined;
114
+ const context = truncateText(parts.join("\\n\\n"), MAX_CONTEXT_CHARS);
115
+ return {
116
+ additional_context: context.text,
117
+ truncated: context.truncated,
118
+ originalLength: context.originalLength,
119
+ emittedLength: context.text.length,
120
+ };
121
+ } catch {
122
+ return undefined;
123
+ }
124
+ }
125
+
126
+ function runHoldpointChecks(repoRoot) {
127
+ const startedAt = Date.now();
128
+ try {
129
+ const output = execSync(CHECK_COMMAND, {
130
+ cwd: repoRoot,
131
+ stdio: "pipe",
132
+ encoding: "utf8",
133
+ maxBuffer: CHECK_MAX_BUFFER_BYTES,
134
+ });
135
+ return {
136
+ ok: true,
137
+ durationMs: Date.now() - startedAt,
138
+ output: String(output || "").trim(),
139
+ };
140
+ } catch (error) {
141
+ const output = [error && error.stdout, error && error.stderr, error && error.message]
142
+ .filter(Boolean)
143
+ .join("\\n")
144
+ .trim();
145
+ const truncated = truncateText(output, MAX_CHECK_OUTPUT_CHARS);
146
+ return {
147
+ ok: false,
148
+ durationMs: Date.now() - startedAt,
149
+ output: truncated.text || "Holdpoint checks failed. Fix the issues above, then re-attempt.",
150
+ truncated: truncated.truncated,
151
+ originalLength: truncated.originalLength,
152
+ };
153
+ }
154
+ }
155
+
156
+ function shouldRunCompletionChecks(input) {
157
+ const name = eventName(input);
158
+ if (name === "stop") return true;
159
+ if (name === "subagentStop") {
160
+ return input && input.status === "completed";
161
+ }
162
+ return false;
163
+ }
164
+
165
+ const input = readInput();
166
+ const cwd = typeof input.cwd === "string" ? input.cwd : process.cwd();
167
+ const repoRoot = resolveRepoRoot(cwd);
168
+ const name = eventName(input);
169
+
170
+ if (name === "sessionStart") {
171
+ const context = readSessionContext(repoRoot);
172
+ sendLiveEvent({
173
+ ...input,
174
+ holdpoint_context: context
175
+ ? {
176
+ truncated: context.truncated,
177
+ originalLength: context.originalLength,
178
+ emittedLength: context.emittedLength,
179
+ }
180
+ : undefined,
181
+ });
182
+ if (context?.additional_context) {
183
+ writeFileSync(1, JSON.stringify({ additional_context: context.additional_context }) + "\\n");
184
+ }
185
+ process.exit(0);
186
+ }
187
+
188
+ if (shouldRunCompletionChecks(input)) {
189
+ const result = runHoldpointChecks(repoRoot);
190
+ sendLiveEvent({ ...input, holdpoint_check: result });
191
+ if (result.ok) {
192
+ process.exit(0);
193
+ }
194
+ process.stdout.write(
195
+ JSON.stringify({
196
+ followup_message:
197
+ result.output +
198
+ "\\n\\nHoldpoint checks failed. Fix the issues above, then run the checks again before finishing.",
199
+ }) + "\\n",
200
+ );
201
+ process.exit(0);
202
+ }
203
+
204
+ sendLiveEvent(input);
205
+
206
+ if (
207
+ name === "preToolUse" ||
208
+ name === "beforeShellExecution" ||
209
+ name === "beforeMCPExecution" ||
210
+ name === "beforeReadFile" ||
211
+ name === "subagentStart"
212
+ ) {
213
+ process.stdout.write(JSON.stringify({ permission: "allow" }) + "\\n");
214
+ } else if (name === "beforeSubmitPrompt") {
215
+ process.stdout.write(JSON.stringify({ continue: true }) + "\\n");
216
+ }
217
+ process.exit(0);
218
+ `;
219
+ }
220
+ function buildContextScript() {
221
+ return buildCheckScript();
222
+ }
2
223
  function buildEngine(config) {
3
224
  const deterministicList = config.checks.filter((c) => c.cmd !== void 0).map((c) => ` - [${c.when ?? "always"}] ${c.label}: \`${c.cmd ?? "(no cmd)"}\``).join("\n");
4
225
  const promptList = config.checks.filter((c) => c.prompt !== void 0).map((c) => ` - [${c.when ?? "always"}] ${c.label}: ${c.prompt ?? ""}`).join("\n");
5
- return `
6
- # \u2500\u2500\u2500 Holdpoint Rules (auto-generated) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
226
+ return `# \u2500\u2500\u2500 Holdpoint Rules (auto-generated) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
7
227
  # DO NOT EDIT this block manually. Re-generate with: npx holdpoint update
8
228
 
9
229
  ## Mandatory pre-completion checks
10
230
 
11
- Before marking ANY task as done or making a final commit, you MUST:
231
+ Holdpoint also installed Cursor project hooks in \`.cursor/hooks.json\`. Before
232
+ marking ANY task as done or making a final commit, you MUST:
12
233
 
13
234
  1. Run all Holdpoint tasks and confirm they pass:
14
235
  ${deterministicList || " (no tasks configured)"}
@@ -29,7 +250,263 @@ ${promptList || " (no prompt checks configured)"}
29
250
  # \u2500\u2500\u2500 End Holdpoint Rules \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
30
251
  `;
31
252
  }
253
+
254
+ // src/live-adapter.ts
255
+ import { execFileSync } from "child_process";
256
+ import { createHash, randomUUID } from "crypto";
257
+ import { existsSync, realpathSync } from "fs";
258
+ import { resolve } from "path";
259
+ function asObject(value) {
260
+ return value != null && typeof value === "object" && !Array.isArray(value) ? value : {};
261
+ }
262
+ function asString(value) {
263
+ return typeof value === "string" && value.trim() ? value : void 0;
264
+ }
265
+ function asNumber(value) {
266
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
267
+ }
268
+ function sha12(value) {
269
+ return createHash("sha256").update(value).digest("hex").slice(0, 12);
270
+ }
271
+ function resolveRepoRoot(cwd) {
272
+ try {
273
+ return realpathSync(
274
+ execFileSync("git", ["rev-parse", "--show-toplevel"], {
275
+ cwd,
276
+ encoding: "utf8",
277
+ stdio: ["ignore", "pipe", "ignore"]
278
+ }).trim()
279
+ );
280
+ } catch {
281
+ try {
282
+ return realpathSync(cwd);
283
+ } catch {
284
+ return resolve(cwd);
285
+ }
286
+ }
287
+ }
288
+ function extractStringArray(value) {
289
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
290
+ }
291
+ function normalizeWriteTarget(cwd, target) {
292
+ const resolved = resolve(cwd, target);
293
+ return existsSync(resolved) ? realpathSync.native(resolved) : resolved;
294
+ }
295
+ function extractWriteTargets(input, cwd) {
296
+ const candidates = /* @__PURE__ */ new Set();
297
+ const toolInput = asObject(input.tool_input);
298
+ for (const key of ["file_path", "filePath", "path"]) {
299
+ const value = toolInput[key] ?? input[key];
300
+ if (typeof value === "string" && value.trim()) {
301
+ candidates.add(normalizeWriteTarget(cwd, value));
302
+ }
303
+ }
304
+ for (const key of ["paths", "file_paths", "modified_files"]) {
305
+ for (const value of extractStringArray(
306
+ toolInput[key] ?? input[key]
307
+ )) {
308
+ if (value.trim()) {
309
+ candidates.add(normalizeWriteTarget(cwd, value));
310
+ }
311
+ }
312
+ }
313
+ return candidates.size > 0 ? [...candidates] : void 0;
314
+ }
315
+ function truncate(value, max) {
316
+ if (value.length <= max) return { value };
317
+ return { value: value.slice(0, max), truncatedAt: max };
318
+ }
319
+ function sessionId(input) {
320
+ return input.conversation_id ?? input.session_id ?? input.generation_id;
321
+ }
322
+ function mapSessionEndReason(reason) {
323
+ if (reason === "completed" || reason === "error") return reason;
324
+ if (reason === "aborted" || reason === "window_close" || reason === "user_close") return "user";
325
+ return reason ? "completed" : void 0;
326
+ }
327
+ function buildCursorHookEvent(raw, options) {
328
+ const input = asObject(raw);
329
+ const id = sessionId(input);
330
+ const hookEventName = asString(input.hook_event_name);
331
+ if (!id || !hookEventName) {
332
+ return null;
333
+ }
334
+ const cwd = asString(input.cwd) ?? input.workspace_roots?.[0] ?? options?.cwd ?? process.cwd();
335
+ const repoRoot = resolveRepoRoot(cwd);
336
+ const base = {
337
+ v: 1,
338
+ id: randomUUID(),
339
+ ts: Date.now(),
340
+ engine: "cursor",
341
+ session_id: id,
342
+ project_hash: sha12(repoRoot),
343
+ cwd: repoRoot
344
+ };
345
+ const toolName = asString(input.tool_name) ?? (input.command ? "Shell" : void 0);
346
+ const toolUseId = input.tool_use_id ?? input.generation_id ?? randomUUID();
347
+ const writeTargets = extractWriteTargets(input, cwd);
348
+ switch (hookEventName) {
349
+ case "sessionStart":
350
+ return {
351
+ ...base,
352
+ type: "session_start",
353
+ payload: {
354
+ ...input.holdpoint_context?.truncated ? { tools_available: ["context_truncated"] } : {}
355
+ }
356
+ };
357
+ case "sessionEnd":
358
+ return {
359
+ ...base,
360
+ type: "session_end",
361
+ payload: {
362
+ ...mapSessionEndReason(asString(input.reason) ?? asString(input.status)) ? { reason: mapSessionEndReason(asString(input.reason) ?? asString(input.status)) } : {}
363
+ }
364
+ };
365
+ case "beforeSubmitPrompt": {
366
+ const prompt = asString(input.prompt) ?? "";
367
+ const { value, truncatedAt } = truncate(prompt, 1e4);
368
+ return {
369
+ ...base,
370
+ type: "prompt_submit",
371
+ payload: {
372
+ prompt: value,
373
+ ...truncatedAt ? { truncated_at: truncatedAt } : {}
374
+ }
375
+ };
376
+ }
377
+ case "preToolUse":
378
+ return {
379
+ ...base,
380
+ type: "tool_pre",
381
+ payload: {
382
+ tool_name: toolName ?? "unknown",
383
+ tool_use_id: toolUseId,
384
+ tool_input: asObject(input.tool_input),
385
+ ...writeTargets ? { write_targets: writeTargets } : {}
386
+ }
387
+ };
388
+ case "postToolUse":
389
+ case "afterShellExecution":
390
+ case "afterMCPExecution":
391
+ return {
392
+ ...base,
393
+ type: "tool_post",
394
+ payload: {
395
+ tool_name: toolName ?? (hookEventName === "afterShellExecution" ? "Shell" : "unknown"),
396
+ tool_use_id: toolUseId,
397
+ success: true,
398
+ duration_ms: asNumber(input.duration) ?? asNumber(input.duration_ms) ?? 0,
399
+ ...input.output ?? input.tool_output ? { output_summary: truncate(String(input.output ?? input.tool_output), 2e3).value } : {},
400
+ ...writeTargets ? { write_targets: writeTargets } : {}
401
+ }
402
+ };
403
+ case "postToolUseFailure":
404
+ return {
405
+ ...base,
406
+ type: "tool_failure",
407
+ payload: {
408
+ tool_name: toolName ?? "unknown",
409
+ tool_use_id: toolUseId,
410
+ error: asString(input.error_message) ?? asString(input.failure_type) ?? "Cursor tool failed"
411
+ }
412
+ };
413
+ case "beforeShellExecution":
414
+ case "beforeMCPExecution":
415
+ case "beforeReadFile":
416
+ return {
417
+ ...base,
418
+ type: "tool_pre",
419
+ payload: {
420
+ tool_name: hookEventName === "beforeShellExecution" ? "Shell" : hookEventName === "beforeMCPExecution" ? "MCP" : hookEventName === "beforeReadFile" ? "Read" : "unknown",
421
+ tool_use_id: toolUseId,
422
+ tool_input: hookEventName === "beforeShellExecution" && input.command ? { command: input.command } : hookEventName === "beforeReadFile" && input.file_path ? { file_path: input.file_path } : asObject(input.tool_input),
423
+ ...writeTargets ? { write_targets: writeTargets } : {}
424
+ }
425
+ };
426
+ case "afterFileEdit":
427
+ return {
428
+ ...base,
429
+ type: "tool_post",
430
+ payload: {
431
+ tool_name: "Write",
432
+ tool_use_id: toolUseId,
433
+ success: true,
434
+ duration_ms: asNumber(input.duration) ?? asNumber(input.duration_ms) ?? 0,
435
+ ...writeTargets ? { write_targets: writeTargets } : {}
436
+ }
437
+ };
438
+ case "stop": {
439
+ const check = input.holdpoint_check;
440
+ if (check?.ok === false) {
441
+ return {
442
+ ...base,
443
+ type: "stop_block",
444
+ payload: {
445
+ reason: check.output ?? "Holdpoint checks failed",
446
+ failing_checks: []
447
+ }
448
+ };
449
+ }
450
+ return {
451
+ ...base,
452
+ type: "stop_pass",
453
+ payload: {
454
+ duration_ms: check?.durationMs ?? asNumber(input.duration_ms) ?? 0
455
+ }
456
+ };
457
+ }
458
+ case "subagentStart":
459
+ case "subagentStop":
460
+ case "preCompact":
461
+ case "afterAgentResponse":
462
+ return {
463
+ ...base,
464
+ type: "meta",
465
+ payload: {
466
+ kind: "cursor_lifecycle",
467
+ hook_event_name: hookEventName,
468
+ status: input.status,
469
+ subagent_type: input.subagent_type
470
+ }
471
+ };
472
+ default:
473
+ return {
474
+ ...base,
475
+ type: "meta",
476
+ payload: {
477
+ kind: "cursor_hook",
478
+ hook_event_name: hookEventName
479
+ }
480
+ };
481
+ }
482
+ }
483
+ var adapter = {
484
+ id: "cursor",
485
+ displayName: "Cursor",
486
+ capabilities: {
487
+ can_stream: true
488
+ },
489
+ generateBridgeCommand() {
490
+ return "node_modules/.bin/holdpoint event --engine cursor --from-hook";
491
+ },
492
+ translateHookInput(raw, options) {
493
+ return buildCursorHookEvent(raw, options);
494
+ }
495
+ };
496
+
497
+ // src/manifest.ts
498
+ var manifest = {
499
+ manifestVersion: 1,
500
+ id: "cursor",
501
+ displayName: "Cursor"
502
+ };
32
503
  export {
33
- buildEngine
504
+ HOLDPOINT_CURSOR_HOOK_MARKER,
505
+ adapter,
506
+ buildCheckScript,
507
+ buildContextScript,
508
+ buildEngine,
509
+ buildHooksJson,
510
+ manifest
34
511
  };
35
512
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine.ts"],"sourcesContent":["import type { HoldpointConfig } from \"@holdpoint/types\";\n\n/**\n * Generate .cursorrules additions from a HoldpointConfig.\n *\n * Cursor has no programmatic hooks, so we inject natural-language rules that\n * instruct the agent to run and respect Holdpoint checks before completing work.\n */\nexport function buildEngine(config: HoldpointConfig): string {\n const deterministicList = config.checks\n .filter((c) => c.cmd !== undefined)\n .map((c) => ` - [${c.when ?? \"always\"}] ${c.label}: \\`${c.cmd ?? \"(no cmd)\"}\\``)\n .join(\"\\n\");\n\n const promptList = config.checks\n .filter((c) => c.prompt !== undefined)\n .map((c) => ` - [${c.when ?? \"always\"}] ${c.label}: ${c.prompt ?? \"\"}`)\n .join(\"\\n\");\n\n return `\n# ─── Holdpoint Rules (auto-generated) ─────────────────────────────────────────\n# DO NOT EDIT this block manually. Re-generate with: npx holdpoint update\n\n## Mandatory pre-completion checks\n\nBefore marking ANY task as done or making a final commit, you MUST:\n\n1. Run all Holdpoint tasks and confirm they pass:\n${deterministicList || \" (no tasks configured)\"}\n\n2. Act on all matching agent prompts:\n${promptList || \" (no prompt checks configured)\"}\n\n3. If any task exits non-zero, fix the underlying issue before\n proceeding. Do NOT suppress errors or skip tasks.\n\n4. For prompt checks, explicitly state in your response that you have acted on\n each item before marking the task complete.\n\n## Running checks\n Run: \\`node_modules/.bin/holdpoint check --staged\\` to execute all tasks.\n Fix all failures before proceeding.\n\n# ─── End Holdpoint Rules ───────────────────────────────────────────────────────\n`;\n}\n"],"mappings":";AAQO,SAAS,YAAY,QAAiC;AAC3D,QAAM,oBAAoB,OAAO,OAC9B,OAAO,CAAC,MAAM,EAAE,QAAQ,MAAS,EACjC,IAAI,CAAC,MAAM,QAAQ,EAAE,QAAQ,QAAQ,KAAK,EAAE,KAAK,OAAO,EAAE,OAAO,UAAU,IAAI,EAC/E,KAAK,IAAI;AAEZ,QAAM,aAAa,OAAO,OACvB,OAAO,CAAC,MAAM,EAAE,WAAW,MAAS,EACpC,IAAI,CAAC,MAAM,QAAQ,EAAE,QAAQ,QAAQ,KAAK,EAAE,KAAK,KAAK,EAAE,UAAU,EAAE,EAAE,EACtE,KAAK,IAAI;AAEZ,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASP,qBAAqB,yBAAyB;AAAA;AAAA;AAAA,EAG9C,cAAc,iCAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcjD;","names":[]}
1
+ {"version":3,"sources":["../src/engine.ts","../src/live-adapter.ts","../src/manifest.ts"],"sourcesContent":["import type { HoldpointConfig } from \"@holdpoint/types\";\n\nexport const HOLDPOINT_CURSOR_HOOK_MARKER = \"HOLDPOINT_MANAGED=cursor\";\n\nconst HOOK_COMMAND = `node .cursor/holdpoint-hook.mjs # ${HOLDPOINT_CURSOR_HOOK_MARKER}`;\n\ninterface CursorHook {\n command: string;\n timeout?: number;\n matcher?: string;\n loop_limit?: number;\n failClosed?: boolean;\n}\n\ninterface CursorHooksJson {\n version: 1;\n hooks: Record<string, CursorHook[]>;\n}\n\nfunction hook(options: Omit<CursorHook, \"command\"> = {}): CursorHook {\n return {\n command: HOOK_COMMAND,\n ...options,\n };\n}\n\n/**\n * Generate project-level Cursor hooks.\n *\n * Cursor project hooks live at `.cursor/hooks.json`, run from the project root,\n * and can observe, inject context, or auto-continue the agent loop. Holdpoint\n * uses a single generated script so hook behavior stays in sync with\n * `checks.yaml` after every `holdpoint update`.\n */\nexport function buildHooksJson(config: HoldpointConfig): string {\n const hooks: CursorHooksJson[\"hooks\"] = {\n beforeSubmitPrompt: [hook({ timeout: 30 })],\n preToolUse: [hook({ timeout: 30, matcher: \"Shell|Read|Write|Grep|Task|MCP:.*\" })],\n postToolUse: [hook({ timeout: 30 })],\n postToolUseFailure: [hook({ timeout: 30 })],\n beforeShellExecution: [hook({ timeout: 30 })],\n afterShellExecution: [hook({ timeout: 30 })],\n beforeMCPExecution: [hook({ timeout: 30 })],\n afterMCPExecution: [hook({ timeout: 30 })],\n beforeReadFile: [hook({ timeout: 30 })],\n afterFileEdit: [hook({ timeout: 30 })],\n subagentStart: [hook({ timeout: 30 })],\n subagentStop: [hook({ timeout: 600, loop_limit: 5 })],\n preCompact: [hook({ timeout: 30 })],\n afterAgentResponse: [hook({ timeout: 30 })],\n stop: [hook({ timeout: 600, loop_limit: 5 })],\n };\n\n if (config.session_context_files?.length) {\n hooks.sessionStart = [hook({ timeout: 30 })];\n }\n\n return JSON.stringify({ version: 1, hooks }, null, 2) + \"\\n\";\n}\n\n/**\n * Generate `.cursor/holdpoint-hook.mjs`.\n *\n * The script speaks Cursor's native hook protocol:\n * - `sessionStart` returns `additional_context` when session_context_files exist.\n * - `stop` and completed `subagentStop` run Holdpoint checks and return a\n * `followup_message` on failure so Cursor keeps iterating.\n * - all other hooks are used for Live telemetry and emit either an allow\n * response or no output, depending on that hook's schema.\n */\nexport function buildCheckScript(): string {\n return `#!/usr/bin/env node\n// AUTO-GENERATED by Holdpoint — do not edit. Re-generate: npx holdpoint update\nimport { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { isAbsolute, join, relative, resolve } from \"node:path\";\n\nconst CHECK_COMMAND = \"node_modules/.bin/holdpoint check --staged\";\nconst LIVE_COMMAND = \"node_modules/.bin/holdpoint event --engine cursor --from-hook\";\nconst MAX_CONTEXT_CHARS = 100_000;\nconst MAX_CHECK_OUTPUT_CHARS = 60_000;\nconst CHECK_MAX_BUFFER_BYTES = 1024 * 1024 * 10;\n\nfunction readInput() {\n try {\n const raw = readFileSync(0, \"utf8\").trim();\n return raw ? JSON.parse(raw) : {};\n } catch {\n return {};\n }\n}\n\nfunction resolveRepoRoot(cwd = process.cwd()) {\n try {\n return execSync(\"git rev-parse --show-toplevel\", {\n cwd,\n encoding: \"utf8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n }).trim();\n } catch {\n return cwd;\n }\n}\n\nfunction eventName(input) {\n return String(input && input.hook_event_name ? input.hook_event_name : \"\");\n}\n\nfunction truncateText(value, maxChars) {\n const text = String(value || \"\");\n if (text.length <= maxChars) return { text, truncated: false, originalLength: text.length };\n return {\n text: text.slice(0, maxChars) + \"\\\\n\\\\n[Holdpoint output truncated to \" + maxChars + \" chars.]\",\n truncated: true,\n originalLength: text.length,\n };\n}\n\nfunction isPathInsideRoot(repoRoot, absPath) {\n const rel = relative(repoRoot, absPath);\n return rel === \"\" || (!rel.startsWith(\"..\") && !isAbsolute(rel));\n}\n\nfunction sendLiveEvent(input) {\n try {\n execSync(LIVE_COMMAND, {\n input: JSON.stringify(input),\n encoding: \"utf8\",\n stdio: [\"pipe\", \"ignore\", \"ignore\"],\n });\n } catch {\n // Live telemetry is best-effort and must never break Cursor's hook flow.\n }\n}\n\nfunction readSessionContext(repoRoot) {\n const configPath = join(repoRoot, \".github/holdpoint/generated/checks.immutable.json\");\n if (!existsSync(configPath)) return undefined;\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf8\"));\n const files = Array.isArray(config.session_context_files) ? config.session_context_files : [];\n const parts = [];\n for (const file of files) {\n if (typeof file !== \"string\" || !file.trim()) continue;\n const abs = resolve(repoRoot, file);\n if (!isPathInsideRoot(repoRoot, abs) || !existsSync(abs)) continue;\n try {\n parts.push(\"<!-- \" + file + \" -->\\\\n\" + readFileSync(abs, \"utf8\"));\n } catch {}\n }\n if (parts.length === 0) return undefined;\n const context = truncateText(parts.join(\"\\\\n\\\\n\"), MAX_CONTEXT_CHARS);\n return {\n additional_context: context.text,\n truncated: context.truncated,\n originalLength: context.originalLength,\n emittedLength: context.text.length,\n };\n } catch {\n return undefined;\n }\n}\n\nfunction runHoldpointChecks(repoRoot) {\n const startedAt = Date.now();\n try {\n const output = execSync(CHECK_COMMAND, {\n cwd: repoRoot,\n stdio: \"pipe\",\n encoding: \"utf8\",\n maxBuffer: CHECK_MAX_BUFFER_BYTES,\n });\n return {\n ok: true,\n durationMs: Date.now() - startedAt,\n output: String(output || \"\").trim(),\n };\n } catch (error) {\n const output = [error && error.stdout, error && error.stderr, error && error.message]\n .filter(Boolean)\n .join(\"\\\\n\")\n .trim();\n const truncated = truncateText(output, MAX_CHECK_OUTPUT_CHARS);\n return {\n ok: false,\n durationMs: Date.now() - startedAt,\n output: truncated.text || \"Holdpoint checks failed. Fix the issues above, then re-attempt.\",\n truncated: truncated.truncated,\n originalLength: truncated.originalLength,\n };\n }\n}\n\nfunction shouldRunCompletionChecks(input) {\n const name = eventName(input);\n if (name === \"stop\") return true;\n if (name === \"subagentStop\") {\n return input && input.status === \"completed\";\n }\n return false;\n}\n\nconst input = readInput();\nconst cwd = typeof input.cwd === \"string\" ? input.cwd : process.cwd();\nconst repoRoot = resolveRepoRoot(cwd);\nconst name = eventName(input);\n\nif (name === \"sessionStart\") {\n const context = readSessionContext(repoRoot);\n sendLiveEvent({\n ...input,\n holdpoint_context: context\n ? {\n truncated: context.truncated,\n originalLength: context.originalLength,\n emittedLength: context.emittedLength,\n }\n : undefined,\n });\n if (context?.additional_context) {\n writeFileSync(1, JSON.stringify({ additional_context: context.additional_context }) + \"\\\\n\");\n }\n process.exit(0);\n}\n\nif (shouldRunCompletionChecks(input)) {\n const result = runHoldpointChecks(repoRoot);\n sendLiveEvent({ ...input, holdpoint_check: result });\n if (result.ok) {\n process.exit(0);\n }\n process.stdout.write(\n JSON.stringify({\n followup_message:\n result.output +\n \"\\\\n\\\\nHoldpoint checks failed. Fix the issues above, then run the checks again before finishing.\",\n }) + \"\\\\n\",\n );\n process.exit(0);\n}\n\nsendLiveEvent(input);\n\nif (\n name === \"preToolUse\" ||\n name === \"beforeShellExecution\" ||\n name === \"beforeMCPExecution\" ||\n name === \"beforeReadFile\" ||\n name === \"subagentStart\"\n) {\n process.stdout.write(JSON.stringify({ permission: \"allow\" }) + \"\\\\n\");\n} else if (name === \"beforeSubmitPrompt\") {\n process.stdout.write(JSON.stringify({ continue: true }) + \"\\\\n\");\n}\nprocess.exit(0);\n`;\n}\n\n/**\n * Generate a standalone context-injection script for sessionStart tests.\n * Cursor uses the same generated dispatcher for context, telemetry, and gates.\n */\nexport function buildContextScript(): string {\n return buildCheckScript();\n}\n\n/**\n * Generate .cursorrules additions from a HoldpointConfig.\n *\n * Cursor now enforces Holdpoint through `.cursor/hooks.json`; this rules block\n * remains useful context for the agent and for Cursor cloud cases where not all\n * local hook stages are available.\n */\nexport function buildEngine(config: HoldpointConfig): string {\n const deterministicList = config.checks\n .filter((c) => c.cmd !== undefined)\n .map((c) => ` - [${c.when ?? \"always\"}] ${c.label}: \\`${c.cmd ?? \"(no cmd)\"}\\``)\n .join(\"\\n\");\n\n const promptList = config.checks\n .filter((c) => c.prompt !== undefined)\n .map((c) => ` - [${c.when ?? \"always\"}] ${c.label}: ${c.prompt ?? \"\"}`)\n .join(\"\\n\");\n\n return `# ─── Holdpoint Rules (auto-generated) ─────────────────────────────────────────\n# DO NOT EDIT this block manually. Re-generate with: npx holdpoint update\n\n## Mandatory pre-completion checks\n\nHoldpoint also installed Cursor project hooks in \\`.cursor/hooks.json\\`. Before\nmarking ANY task as done or making a final commit, you MUST:\n\n1. Run all Holdpoint tasks and confirm they pass:\n${deterministicList || \" (no tasks configured)\"}\n\n2. Act on all matching agent prompts:\n${promptList || \" (no prompt checks configured)\"}\n\n3. If any task exits non-zero, fix the underlying issue before\n proceeding. Do NOT suppress errors or skip tasks.\n\n4. For prompt checks, explicitly state in your response that you have acted on\n each item before marking the task complete.\n\n## Running checks\n Run: \\`node_modules/.bin/holdpoint check --staged\\` to execute all tasks.\n Fix all failures before proceeding.\n\n# ─── End Holdpoint Rules ───────────────────────────────────────────────────────\n`;\n}\n","import { execFileSync } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { existsSync, realpathSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { EventV1 } from \"@holdpoint/live-protocol\";\nimport type { TranslateHookInputOptions } from \"@holdpoint/sdk\";\n\ninterface CursorHookInput {\n conversation_id?: string;\n generation_id?: string;\n session_id?: string;\n hook_event_name?: string;\n cwd?: string;\n workspace_roots?: string[];\n model?: string;\n prompt?: string;\n tool_name?: string;\n tool_input?: unknown;\n tool_output?: string;\n tool_use_id?: string;\n duration?: number;\n duration_ms?: number;\n error_message?: string;\n failure_type?: string;\n status?: string;\n reason?: string;\n command?: string;\n output?: string;\n file_path?: string;\n modified_files?: string[];\n subagent_id?: string;\n subagent_type?: string;\n task?: string;\n holdpoint_check?: {\n ok?: boolean;\n durationMs?: number;\n output?: string;\n };\n holdpoint_context?: {\n truncated?: boolean;\n originalLength?: number;\n emittedLength?: number;\n };\n}\n\nfunction asObject(value: unknown): Record<string, unknown> {\n return value != null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction sha12(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\").slice(0, 12);\n}\n\nfunction resolveRepoRoot(cwd: string): string {\n try {\n return realpathSync(\n execFileSync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim(),\n );\n } catch {\n try {\n return realpathSync(cwd);\n } catch {\n return resolve(cwd);\n }\n }\n}\n\nfunction extractStringArray(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((entry): entry is string => typeof entry === \"string\")\n : [];\n}\n\nfunction normalizeWriteTarget(cwd: string, target: string): string {\n const resolved = resolve(cwd, target);\n return existsSync(resolved) ? realpathSync.native(resolved) : resolved;\n}\n\nfunction extractWriteTargets(input: CursorHookInput, cwd: string): string[] | undefined {\n const candidates = new Set<string>();\n const toolInput = asObject(input.tool_input);\n\n for (const key of [\"file_path\", \"filePath\", \"path\"]) {\n const value = toolInput[key] ?? (input as Record<string, unknown>)[key];\n if (typeof value === \"string\" && value.trim()) {\n candidates.add(normalizeWriteTarget(cwd, value));\n }\n }\n\n for (const key of [\"paths\", \"file_paths\", \"modified_files\"]) {\n for (const value of extractStringArray(\n toolInput[key] ?? (input as Record<string, unknown>)[key],\n )) {\n if (value.trim()) {\n candidates.add(normalizeWriteTarget(cwd, value));\n }\n }\n }\n\n return candidates.size > 0 ? [...candidates] : undefined;\n}\n\nfunction truncate(value: string, max: number): { value: string; truncatedAt?: number } {\n if (value.length <= max) return { value };\n return { value: value.slice(0, max), truncatedAt: max };\n}\n\nfunction sessionId(input: CursorHookInput): string | undefined {\n return input.conversation_id ?? input.session_id ?? input.generation_id;\n}\n\nfunction mapSessionEndReason(\n reason: string | undefined,\n): \"user\" | \"completed\" | \"error\" | undefined {\n if (reason === \"completed\" || reason === \"error\") return reason;\n if (reason === \"aborted\" || reason === \"window_close\" || reason === \"user_close\") return \"user\";\n return reason ? \"completed\" : undefined;\n}\n\nfunction buildCursorHookEvent(raw: unknown, options?: TranslateHookInputOptions): EventV1 | null {\n const input = asObject(raw) as CursorHookInput;\n const id = sessionId(input);\n const hookEventName = asString(input.hook_event_name);\n if (!id || !hookEventName) {\n return null;\n }\n\n const cwd = asString(input.cwd) ?? input.workspace_roots?.[0] ?? options?.cwd ?? process.cwd();\n const repoRoot = resolveRepoRoot(cwd);\n const base = {\n v: 1 as const,\n id: randomUUID(),\n ts: Date.now(),\n engine: \"cursor\",\n session_id: id,\n project_hash: sha12(repoRoot),\n cwd: repoRoot,\n };\n const toolName = asString(input.tool_name) ?? (input.command ? \"Shell\" : undefined);\n const toolUseId = input.tool_use_id ?? input.generation_id ?? randomUUID();\n const writeTargets = extractWriteTargets(input, cwd);\n\n switch (hookEventName) {\n case \"sessionStart\":\n return {\n ...base,\n type: \"session_start\",\n payload: {\n ...(input.holdpoint_context?.truncated ? { tools_available: [\"context_truncated\"] } : {}),\n },\n };\n case \"sessionEnd\":\n return {\n ...base,\n type: \"session_end\",\n payload: {\n ...(mapSessionEndReason(asString(input.reason) ?? asString(input.status))\n ? { reason: mapSessionEndReason(asString(input.reason) ?? asString(input.status)) }\n : {}),\n },\n };\n case \"beforeSubmitPrompt\": {\n const prompt = asString(input.prompt) ?? \"\";\n const { value, truncatedAt } = truncate(prompt, 10_000);\n return {\n ...base,\n type: \"prompt_submit\",\n payload: {\n prompt: value,\n ...(truncatedAt ? { truncated_at: truncatedAt } : {}),\n },\n };\n }\n case \"preToolUse\":\n return {\n ...base,\n type: \"tool_pre\",\n payload: {\n tool_name: toolName ?? \"unknown\",\n tool_use_id: toolUseId,\n tool_input: asObject(input.tool_input),\n ...(writeTargets ? { write_targets: writeTargets } : {}),\n },\n };\n case \"postToolUse\":\n case \"afterShellExecution\":\n case \"afterMCPExecution\":\n return {\n ...base,\n type: \"tool_post\",\n payload: {\n tool_name: toolName ?? (hookEventName === \"afterShellExecution\" ? \"Shell\" : \"unknown\"),\n tool_use_id: toolUseId,\n success: true,\n duration_ms: asNumber(input.duration) ?? asNumber(input.duration_ms) ?? 0,\n ...((input.output ?? input.tool_output)\n ? { output_summary: truncate(String(input.output ?? input.tool_output), 2_000).value }\n : {}),\n ...(writeTargets ? { write_targets: writeTargets } : {}),\n },\n };\n case \"postToolUseFailure\":\n return {\n ...base,\n type: \"tool_failure\",\n payload: {\n tool_name: toolName ?? \"unknown\",\n tool_use_id: toolUseId,\n error:\n asString(input.error_message) ?? asString(input.failure_type) ?? \"Cursor tool failed\",\n },\n };\n case \"beforeShellExecution\":\n case \"beforeMCPExecution\":\n case \"beforeReadFile\":\n return {\n ...base,\n type: \"tool_pre\",\n payload: {\n tool_name:\n hookEventName === \"beforeShellExecution\"\n ? \"Shell\"\n : hookEventName === \"beforeMCPExecution\"\n ? \"MCP\"\n : hookEventName === \"beforeReadFile\"\n ? \"Read\"\n : \"unknown\",\n tool_use_id: toolUseId,\n tool_input:\n hookEventName === \"beforeShellExecution\" && input.command\n ? { command: input.command }\n : hookEventName === \"beforeReadFile\" && input.file_path\n ? { file_path: input.file_path }\n : asObject(input.tool_input),\n ...(writeTargets ? { write_targets: writeTargets } : {}),\n },\n };\n case \"afterFileEdit\":\n return {\n ...base,\n type: \"tool_post\",\n payload: {\n tool_name: \"Write\",\n tool_use_id: toolUseId,\n success: true,\n duration_ms: asNumber(input.duration) ?? asNumber(input.duration_ms) ?? 0,\n ...(writeTargets ? { write_targets: writeTargets } : {}),\n },\n };\n case \"stop\": {\n const check = input.holdpoint_check;\n if (check?.ok === false) {\n return {\n ...base,\n type: \"stop_block\",\n payload: {\n reason: check.output ?? \"Holdpoint checks failed\",\n failing_checks: [],\n },\n };\n }\n return {\n ...base,\n type: \"stop_pass\",\n payload: {\n duration_ms: check?.durationMs ?? asNumber(input.duration_ms) ?? 0,\n },\n };\n }\n case \"subagentStart\":\n case \"subagentStop\":\n case \"preCompact\":\n case \"afterAgentResponse\":\n return {\n ...base,\n type: \"meta\",\n payload: {\n kind: \"cursor_lifecycle\",\n hook_event_name: hookEventName,\n status: input.status,\n subagent_type: input.subagent_type,\n },\n };\n default:\n return {\n ...base,\n type: \"meta\",\n payload: {\n kind: \"cursor_hook\",\n hook_event_name: hookEventName,\n },\n };\n }\n}\n\nexport const adapter = {\n id: \"cursor\",\n displayName: \"Cursor\",\n capabilities: {\n can_stream: true,\n },\n generateBridgeCommand(): string {\n return \"node_modules/.bin/holdpoint event --engine cursor --from-hook\";\n },\n translateHookInput(raw: unknown, options?: TranslateHookInputOptions) {\n return buildCursorHookEvent(raw, options);\n },\n};\n","export const manifest = {\n manifestVersion: 1,\n id: \"cursor\",\n displayName: \"Cursor\",\n} as const;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holdpoint/engine-cursor",
3
- "version": "0.1.0-alpha.10",
3
+ "version": "0.1.0-alpha.11",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "tag": "alpha"
@@ -18,6 +18,7 @@
18
18
  "license": "MIT",
19
19
  "keywords": [
20
20
  "holdpoint",
21
+ "holdpoint-engine",
21
22
  "ai",
22
23
  "agents",
23
24
  "claude-code",
@@ -29,6 +30,10 @@
29
30
  "yaml"
30
31
  ],
31
32
  "type": "module",
33
+ "holdpoint": {
34
+ "manifest": "./dist/index.js",
35
+ "adapter": "./dist/index.js"
36
+ },
32
37
  "exports": {
33
38
  ".": {
34
39
  "types": "./dist/index.d.ts",
@@ -43,12 +48,15 @@
43
48
  "LICENSE"
44
49
  ],
45
50
  "dependencies": {
46
- "@holdpoint/types": "0.1.0-alpha.7"
51
+ "@holdpoint/live-protocol": "0.1.0-alpha.3",
52
+ "@holdpoint/types": "0.1.0-alpha.8",
53
+ "@holdpoint/sdk": "0.1.0-alpha.3"
47
54
  },
48
55
  "devDependencies": {
56
+ "@types/node": "^25.9.1",
49
57
  "tsup": "^8.3.5",
50
- "typescript": "^5.7.2",
51
- "vitest": "^2.1.8"
58
+ "typescript": "^6.0.3",
59
+ "vitest": "^4.1.7"
52
60
  },
53
61
  "scripts": {
54
62
  "build": "tsup",