@webpieces/ai-hook-rules 0.3.163 → 0.3.164

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/ai-hook-rules",
3
- "version": "0.3.163",
3
+ "version": "0.3.164",
4
4
  "description": "Pluggable write-time validation framework for AI coding agents (@webpieces/ai-hook-rules). Claude Code PreToolUse + openclaw before_tool_call adapters share one rule engine.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -36,7 +36,7 @@
36
36
  "directory": "packages/tooling/ai-hook-rules"
37
37
  },
38
38
  "dependencies": {
39
- "@webpieces/rules-config": "0.3.163"
39
+ "@webpieces/rules-config": "0.3.164"
40
40
  },
41
41
  "publishConfig": {
42
42
  "access": "public"
@@ -45,7 +45,7 @@ function run(rawInput) {
45
45
  const localHook = (0, path_1.join)(cwd, 'node_modules', '.bin', 'wp-ai-hook');
46
46
  if ((0, fs_1.existsSync)(localHook)) {
47
47
  const result = (0, child_process_1.spawnSync)(localHook, [], {
48
- input: rawInput,
48
+ input: Buffer.from(rawInput),
49
49
  encoding: 'buffer',
50
50
  });
51
51
  if (result.stdout?.length)
@@ -1 +1 @@
1
- {"version":3,"file":"global-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/global-hook.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { spawnSync } from 'child_process';\n\ninterface SkipHooksFile {\n expires: number | null;\n reason?: string;\n}\n\ninterface HookPayload {\n tool_name?: string;\n tool_input?: {\n file_path?: string;\n };\n}\n\nfunction readSkipHooks(cwd: string): SkipHooksFile | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const skipPath = join(cwd, '.webpieces', 'skiphooks');\n if (!existsSync(skipPath)) return null;\n return JSON.parse(readFileSync(skipPath, 'utf8')) as SkipHooksFile;\n } catch (err: unknown) {\n // eslint-disable-next-line @webpieces/catch-error-pattern -- intentionally discard; malformed .skiphooks must not crash global hook\n return null;\n }\n}\n\nfunction run(rawInput: string): void {\n const cwd = process.cwd();\n\n let payload: HookPayload | null = null;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n if (rawInput.trim()) {\n payload = JSON.parse(rawInput) as HookPayload;\n }\n } catch (err: unknown) {\n //const error = toError(err); -- malformed stdin is intentionally ignored; must not crash global hook\n }\n\n // 1. Check .skiphooks — if valid and not expired, allow everything\n const skipHooks = readSkipHooks(cwd);\n if (skipHooks !== null) {\n const nowSeconds = Date.now() / 1000;\n if (skipHooks.expires === null || nowSeconds < skipHooks.expires) {\n process.exit(0);\n }\n }\n\n // 2. Always allow writing .webpieces/skiphooks (this is the escape hatch)\n const filePath = payload?.tool_input?.file_path ?? '';\n if (payload?.tool_name === 'Write' && filePath.endsWith('.webpieces/skiphooks')) {\n process.exit(0);\n }\n\n // 3. Delegate to per-repo hook if present\n const localHook = join(cwd, 'node_modules', '.bin', 'wp-ai-hook');\n if (existsSync(localHook)) {\n const result = spawnSync(localHook, [], {\n input: rawInput,\n encoding: 'buffer',\n });\n if (result.stdout?.length) process.stdout.write(result.stdout);\n if (result.stderr?.length) process.stderr.write(result.stderr);\n process.exit(result.status ?? 0);\n }\n\n // 4. No local hook found — block and instruct AI\n process.stdout.write(\n 'This project is missing the webpieces AI hook (./node_modules/.bin/wp-ai-hook not found).\\n' +\n 'Ask the human one of:\\n' +\n ' A) Install webpieces: run `npm install @webpieces/ai-hook-rules` then retry\\n' +\n ' B) Skip hooks: ask how long (1 hour / 1 day / forever), then write .webpieces/skiphooks:\\n' +\n ' {\"expires\": <unix epoch seconds>, \"reason\": \"...\"}\\n' +\n ' or for forever: {\"expires\": null, \"reason\": \"...\"}\\n' +\n ' Note: writing .webpieces/skiphooks is ALWAYS allowed even while blocked.\\n',\n );\n process.exit(2);\n}\n\nlet stdinData = '';\nprocess.stdin.resume();\nprocess.stdin.setEncoding('utf8');\nprocess.stdin.on('data', (chunk: string) => {\n stdinData += chunk;\n});\nprocess.stdin.on('end', () => {\n run(stdinData);\n});\n"]}
1
+ {"version":3,"file":"global-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/global-hook.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { spawnSync } from 'child_process';\n\ninterface SkipHooksFile {\n expires: number | null;\n reason?: string;\n}\n\ninterface HookPayload {\n tool_name?: string;\n tool_input?: {\n file_path?: string;\n };\n}\n\nfunction readSkipHooks(cwd: string): SkipHooksFile | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const skipPath = join(cwd, '.webpieces', 'skiphooks');\n if (!existsSync(skipPath)) return null;\n return JSON.parse(readFileSync(skipPath, 'utf8')) as SkipHooksFile;\n } catch (err: unknown) {\n // eslint-disable-next-line @webpieces/catch-error-pattern -- intentionally discard; malformed .skiphooks must not crash global hook\n return null;\n }\n}\n\nfunction run(rawInput: string): void {\n const cwd = process.cwd();\n\n let payload: HookPayload | null = null;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n if (rawInput.trim()) {\n payload = JSON.parse(rawInput) as HookPayload;\n }\n } catch (err: unknown) {\n //const error = toError(err); -- malformed stdin is intentionally ignored; must not crash global hook\n }\n\n // 1. Check .skiphooks — if valid and not expired, allow everything\n const skipHooks = readSkipHooks(cwd);\n if (skipHooks !== null) {\n const nowSeconds = Date.now() / 1000;\n if (skipHooks.expires === null || nowSeconds < skipHooks.expires) {\n process.exit(0);\n }\n }\n\n // 2. Always allow writing .webpieces/skiphooks (this is the escape hatch)\n const filePath = payload?.tool_input?.file_path ?? '';\n if (payload?.tool_name === 'Write' && filePath.endsWith('.webpieces/skiphooks')) {\n process.exit(0);\n }\n\n // 3. Delegate to per-repo hook if present\n const localHook = join(cwd, 'node_modules', '.bin', 'wp-ai-hook');\n if (existsSync(localHook)) {\n const result = spawnSync(localHook, [], {\n input: Buffer.from(rawInput),\n encoding: 'buffer',\n });\n if (result.stdout?.length) process.stdout.write(result.stdout);\n if (result.stderr?.length) process.stderr.write(result.stderr);\n process.exit(result.status ?? 0);\n }\n\n // 4. No local hook found — block and instruct AI\n process.stdout.write(\n 'This project is missing the webpieces AI hook (./node_modules/.bin/wp-ai-hook not found).\\n' +\n 'Ask the human one of:\\n' +\n ' A) Install webpieces: run `npm install @webpieces/ai-hook-rules` then retry\\n' +\n ' B) Skip hooks: ask how long (1 hour / 1 day / forever), then write .webpieces/skiphooks:\\n' +\n ' {\"expires\": <unix epoch seconds>, \"reason\": \"...\"}\\n' +\n ' or for forever: {\"expires\": null, \"reason\": \"...\"}\\n' +\n ' Note: writing .webpieces/skiphooks is ALWAYS allowed even while blocked.\\n',\n );\n process.exit(2);\n}\n\nlet stdinData = '';\nprocess.stdin.resume();\nprocess.stdin.setEncoding('utf8');\nprocess.stdin.on('data', (chunk: string) => {\n stdinData += chunk;\n});\nprocess.stdin.on('end', () => {\n run(stdinData);\n});\n"]}