diff --git a/.claude/scripts/block-node.ts b/.claude/scripts/block-node.ts new file mode 100644 index 000000000..4de815317 --- /dev/null +++ b/.claude/scripts/block-node.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env -S deno run --allow-read +/** + * .claude/scripts/block-node.ts + * + * Claude Code Pre-Tool hook. + * - Blocks shell commands that invoke npm, npx, yarn, pnpm, or node. + * - Prints a Deno-friendly reminder to stderr. + * - Exits 2 so Claude blocks the tool call and shows the message. + */ + +const rawInput = await new Response(Deno.stdin.readable).text(); + +let cmd = ""; +try { + const payload = JSON.parse(rawInput); + cmd = payload?.tool_input?.command ?? ""; +} catch { + // If the JSON is malformed we allow the call rather than choke the hook. +} + +if (/\b(npm|npx|yarn|pnpm|node)\b/.test(cmd)) { + console.error( + "We use **Deno** in this repo – please rewrite the command accordingly.", + ); + Deno.exit(2); // Claude interprets exit-code 2 as “block & surface stderr” +} + +Deno.exit(0); // Let the tool call proceed diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..7a1a63b02 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,26 @@ +{ + "permissions": { + "allow": [ + "Bash(grep:*)", + "Bash(deno test:*)", + "Bash(deno task test:*)", + "Bash(find:*)", + "Bash(deno lint:*)", + "Bash(rg:*)" + ], + "deny": [] + }, + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "deno run \"$(git rev-parse --show-toplevel)/.claude/scripts/block-node.ts\"" + } + ] + } + ] + } +} diff --git a/.gitignore b/.gitignore index 2462c1755..5702b4fcb 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ packages/seeder/results .claude-prompt.tmp .pr-desc.tmp dist/ +.claude/settings.local.json