@holdpoint/engine-cursor 0.1.0-alpha.14 → 0.1.0-alpha.15
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 +5 -166
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,170 +1,7 @@
|
|
|
1
1
|
// src/engine.ts
|
|
2
|
+
import { buildSecurityScanScript } from "@holdpoint/types";
|
|
2
3
|
var HOLDPOINT_CURSOR_HOOK_MARKER = "HOLDPOINT_MANAGED=cursor";
|
|
3
4
|
var HOOK_COMMAND = `node .cursor/holdpoint-hook.mjs # ${HOLDPOINT_CURSOR_HOOK_MARKER}`;
|
|
4
|
-
var SECURITY_SCAN_PACKAGES = [
|
|
5
|
-
"@anthropic-ai/mcp-server-brave-search",
|
|
6
|
-
"@anthropic-ai/mcp-server-fetch",
|
|
7
|
-
"@modelcontextprotocol/server-filesystem",
|
|
8
|
-
"@modelcontextprotocol/server-github",
|
|
9
|
-
"@modelcontextprotocol/server-gitlab",
|
|
10
|
-
"@modelcontextprotocol/server-google-maps",
|
|
11
|
-
"@modelcontextprotocol/server-postgres",
|
|
12
|
-
"@modelcontextprotocol/server-slack",
|
|
13
|
-
"@modelcontextprotocol/server-memory",
|
|
14
|
-
"@modelcontextprotocol/server-puppeteer",
|
|
15
|
-
"@modelcontextprotocol/server-sequential-thinking",
|
|
16
|
-
"@modelcontextprotocol/server-everything"
|
|
17
|
-
];
|
|
18
|
-
function buildSecurityScanScript() {
|
|
19
|
-
return `
|
|
20
|
-
// Keep behavior in sync with packages/cli/src/lib/scan.ts.
|
|
21
|
-
const SECURITY_SCAN_VERIFIED = new Set(${JSON.stringify(SECURITY_SCAN_PACKAGES)});
|
|
22
|
-
const SECURITY_SCAN_SEVERITIES = new Set(["high", "critical"]);
|
|
23
|
-
function securityScanReadJson(path) {
|
|
24
|
-
try { return JSON.parse(readFileSync(path, "utf8")); } catch { return undefined; }
|
|
25
|
-
}
|
|
26
|
-
function securityScanStringArray(value) {
|
|
27
|
-
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
28
|
-
}
|
|
29
|
-
function securityScanPackageName(value) {
|
|
30
|
-
const normalized = String(value || "").replace(/\\\\/g, "/");
|
|
31
|
-
const idx = normalized.lastIndexOf("node_modules/");
|
|
32
|
-
if (idx >= 0) {
|
|
33
|
-
const parts = normalized.slice(idx + "node_modules/".length).split("/").filter(Boolean);
|
|
34
|
-
if (parts[0] && parts[0].startsWith("@") && parts[1]) return parts[0] + "/" + parts[1];
|
|
35
|
-
return parts[0];
|
|
36
|
-
}
|
|
37
|
-
if (normalized.startsWith("@")) {
|
|
38
|
-
const parts = normalized.split("/");
|
|
39
|
-
return parts[0] && parts[1] ? parts[0] + "/" + parts[1] : undefined;
|
|
40
|
-
}
|
|
41
|
-
if (!normalized.includes("/") && /^[a-z0-9@._-]+$/i.test(normalized)) return normalized;
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
function securityScanMcpEntries(config) {
|
|
45
|
-
if (!config || typeof config !== "object") return [];
|
|
46
|
-
const servers = config.mcpServers || config.servers;
|
|
47
|
-
if (!servers || typeof servers !== "object" || Array.isArray(servers)) return [];
|
|
48
|
-
return Object.entries(servers).map(([key, raw]) => {
|
|
49
|
-
const server = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
50
|
-
return {
|
|
51
|
-
key,
|
|
52
|
-
name: typeof server.name === "string" ? server.name : undefined,
|
|
53
|
-
command: typeof server.command === "string" ? server.command : undefined,
|
|
54
|
-
args: securityScanStringArray(server.args),
|
|
55
|
-
};
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
function securityScanMcp(root) {
|
|
59
|
-
const results = [];
|
|
60
|
-
for (const file of [join(root, ".mcp.json"), join(root, ".claude/mcp.json")]) {
|
|
61
|
-
if (!existsSync(file)) continue;
|
|
62
|
-
for (const entry of securityScanMcpEntries(securityScanReadJson(file))) {
|
|
63
|
-
const values = [entry.name, entry.command, ...entry.args]
|
|
64
|
-
.filter((value) => typeof value === "string" && value.trim())
|
|
65
|
-
.map((value) => value.trim());
|
|
66
|
-
const packages = values.map(securityScanPackageName).filter(Boolean);
|
|
67
|
-
const verified = [...values, ...packages].some((candidate) => SECURITY_SCAN_VERIFIED.has(candidate));
|
|
68
|
-
results.push({ server: entry.name || entry.key, verified });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return results;
|
|
72
|
-
}
|
|
73
|
-
function securityScanPackageManager(root) {
|
|
74
|
-
if (existsSync(join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
75
|
-
if (existsSync(join(root, "yarn.lock"))) return "yarn";
|
|
76
|
-
if (existsSync(join(root, "package-lock.json")) || existsSync(join(root, "npm-shrinkwrap.json"))) return "npm";
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
function securityScanFirstTitle(value) {
|
|
80
|
-
if (typeof value === "string") return value;
|
|
81
|
-
if (Array.isArray(value)) {
|
|
82
|
-
for (const entry of value) {
|
|
83
|
-
const title = securityScanFirstTitle(entry);
|
|
84
|
-
if (title) return title;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (value && typeof value === "object" && typeof value.title === "string") return value.title;
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
function securityScanAddFinding(findings, seen, name, severity, title) {
|
|
91
|
-
if (!name || typeof severity !== "string") return;
|
|
92
|
-
const normalizedSeverity = severity.toLowerCase();
|
|
93
|
-
if (!SECURITY_SCAN_SEVERITIES.has(normalizedSeverity)) return;
|
|
94
|
-
const normalizedTitle = securityScanFirstTitle(title) || "Security advisory";
|
|
95
|
-
const key = name + "\\0" + normalizedSeverity + "\\0" + normalizedTitle;
|
|
96
|
-
if (seen.has(key)) return;
|
|
97
|
-
seen.add(key);
|
|
98
|
-
findings.push({ name, severity: normalizedSeverity, title: normalizedTitle });
|
|
99
|
-
}
|
|
100
|
-
function securityScanParseAudit(raw) {
|
|
101
|
-
const findings = [];
|
|
102
|
-
const seen = new Set();
|
|
103
|
-
const parseOne = (data) => {
|
|
104
|
-
if (!data || typeof data !== "object") return;
|
|
105
|
-
if (data.vulnerabilities && typeof data.vulnerabilities === "object") {
|
|
106
|
-
for (const [name, vuln] of Object.entries(data.vulnerabilities)) {
|
|
107
|
-
if (!vuln || typeof vuln !== "object") continue;
|
|
108
|
-
securityScanAddFinding(findings, seen, name, vuln.severity, vuln.via || vuln.title);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (data.advisories && typeof data.advisories === "object") {
|
|
112
|
-
for (const advisory of Object.values(data.advisories)) {
|
|
113
|
-
if (!advisory || typeof advisory !== "object") continue;
|
|
114
|
-
securityScanAddFinding(findings, seen, advisory.module_name, advisory.severity, advisory.title);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (data.type === "auditAdvisory" && data.data && data.data.advisory) {
|
|
118
|
-
const advisory = data.data.advisory;
|
|
119
|
-
securityScanAddFinding(findings, seen, advisory.module_name, advisory.severity, advisory.title);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
try { parseOne(JSON.parse(raw)); }
|
|
123
|
-
catch {
|
|
124
|
-
for (const line of String(raw || "").split(/\\r?\\n/)) {
|
|
125
|
-
if (!line.trim()) continue;
|
|
126
|
-
try { parseOne(JSON.parse(line)); } catch {}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return findings.slice(0, 5);
|
|
130
|
-
}
|
|
131
|
-
function securityScanAudit(root) {
|
|
132
|
-
const pm = securityScanPackageManager(root);
|
|
133
|
-
if (!pm) return { pm: null, findings: [] };
|
|
134
|
-
try {
|
|
135
|
-
const stdout = execSync(pm + " audit --json", {
|
|
136
|
-
cwd: root,
|
|
137
|
-
encoding: "utf8",
|
|
138
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
139
|
-
maxBuffer: 1024 * 1024 * 10,
|
|
140
|
-
timeout: 8000,
|
|
141
|
-
});
|
|
142
|
-
return { pm, findings: securityScanParseAudit(stdout) };
|
|
143
|
-
} catch (err) {
|
|
144
|
-
const stdout = err && typeof err.stdout === "string" ? err.stdout : "";
|
|
145
|
-
return { pm, findings: stdout.trim() ? securityScanParseAudit(stdout) : [] };
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function formatSecurityScan(root) {
|
|
149
|
-
const unverified = securityScanMcp(root).filter((entry) => !entry.verified);
|
|
150
|
-
const audit = securityScanAudit(root);
|
|
151
|
-
if (unverified.length === 0 && audit.findings.length === 0) return null;
|
|
152
|
-
const lines = ["\u26A0 Holdpoint Security Scan", ""];
|
|
153
|
-
if (unverified.length > 0) {
|
|
154
|
-
lines.push("MCP servers \u2014 unverified:");
|
|
155
|
-
for (const entry of unverified) lines.push(" \u2022 " + entry.server + " (source unknown \u2014 review before trusting)");
|
|
156
|
-
lines.push("");
|
|
157
|
-
}
|
|
158
|
-
if (audit.findings.length > 0) {
|
|
159
|
-
lines.push((audit.pm || "npm") + " audit \u2014 high/critical:");
|
|
160
|
-
for (const dep of audit.findings) lines.push(" \u2022 " + dep.name + " \xB7 " + dep.title + " (" + dep.severity + ")");
|
|
161
|
-
lines.push("");
|
|
162
|
-
}
|
|
163
|
-
lines.push("Review these before allowing the agent to install dependencies or invoke tools.");
|
|
164
|
-
return lines.join("\\n");
|
|
165
|
-
}
|
|
166
|
-
`;
|
|
167
|
-
}
|
|
168
5
|
function hook(options = {}) {
|
|
169
6
|
return {
|
|
170
7
|
command: HOOK_COMMAND,
|
|
@@ -289,10 +126,12 @@ function gatherHookContext(repoRoot, hook) {
|
|
|
289
126
|
const includeHooks = hook === "session_start" ? ["session_start", "message_submit"] : [hook];
|
|
290
127
|
|
|
291
128
|
if (hook === "session_start") {
|
|
292
|
-
const scan = formatSecurityScan(repoRoot);
|
|
293
|
-
if (scan) parts.push(scan);
|
|
294
129
|
const files = Array.isArray(cfg.session_context_files) ? cfg.session_context_files : [];
|
|
295
130
|
for (const f of files) { const c = readFileContext(repoRoot, f); if (c) parts.push(c); }
|
|
131
|
+
if (cfg.security_scan !== false) {
|
|
132
|
+
const scan = formatSecurityScan(repoRoot);
|
|
133
|
+
if (scan) parts.push(scan);
|
|
134
|
+
}
|
|
296
135
|
}
|
|
297
136
|
for (const c of checks) {
|
|
298
137
|
const on = typeof c.on === "string" ? c.on : "before_done";
|
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\nconst SECURITY_SCAN_PACKAGES = [\n \"@anthropic-ai/mcp-server-brave-search\",\n \"@anthropic-ai/mcp-server-fetch\",\n \"@modelcontextprotocol/server-filesystem\",\n \"@modelcontextprotocol/server-github\",\n \"@modelcontextprotocol/server-gitlab\",\n \"@modelcontextprotocol/server-google-maps\",\n \"@modelcontextprotocol/server-postgres\",\n \"@modelcontextprotocol/server-slack\",\n \"@modelcontextprotocol/server-memory\",\n \"@modelcontextprotocol/server-puppeteer\",\n \"@modelcontextprotocol/server-sequential-thinking\",\n \"@modelcontextprotocol/server-everything\",\n];\n\nfunction buildSecurityScanScript(): string {\n return `\n// Keep behavior in sync with packages/cli/src/lib/scan.ts.\nconst SECURITY_SCAN_VERIFIED = new Set(${JSON.stringify(SECURITY_SCAN_PACKAGES)});\nconst SECURITY_SCAN_SEVERITIES = new Set([\"high\", \"critical\"]);\nfunction securityScanReadJson(path) {\n try { return JSON.parse(readFileSync(path, \"utf8\")); } catch { return undefined; }\n}\nfunction securityScanStringArray(value) {\n return Array.isArray(value) ? value.filter((entry) => typeof entry === \"string\") : [];\n}\nfunction securityScanPackageName(value) {\n const normalized = String(value || \"\").replace(/\\\\\\\\/g, \"/\");\n const idx = normalized.lastIndexOf(\"node_modules/\");\n if (idx >= 0) {\n const parts = normalized.slice(idx + \"node_modules/\".length).split(\"/\").filter(Boolean);\n if (parts[0] && parts[0].startsWith(\"@\") && parts[1]) return parts[0] + \"/\" + parts[1];\n return parts[0];\n }\n if (normalized.startsWith(\"@\")) {\n const parts = normalized.split(\"/\");\n return parts[0] && parts[1] ? parts[0] + \"/\" + parts[1] : undefined;\n }\n if (!normalized.includes(\"/\") && /^[a-z0-9@._-]+$/i.test(normalized)) return normalized;\n return undefined;\n}\nfunction securityScanMcpEntries(config) {\n if (!config || typeof config !== \"object\") return [];\n const servers = config.mcpServers || config.servers;\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) return [];\n return Object.entries(servers).map(([key, raw]) => {\n const server = raw && typeof raw === \"object\" && !Array.isArray(raw) ? raw : {};\n return {\n key,\n name: typeof server.name === \"string\" ? server.name : undefined,\n command: typeof server.command === \"string\" ? server.command : undefined,\n args: securityScanStringArray(server.args),\n };\n });\n}\nfunction securityScanMcp(root) {\n const results = [];\n for (const file of [join(root, \".mcp.json\"), join(root, \".claude/mcp.json\")]) {\n if (!existsSync(file)) continue;\n for (const entry of securityScanMcpEntries(securityScanReadJson(file))) {\n const values = [entry.name, entry.command, ...entry.args]\n .filter((value) => typeof value === \"string\" && value.trim())\n .map((value) => value.trim());\n const packages = values.map(securityScanPackageName).filter(Boolean);\n const verified = [...values, ...packages].some((candidate) => SECURITY_SCAN_VERIFIED.has(candidate));\n results.push({ server: entry.name || entry.key, verified });\n }\n }\n return results;\n}\nfunction securityScanPackageManager(root) {\n if (existsSync(join(root, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(root, \"yarn.lock\"))) return \"yarn\";\n if (existsSync(join(root, \"package-lock.json\")) || existsSync(join(root, \"npm-shrinkwrap.json\"))) return \"npm\";\n return null;\n}\nfunction securityScanFirstTitle(value) {\n if (typeof value === \"string\") return value;\n if (Array.isArray(value)) {\n for (const entry of value) {\n const title = securityScanFirstTitle(entry);\n if (title) return title;\n }\n }\n if (value && typeof value === \"object\" && typeof value.title === \"string\") return value.title;\n return undefined;\n}\nfunction securityScanAddFinding(findings, seen, name, severity, title) {\n if (!name || typeof severity !== \"string\") return;\n const normalizedSeverity = severity.toLowerCase();\n if (!SECURITY_SCAN_SEVERITIES.has(normalizedSeverity)) return;\n const normalizedTitle = securityScanFirstTitle(title) || \"Security advisory\";\n const key = name + \"\\\\0\" + normalizedSeverity + \"\\\\0\" + normalizedTitle;\n if (seen.has(key)) return;\n seen.add(key);\n findings.push({ name, severity: normalizedSeverity, title: normalizedTitle });\n}\nfunction securityScanParseAudit(raw) {\n const findings = [];\n const seen = new Set();\n const parseOne = (data) => {\n if (!data || typeof data !== \"object\") return;\n if (data.vulnerabilities && typeof data.vulnerabilities === \"object\") {\n for (const [name, vuln] of Object.entries(data.vulnerabilities)) {\n if (!vuln || typeof vuln !== \"object\") continue;\n securityScanAddFinding(findings, seen, name, vuln.severity, vuln.via || vuln.title);\n }\n }\n if (data.advisories && typeof data.advisories === \"object\") {\n for (const advisory of Object.values(data.advisories)) {\n if (!advisory || typeof advisory !== \"object\") continue;\n securityScanAddFinding(findings, seen, advisory.module_name, advisory.severity, advisory.title);\n }\n }\n if (data.type === \"auditAdvisory\" && data.data && data.data.advisory) {\n const advisory = data.data.advisory;\n securityScanAddFinding(findings, seen, advisory.module_name, advisory.severity, advisory.title);\n }\n };\n try { parseOne(JSON.parse(raw)); }\n catch {\n for (const line of String(raw || \"\").split(/\\\\r?\\\\n/)) {\n if (!line.trim()) continue;\n try { parseOne(JSON.parse(line)); } catch {}\n }\n }\n return findings.slice(0, 5);\n}\nfunction securityScanAudit(root) {\n const pm = securityScanPackageManager(root);\n if (!pm) return { pm: null, findings: [] };\n try {\n const stdout = execSync(pm + \" audit --json\", {\n cwd: root,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n maxBuffer: 1024 * 1024 * 10,\n timeout: 8000,\n });\n return { pm, findings: securityScanParseAudit(stdout) };\n } catch (err) {\n const stdout = err && typeof err.stdout === \"string\" ? err.stdout : \"\";\n return { pm, findings: stdout.trim() ? securityScanParseAudit(stdout) : [] };\n }\n}\nfunction formatSecurityScan(root) {\n const unverified = securityScanMcp(root).filter((entry) => !entry.verified);\n const audit = securityScanAudit(root);\n if (unverified.length === 0 && audit.findings.length === 0) return null;\n const lines = [\"⚠ Holdpoint Security Scan\", \"\"];\n if (unverified.length > 0) {\n lines.push(\"MCP servers — unverified:\");\n for (const entry of unverified) lines.push(\" • \" + entry.server + \" (source unknown — review before trusting)\");\n lines.push(\"\");\n }\n if (audit.findings.length > 0) {\n lines.push((audit.pm || \"npm\") + \" audit — high/critical:\");\n for (const dep of audit.findings) lines.push(\" • \" + dep.name + \" · \" + dep.title + \" (\" + dep.severity + \")\");\n lines.push(\"\");\n }\n lines.push(\"Review these before allowing the agent to install dependencies or invoke tools.\");\n return lines.join(\"\\\\n\");\n}\n`;\n}\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 sessionStart: [hook({ timeout: 30 })],\n };\n\n void config;\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${buildSecurityScanScript()}\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 scan = formatSecurityScan(repoRoot);\n if (scan) parts.push(scan);\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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/engine.ts","../src/live-adapter.ts","../src/manifest.ts"],"sourcesContent":["import { buildSecurityScanScript } from \"@holdpoint/types\";\nimport 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 sessionStart: [hook({ timeout: 30 })],\n };\n\n void config;\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${buildSecurityScanScript()}\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 if (cfg.security_scan !== false) {\n const scan = formatSecurityScan(repoRoot);\n if (scan) parts.push(scan);\n }\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":";AAAA,SAAS,+BAA+B;AAGjC,IAAM,+BAA+B;AAE5C,IAAM,eAAe,qCAAqC,4BAA4B;AAetF,SAAS,KAAK,UAAuC,CAAC,GAAe;AACnE,SAAO;AAAA,IACL,SAAS;AAAA,IACT,GAAG;AAAA,EACL;AACF;AAUO,SAAS,eAAe,QAAiC;AAC9D,QAAM,QAAkC;AAAA,IACtC,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IAC1C,YAAY,CAAC,KAAK,EAAE,SAAS,IAAI,SAAS,oCAAoC,CAAC,CAAC;AAAA,IAChF,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IACnC,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IAC1C,sBAAsB,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IAC5C,qBAAqB,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IAC3C,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IAC1C,mBAAmB,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IACzC,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IACtC,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IACrC,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IACrC,cAAc,CAAC,KAAK,EAAE,SAAS,KAAK,YAAY,EAAE,CAAC,CAAC;AAAA,IACpD,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IAClC,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,IAC1C,MAAM,CAAC,KAAK,EAAE,SAAS,KAAK,YAAY,EAAE,CAAC,CAAC;AAAA,IAC5C,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,EACtC;AAEA,OAAK;AACL,SAAO,KAAK,UAAU,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC,IAAI;AAC1D;AAYO,SAAS,mBAA2B;AACzC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKP,wBAAwB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyO3B;AAMO,SAAS,qBAA6B;AAC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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.
|
|
3
|
+
"version": "0.1.0-alpha.15",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
"LICENSE"
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@holdpoint/
|
|
50
|
+
"@holdpoint/live-protocol": "0.1.0-alpha.4",
|
|
51
51
|
"@holdpoint/sdk": "0.1.0-alpha.4",
|
|
52
|
-
"@holdpoint/
|
|
52
|
+
"@holdpoint/types": "0.1.0-alpha.11"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@types/node": "^25.9.
|
|
55
|
+
"@types/node": "^25.9.2",
|
|
56
56
|
"tsup": "^8.3.5",
|
|
57
57
|
"typescript": "^6.0.3",
|
|
58
58
|
"vitest": "^4.1.8"
|