@holdpoint/types 0.1.0-alpha.1 → 0.1.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,40 @@
1
- /** Lifecycle hook that fires the check. Currently only "before_done". */
2
- type HookEvent = "before_done";
1
+ /** npm package names recognised as first-party / well-known MCP servers. */
2
+ declare const SECURITY_SCAN_PACKAGES: string[];
3
+ /**
4
+ * Build the injected security-scan script body. The returned string defines
5
+ * `formatSecurityScan(root)` (plus its helpers) which returns a banner string or
6
+ * `null` when nothing noteworthy is found.
7
+ */
8
+ declare function buildSecurityScanScript(): string;
9
+
10
+ /**
11
+ * Lifecycle hook a check/action attaches to. `before_done` is the completion
12
+ * gate (default). The others let checks seed context or run earlier in the loop:
13
+ * - `session_start` — a fresh session or resume
14
+ * - `message_submit` — every user prompt
15
+ * - `before_tool` — immediately before a tool call (can block it)
16
+ * - `after_tool` — immediately after a tool call (advisory)
17
+ * - `session_end` — the session is ending (advisory)
18
+ * - `before_done` — the completion gate; blocks finishing on failure
19
+ *
20
+ * Engine support varies; engines honor what they can and skip the rest.
21
+ */
22
+ type HookEvent = "session_start" | "message_submit" | "before_tool" | "after_tool" | "session_end" | "before_done";
23
+ /** Ordered list of all hook events, earliest-to-latest in the agent loop. */
24
+ declare const HOOK_EVENTS: HookEvent[];
25
+ /**
26
+ * Context-seeding behavior: inject text, file contents, and/or the current
27
+ * datetime as agent context at the check's hook point. An alternative to
28
+ * `cmd` (command) and `prompt` (agent instruction).
29
+ */
30
+ interface InjectSpec {
31
+ /** Literal text injected as agent context. */
32
+ text?: string;
33
+ /** Repo-relative files whose contents are injected as context. */
34
+ files?: string[];
35
+ /** Inject the current date and time. */
36
+ datetime?: boolean;
37
+ }
3
38
  /** Named file-scope filter. Custom regexes are plain strings not in this union. */
4
39
  type WhenScope = "frontend" | "backend" | "socket" | "visual" | "python" | "go" | "rust" | "java" | "ruby" | "database" | "prisma" | "testing" | "infra" | "ci" | "docs" | "structural";
5
40
  type ConditionOperator = "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
@@ -26,9 +61,16 @@ interface CheckDef {
26
61
  cmd?: string;
27
62
  /** Structured prompt/instruction the agent must read and act on before finishing */
28
63
  prompt?: string;
64
+ /** Context-seeding behavior — inject text/files/datetime at the hook point */
65
+ inject?: InjectSpec;
29
66
  /** Reference to a ConditionDef id */
30
67
  conditionId?: string;
31
68
  }
69
+ /** The effective hook of a check (`before_done` when unset). */
70
+ declare function checkHook(check: CheckDef): HookEvent;
71
+ /** A check's behavior kind, derived from which behavior field is set. */
72
+ type CheckBehavior = "cmd" | "prompt" | "inject";
73
+ declare function checkBehavior(check: CheckDef): CheckBehavior;
32
74
  interface HoldpointContext {
33
75
  guides: Record<string, string>;
34
76
  }
@@ -50,6 +92,34 @@ interface HoldpointConfig {
50
92
  * Paths are repo-root-relative. Useful for injecting MASTER_PROMPT.md, AGENT_CONTEXT.md, etc.
51
93
  */
52
94
  session_context_files?: string[];
95
+ /**
96
+ * Inject the current date and time into every prompt submission as `additionalContext`.
97
+ * Helps models avoid knowledge-cutoff confusion. Defaults to `true`; set to `false` to opt out.
98
+ */
99
+ inject_datetime?: boolean;
100
+ /**
101
+ * Run the session-start security scan (unverified MCP servers + `audit`
102
+ * high/critical advisories) and inject its banner as session context.
103
+ * Defaults to `true`; set to `false` to skip the scan entirely — including the
104
+ * dependency-audit subprocess, which otherwise runs on session start.
105
+ */
106
+ security_scan?: boolean;
107
+ /**
108
+ * Per-engine overrides. Values here win over engine defaults — useful when the
109
+ * project IS the holdpoint repo and should invoke the local CLI instead of npx.
110
+ */
111
+ engines?: {
112
+ claude?: {
113
+ stop_command?: string;
114
+ live_command?: string;
115
+ };
116
+ codex?: {
117
+ stop_command?: string;
118
+ };
119
+ copilot?: {
120
+ check_command?: string;
121
+ };
122
+ };
53
123
  }
54
124
  type CheckStatus = "pass" | "fail" | "skip" | "pending";
55
125
  interface CheckResult {
@@ -70,21 +140,41 @@ interface ValidationResult {
70
140
  valid: boolean;
71
141
  errors: ValidationError[];
72
142
  }
73
- type AgentType = "copilot" | "claude" | "cursor" | "unknown";
74
- type StackType = "typescript" | "python" | "go" | "nextjs" | "fullstack" | "unknown";
75
- type NodeKind = "trigger" | "filter" | "task" | "prompt" | "condition";
76
- interface CanvasNodeData {
77
- [key: string]: unknown;
78
- kind: NodeKind;
143
+ type AgentType = "copilot" | "claude" | "cursor" | "codex" | "unknown";
144
+ /** Result of a single check within a run (cmd or prompt). */
145
+ interface CheckRunResult {
146
+ id: string;
79
147
  label: string;
80
- /** Lifecycle hook on the trigger node */
81
- on?: HookEvent;
82
- /** File filter on the trigger node */
83
- when?: string;
84
- cmd?: string;
85
- prompt?: string;
86
- condition?: ConditionDef;
87
- conditionId?: string;
148
+ /** "cmd" for automated checks, "prompt" for manual agent checks */
149
+ kind: "cmd" | "prompt";
150
+ /** "pass" | "fail" | "skip" for cmd checks; "shown" for prompt checks displayed to agent */
151
+ status: "pass" | "fail" | "skip" | "shown";
152
+ output?: string;
153
+ exitCode?: number;
154
+ skipReason?: string;
155
+ }
156
+ /** A single holdpoint check run recorded after `holdpoint check` completes. */
157
+ interface CheckRun {
158
+ /** Full HEAD commit SHA, or null if not in a git repo / no commits. */
159
+ sha: string | null;
160
+ /** First 8 chars of sha, or null. */
161
+ shortSha: string | null;
162
+ /** ISO 8601 timestamp. */
163
+ timestamp: string;
164
+ /** Changed files used for trigger matching. Empty array means "all checks ran". */
165
+ files: string[];
166
+ results: CheckRunResult[];
167
+ summary: {
168
+ passed: number;
169
+ failed: number;
170
+ skipped: number;
171
+ shown: number;
172
+ };
173
+ }
174
+ /** Contents of `.holdpoint/check-reports.json` — list of recent check runs. */
175
+ interface CheckReports {
176
+ /** Runs ordered newest-first. Capped at 50 entries. */
177
+ runs: CheckRun[];
88
178
  }
89
179
 
90
- export type { AgentType, CanvasNodeData, CheckDef, CheckResult, CheckStatus, ConditionDef, ConditionOperator, HoldpointConfig, HoldpointContext, HookEvent, NodeKind, StackType, ValidationError, ValidationResult, WhenScope };
180
+ export { type AgentType, type CheckBehavior, type CheckDef, type CheckReports, type CheckResult, type CheckRun, type CheckRunResult, type CheckStatus, type ConditionDef, type ConditionOperator, HOOK_EVENTS, type HoldpointConfig, type HoldpointContext, type HookEvent, type InjectSpec, SECURITY_SCAN_PACKAGES, type ValidationError, type ValidationResult, type WhenScope, buildSecurityScanScript, checkBehavior, checkHook };
package/dist/index.js CHANGED
@@ -1 +1,311 @@
1
+ // src/scan.ts
2
+ var SECURITY_SCAN_PACKAGES = [
3
+ "@anthropic-ai/mcp-server-brave-search",
4
+ "@anthropic-ai/mcp-server-fetch",
5
+ "@modelcontextprotocol/server-filesystem",
6
+ "@modelcontextprotocol/server-github",
7
+ "@modelcontextprotocol/server-gitlab",
8
+ "@modelcontextprotocol/server-google-maps",
9
+ "@modelcontextprotocol/server-postgres",
10
+ "@modelcontextprotocol/server-slack",
11
+ "@modelcontextprotocol/server-memory",
12
+ "@modelcontextprotocol/server-puppeteer",
13
+ "@modelcontextprotocol/server-sequential-thinking",
14
+ "@modelcontextprotocol/server-everything"
15
+ ];
16
+ function buildSecurityScanScript() {
17
+ return `
18
+ // Generated from @holdpoint/types buildSecurityScanScript \u2014 do not edit by hand.
19
+ const SECURITY_SCAN_VERIFIED = new Set(${JSON.stringify(SECURITY_SCAN_PACKAGES)});
20
+ const SECURITY_SCAN_SEVERITIES = new Set(["high", "critical"]);
21
+ function securityScanReadJson(path) {
22
+ try { return JSON.parse(readFileSync(path, "utf8")); } catch { return undefined; }
23
+ }
24
+ function securityScanStringArray(value) {
25
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
26
+ }
27
+ function securityScanPackageName(value) {
28
+ const normalized = String(value || "").replace(/\\\\/g, "/");
29
+ const idx = normalized.lastIndexOf("node_modules/");
30
+ if (idx >= 0) {
31
+ const parts = normalized.slice(idx + "node_modules/".length).split("/").filter(Boolean);
32
+ if (parts[0] && parts[0].startsWith("@") && parts[1]) return parts[0] + "/" + parts[1];
33
+ return parts[0];
34
+ }
35
+ if (normalized.startsWith("@")) {
36
+ const parts = normalized.split("/");
37
+ return parts[0] && parts[1] ? parts[0] + "/" + parts[1] : undefined;
38
+ }
39
+ if (!normalized.includes("/") && /^[a-z0-9@._-]+$/i.test(normalized)) return normalized;
40
+ return undefined;
41
+ }
42
+ function securityScanMcpEntries(config) {
43
+ if (!config || typeof config !== "object") return [];
44
+ const servers = config.mcpServers || config.servers;
45
+ if (!servers || typeof servers !== "object" || Array.isArray(servers)) return [];
46
+ return Object.entries(servers).map(([key, raw]) => {
47
+ const server = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
48
+ return {
49
+ key,
50
+ name: typeof server.name === "string" ? server.name : undefined,
51
+ command: typeof server.command === "string" ? server.command : undefined,
52
+ args: securityScanStringArray(server.args),
53
+ };
54
+ });
55
+ }
56
+ // Strip a leading version/dist-tag from a package spec, leaving the bare name.
57
+ // "@scope/name@1.2.3" -> "@scope/name"; "pkg@latest" -> "pkg". Leaves paths/urls alone.
58
+ function securityScanStripVersion(spec) {
59
+ const s = String(spec || "");
60
+ if (!s) return s;
61
+ if (s.startsWith("@")) {
62
+ // Scoped: keep through the "@scope/name" segment, then cut at a later "@".
63
+ const slash = s.indexOf("/");
64
+ if (slash < 0) return s;
65
+ const rest = s.slice(slash + 1);
66
+ const at = rest.indexOf("@");
67
+ return at >= 0 ? s.slice(0, slash + 1 + at) : s;
68
+ }
69
+ const at = s.indexOf("@");
70
+ return at > 0 ? s.slice(0, at) : s;
71
+ }
72
+ // Pick the first non-flag argument from args, skipping anything starting with "-".
73
+ function securityScanFirstNonFlag(args, startIndex) {
74
+ for (let i = startIndex || 0; i < args.length; i++) {
75
+ const a = args[i];
76
+ if (typeof a === "string" && a && !a.startsWith("-")) return a;
77
+ }
78
+ return undefined;
79
+ }
80
+ // Derive the single package that will actually execute, from command + args ONLY.
81
+ // Returns { pkg, checkable } where checkable indicates the pkg is an npm name we
82
+ // can look up in the verified registry. NEVER consults entry.name or unrelated args.
83
+ function securityScanExecutedPackage(entry) {
84
+ const command = entry.command;
85
+ const args = entry.args || [];
86
+ if (!command) return { pkg: undefined, checkable: false };
87
+ const base = securityScanPackageName(command);
88
+ if (command === "npx" || command === "bunx") {
89
+ const target = securityScanFirstNonFlag(args, 0);
90
+ if (!target) return { pkg: undefined, checkable: false };
91
+ return { pkg: securityScanStripVersion(target), checkable: true };
92
+ }
93
+ if ((command === "pnpm" || command === "yarn") && args[0] === "dlx") {
94
+ const target = securityScanFirstNonFlag(args, 1);
95
+ if (!target) return { pkg: undefined, checkable: false };
96
+ return { pkg: securityScanStripVersion(target), checkable: true };
97
+ }
98
+ if (command === "uvx") {
99
+ // Python/uv package \u2014 not resolvable to an npm name.
100
+ const target = securityScanFirstNonFlag(args, 0);
101
+ return { pkg: target ? securityScanStripVersion(target) : undefined, checkable: false };
102
+ }
103
+ if (command === "node" || base === "node") {
104
+ const script = securityScanFirstNonFlag(args, 0);
105
+ const pkg = script ? securityScanPackageName(script) : undefined;
106
+ return pkg ? { pkg, checkable: true } : { pkg: undefined, checkable: false };
107
+ }
108
+ // Command is itself a path / binary.
109
+ return base ? { pkg: base, checkable: true } : { pkg: undefined, checkable: false };
110
+ }
111
+ function securityScanMcp(root) {
112
+ const results = [];
113
+ for (const file of [join(root, ".mcp.json"), join(root, ".claude/mcp.json")]) {
114
+ if (!existsSync(file)) continue;
115
+ for (const entry of securityScanMcpEntries(securityScanReadJson(file))) {
116
+ const executed = securityScanExecutedPackage(entry);
117
+ const verified = executed.checkable && executed.pkg ? SECURITY_SCAN_VERIFIED.has(executed.pkg) : false;
118
+ results.push({ server: entry.name || entry.key, verified, checkable: executed.checkable });
119
+ }
120
+ }
121
+ return results;
122
+ }
123
+ // Returns { pm, cmd } where pm is the display label and cmd is the full audit
124
+ // command string, or null when no lockfile is present.
125
+ function securityScanPackageManager(root) {
126
+ if (existsSync(join(root, "pnpm-lock.yaml"))) return { pm: "pnpm", cmd: "pnpm audit --json" };
127
+ if (existsSync(join(root, "yarn.lock"))) {
128
+ // Yarn Berry (v2+) has no "yarn audit --json"; it uses "yarn npm audit".
129
+ let berry = existsSync(join(root, ".yarnrc.yml"));
130
+ if (!berry) {
131
+ const pkg = securityScanReadJson(join(root, "package.json"));
132
+ const pm = pkg && typeof pkg.packageManager === "string" ? pkg.packageManager : "";
133
+ const m = /^yarn@(\\d+)/.exec(pm);
134
+ if (m && Number(m[1]) >= 2) berry = true;
135
+ }
136
+ return berry
137
+ ? { pm: "yarn", cmd: "yarn npm audit --all --json" }
138
+ : { pm: "yarn", cmd: "yarn audit --json" };
139
+ }
140
+ if (existsSync(join(root, "package-lock.json")) || existsSync(join(root, "npm-shrinkwrap.json"))) {
141
+ return { pm: "npm", cmd: "npm audit --json" };
142
+ }
143
+ return null;
144
+ }
145
+ function securityScanFirstTitle(value) {
146
+ if (typeof value === "string") return value;
147
+ if (Array.isArray(value)) {
148
+ for (const entry of value) {
149
+ const title = securityScanFirstTitle(entry);
150
+ if (title) return title;
151
+ }
152
+ }
153
+ if (value && typeof value === "object" && typeof value.title === "string") return value.title;
154
+ return undefined;
155
+ }
156
+ // #7: For npm v7+ transitive vulns, "via" is an array of STRINGS naming parent
157
+ // packages \u2014 those must never become the advisory title. Resolve a "via" value to
158
+ // a safe title: object entries with a string .title are real advisories; an array
159
+ // of plain strings falls back to "Vulnerable dependency (via <parent>)" so a raw
160
+ // package name never lands in the title slot.
161
+ function securityScanTitleFromVia(via) {
162
+ const objectTitle = securityScanFirstTitle(
163
+ Array.isArray(via)
164
+ ? via.filter((entry) => entry && typeof entry === "object")
165
+ : via && typeof via === "object" ? via : undefined,
166
+ );
167
+ if (objectTitle) return objectTitle;
168
+ const parents = securityScanStringArray(Array.isArray(via) ? via : [via]);
169
+ if (parents.length > 0) return "Vulnerable dependency (via " + parents[0] + ")";
170
+ return undefined;
171
+ }
172
+ function securityScanAddFinding(findings, seen, name, severity, title) {
173
+ if (!name || typeof severity !== "string") return;
174
+ const normalizedSeverity = severity.toLowerCase();
175
+ if (!SECURITY_SCAN_SEVERITIES.has(normalizedSeverity)) return;
176
+ const normalizedTitle = securityScanFirstTitle(title) || "Security advisory";
177
+ const key = name + "\\0" + normalizedSeverity + "\\0" + normalizedTitle;
178
+ if (seen.has(key)) return;
179
+ seen.add(key);
180
+ findings.push({ name, severity: normalizedSeverity, title: normalizedTitle });
181
+ }
182
+ function securityScanParseAudit(raw) {
183
+ const findings = [];
184
+ const seen = new Set();
185
+ const parseOne = (data) => {
186
+ if (!data || typeof data !== "object") return;
187
+ if (data.vulnerabilities && typeof data.vulnerabilities === "object") {
188
+ for (const [name, vuln] of Object.entries(data.vulnerabilities)) {
189
+ if (!vuln || typeof vuln !== "object") continue;
190
+ // #7: resolve "via" safely so string-parent names never become the title.
191
+ const title = vuln.via !== undefined ? securityScanTitleFromVia(vuln.via) : vuln.title;
192
+ securityScanAddFinding(findings, seen, name, vuln.severity, title);
193
+ }
194
+ }
195
+ if (data.advisories && typeof data.advisories === "object") {
196
+ for (const advisory of Object.values(data.advisories)) {
197
+ if (!advisory || typeof advisory !== "object") continue;
198
+ securityScanAddFinding(findings, seen, advisory.module_name, advisory.severity, advisory.title);
199
+ }
200
+ }
201
+ if (data.type === "auditAdvisory" && data.data && data.data.advisory) {
202
+ const advisory = data.data.advisory;
203
+ securityScanAddFinding(findings, seen, advisory.module_name, advisory.severity, advisory.title);
204
+ }
205
+ };
206
+ try { parseOne(JSON.parse(raw)); }
207
+ catch {
208
+ for (const line of String(raw || "").split(/\\r?\\n/)) {
209
+ if (!line.trim()) continue;
210
+ try { parseOne(JSON.parse(line)); } catch {}
211
+ }
212
+ }
213
+ // #4: sort by severity (critical outranks high) BEFORE capping so criticals are
214
+ // never dropped by insertion order. Comparator is stable for equal severities.
215
+ const rank = (s) => (s === "critical" ? 0 : 1);
216
+ findings.sort((a, b) => rank(a.severity) - rank(b.severity));
217
+ return findings.slice(0, 5);
218
+ }
219
+ function securityScanAudit(root) {
220
+ const manager = securityScanPackageManager(root);
221
+ if (!manager) return { pm: null, findings: [] };
222
+ const pm = manager.pm;
223
+ try {
224
+ const stdout = execSync(manager.cmd, {
225
+ cwd: root,
226
+ encoding: "utf8",
227
+ stdio: ["ignore", "pipe", "pipe"],
228
+ maxBuffer: 1024 * 1024 * 10,
229
+ timeout: 8000,
230
+ });
231
+ return { pm, findings: securityScanParseAudit(stdout) };
232
+ } catch (err) {
233
+ const stdout = err && typeof err.stdout === "string" ? err.stdout : "";
234
+ if (stdout.trim()) {
235
+ // #6: non-zero exit with JSON on stdout is the normal "vulns found" path.
236
+ return { pm, findings: securityScanParseAudit(stdout) };
237
+ }
238
+ // #6: timeout / error with no usable stdout \u2014 return a sentinel so the banner
239
+ // can distinguish "audit didn't complete" from "clean repo".
240
+ return { pm, findings: [], error: "timeout" };
241
+ }
242
+ }
243
+ function formatSecurityScan(root) {
244
+ const mcp = securityScanMcp(root);
245
+ // #1/#11: npm-checkable servers not in the registry are genuinely unreviewed;
246
+ // non-checkable (uvx/python/unresolvable) servers get a calmer, separate group.
247
+ const unverified = mcp.filter((entry) => !entry.verified && entry.checkable !== false);
248
+ const nonCheckable = mcp.filter((entry) => entry.checkable === false);
249
+ const audit = securityScanAudit(root);
250
+ const auditDidNotComplete = audit.error && audit.findings.length === 0;
251
+ if (
252
+ unverified.length === 0 &&
253
+ nonCheckable.length === 0 &&
254
+ audit.findings.length === 0 &&
255
+ !auditDidNotComplete
256
+ ) {
257
+ return null;
258
+ }
259
+ const lines = ["\u26A0 Holdpoint Security Scan", ""];
260
+ if (unverified.length > 0) {
261
+ lines.push("MCP servers \u2014 unverified:");
262
+ for (const entry of unverified) lines.push(" \u2022 " + entry.server + " (source unknown \u2014 review before trusting)");
263
+ lines.push("");
264
+ }
265
+ if (nonCheckable.length > 0) {
266
+ lines.push("MCP servers \u2014 source not checkable (non-npm):");
267
+ for (const entry of nonCheckable) lines.push(" \u2022 " + entry.server + " (can't verify automatically \u2014 review the source)");
268
+ lines.push("");
269
+ }
270
+ if (audit.findings.length > 0) {
271
+ lines.push((audit.pm || "npm") + " audit \u2014 high/critical:");
272
+ for (const dep of audit.findings) lines.push(" \u2022 " + dep.name + " \xB7 " + dep.title + " (" + dep.severity + ")");
273
+ lines.push("");
274
+ } else if (auditDidNotComplete) {
275
+ // #6: surface an incomplete audit so a timeout no longer looks like a clean repo.
276
+ const pm = audit.pm || "npm";
277
+ lines.push((audit.pm || "npm") + " audit \u2014 high/critical:");
278
+ lines.push(" \u2022 dependency audit did not complete (timeout) \u2014 run \`" + pm + " audit\` manually");
279
+ lines.push("");
280
+ }
281
+ lines.push("Review these before allowing the agent to install dependencies or invoke tools.");
282
+ return lines.join("\\n");
283
+ }
284
+ `;
285
+ }
286
+
287
+ // src/index.ts
288
+ var HOOK_EVENTS = [
289
+ "session_start",
290
+ "message_submit",
291
+ "before_tool",
292
+ "after_tool",
293
+ "session_end",
294
+ "before_done"
295
+ ];
296
+ function checkHook(check) {
297
+ return check.on ?? "before_done";
298
+ }
299
+ function checkBehavior(check) {
300
+ if (check.cmd !== void 0) return "cmd";
301
+ if (check.inject !== void 0) return "inject";
302
+ return "prompt";
303
+ }
304
+ export {
305
+ HOOK_EVENTS,
306
+ SECURITY_SCAN_PACKAGES,
307
+ buildSecurityScanScript,
308
+ checkBehavior,
309
+ checkHook
310
+ };
1
311
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/scan.ts","../src/index.ts"],"sourcesContent":["// ─── Session security scan (shared source) ───────────────────────────────────\n//\n// This is the SINGLE source of truth for the session-start security scan that\n// every engine injects as a `node -e` hook. Engines import `SECURITY_SCAN_PACKAGES`\n// and `buildSecurityScanScript()` from here and embed the returned string into\n// their generated context script. The CLI's `holdpoint update` regenerates the\n// per-engine hook files (.codex/.cursor/.github) from the engine output, so a fix\n// here propagates everywhere after a build + update.\n//\n// The returned string is plain ES that runs synchronously inside the hook process.\n// It assumes `readFileSync`, `existsSync`, `join`, and `execSync` are already in\n// scope (the engine context script imports them before embedding this block).\n\n/** npm package names recognised as first-party / well-known MCP servers. */\nexport const 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\n/**\n * Build the injected security-scan script body. The returned string defines\n * `formatSecurityScan(root)` (plus its helpers) which returns a banner string or\n * `null` when nothing noteworthy is found.\n */\nexport function buildSecurityScanScript(): string {\n return `\n// Generated from @holdpoint/types buildSecurityScanScript — do not edit by hand.\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}\n// Strip a leading version/dist-tag from a package spec, leaving the bare name.\n// \"@scope/name@1.2.3\" -> \"@scope/name\"; \"pkg@latest\" -> \"pkg\". Leaves paths/urls alone.\nfunction securityScanStripVersion(spec) {\n const s = String(spec || \"\");\n if (!s) return s;\n if (s.startsWith(\"@\")) {\n // Scoped: keep through the \"@scope/name\" segment, then cut at a later \"@\".\n const slash = s.indexOf(\"/\");\n if (slash < 0) return s;\n const rest = s.slice(slash + 1);\n const at = rest.indexOf(\"@\");\n return at >= 0 ? s.slice(0, slash + 1 + at) : s;\n }\n const at = s.indexOf(\"@\");\n return at > 0 ? s.slice(0, at) : s;\n}\n// Pick the first non-flag argument from args, skipping anything starting with \"-\".\nfunction securityScanFirstNonFlag(args, startIndex) {\n for (let i = startIndex || 0; i < args.length; i++) {\n const a = args[i];\n if (typeof a === \"string\" && a && !a.startsWith(\"-\")) return a;\n }\n return undefined;\n}\n// Derive the single package that will actually execute, from command + args ONLY.\n// Returns { pkg, checkable } where checkable indicates the pkg is an npm name we\n// can look up in the verified registry. NEVER consults entry.name or unrelated args.\nfunction securityScanExecutedPackage(entry) {\n const command = entry.command;\n const args = entry.args || [];\n if (!command) return { pkg: undefined, checkable: false };\n const base = securityScanPackageName(command);\n if (command === \"npx\" || command === \"bunx\") {\n const target = securityScanFirstNonFlag(args, 0);\n if (!target) return { pkg: undefined, checkable: false };\n return { pkg: securityScanStripVersion(target), checkable: true };\n }\n if ((command === \"pnpm\" || command === \"yarn\") && args[0] === \"dlx\") {\n const target = securityScanFirstNonFlag(args, 1);\n if (!target) return { pkg: undefined, checkable: false };\n return { pkg: securityScanStripVersion(target), checkable: true };\n }\n if (command === \"uvx\") {\n // Python/uv package — not resolvable to an npm name.\n const target = securityScanFirstNonFlag(args, 0);\n return { pkg: target ? securityScanStripVersion(target) : undefined, checkable: false };\n }\n if (command === \"node\" || base === \"node\") {\n const script = securityScanFirstNonFlag(args, 0);\n const pkg = script ? securityScanPackageName(script) : undefined;\n return pkg ? { pkg, checkable: true } : { pkg: undefined, checkable: false };\n }\n // Command is itself a path / binary.\n return base ? { pkg: base, checkable: true } : { pkg: undefined, checkable: false };\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 executed = securityScanExecutedPackage(entry);\n const verified = executed.checkable && executed.pkg ? SECURITY_SCAN_VERIFIED.has(executed.pkg) : false;\n results.push({ server: entry.name || entry.key, verified, checkable: executed.checkable });\n }\n }\n return results;\n}\n// Returns { pm, cmd } where pm is the display label and cmd is the full audit\n// command string, or null when no lockfile is present.\nfunction securityScanPackageManager(root) {\n if (existsSync(join(root, \"pnpm-lock.yaml\"))) return { pm: \"pnpm\", cmd: \"pnpm audit --json\" };\n if (existsSync(join(root, \"yarn.lock\"))) {\n // Yarn Berry (v2+) has no \"yarn audit --json\"; it uses \"yarn npm audit\".\n let berry = existsSync(join(root, \".yarnrc.yml\"));\n if (!berry) {\n const pkg = securityScanReadJson(join(root, \"package.json\"));\n const pm = pkg && typeof pkg.packageManager === \"string\" ? pkg.packageManager : \"\";\n const m = /^yarn@(\\\\d+)/.exec(pm);\n if (m && Number(m[1]) >= 2) berry = true;\n }\n return berry\n ? { pm: \"yarn\", cmd: \"yarn npm audit --all --json\" }\n : { pm: \"yarn\", cmd: \"yarn audit --json\" };\n }\n if (existsSync(join(root, \"package-lock.json\")) || existsSync(join(root, \"npm-shrinkwrap.json\"))) {\n return { pm: \"npm\", cmd: \"npm audit --json\" };\n }\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}\n// #7: For npm v7+ transitive vulns, \"via\" is an array of STRINGS naming parent\n// packages — those must never become the advisory title. Resolve a \"via\" value to\n// a safe title: object entries with a string .title are real advisories; an array\n// of plain strings falls back to \"Vulnerable dependency (via <parent>)\" so a raw\n// package name never lands in the title slot.\nfunction securityScanTitleFromVia(via) {\n const objectTitle = securityScanFirstTitle(\n Array.isArray(via)\n ? via.filter((entry) => entry && typeof entry === \"object\")\n : via && typeof via === \"object\" ? via : undefined,\n );\n if (objectTitle) return objectTitle;\n const parents = securityScanStringArray(Array.isArray(via) ? via : [via]);\n if (parents.length > 0) return \"Vulnerable dependency (via \" + parents[0] + \")\";\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 // #7: resolve \"via\" safely so string-parent names never become the title.\n const title = vuln.via !== undefined ? securityScanTitleFromVia(vuln.via) : vuln.title;\n securityScanAddFinding(findings, seen, name, vuln.severity, 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 // #4: sort by severity (critical outranks high) BEFORE capping so criticals are\n // never dropped by insertion order. Comparator is stable for equal severities.\n const rank = (s) => (s === \"critical\" ? 0 : 1);\n findings.sort((a, b) => rank(a.severity) - rank(b.severity));\n return findings.slice(0, 5);\n}\nfunction securityScanAudit(root) {\n const manager = securityScanPackageManager(root);\n if (!manager) return { pm: null, findings: [] };\n const pm = manager.pm;\n try {\n const stdout = execSync(manager.cmd, {\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 if (stdout.trim()) {\n // #6: non-zero exit with JSON on stdout is the normal \"vulns found\" path.\n return { pm, findings: securityScanParseAudit(stdout) };\n }\n // #6: timeout / error with no usable stdout — return a sentinel so the banner\n // can distinguish \"audit didn't complete\" from \"clean repo\".\n return { pm, findings: [], error: \"timeout\" };\n }\n}\nfunction formatSecurityScan(root) {\n const mcp = securityScanMcp(root);\n // #1/#11: npm-checkable servers not in the registry are genuinely unreviewed;\n // non-checkable (uvx/python/unresolvable) servers get a calmer, separate group.\n const unverified = mcp.filter((entry) => !entry.verified && entry.checkable !== false);\n const nonCheckable = mcp.filter((entry) => entry.checkable === false);\n const audit = securityScanAudit(root);\n const auditDidNotComplete = audit.error && audit.findings.length === 0;\n if (\n unverified.length === 0 &&\n nonCheckable.length === 0 &&\n audit.findings.length === 0 &&\n !auditDidNotComplete\n ) {\n return null;\n }\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 (nonCheckable.length > 0) {\n lines.push(\"MCP servers — source not checkable (non-npm):\");\n for (const entry of nonCheckable) lines.push(\" • \" + entry.server + \" (can't verify automatically — review the source)\");\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 } else if (auditDidNotComplete) {\n // #6: surface an incomplete audit so a timeout no longer looks like a clean repo.\n const pm = audit.pm || \"npm\";\n lines.push((audit.pm || \"npm\") + \" audit — high/critical:\");\n lines.push(\" • dependency audit did not complete (timeout) — run \\`\" + pm + \" audit\\` manually\");\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","export { SECURITY_SCAN_PACKAGES, buildSecurityScanScript } from \"./scan.js\";\n\n// ─── Hook & when types ───────────────────────────────────────────────────────\n\n/**\n * Lifecycle hook a check/action attaches to. `before_done` is the completion\n * gate (default). The others let checks seed context or run earlier in the loop:\n * - `session_start` — a fresh session or resume\n * - `message_submit` — every user prompt\n * - `before_tool` — immediately before a tool call (can block it)\n * - `after_tool` — immediately after a tool call (advisory)\n * - `session_end` — the session is ending (advisory)\n * - `before_done` — the completion gate; blocks finishing on failure\n *\n * Engine support varies; engines honor what they can and skip the rest.\n */\nexport type HookEvent =\n | \"session_start\"\n | \"message_submit\"\n | \"before_tool\"\n | \"after_tool\"\n | \"session_end\"\n | \"before_done\";\n\n/** Ordered list of all hook events, earliest-to-latest in the agent loop. */\nexport const HOOK_EVENTS: HookEvent[] = [\n \"session_start\",\n \"message_submit\",\n \"before_tool\",\n \"after_tool\",\n \"session_end\",\n \"before_done\",\n];\n\n/**\n * Context-seeding behavior: inject text, file contents, and/or the current\n * datetime as agent context at the check's hook point. An alternative to\n * `cmd` (command) and `prompt` (agent instruction).\n */\nexport interface InjectSpec {\n /** Literal text injected as agent context. */\n text?: string;\n /** Repo-relative files whose contents are injected as context. */\n files?: string[];\n /** Inject the current date and time. */\n datetime?: boolean;\n}\n\n/** Named file-scope filter. Custom regexes are plain strings not in this union. */\nexport type WhenScope =\n // Web / app layers\n | \"frontend\"\n | \"backend\"\n | \"socket\"\n | \"visual\"\n // Languages\n | \"python\"\n | \"go\"\n | \"rust\"\n | \"java\"\n | \"ruby\"\n // Cross-cutting concerns\n | \"database\"\n | \"prisma\"\n | \"testing\"\n | \"infra\"\n | \"ci\"\n | \"docs\"\n // Project structure / dependency manifests\n | \"structural\";\n\n// ─── Condition types ─────────────────────────────────────────────────────────\n\nexport type ConditionOperator = \"file_exists\" | \"file_contains\" | \"env_var_set\" | \"shell_returns_0\";\n\nexport interface ConditionDef {\n id: string;\n operator: ConditionOperator;\n /** Path glob for file_exists / file_contains */\n path?: string;\n /** Substring to look for in file_contains */\n contains?: string;\n /** Env var name for env_var_set */\n envVar?: string;\n /** Shell command for shell_returns_0 */\n cmd?: string;\n}\n\n// ─── Check types ─────────────────────────────────────────────────────────────\n\nexport interface CheckDef {\n id: string;\n label: string;\n /** Lifecycle hook — defaults to \"before_done\" when absent */\n on?: HookEvent;\n /** File filter: a named WhenScope or a regex string. Absent = run always. */\n when?: string;\n /** Shell command — task (runs automatically) */\n cmd?: string;\n /** Structured prompt/instruction the agent must read and act on before finishing */\n prompt?: string;\n /** Context-seeding behavior — inject text/files/datetime at the hook point */\n inject?: InjectSpec;\n /** Reference to a ConditionDef id */\n conditionId?: string;\n}\n\n/** The effective hook of a check (`before_done` when unset). */\nexport function checkHook(check: CheckDef): HookEvent {\n return check.on ?? \"before_done\";\n}\n\n/** A check's behavior kind, derived from which behavior field is set. */\nexport type CheckBehavior = \"cmd\" | \"prompt\" | \"inject\";\n\nexport function checkBehavior(check: CheckDef): CheckBehavior {\n if (check.cmd !== undefined) return \"cmd\";\n if (check.inject !== undefined) return \"inject\";\n return \"prompt\";\n}\n\n// ─── Top-level config ─────────────────────────────────────────────────────────\n\nexport interface HoldpointContext {\n guides: Record<string, string>;\n}\n\nexport interface HoldpointConfig {\n version: number;\n context: HoldpointContext;\n conditions: ConditionDef[];\n /** All checks — each has `on`, optional `when`, and either `cmd` (task) or `prompt` (agent instruction). */\n checks: CheckDef[];\n /**\n * Named regex patterns for use in `when:` fields.\n * Keys are human-readable names (e.g. \"checks-file\", \"api-routes\").\n * Values are regex strings matched against changed file paths.\n * Built-in scope names (frontend, backend, structural, etc.) cannot be overridden here.\n */\n patterns?: Record<string, string>;\n /**\n * Files to inject as `additionalContext` at the start of every agent session.\n * Paths are repo-root-relative. Useful for injecting MASTER_PROMPT.md, AGENT_CONTEXT.md, etc.\n */\n session_context_files?: string[];\n /**\n * Inject the current date and time into every prompt submission as `additionalContext`.\n * Helps models avoid knowledge-cutoff confusion. Defaults to `true`; set to `false` to opt out.\n */\n inject_datetime?: boolean;\n /**\n * Run the session-start security scan (unverified MCP servers + `audit`\n * high/critical advisories) and inject its banner as session context.\n * Defaults to `true`; set to `false` to skip the scan entirely — including the\n * dependency-audit subprocess, which otherwise runs on session start.\n */\n security_scan?: boolean;\n /**\n * Per-engine overrides. Values here win over engine defaults — useful when the\n * project IS the holdpoint repo and should invoke the local CLI instead of npx.\n */\n engines?: {\n claude?: { stop_command?: string; live_command?: string };\n codex?: { stop_command?: string };\n copilot?: { check_command?: string };\n };\n}\n\n// ─── Runtime result types ────────────────────────────────────────────────────\n\nexport type CheckStatus = \"pass\" | \"fail\" | \"skip\" | \"pending\";\n\nexport interface CheckResult {\n check: CheckDef;\n status: CheckStatus;\n /** stdout/stderr from a deterministic check */\n output?: string;\n /** Exit code from a deterministic check */\n exitCode?: number;\n /** Human-readable reason for skip */\n skipReason?: string;\n}\n\nexport interface ValidationError {\n path: string;\n message: string;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: ValidationError[];\n}\n\n// ─── Agent detection ─────────────────────────────────────────────────────────\n\nexport type AgentType = \"copilot\" | \"claude\" | \"cursor\" | \"codex\" | \"unknown\";\n\n// ─── Check run report types ────────────────────────────────────────────────────\n\n/** Result of a single check within a run (cmd or prompt). */\nexport interface CheckRunResult {\n id: string;\n label: string;\n /** \"cmd\" for automated checks, \"prompt\" for manual agent checks */\n kind: \"cmd\" | \"prompt\";\n /** \"pass\" | \"fail\" | \"skip\" for cmd checks; \"shown\" for prompt checks displayed to agent */\n status: \"pass\" | \"fail\" | \"skip\" | \"shown\";\n output?: string;\n exitCode?: number;\n skipReason?: string;\n}\n\n/** A single holdpoint check run recorded after `holdpoint check` completes. */\nexport interface CheckRun {\n /** Full HEAD commit SHA, or null if not in a git repo / no commits. */\n sha: string | null;\n /** First 8 chars of sha, or null. */\n shortSha: string | null;\n /** ISO 8601 timestamp. */\n timestamp: string;\n /** Changed files used for trigger matching. Empty array means \"all checks ran\". */\n files: string[];\n results: CheckRunResult[];\n summary: { passed: number; failed: number; skipped: number; shown: number };\n}\n\n/** Contents of `.holdpoint/check-reports.json` — list of recent check runs. */\nexport interface CheckReports {\n /** Runs ordered newest-first. Capped at 50 entries. */\n runs: CheckRun[];\n}\n"],"mappings":";AAcO,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,SAAS,0BAAkC;AAChD,SAAO;AAAA;AAAA,yCAEgC,KAAK,UAAU,sBAAsB,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;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;AA0Q/E;;;ACtRO,IAAM,cAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA4EO,SAAS,UAAU,OAA4B;AACpD,SAAO,MAAM,MAAM;AACrB;AAKO,SAAS,cAAc,OAAgC;AAC5D,MAAI,MAAM,QAAQ,OAAW,QAAO;AACpC,MAAI,MAAM,WAAW,OAAW,QAAO;AACvC,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@holdpoint/types",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.11",
4
4
  "publishConfig": {
5
- "access": "public",
6
- "tag": "alpha"
5
+ "access": "public"
7
6
  },
8
7
  "description": "Shared TypeScript types for Holdpoint",
9
8
  "homepage": "https://holdpoint.dev",
@@ -44,7 +43,7 @@
44
43
  ],
45
44
  "devDependencies": {
46
45
  "tsup": "^8.3.5",
47
- "typescript": "^5.7.2"
46
+ "typescript": "^6.0.3"
48
47
  },
49
48
  "scripts": {
50
49
  "build": "tsup",