@scopebond/hook 0.2.1 → 0.3.0

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/cli.js CHANGED
@@ -4,19 +4,39 @@
4
4
  //
5
5
  // scopebond-hook claude evaluate a Claude Code PreToolUse call (stdin JSON)
6
6
  // scopebond-hook cursor evaluate a Cursor hook event (stdin JSON)
7
- // scopebond-hook init [--cursor] scaffold keys + starter policy, print the config
7
+ // scopebond-hook init [--cursor] [--no-install] scaffold keys + starter policy and configure the agent
8
8
  // scopebond-hook connect <url> <bundle.json> [--cursor]
9
9
  // enroll with a Cloud workspace and start exporting
10
+ // scopebond-hook log [-n N] show the most recent local receipts
11
+ // scopebond-hook verify verify every local receipt offline against the attester key
12
+ // scopebond-hook test "<shell command>" show the decision for a command without recording it
10
13
  // scopebond-hook flush deliver any queued receipts to Cloud now
11
14
  //
12
15
  // Config dir: $SCOPEBOND_HOOK_DIR, else ./.scopebond
13
16
  // Fail-closed: any error denies the action with a repair message.
14
- import { readFileSync, existsSync } from "node:fs";
17
+ import { readFileSync, existsSync, mkdtempSync, rmSync } from "node:fs";
15
18
  import { join } from "node:path";
16
- import { mapClaudeToolUse, mapCursorEvent } from "./map.js";
19
+ import { tmpdir } from "node:os";
20
+ import { execFileSync } from "node:child_process";
21
+ import { verifyReceipt } from "@scopebond/gateway";
22
+ import { openReceiptStore, loadOrCreateAttester } from "@scopebond/gateway/node";
23
+ import { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from "./map.js";
17
24
  import { createHookRuntime } from "./runtime.js";
18
25
  import { scaffold, harnessSnippet, installHarness } from "./init.js";
19
26
  import { connectCloud, loadConnection } from "./cloud.js";
27
+ /** The current git branch in `cwd` (best-effort). A bare `git push` pushes it, so
28
+ * the runtime fills it in before evaluating; on failure the ref stays absent and
29
+ * the starter policy fails closed. */
30
+ function currentBranch(cwd) {
31
+ try {
32
+ return execFileSync("git", ["-C", cwd, "symbolic-ref", "--quiet", "--short", "HEAD"], {
33
+ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"],
34
+ }).trim() || null;
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
20
40
  /** Read an enrollment bundle from a file path, an inline base64 blob, or inline raw
21
41
  * JSON (the portal hands out a base64 blob so it is one clean argument). */
22
42
  function readBundleArg(bundleArg, stdin) {
@@ -79,18 +99,15 @@ async function runClaude() {
79
99
  denyClaude("hook received invalid JSON on stdin");
80
100
  }
81
101
  try {
102
+ const cwd = input?.cwd ? String(input.cwd) : process.cwd();
82
103
  const runtime = createHookRuntime(runtimePaths(configDir()));
83
- const decision = await runtime.evaluate(mapClaudeToolUse(input));
104
+ const decision = await runtime.evaluate(fillPushBranch(mapClaudeToolUse(input), currentBranch(cwd)));
84
105
  await runtime.flush();
85
106
  if (decision.decision === "deny")
86
107
  denyClaude(decision.reason);
87
- if (decision.decision === "allow") {
88
- process.stdout.write(JSON.stringify({
89
- hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow", permissionDecisionReason: decision.reason },
90
- }) + "\n");
91
- process.exit(0);
92
- }
93
- // not_evaluated: defer to the harness default (grant nothing, block nothing).
108
+ // allow and not_evaluated both DEFER to Claude Code's own permission flow: the
109
+ // hook records the receipt but never returns permissionDecision:"allow", which
110
+ // would suppress the user's normal review. Scopebond blocks; it does not approve.
94
111
  process.exit(0);
95
112
  }
96
113
  catch (error) {
@@ -107,10 +124,13 @@ async function runCursor() {
107
124
  let permission = "deny";
108
125
  let message = "Scopebond hook failed closed";
109
126
  try {
127
+ const cwd = input?.cwd ? String(input.cwd) : process.cwd();
110
128
  const runtime = createHookRuntime(runtimePaths(configDir()));
111
- const decision = await runtime.evaluate(mapCursorEvent(event, input));
129
+ const decision = await runtime.evaluate(fillPushBranch(mapCursorEvent(event, input), currentBranch(cwd)));
112
130
  await runtime.flush();
113
- permission = decision.decision === "deny" ? "deny" : decision.decision === "allow" ? "allow" : "ask";
131
+ // Only an out-of-policy action is denied outright; an allowed or unevaluated
132
+ // action defers to Cursor's own prompt ("ask"), never a silent auto-allow.
133
+ permission = decision.decision === "deny" ? "deny" : "ask";
114
134
  message = decision.reason;
115
135
  }
116
136
  catch (error) {
@@ -128,11 +148,125 @@ function runInit(args) {
128
148
  console.log(` machine key ${agentKid}`);
129
149
  console.log(` policy ${policyPath} (starter — edit the limits)`);
130
150
  console.log("");
131
- console.log(`Add this to your ${harness === "cursor" ? ".cursor/hooks.json" : ".claude/settings.json"}:`);
132
- console.log(harnessSnippet(harness));
151
+ // Configure the agent automatically by default (idempotent), so there is no
152
+ // hand-editing step; --no-install prints the snippet instead.
153
+ if (!args.includes("--no-install")) {
154
+ const file = installHarness(harness);
155
+ console.log(`✓ ${harness === "cursor" ? "Cursor" : "Claude Code"} configured in ${file}`);
156
+ }
157
+ else {
158
+ console.log(`Add this to your ${harness === "cursor" ? ".cursor/hooks.json" : ".claude/settings.json"}:`);
159
+ console.log(harnessSnippet(harness));
160
+ }
133
161
  console.log("");
134
- console.log("Then run one safe command in the agent and see the receipt in .scopebond/receipts.db.");
135
- console.log("To send receipts to your workspace, run: scopebond-hook connect <workspace-url> scopebond-enrollment.json");
162
+ console.log("Next: run one command in the agent, then `scopebond-hook log` to see the receipt");
163
+ console.log("and `scopebond-hook verify` to check it offline. Try `scopebond-hook test \"rm -rf /\"`.");
164
+ console.log("To send receipts to a workspace: scopebond-hook connect <workspace-url> <enrollment>");
165
+ }
166
+ function decisionOf(payload) {
167
+ const rr = String(payload.realtime_result ?? "");
168
+ const state = String(payload.execution?.state ?? "");
169
+ if (state === "observed_not_evaluated")
170
+ return "not_evaluated";
171
+ if (rr === "deny")
172
+ return state === "cooperative_allow" || state === "executed" ? "monitor" : "deny";
173
+ return "allow";
174
+ }
175
+ function describeIntent(payload) {
176
+ const intent = (payload.intent ?? {});
177
+ const p = (intent.params ?? {});
178
+ const bits = intent.action_type === "shell.exec" ? String(p.program ?? "")
179
+ : intent.action_type === "git.push" ? `${p.remote ?? ""} ${p.ref ?? ""}`.trim()
180
+ : intent.action_type === "file.write" || intent.action_type === "file.read" ? String(p.path ?? "")
181
+ : intent.action_type === "mcp.tool.call" ? `${p.server ?? ""}/${p.tool ?? ""}`
182
+ : intent.action_type === "net.fetch" ? String(p.host ?? "") : "";
183
+ return `${String(intent.action_type ?? "?")}${bits ? ` ${bits}` : ""}`;
184
+ }
185
+ async function runLog(args) {
186
+ const dir = configDir();
187
+ const dbPath = join(dir, "receipts.db");
188
+ if (!existsSync(dbPath)) {
189
+ console.log("no receipts yet — run a command in the agent first.");
190
+ process.exit(0);
191
+ }
192
+ const nIdx = args.indexOf("-n");
193
+ const n = nIdx >= 0 ? Math.max(1, Number(args[nIdx + 1]) || 20) : 20;
194
+ const { store } = openReceiptStore({ db: dbPath });
195
+ const all = await Promise.resolve(store.list());
196
+ const recent = all.slice(-n);
197
+ if (recent.length === 0) {
198
+ console.log("no receipts yet.");
199
+ process.exit(0);
200
+ }
201
+ for (const r of recent) {
202
+ const p = r.payload;
203
+ console.log(`${String(p.timestamp ?? "")} ${decisionOf(p).padEnd(13)} ${describeIntent(p)}`);
204
+ }
205
+ console.log(`\n${recent.length} of ${all.length} receipt(s). Verify them: scopebond-hook verify`);
206
+ }
207
+ async function runVerify() {
208
+ const dir = configDir();
209
+ const dbPath = join(dir, "receipts.db");
210
+ const attesterPath = join(dir, "attester.key");
211
+ if (!existsSync(dbPath) || !existsSync(attesterPath)) {
212
+ console.log("nothing to verify yet (no receipts or no attester key).");
213
+ process.exit(0);
214
+ }
215
+ const { attester } = loadOrCreateAttester({ file: attesterPath });
216
+ const { store } = openReceiptStore({ db: dbPath });
217
+ const all = await Promise.resolve(store.list());
218
+ let ok = 0;
219
+ const bad = [];
220
+ for (const r of all) {
221
+ const result = verifyReceipt(r, attester.publicKeyPem);
222
+ if (result.valid)
223
+ ok += 1;
224
+ else {
225
+ const failed = Object.entries(result).filter(([k, v]) => k.endsWith("_valid") && v === false).map(([k]) => k);
226
+ bad.push(`${String(r.payload.timestamp ?? "")}: ${failed.join(", ") || "invalid"}`);
227
+ }
228
+ }
229
+ console.log(`${ok}/${all.length} receipt(s) verify offline against ${attesterPath}.`);
230
+ if (bad.length) {
231
+ for (const b of bad)
232
+ console.error(` ✗ ${b}`);
233
+ process.exit(1);
234
+ }
235
+ process.exit(0);
236
+ }
237
+ async function runTest(args) {
238
+ const command = args.find((a) => !a.startsWith("-"));
239
+ if (!command) {
240
+ console.error('usage: scopebond-hook test "<shell command>"');
241
+ process.exit(1);
242
+ }
243
+ const dir = configDir();
244
+ if (!existsSync(join(dir, "policy.json"))) {
245
+ console.error("no policy yet — run `scopebond-hook init` first.");
246
+ process.exit(1);
247
+ }
248
+ // Evaluate against the real policy and keys, but a throwaway store, so `test`
249
+ // never records a receipt or exports anything.
250
+ const tmp = mkdtempSync(join(tmpdir(), "sb-hook-test-"));
251
+ try {
252
+ const runtime = createHookRuntime({
253
+ policyPath: join(dir, "policy.json"), keyPath: join(dir, "agent.key"),
254
+ attesterPath: join(dir, "attester.key"), dbPath: join(tmp, "receipts.db"),
255
+ strict: process.argv.includes("--strict"),
256
+ });
257
+ const mapped = fillPushBranch(mapClaudeToolUse({ tool_name: "Bash", tool_input: { command } }), currentBranch(process.cwd()));
258
+ console.log(`command: ${command}`);
259
+ for (const m of mapped) {
260
+ const d = await runtime.evaluateOne(m);
261
+ console.log(` ${describeIntent({ intent: m.intent }).padEnd(28)} → ${d.decision}${d.reason ? ` (${d.reason})` : ""}`);
262
+ }
263
+ const overall = await runtime.evaluate(mapped);
264
+ console.log(`\noverall: ${overall.decision}${overall.reason ? ` · ${overall.reason}` : ""}`);
265
+ process.exit(overall.decision === "deny" ? 2 : 0);
266
+ }
267
+ finally {
268
+ rmSync(tmp, { recursive: true, force: true });
269
+ }
136
270
  }
137
271
  async function runConnect(args) {
138
272
  const positional = args.filter((a) => !a.startsWith("--"));
@@ -203,11 +337,20 @@ else if (cmd === "init") {
203
337
  else if (cmd === "connect") {
204
338
  await runConnect(rest);
205
339
  }
340
+ else if (cmd === "log") {
341
+ await runLog(rest);
342
+ }
343
+ else if (cmd === "verify") {
344
+ await runVerify();
345
+ }
346
+ else if (cmd === "test") {
347
+ await runTest(rest);
348
+ }
206
349
  else if (cmd === "flush") {
207
350
  await runFlush();
208
351
  }
209
352
  else {
210
- console.error("usage: scopebond-hook <claude|cursor|init|connect|flush> [--cursor] [--force] [--strict]");
353
+ console.error("usage: scopebond-hook <claude|cursor|init|connect|log|verify|test|flush> [--cursor] [--force] [--strict] [--no-install]");
211
354
  process.exit(1);
212
355
  }
213
356
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n// scopebond-hook — govern a coding agent's tool calls against policy, in-path,\n// before they run, with a signed local receipt.\n//\n// scopebond-hook claude evaluate a Claude Code PreToolUse call (stdin JSON)\n// scopebond-hook cursor evaluate a Cursor hook event (stdin JSON)\n// scopebond-hook init [--cursor] scaffold keys + starter policy, print the config\n// scopebond-hook connect <url> <bundle.json> [--cursor]\n// enroll with a Cloud workspace and start exporting\n// scopebond-hook flush deliver any queued receipts to Cloud now\n//\n// Config dir: $SCOPEBOND_HOOK_DIR, else ./.scopebond\n// Fail-closed: any error denies the action with a repair message.\n\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CloudEnrollmentBundle } from \"@scopebond/gateway\";\nimport { mapClaudeToolUse, mapCursorEvent } from \"./map.js\";\nimport { createHookRuntime } from \"./runtime.js\";\nimport { scaffold, harnessSnippet, installHarness } from \"./init.js\";\nimport { connectCloud, loadConnection, connectionPath } from \"./cloud.js\";\n\n/** Read an enrollment bundle from a file path, an inline base64 blob, or inline raw\n * JSON (the portal hands out a base64 blob so it is one clean argument). */\nfunction readBundleArg(bundleArg: string | undefined, stdin: () => string): CloudEnrollmentBundle {\n let text: string;\n if (!bundleArg) text = stdin();\n else if (existsSync(bundleArg)) text = readFileSync(bundleArg, \"utf8\");\n else if (bundleArg.trim().startsWith(\"{\")) text = bundleArg;\n else { try { text = Buffer.from(bundleArg, \"base64\").toString(\"utf8\"); } catch { text = bundleArg; } }\n return JSON.parse(text) as CloudEnrollmentBundle;\n}\n\nfunction configDir(): string {\n return process.env.SCOPEBOND_HOOK_DIR ?? join(process.cwd(), \".scopebond\");\n}\nfunction runtimePaths(dir: string) {\n const connection = loadConnection(dir);\n return {\n policyPath: join(dir, \"policy.json\"),\n keyPath: join(dir, \"agent.key\"),\n attesterPath: join(dir, \"attester.key\"),\n dbPath: join(dir, \"receipts.db\"),\n // Strict: deny (not just observe) tools with no taxonomy mapping.\n strict: process.argv.includes(\"--strict\") || process.env.SCOPEBOND_HOOK_STRICT === \"1\",\n // When connected, auto-export receipts. A short bounded flush keeps the hot path\n // fast; undelivered receipts persist in the durable outbox and flush next time\n // (or via `scopebond-hook flush`, e.g. on a session-end hook).\n ...(connection ? { cloud: { connection, flushTimeoutMs: Number(process.env.SCOPEBOND_HOOK_FLUSH_MS ?? 800) } } : {}),\n };\n}\nfunction readStdin(): string {\n try { return readFileSync(0, \"utf8\"); } catch { return \"\"; }\n}\n\nfunction denyClaude(reason: string): never {\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: { hookEventName: \"PreToolUse\", permissionDecision: \"deny\", permissionDecisionReason: reason },\n }) + \"\\n\");\n process.stderr.write(`Scopebond: ${reason}\\n`);\n process.exit(2);\n}\n\nasync function runClaude(): Promise<void> {\n let input: Record<string, unknown>;\n try { input = JSON.parse(readStdin()); } catch { denyClaude(\"hook received invalid JSON on stdin\"); }\n try {\n const runtime = createHookRuntime(runtimePaths(configDir()));\n const decision = await runtime.evaluate(mapClaudeToolUse(input!));\n await runtime.flush();\n if (decision.decision === \"deny\") denyClaude(decision.reason);\n if (decision.decision === \"allow\") {\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: { hookEventName: \"PreToolUse\", permissionDecision: \"allow\", permissionDecisionReason: decision.reason },\n }) + \"\\n\");\n process.exit(0);\n }\n // not_evaluated: defer to the harness default (grant nothing, block nothing).\n process.exit(0);\n } catch (error) {\n denyClaude(`Scopebond hook failed closed: ${(error as Error).message}. Repair: run \\`scopebond-hook init\\`.`);\n }\n}\n\nasync function runCursor(): Promise<void> {\n let input: Record<string, unknown> = {};\n try { input = JSON.parse(readStdin()); } catch { /* fall through to fail-closed deny below */ }\n const event = String(input?.hook_event_name ?? input?.event ?? process.argv[3] ?? \"\");\n let permission: \"allow\" | \"deny\" | \"ask\" = \"deny\";\n let message = \"Scopebond hook failed closed\";\n try {\n const runtime = createHookRuntime(runtimePaths(configDir()));\n const decision = await runtime.evaluate(mapCursorEvent(event, input));\n await runtime.flush();\n permission = decision.decision === \"deny\" ? \"deny\" : decision.decision === \"allow\" ? \"allow\" : \"ask\";\n message = decision.reason;\n } catch (error) {\n permission = \"deny\";\n message = `Scopebond hook failed closed: ${(error as Error).message}. Repair: run \\`scopebond-hook init\\`.`;\n }\n process.stdout.write(JSON.stringify({ permission, agentMessage: message }) + \"\\n\");\n process.exit(0);\n}\n\nfunction runInit(args: string[]): void {\n const harness = args.includes(\"--cursor\") ? \"cursor\" : \"claude\";\n const dir = configDir();\n const { agentKid, policyPath } = scaffold(dir, { force: args.includes(\"--force\") });\n console.log(`Scopebond hook enrolled in ${dir}`);\n console.log(` machine key ${agentKid}`);\n console.log(` policy ${policyPath} (starter — edit the limits)`);\n console.log(\"\");\n console.log(`Add this to your ${harness === \"cursor\" ? \".cursor/hooks.json\" : \".claude/settings.json\"}:`);\n console.log(harnessSnippet(harness));\n console.log(\"\");\n console.log(\"Then run one safe command in the agent and see the receipt in .scopebond/receipts.db.\");\n console.log(\"To send receipts to your workspace, run: scopebond-hook connect <workspace-url> scopebond-enrollment.json\");\n}\n\nasync function runConnect(args: string[]): Promise<void> {\n const positional = args.filter((a) => !a.startsWith(\"--\"));\n const url = positional[0];\n const bundleArg = positional[1];\n const harness = args.includes(\"--cursor\") ? \"cursor\" : \"claude\";\n if (!url) {\n console.error(\"usage: scopebond-hook connect <workspace-url> <enrollment> [--cursor] [--no-install]\");\n process.exit(1);\n }\n const dir = configDir();\n // One command sets everything up: scaffold the key, attester and starter policy if\n // they do not exist, then enroll and persist the scoped machine credential. The\n // enrollment can be a file, an inline base64 blob (what the portal hands out) or\n // raw JSON — the user never has to save or open a JSON file.\n scaffold(dir, {});\n let bundle: CloudEnrollmentBundle;\n try { bundle = readBundleArg(bundleArg, readStdin); }\n catch { console.error(\"could not read the enrollment (expected a file, inline blob, or JSON on stdin)\"); process.exit(1); }\n try {\n const c = await connectCloud(dir, url, bundle);\n console.log(`✓ Connected to ${c.url}`);\n // Configure the agent automatically (merges into the existing config), unless the\n // caller opts out. This removes the \"paste this snippet\" step.\n if (!args.includes(\"--no-install\")) {\n const file = installHarness(harness);\n console.log(`✓ ${harness === \"cursor\" ? \"Cursor\" : \"Claude Code\"} configured in ${file}`);\n } else {\n console.log(`Add this to your ${harness === \"cursor\" ? \".cursor/hooks.json\" : \".claude/settings.json\"}:`);\n console.log(harnessSnippet(harness));\n }\n console.log(\"\");\n console.log(\"Run your agent — the first action appears in your workspace within seconds.\");\n } catch (error) {\n console.error(`connect failed: ${(error as Error).message}`);\n process.exit(1);\n }\n}\n\nasync function runFlush(): Promise<void> {\n const dir = configDir();\n if (!loadConnection(dir)) { console.error(\"not connected to a workspace; run `scopebond-hook connect` first\"); process.exit(1); }\n const runtime = createHookRuntime(runtimePaths(dir));\n await runtime.exporter?.flush();\n const status = runtime.exporter?.status();\n console.log(`flushed; ${status?.pending ?? 0} receipt(s) still pending${status?.lastError ? ` (last error: ${status.lastError})` : \"\"}`);\n process.exit(status && status.pending > 0 ? 1 : 0);\n}\n\nconst [cmd, ...rest] = process.argv.slice(2);\nif (cmd === \"claude\") { await runClaude(); }\nelse if (cmd === \"cursor\") { await runCursor(); }\nelse if (cmd === \"init\") { runInit(rest); }\nelse if (cmd === \"connect\") { await runConnect(rest); }\nelse if (cmd === \"flush\") { await runFlush(); }\nelse {\n console.error(\"usage: scopebond-hook <claude|cursor|init|connect|flush> [--cursor] [--force] [--strict]\");\n process.exit(1);\n}\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n// scopebond-hook — govern a coding agent's tool calls against policy, in-path,\n// before they run, with a signed local receipt.\n//\n// scopebond-hook claude evaluate a Claude Code PreToolUse call (stdin JSON)\n// scopebond-hook cursor evaluate a Cursor hook event (stdin JSON)\n// scopebond-hook init [--cursor] [--no-install] scaffold keys + starter policy and configure the agent\n// scopebond-hook connect <url> <bundle.json> [--cursor]\n// enroll with a Cloud workspace and start exporting\n// scopebond-hook log [-n N] show the most recent local receipts\n// scopebond-hook verify verify every local receipt offline against the attester key\n// scopebond-hook test \"<shell command>\" show the decision for a command without recording it\n// scopebond-hook flush deliver any queued receipts to Cloud now\n//\n// Config dir: $SCOPEBOND_HOOK_DIR, else ./.scopebond\n// Fail-closed: any error denies the action with a repair message.\n\nimport { readFileSync, existsSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { execFileSync } from \"node:child_process\";\nimport type { CloudEnrollmentBundle } from \"@scopebond/gateway\";\nimport { verifyReceipt } from \"@scopebond/gateway\";\nimport { openReceiptStore, loadOrCreateAttester } from \"@scopebond/gateway/node\";\nimport { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from \"./map.js\";\nimport { createHookRuntime } from \"./runtime.js\";\nimport { scaffold, harnessSnippet, installHarness } from \"./init.js\";\nimport { connectCloud, loadConnection, connectionPath } from \"./cloud.js\";\n\n/** The current git branch in `cwd` (best-effort). A bare `git push` pushes it, so\n * the runtime fills it in before evaluating; on failure the ref stays absent and\n * the starter policy fails closed. */\nfunction currentBranch(cwd: string): string | null {\n try {\n return execFileSync(\"git\", [\"-C\", cwd, \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"], {\n encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim() || null;\n } catch { return null; }\n}\n\n/** Read an enrollment bundle from a file path, an inline base64 blob, or inline raw\n * JSON (the portal hands out a base64 blob so it is one clean argument). */\nfunction readBundleArg(bundleArg: string | undefined, stdin: () => string): CloudEnrollmentBundle {\n let text: string;\n if (!bundleArg) text = stdin();\n else if (existsSync(bundleArg)) text = readFileSync(bundleArg, \"utf8\");\n else if (bundleArg.trim().startsWith(\"{\")) text = bundleArg;\n else { try { text = Buffer.from(bundleArg, \"base64\").toString(\"utf8\"); } catch { text = bundleArg; } }\n return JSON.parse(text) as CloudEnrollmentBundle;\n}\n\nfunction configDir(): string {\n return process.env.SCOPEBOND_HOOK_DIR ?? join(process.cwd(), \".scopebond\");\n}\nfunction runtimePaths(dir: string) {\n const connection = loadConnection(dir);\n return {\n policyPath: join(dir, \"policy.json\"),\n keyPath: join(dir, \"agent.key\"),\n attesterPath: join(dir, \"attester.key\"),\n dbPath: join(dir, \"receipts.db\"),\n // Strict: deny (not just observe) tools with no taxonomy mapping.\n strict: process.argv.includes(\"--strict\") || process.env.SCOPEBOND_HOOK_STRICT === \"1\",\n // When connected, auto-export receipts. A short bounded flush keeps the hot path\n // fast; undelivered receipts persist in the durable outbox and flush next time\n // (or via `scopebond-hook flush`, e.g. on a session-end hook).\n ...(connection ? { cloud: { connection, flushTimeoutMs: Number(process.env.SCOPEBOND_HOOK_FLUSH_MS ?? 800) } } : {}),\n };\n}\nfunction readStdin(): string {\n try { return readFileSync(0, \"utf8\"); } catch { return \"\"; }\n}\n\nfunction denyClaude(reason: string): never {\n process.stdout.write(JSON.stringify({\n hookSpecificOutput: { hookEventName: \"PreToolUse\", permissionDecision: \"deny\", permissionDecisionReason: reason },\n }) + \"\\n\");\n process.stderr.write(`Scopebond: ${reason}\\n`);\n process.exit(2);\n}\n\nasync function runClaude(): Promise<void> {\n let input: Record<string, unknown>;\n try { input = JSON.parse(readStdin()); } catch { denyClaude(\"hook received invalid JSON on stdin\"); }\n try {\n const cwd = input?.cwd ? String(input.cwd) : process.cwd();\n const runtime = createHookRuntime(runtimePaths(configDir()));\n const decision = await runtime.evaluate(fillPushBranch(mapClaudeToolUse(input!), currentBranch(cwd)));\n await runtime.flush();\n if (decision.decision === \"deny\") denyClaude(decision.reason);\n // allow and not_evaluated both DEFER to Claude Code's own permission flow: the\n // hook records the receipt but never returns permissionDecision:\"allow\", which\n // would suppress the user's normal review. Scopebond blocks; it does not approve.\n process.exit(0);\n } catch (error) {\n denyClaude(`Scopebond hook failed closed: ${(error as Error).message}. Repair: run \\`scopebond-hook init\\`.`);\n }\n}\n\nasync function runCursor(): Promise<void> {\n let input: Record<string, unknown> = {};\n try { input = JSON.parse(readStdin()); } catch { /* fall through to fail-closed deny below */ }\n const event = String(input?.hook_event_name ?? input?.event ?? process.argv[3] ?? \"\");\n let permission: \"allow\" | \"deny\" | \"ask\" = \"deny\";\n let message = \"Scopebond hook failed closed\";\n try {\n const cwd = input?.cwd ? String(input.cwd) : process.cwd();\n const runtime = createHookRuntime(runtimePaths(configDir()));\n const decision = await runtime.evaluate(fillPushBranch(mapCursorEvent(event, input), currentBranch(cwd)));\n await runtime.flush();\n // Only an out-of-policy action is denied outright; an allowed or unevaluated\n // action defers to Cursor's own prompt (\"ask\"), never a silent auto-allow.\n permission = decision.decision === \"deny\" ? \"deny\" : \"ask\";\n message = decision.reason;\n } catch (error) {\n permission = \"deny\";\n message = `Scopebond hook failed closed: ${(error as Error).message}. Repair: run \\`scopebond-hook init\\`.`;\n }\n process.stdout.write(JSON.stringify({ permission, agentMessage: message }) + \"\\n\");\n process.exit(0);\n}\n\nfunction runInit(args: string[]): void {\n const harness = args.includes(\"--cursor\") ? \"cursor\" : \"claude\";\n const dir = configDir();\n const { agentKid, policyPath } = scaffold(dir, { force: args.includes(\"--force\") });\n console.log(`Scopebond hook enrolled in ${dir}`);\n console.log(` machine key ${agentKid}`);\n console.log(` policy ${policyPath} (starter — edit the limits)`);\n console.log(\"\");\n // Configure the agent automatically by default (idempotent), so there is no\n // hand-editing step; --no-install prints the snippet instead.\n if (!args.includes(\"--no-install\")) {\n const file = installHarness(harness);\n console.log(`✓ ${harness === \"cursor\" ? \"Cursor\" : \"Claude Code\"} configured in ${file}`);\n } else {\n console.log(`Add this to your ${harness === \"cursor\" ? \".cursor/hooks.json\" : \".claude/settings.json\"}:`);\n console.log(harnessSnippet(harness));\n }\n console.log(\"\");\n console.log(\"Next: run one command in the agent, then `scopebond-hook log` to see the receipt\");\n console.log(\"and `scopebond-hook verify` to check it offline. Try `scopebond-hook test \\\"rm -rf /\\\"`.\");\n console.log(\"To send receipts to a workspace: scopebond-hook connect <workspace-url> <enrollment>\");\n}\n\nfunction decisionOf(payload: Record<string, unknown>): string {\n const rr = String(payload.realtime_result ?? \"\");\n const state = String((payload.execution as Record<string, unknown> | undefined)?.state ?? \"\");\n if (state === \"observed_not_evaluated\") return \"not_evaluated\";\n if (rr === \"deny\") return state === \"cooperative_allow\" || state === \"executed\" ? \"monitor\" : \"deny\";\n return \"allow\";\n}\n\nfunction describeIntent(payload: Record<string, unknown>): string {\n const intent = (payload.intent ?? {}) as Record<string, unknown>;\n const p = (intent.params ?? {}) as Record<string, unknown>;\n const bits = intent.action_type === \"shell.exec\" ? String(p.program ?? \"\")\n : intent.action_type === \"git.push\" ? `${p.remote ?? \"\"} ${p.ref ?? \"\"}`.trim()\n : intent.action_type === \"file.write\" || intent.action_type === \"file.read\" ? String(p.path ?? \"\")\n : intent.action_type === \"mcp.tool.call\" ? `${p.server ?? \"\"}/${p.tool ?? \"\"}`\n : intent.action_type === \"net.fetch\" ? String(p.host ?? \"\") : \"\";\n return `${String(intent.action_type ?? \"?\")}${bits ? ` ${bits}` : \"\"}`;\n}\n\nasync function runLog(args: string[]): Promise<void> {\n const dir = configDir();\n const dbPath = join(dir, \"receipts.db\");\n if (!existsSync(dbPath)) { console.log(\"no receipts yet — run a command in the agent first.\"); process.exit(0); }\n const nIdx = args.indexOf(\"-n\");\n const n = nIdx >= 0 ? Math.max(1, Number(args[nIdx + 1]) || 20) : 20;\n const { store } = openReceiptStore({ db: dbPath });\n const all = await Promise.resolve(store.list());\n const recent = all.slice(-n);\n if (recent.length === 0) { console.log(\"no receipts yet.\"); process.exit(0); }\n for (const r of recent) {\n const p = r.payload as unknown as Record<string, unknown>;\n console.log(`${String(p.timestamp ?? \"\")} ${decisionOf(p).padEnd(13)} ${describeIntent(p)}`);\n }\n console.log(`\\n${recent.length} of ${all.length} receipt(s). Verify them: scopebond-hook verify`);\n}\n\nasync function runVerify(): Promise<void> {\n const dir = configDir();\n const dbPath = join(dir, \"receipts.db\");\n const attesterPath = join(dir, \"attester.key\");\n if (!existsSync(dbPath) || !existsSync(attesterPath)) { console.log(\"nothing to verify yet (no receipts or no attester key).\"); process.exit(0); }\n const { attester } = loadOrCreateAttester({ file: attesterPath });\n const { store } = openReceiptStore({ db: dbPath });\n const all = await Promise.resolve(store.list());\n let ok = 0;\n const bad: string[] = [];\n for (const r of all) {\n const result = verifyReceipt(r, attester.publicKeyPem) as unknown as Record<string, boolean>;\n if (result.valid) ok += 1;\n else {\n const failed = Object.entries(result).filter(([k, v]) => k.endsWith(\"_valid\") && v === false).map(([k]) => k);\n bad.push(`${String((r.payload as unknown as Record<string, unknown>).timestamp ?? \"\")}: ${failed.join(\", \") || \"invalid\"}`);\n }\n }\n console.log(`${ok}/${all.length} receipt(s) verify offline against ${attesterPath}.`);\n if (bad.length) { for (const b of bad) console.error(` ✗ ${b}`); process.exit(1); }\n process.exit(0);\n}\n\nasync function runTest(args: string[]): Promise<void> {\n const command = args.find((a) => !a.startsWith(\"-\"));\n if (!command) { console.error('usage: scopebond-hook test \"<shell command>\"'); process.exit(1); }\n const dir = configDir();\n if (!existsSync(join(dir, \"policy.json\"))) { console.error(\"no policy yet — run `scopebond-hook init` first.\"); process.exit(1); }\n // Evaluate against the real policy and keys, but a throwaway store, so `test`\n // never records a receipt or exports anything.\n const tmp = mkdtempSync(join(tmpdir(), \"sb-hook-test-\"));\n try {\n const runtime = createHookRuntime({\n policyPath: join(dir, \"policy.json\"), keyPath: join(dir, \"agent.key\"),\n attesterPath: join(dir, \"attester.key\"), dbPath: join(tmp, \"receipts.db\"),\n strict: process.argv.includes(\"--strict\"),\n });\n const mapped = fillPushBranch(mapClaudeToolUse({ tool_name: \"Bash\", tool_input: { command } }), currentBranch(process.cwd()));\n console.log(`command: ${command}`);\n for (const m of mapped) {\n const d = await runtime.evaluateOne(m);\n console.log(` ${describeIntent({ intent: m.intent }).padEnd(28)} → ${d.decision}${d.reason ? ` (${d.reason})` : \"\"}`);\n }\n const overall = await runtime.evaluate(mapped);\n console.log(`\\noverall: ${overall.decision}${overall.reason ? ` · ${overall.reason}` : \"\"}`);\n process.exit(overall.decision === \"deny\" ? 2 : 0);\n } finally {\n rmSync(tmp, { recursive: true, force: true });\n }\n}\n\nasync function runConnect(args: string[]): Promise<void> {\n const positional = args.filter((a) => !a.startsWith(\"--\"));\n const url = positional[0];\n const bundleArg = positional[1];\n const harness = args.includes(\"--cursor\") ? \"cursor\" : \"claude\";\n if (!url) {\n console.error(\"usage: scopebond-hook connect <workspace-url> <enrollment> [--cursor] [--no-install]\");\n process.exit(1);\n }\n const dir = configDir();\n // One command sets everything up: scaffold the key, attester and starter policy if\n // they do not exist, then enroll and persist the scoped machine credential. The\n // enrollment can be a file, an inline base64 blob (what the portal hands out) or\n // raw JSON — the user never has to save or open a JSON file.\n scaffold(dir, {});\n let bundle: CloudEnrollmentBundle;\n try { bundle = readBundleArg(bundleArg, readStdin); }\n catch { console.error(\"could not read the enrollment (expected a file, inline blob, or JSON on stdin)\"); process.exit(1); }\n try {\n const c = await connectCloud(dir, url, bundle);\n console.log(`✓ Connected to ${c.url}`);\n // Configure the agent automatically (merges into the existing config), unless the\n // caller opts out. This removes the \"paste this snippet\" step.\n if (!args.includes(\"--no-install\")) {\n const file = installHarness(harness);\n console.log(`✓ ${harness === \"cursor\" ? \"Cursor\" : \"Claude Code\"} configured in ${file}`);\n } else {\n console.log(`Add this to your ${harness === \"cursor\" ? \".cursor/hooks.json\" : \".claude/settings.json\"}:`);\n console.log(harnessSnippet(harness));\n }\n console.log(\"\");\n console.log(\"Run your agent — the first action appears in your workspace within seconds.\");\n } catch (error) {\n console.error(`connect failed: ${(error as Error).message}`);\n process.exit(1);\n }\n}\n\nasync function runFlush(): Promise<void> {\n const dir = configDir();\n if (!loadConnection(dir)) { console.error(\"not connected to a workspace; run `scopebond-hook connect` first\"); process.exit(1); }\n const runtime = createHookRuntime(runtimePaths(dir));\n await runtime.exporter?.flush();\n const status = runtime.exporter?.status();\n console.log(`flushed; ${status?.pending ?? 0} receipt(s) still pending${status?.lastError ? ` (last error: ${status.lastError})` : \"\"}`);\n process.exit(status && status.pending > 0 ? 1 : 0);\n}\n\nconst [cmd, ...rest] = process.argv.slice(2);\nif (cmd === \"claude\") { await runClaude(); }\nelse if (cmd === \"cursor\") { await runCursor(); }\nelse if (cmd === \"init\") { runInit(rest); }\nelse if (cmd === \"connect\") { await runConnect(rest); }\nelse if (cmd === \"log\") { await runLog(rest); }\nelse if (cmd === \"verify\") { await runVerify(); }\nelse if (cmd === \"test\") { await runTest(rest); }\nelse if (cmd === \"flush\") { await runFlush(); }\nelse {\n console.error(\"usage: scopebond-hook <claude|cursor|init|connect|log|verify|test|flush> [--cursor] [--force] [--strict] [--no-install]\");\n process.exit(1);\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export { mapClaudeToolUse, mapCursorEvent } from "./map.js";
1
+ export { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from "./map.js";
2
2
  export type { Mapped, NormalizedIntent } from "./map.js";
3
3
  export { createHookRuntime, starterPolicy } from "./runtime.js";
4
4
  export type { RuntimeConfig, Decision } from "./runtime.js";
5
5
  export { scaffold, harnessSnippet, installHarness } from "./init.js";
6
6
  export { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from "./cloud.js";
7
7
  export type { HookConnection } from "./cloud.js";
8
- export { scrubSecrets, redactCommand, digest, sha256 } from "./minimize.js";
8
+ export { scrubSecrets, scrubParam, redactCommand, digest, sha256 } from "./minimize.js";
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC5D,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACxG,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC5E,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACxG,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // @scopebond/hook — the Claude Code + Cursor connector. The library surface is
2
2
  // the deterministic mapper and the check-only runtime; the `scopebond-hook`
3
3
  // binary is the thin harness adapter over them.
4
- export { mapClaudeToolUse, mapCursorEvent } from "./map.js";
4
+ export { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from "./map.js";
5
5
  export { createHookRuntime, starterPolicy } from "./runtime.js";
6
6
  export { scaffold, harnessSnippet, installHarness } from "./init.js";
7
7
  export { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from "./cloud.js";
8
- export { scrubSecrets, redactCommand, digest, sha256 } from "./minimize.js";
8
+ export { scrubSecrets, scrubParam, redactCommand, digest, sha256 } from "./minimize.js";
9
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,4EAA4E;AAC5E,gDAAgD;AAEhD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAExG,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC","sourcesContent":["// @scopebond/hook — the Claude Code + Cursor connector. The library surface is\n// the deterministic mapper and the check-only runtime; the `scopebond-hook`\n// binary is the thin harness adapter over them.\n\nexport { mapClaudeToolUse, mapCursorEvent } from \"./map.js\";\nexport type { Mapped, NormalizedIntent } from \"./map.js\";\nexport { createHookRuntime, starterPolicy } from \"./runtime.js\";\nexport type { RuntimeConfig, Decision } from \"./runtime.js\";\nexport { scaffold, harnessSnippet, installHarness } from \"./init.js\";\nexport { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from \"./cloud.js\";\nexport type { HookConnection } from \"./cloud.js\";\nexport { scrubSecrets, redactCommand, digest, sha256 } from \"./minimize.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,4EAA4E;AAC5E,gDAAgD;AAEhD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAExG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC","sourcesContent":["// @scopebond/hook — the Claude Code + Cursor connector. The library surface is\n// the deterministic mapper and the check-only runtime; the `scopebond-hook`\n// binary is the thin harness adapter over them.\n\nexport { mapClaudeToolUse, mapCursorEvent, fillPushBranch } from \"./map.js\";\nexport type { Mapped, NormalizedIntent } from \"./map.js\";\nexport { createHookRuntime, starterPolicy } from \"./runtime.js\";\nexport type { RuntimeConfig, Decision } from \"./runtime.js\";\nexport { scaffold, harnessSnippet, installHarness } from \"./init.js\";\nexport { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from \"./cloud.js\";\nexport type { HookConnection } from \"./cloud.js\";\nexport { scrubSecrets, scrubParam, redactCommand, digest, sha256 } from \"./minimize.js\";\n"]}
package/dist/init.d.ts CHANGED
@@ -9,6 +9,6 @@ export declare function scaffold(dir: string, opts?: {
9
9
  * lets `connect` finish setup without the user hand-editing JSON.
10
10
  * `cwd` defaults to the current directory (the project root the user runs it in). */
11
11
  export declare function installHarness(harness: "claude" | "cursor", cwd?: string): string;
12
- /** The harness configuration snippet to install the hook. */
12
+ /** The harness configuration snippet to install the hook (version-pinned npx). */
13
13
  export declare function harnessSnippet(harness: "claude" | "cursor"): string;
14
14
  //# sourceMappingURL=init.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAUA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAgB9G;AAED;;;sFAGsF;AACtF,wBAAgB,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CAsBhG;AAED,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAiBnE"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAWA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAgB9G;AAED;;;sFAGsF;AACtF,wBAAgB,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CA6BhG;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAkBnE"}
package/dist/init.js CHANGED
@@ -6,6 +6,7 @@ import { dirname, join } from "node:path";
6
6
  import { loadOrCreateAttester } from "@scopebond/gateway/node";
7
7
  import { createSigner } from "@scopebond/sdk";
8
8
  import { starterPolicy } from "./runtime.js";
9
+ import { hookCommand } from "./version.js";
9
10
  export function scaffold(dir, opts = {}) {
10
11
  mkdirSync(dir, { recursive: true });
11
12
  const keyPath = join(dir, "agent.key");
@@ -42,40 +43,52 @@ export function installHarness(harness, cwd = process.cwd()) {
42
43
  catch { /* start fresh on unreadable */ }
43
44
  }
44
45
  const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});
45
- const has = (list, cmd) => Array.isArray(list) && list.some((e) => isRecord(e) && (e.command === cmd
46
- || (Array.isArray(e.hooks) && e.hooks.some((h) => isRecord(h) && h.command === cmd))));
46
+ const cursorCmd = hookCommand("cursor");
47
+ const claudeCmd = hookCommand("claude");
48
+ // Match any existing Scopebond hook entry (pinned or a legacy bare `scopebond-hook`)
49
+ // so re-running never appends a duplicate and an old bare command is replaced.
50
+ const isScopebond = (cmd) => typeof cmd === "string" && /(^|\s)(npx\s+.*)?@scopebond\/hook|(^|\s)scopebond-hook(\s|$)/.test(cmd);
51
+ const entryMatches = (e) => isRecord(e) && (isScopebond(e.command) || (Array.isArray(e.hooks) && e.hooks.some((h) => isRecord(h) && isScopebond(h.command))));
47
52
  if (harness === "cursor") {
48
53
  config.version = config.version ?? 1;
49
54
  for (const event of ["beforeShellExecution", "beforeMCPExecution", "beforeReadFile", "afterFileEdit"]) {
50
55
  const list = Array.isArray(hooks[event]) ? hooks[event] : (hooks[event] = []);
51
- if (!has(list, "scopebond-hook cursor"))
52
- list.push({ command: "scopebond-hook cursor" });
56
+ const existing = list.findIndex(entryMatches);
57
+ if (existing >= 0)
58
+ list[existing] = { command: cursorCmd };
59
+ else
60
+ list.push({ command: cursorCmd });
53
61
  }
54
62
  }
55
63
  else {
56
64
  const list = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : (hooks.PreToolUse = []);
57
- if (!has(list, "scopebond-hook claude"))
58
- list.push({ matcher: "*", hooks: [{ type: "command", command: "scopebond-hook claude" }] });
65
+ const existing = list.findIndex(entryMatches);
66
+ const entry = { matcher: "*", hooks: [{ type: "command", command: claudeCmd }] };
67
+ if (existing >= 0)
68
+ list[existing] = entry;
69
+ else
70
+ list.push(entry);
59
71
  }
60
72
  writeFileSync(file, JSON.stringify(config, null, 2) + "\n");
61
73
  return file;
62
74
  }
63
- /** The harness configuration snippet to install the hook. */
75
+ /** The harness configuration snippet to install the hook (version-pinned npx). */
64
76
  export function harnessSnippet(harness) {
65
77
  if (harness === "cursor") {
78
+ const cmd = hookCommand("cursor");
66
79
  return JSON.stringify({
67
80
  version: 1,
68
81
  hooks: {
69
- beforeShellExecution: [{ command: "scopebond-hook cursor" }],
70
- beforeMCPExecution: [{ command: "scopebond-hook cursor" }],
71
- beforeReadFile: [{ command: "scopebond-hook cursor" }],
72
- afterFileEdit: [{ command: "scopebond-hook cursor" }],
82
+ beforeShellExecution: [{ command: cmd }],
83
+ beforeMCPExecution: [{ command: cmd }],
84
+ beforeReadFile: [{ command: cmd }],
85
+ afterFileEdit: [{ command: cmd }],
73
86
  },
74
87
  }, null, 2);
75
88
  }
76
89
  return JSON.stringify({
77
90
  hooks: {
78
- PreToolUse: [{ matcher: "*", hooks: [{ type: "command", command: "scopebond-hook claude" }] }],
91
+ PreToolUse: [{ matcher: "*", hooks: [{ type: "command", command: hookCommand("claude") }] }],
79
92
  },
80
93
  }, null, 2);
81
94
  }
package/dist/init.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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","sourcesContent":["// `scopebond-hook init` — scaffold the local enrollment: a machine signing key, a\n// gateway countersigning key and a starter policy. Keys and receipts stay on the\n// machine; no credentials are handled.\n\nimport { mkdirSync, writeFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { loadOrCreateAttester } from \"@scopebond/gateway/node\";\nimport { createSigner } from \"@scopebond/sdk\";\nimport { starterPolicy } from \"./runtime.js\";\n\nexport function scaffold(dir: string, opts: { force?: boolean } = {}): { agentKid: string; policyPath: string } {\n mkdirSync(dir, { recursive: true });\n const keyPath = join(dir, \"agent.key\");\n const attesterPath = join(dir, \"attester.key\");\n const policyPath = join(dir, \"policy.json\");\n // Never let the signing keys, the Cloud credential or the local log be committed.\n const gitignorePath = join(dir, \".gitignore\");\n if (!existsSync(gitignorePath)) writeFileSync(gitignorePath, \"*\\n\");\n // Machine signing key (agent) + gateway countersigning key (attester). Reused if present.\n loadOrCreateAttester({ file: keyPath });\n loadOrCreateAttester({ file: attesterPath });\n const agent = createSigner({ privateKeyPem: readFileSync(keyPath, \"utf8\") });\n if (!existsSync(policyPath) || opts.force) {\n writeFileSync(policyPath, JSON.stringify(starterPolicy(agent.kid), null, 2) + \"\\n\");\n }\n return { agentKid: agent.kid, policyPath };\n}\n\n/** Install the hook into the agent's own config file, merging with anything already\n * there (idempotent — safe to run twice). Returns the file it wrote. This is what\n * lets `connect` finish setup without the user hand-editing JSON.\n * `cwd` defaults to the current directory (the project root the user runs it in). */\nexport function installHarness(harness: \"claude\" | \"cursor\", cwd: string = process.cwd()): string {\n const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n const file = harness === \"cursor\" ? join(cwd, \".cursor\", \"hooks.json\") : join(cwd, \".claude\", \"settings.json\");\n mkdirSync(dirname(file), { recursive: true });\n let config: Record<string, unknown> = {};\n if (existsSync(file)) { try { const p = JSON.parse(readFileSync(file, \"utf8\")); if (isRecord(p)) config = p; } catch { /* start fresh on unreadable */ } }\n const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});\n const has = (list: unknown, cmd: string): boolean =>\n Array.isArray(list) && list.some((e) => isRecord(e) && (e.command === cmd\n || (Array.isArray(e.hooks) && e.hooks.some((h) => isRecord(h) && h.command === cmd))));\n if (harness === \"cursor\") {\n config.version = config.version ?? 1;\n for (const event of [\"beforeShellExecution\", \"beforeMCPExecution\", \"beforeReadFile\", \"afterFileEdit\"]) {\n const list = Array.isArray((hooks as Record<string, unknown>)[event]) ? (hooks as Record<string, unknown[]>)[event] : ((hooks as Record<string, unknown[]>)[event] = []);\n if (!has(list, \"scopebond-hook cursor\")) list.push({ command: \"scopebond-hook cursor\" });\n }\n } else {\n const list = Array.isArray((hooks as Record<string, unknown>).PreToolUse) ? (hooks as Record<string, unknown[]>).PreToolUse : ((hooks as Record<string, unknown[]>).PreToolUse = []);\n if (!has(list, \"scopebond-hook claude\")) list.push({ matcher: \"*\", hooks: [{ type: \"command\", command: \"scopebond-hook claude\" }] });\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return file;\n}\n\n/** The harness configuration snippet to install the hook. */\nexport function harnessSnippet(harness: \"claude\" | \"cursor\"): string {\n if (harness === \"cursor\") {\n return JSON.stringify({\n version: 1,\n hooks: {\n beforeShellExecution: [{ command: \"scopebond-hook cursor\" }],\n beforeMCPExecution: [{ command: \"scopebond-hook cursor\" }],\n beforeReadFile: [{ command: \"scopebond-hook cursor\" }],\n afterFileEdit: [{ command: \"scopebond-hook cursor\" }],\n },\n }, null, 2);\n }\n return JSON.stringify({\n hooks: {\n PreToolUse: [{ matcher: \"*\", hooks: [{ type: \"command\", command: \"scopebond-hook claude\" }] }],\n },\n }, null, 2);\n}\n"]}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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","sourcesContent":["// `scopebond-hook init` — scaffold the local enrollment: a machine signing key, a\n// gateway countersigning key and a starter policy. Keys and receipts stay on the\n// machine; no credentials are handled.\n\nimport { mkdirSync, writeFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { loadOrCreateAttester } from \"@scopebond/gateway/node\";\nimport { createSigner } from \"@scopebond/sdk\";\nimport { starterPolicy } from \"./runtime.js\";\nimport { hookCommand } from \"./version.js\";\n\nexport function scaffold(dir: string, opts: { force?: boolean } = {}): { agentKid: string; policyPath: string } {\n mkdirSync(dir, { recursive: true });\n const keyPath = join(dir, \"agent.key\");\n const attesterPath = join(dir, \"attester.key\");\n const policyPath = join(dir, \"policy.json\");\n // Never let the signing keys, the Cloud credential or the local log be committed.\n const gitignorePath = join(dir, \".gitignore\");\n if (!existsSync(gitignorePath)) writeFileSync(gitignorePath, \"*\\n\");\n // Machine signing key (agent) + gateway countersigning key (attester). Reused if present.\n loadOrCreateAttester({ file: keyPath });\n loadOrCreateAttester({ file: attesterPath });\n const agent = createSigner({ privateKeyPem: readFileSync(keyPath, \"utf8\") });\n if (!existsSync(policyPath) || opts.force) {\n writeFileSync(policyPath, JSON.stringify(starterPolicy(agent.kid), null, 2) + \"\\n\");\n }\n return { agentKid: agent.kid, policyPath };\n}\n\n/** Install the hook into the agent's own config file, merging with anything already\n * there (idempotent — safe to run twice). Returns the file it wrote. This is what\n * lets `connect` finish setup without the user hand-editing JSON.\n * `cwd` defaults to the current directory (the project root the user runs it in). */\nexport function installHarness(harness: \"claude\" | \"cursor\", cwd: string = process.cwd()): string {\n const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n const file = harness === \"cursor\" ? join(cwd, \".cursor\", \"hooks.json\") : join(cwd, \".claude\", \"settings.json\");\n mkdirSync(dirname(file), { recursive: true });\n let config: Record<string, unknown> = {};\n if (existsSync(file)) { try { const p = JSON.parse(readFileSync(file, \"utf8\")); if (isRecord(p)) config = p; } catch { /* start fresh on unreadable */ } }\n const hooks = isRecord(config.hooks) ? config.hooks : (config.hooks = {});\n const cursorCmd = hookCommand(\"cursor\");\n const claudeCmd = hookCommand(\"claude\");\n // Match any existing Scopebond hook entry (pinned or a legacy bare `scopebond-hook`)\n // so re-running never appends a duplicate and an old bare command is replaced.\n const isScopebond = (cmd: unknown): boolean => typeof cmd === \"string\" && /(^|\\s)(npx\\s+.*)?@scopebond\\/hook|(^|\\s)scopebond-hook(\\s|$)/.test(cmd);\n const entryMatches = (e: unknown): boolean =>\n isRecord(e) && (isScopebond(e.command) || (Array.isArray(e.hooks) && e.hooks.some((h) => isRecord(h) && isScopebond(h.command))));\n if (harness === \"cursor\") {\n config.version = config.version ?? 1;\n for (const event of [\"beforeShellExecution\", \"beforeMCPExecution\", \"beforeReadFile\", \"afterFileEdit\"]) {\n const list = Array.isArray((hooks as Record<string, unknown>)[event]) ? (hooks as Record<string, unknown[]>)[event] : ((hooks as Record<string, unknown[]>)[event] = []);\n const existing = list.findIndex(entryMatches);\n if (existing >= 0) list[existing] = { command: cursorCmd }; else list.push({ command: cursorCmd });\n }\n } else {\n const list = Array.isArray((hooks as Record<string, unknown>).PreToolUse) ? (hooks as Record<string, unknown[]>).PreToolUse : ((hooks as Record<string, unknown[]>).PreToolUse = []);\n const existing = list.findIndex(entryMatches);\n const entry = { matcher: \"*\", hooks: [{ type: \"command\", command: claudeCmd }] };\n if (existing >= 0) list[existing] = entry; else list.push(entry);\n }\n writeFileSync(file, JSON.stringify(config, null, 2) + \"\\n\");\n return file;\n}\n\n/** The harness configuration snippet to install the hook (version-pinned npx). */\nexport function harnessSnippet(harness: \"claude\" | \"cursor\"): string {\n if (harness === \"cursor\") {\n const cmd = hookCommand(\"cursor\");\n return JSON.stringify({\n version: 1,\n hooks: {\n beforeShellExecution: [{ command: cmd }],\n beforeMCPExecution: [{ command: cmd }],\n beforeReadFile: [{ command: cmd }],\n afterFileEdit: [{ command: cmd }],\n },\n }, null, 2);\n }\n return JSON.stringify({\n hooks: {\n PreToolUse: [{ matcher: \"*\", hooks: [{ type: \"command\", command: hookCommand(\"claude\") }] }],\n },\n }, null, 2);\n}\n"]}
package/dist/map.d.ts CHANGED
@@ -4,14 +4,20 @@ export interface NormalizedIntent {
4
4
  }
5
5
  export interface Mapped {
6
6
  intent: NormalizedIntent;
7
- /** false when no taxonomy type applies: emitted as tool.<name> and routed to the
8
- * observation path (not_evaluated), never granted. */
7
+ /** false when no taxonomy type applies (an unknown tool, or a shell command that
8
+ * could not be parsed): emitted to the observation path (not_evaluated) in normal
9
+ * mode and denied by the closed allowlist in strict mode — never granted. */
9
10
  evaluated: boolean;
10
11
  /** The native tool/event name, for diagnostics. */
11
12
  source: string;
12
13
  }
13
- /** Map a Claude Code PreToolUse payload to a normalized taxonomy action. */
14
- export declare function mapClaudeToolUse(input: Record<string, unknown>): Mapped;
15
- /** Map a Cursor hook event to a normalized taxonomy action. */
16
- export declare function mapCursorEvent(event: string, payload: Record<string, unknown>): Mapped;
14
+ /** A bare `git push` (no ref) pushes the current branch. The mapper cannot know it,
15
+ * so the runtime resolves it and fills it in here, before evaluation — otherwise the
16
+ * starter policy's ref bound would fail-closed on every plain push. Only git.push
17
+ * intents with no ref are touched. */
18
+ export declare function fillPushBranch(mapped: Mapped[], branch: string | null | undefined): Mapped[];
19
+ /** Map a Claude Code PreToolUse payload to the normalized actions it represents. */
20
+ export declare function mapClaudeToolUse(input: Record<string, unknown>): Mapped[];
21
+ /** Map a Cursor hook event to the normalized actions it represents. */
22
+ export declare function mapCursorEvent(event: string, payload: Record<string, unknown>): Mapped[];
17
23
  //# sourceMappingURL=map.d.ts.map
package/dist/map.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AACD,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB;2DACuD;IACvD,SAAS,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB;AAqDD,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAkBvE;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAkBtF"}
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AACD,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB;;kFAE8E;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB;AA+DD;;;uCAGuC;AACvC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAM5F;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAkBzE;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAkBxF"}