@holdpoint/engine-cursor 0.1.0-alpha.11 → 0.1.0-alpha.13
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.js +79 -29
- package/dist/index.js.map +1 -1
- package/package.json +5 -6
package/dist/index.js
CHANGED
|
@@ -25,7 +25,8 @@ function buildHooksJson(config) {
|
|
|
25
25
|
afterAgentResponse: [hook({ timeout: 30 })],
|
|
26
26
|
stop: [hook({ timeout: 600, loop_limit: 5 })]
|
|
27
27
|
};
|
|
28
|
-
|
|
28
|
+
const seedsSession = (config.session_context_files?.length ?? 0) > 0 || config.checks.some((c) => (c.on ?? "before_done") === "session_start");
|
|
29
|
+
if (seedsSession) {
|
|
29
30
|
hooks.sessionStart = [hook({ timeout: 30 })];
|
|
30
31
|
}
|
|
31
32
|
return JSON.stringify({ version: 1, hooks }, null, 2) + "\n";
|
|
@@ -95,38 +96,74 @@ function sendLiveEvent(input) {
|
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
function
|
|
99
|
+
function readConfig(repoRoot) {
|
|
99
100
|
const configPath = join(repoRoot, ".github/holdpoint/generated/checks.immutable.json");
|
|
100
|
-
if (!existsSync(configPath)) return
|
|
101
|
-
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
if (!existsSync(configPath)) return {};
|
|
102
|
+
try { return JSON.parse(readFileSync(configPath, "utf8")); } catch { return {}; }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function readFileContext(repoRoot, file) {
|
|
106
|
+
if (typeof file !== "string" || !file.trim()) return null;
|
|
107
|
+
const abs = resolve(repoRoot, file);
|
|
108
|
+
if (!isPathInsideRoot(repoRoot, abs) || !existsSync(abs)) return null;
|
|
109
|
+
try { return "<!-- " + file + " -->\\n" + readFileSync(abs, "utf8"); } catch { return null; }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const DATETIME_TEXT = () =>
|
|
113
|
+
"Current date and time: " + new Date().toISOString() + " (UTC)\\n" +
|
|
114
|
+
"Provided by Holdpoint \u2014 use this to avoid knowledge-cutoff confusion.";
|
|
115
|
+
|
|
116
|
+
// Gather agent context for a Holdpoint hook. NOTE: Cursor's beforeSubmitPrompt
|
|
117
|
+
// cannot inject context (it only gates submission), so the top-level datetime
|
|
118
|
+
// and message_submit checks are surfaced at sessionStart instead \u2014 the only
|
|
119
|
+
// Cursor stage that accepts additional_context per the hooks API.
|
|
120
|
+
function gatherHookContext(repoRoot, hook) {
|
|
121
|
+
const cfg = readConfig(repoRoot);
|
|
122
|
+
const checks = Array.isArray(cfg.checks) ? cfg.checks : [];
|
|
123
|
+
const parts = [];
|
|
124
|
+
let hasDatetime = false;
|
|
125
|
+
const addDatetime = () => { if (!hasDatetime) { hasDatetime = true; parts.push(DATETIME_TEXT()); } };
|
|
126
|
+
|
|
127
|
+
const includeHooks = hook === "session_start" ? ["session_start", "message_submit"] : [hook];
|
|
128
|
+
|
|
129
|
+
if (hook === "session_start") {
|
|
130
|
+
const files = Array.isArray(cfg.session_context_files) ? cfg.session_context_files : [];
|
|
131
|
+
for (const f of files) { const c = readFileContext(repoRoot, f); if (c) parts.push(c); }
|
|
132
|
+
}
|
|
133
|
+
for (const c of checks) {
|
|
134
|
+
const on = typeof c.on === "string" ? c.on : "before_done";
|
|
135
|
+
if (!includeHooks.includes(on)) continue;
|
|
136
|
+
if (c.inject && typeof c.inject === "object") {
|
|
137
|
+
if (c.inject.datetime === true) addDatetime();
|
|
138
|
+
if (typeof c.inject.text === "string" && c.inject.text.trim()) parts.push(c.inject.text);
|
|
139
|
+
if (Array.isArray(c.inject.files)) for (const f of c.inject.files) { const x = readFileContext(repoRoot, f); if (x) parts.push(x); }
|
|
140
|
+
} else if (typeof c.prompt === "string" && c.prompt.trim()) {
|
|
141
|
+
parts.push("Holdpoint reminder [" + (c.label || c.id || "check") + "]: " + c.prompt);
|
|
112
142
|
}
|
|
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
143
|
}
|
|
144
|
+
// Cursor can only inject once (sessionStart), so fold the per-message datetime in here.
|
|
145
|
+
if (hook === "session_start" && cfg.inject_datetime !== false) addDatetime();
|
|
146
|
+
|
|
147
|
+
if (parts.length === 0) return undefined;
|
|
148
|
+
const context = truncateText(parts.join("\\n\\n"), MAX_CONTEXT_CHARS);
|
|
149
|
+
return {
|
|
150
|
+
additional_context: context.text,
|
|
151
|
+
truncated: context.truncated,
|
|
152
|
+
originalLength: context.originalLength,
|
|
153
|
+
emittedLength: context.text.length,
|
|
154
|
+
};
|
|
124
155
|
}
|
|
125
156
|
|
|
126
|
-
function
|
|
157
|
+
function hasCmdAt(repoRoot, hook) {
|
|
158
|
+
const cfg = readConfig(repoRoot);
|
|
159
|
+
const checks = Array.isArray(cfg.checks) ? cfg.checks : [];
|
|
160
|
+
return checks.some((c) => typeof c.cmd === "string" && (typeof c.on === "string" ? c.on : "before_done") === hook);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function runHoldpointChecks(repoRoot, command = CHECK_COMMAND) {
|
|
127
164
|
const startedAt = Date.now();
|
|
128
165
|
try {
|
|
129
|
-
const output = execSync(
|
|
166
|
+
const output = execSync(command, {
|
|
130
167
|
cwd: repoRoot,
|
|
131
168
|
stdio: "pipe",
|
|
132
169
|
encoding: "utf8",
|
|
@@ -168,7 +205,7 @@ const repoRoot = resolveRepoRoot(cwd);
|
|
|
168
205
|
const name = eventName(input);
|
|
169
206
|
|
|
170
207
|
if (name === "sessionStart") {
|
|
171
|
-
const context =
|
|
208
|
+
const context = gatherHookContext(repoRoot, "session_start");
|
|
172
209
|
sendLiveEvent({
|
|
173
210
|
...input,
|
|
174
211
|
holdpoint_context: context
|
|
@@ -203,8 +240,19 @@ if (shouldRunCompletionChecks(input)) {
|
|
|
203
240
|
|
|
204
241
|
sendLiveEvent(input);
|
|
205
242
|
|
|
206
|
-
if (
|
|
207
|
-
|
|
243
|
+
if (name === "preToolUse") {
|
|
244
|
+
// Gate on before_tool cmd checks; deny the tool if they fail.
|
|
245
|
+
if (hasCmdAt(repoRoot, "before_tool")) {
|
|
246
|
+
const result = runHoldpointChecks(repoRoot, CHECK_COMMAND + " --hook before_tool");
|
|
247
|
+
if (!result.ok) {
|
|
248
|
+
process.stdout.write(
|
|
249
|
+
JSON.stringify({ permission: "deny", agentMessage: result.output }) + "\\n",
|
|
250
|
+
);
|
|
251
|
+
process.exit(0);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
process.stdout.write(JSON.stringify({ permission: "allow" }) + "\\n");
|
|
255
|
+
} else if (
|
|
208
256
|
name === "beforeShellExecution" ||
|
|
209
257
|
name === "beforeMCPExecution" ||
|
|
210
258
|
name === "beforeReadFile" ||
|
|
@@ -212,6 +260,8 @@ if (
|
|
|
212
260
|
) {
|
|
213
261
|
process.stdout.write(JSON.stringify({ permission: "allow" }) + "\\n");
|
|
214
262
|
} else if (name === "beforeSubmitPrompt") {
|
|
263
|
+
// Cursor's beforeSubmitPrompt cannot inject context \u2014 only allow/deny the
|
|
264
|
+
// submission. Per-message context is folded into sessionStart instead.
|
|
215
265
|
process.stdout.write(JSON.stringify({ continue: true }) + "\\n");
|
|
216
266
|
}
|
|
217
267
|
process.exit(0);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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":[]}
|
|
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 const seedsSession =\n (config.session_context_files?.length ?? 0) > 0 ||\n config.checks.some((c) => (c.on ?? \"before_done\") === \"session_start\");\n if (seedsSession) {\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 readConfig(repoRoot) {\n const configPath = join(repoRoot, \".github/holdpoint/generated/checks.immutable.json\");\n if (!existsSync(configPath)) return {};\n try { return JSON.parse(readFileSync(configPath, \"utf8\")); } catch { return {}; }\n}\n\nfunction readFileContext(repoRoot, file) {\n if (typeof file !== \"string\" || !file.trim()) return null;\n const abs = resolve(repoRoot, file);\n if (!isPathInsideRoot(repoRoot, abs) || !existsSync(abs)) return null;\n try { return \"<!-- \" + file + \" -->\\\\n\" + readFileSync(abs, \"utf8\"); } catch { return null; }\n}\n\nconst DATETIME_TEXT = () =>\n \"Current date and time: \" + new Date().toISOString() + \" (UTC)\\\\n\" +\n \"Provided by Holdpoint — use this to avoid knowledge-cutoff confusion.\";\n\n// Gather agent context for a Holdpoint hook. NOTE: Cursor's beforeSubmitPrompt\n// cannot inject context (it only gates submission), so the top-level datetime\n// and message_submit checks are surfaced at sessionStart instead — the only\n// Cursor stage that accepts additional_context per the hooks API.\nfunction gatherHookContext(repoRoot, hook) {\n const cfg = readConfig(repoRoot);\n const checks = Array.isArray(cfg.checks) ? cfg.checks : [];\n const parts = [];\n let hasDatetime = false;\n const addDatetime = () => { if (!hasDatetime) { hasDatetime = true; parts.push(DATETIME_TEXT()); } };\n\n const includeHooks = hook === \"session_start\" ? [\"session_start\", \"message_submit\"] : [hook];\n\n if (hook === \"session_start\") {\n const files = Array.isArray(cfg.session_context_files) ? cfg.session_context_files : [];\n for (const f of files) { const c = readFileContext(repoRoot, f); if (c) parts.push(c); }\n }\n for (const c of checks) {\n const on = typeof c.on === \"string\" ? c.on : \"before_done\";\n if (!includeHooks.includes(on)) continue;\n if (c.inject && typeof c.inject === \"object\") {\n if (c.inject.datetime === true) addDatetime();\n if (typeof c.inject.text === \"string\" && c.inject.text.trim()) parts.push(c.inject.text);\n if (Array.isArray(c.inject.files)) for (const f of c.inject.files) { const x = readFileContext(repoRoot, f); if (x) parts.push(x); }\n } else if (typeof c.prompt === \"string\" && c.prompt.trim()) {\n parts.push(\"Holdpoint reminder [\" + (c.label || c.id || \"check\") + \"]: \" + c.prompt);\n }\n }\n // Cursor can only inject once (sessionStart), so fold the per-message datetime in here.\n if (hook === \"session_start\" && cfg.inject_datetime !== false) addDatetime();\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}\n\nfunction hasCmdAt(repoRoot, hook) {\n const cfg = readConfig(repoRoot);\n const checks = Array.isArray(cfg.checks) ? cfg.checks : [];\n return checks.some((c) => typeof c.cmd === \"string\" && (typeof c.on === \"string\" ? c.on : \"before_done\") === hook);\n}\n\nfunction runHoldpointChecks(repoRoot, command = CHECK_COMMAND) {\n const startedAt = Date.now();\n try {\n const output = execSync(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 = gatherHookContext(repoRoot, \"session_start\");\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 (name === \"preToolUse\") {\n // Gate on before_tool cmd checks; deny the tool if they fail.\n if (hasCmdAt(repoRoot, \"before_tool\")) {\n const result = runHoldpointChecks(repoRoot, CHECK_COMMAND + \" --hook before_tool\");\n if (!result.ok) {\n process.stdout.write(\n JSON.stringify({ permission: \"deny\", agentMessage: result.output }) + \"\\\\n\",\n );\n process.exit(0);\n }\n }\n process.stdout.write(JSON.stringify({ permission: \"allow\" }) + \"\\\\n\");\n} else if (\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 // Cursor's beforeSubmitPrompt cannot inject context — only allow/deny the\n // submission. Per-message context is folded into sessionStart instead.\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,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holdpoint/engine-cursor",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.13",
|
|
4
4
|
"publishConfig": {
|
|
5
|
-
"access": "public"
|
|
6
|
-
"tag": "alpha"
|
|
5
|
+
"access": "public"
|
|
7
6
|
},
|
|
8
7
|
"description": "Holdpoint engine adapter for Cursor",
|
|
9
8
|
"homepage": "https://holdpoint.dev",
|
|
@@ -48,9 +47,9 @@
|
|
|
48
47
|
"LICENSE"
|
|
49
48
|
],
|
|
50
49
|
"dependencies": {
|
|
51
|
-
"@holdpoint/
|
|
52
|
-
"@holdpoint/
|
|
53
|
-
"@holdpoint/
|
|
50
|
+
"@holdpoint/sdk": "0.1.0-alpha.4",
|
|
51
|
+
"@holdpoint/live-protocol": "0.1.0-alpha.4",
|
|
52
|
+
"@holdpoint/types": "0.1.0-alpha.10"
|
|
54
53
|
},
|
|
55
54
|
"devDependencies": {
|
|
56
55
|
"@types/node": "^25.9.1",
|