@webpieces/ai-hook-rules 0.4.569 → 0.4.570

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,14 +1,14 @@
1
1
  {
2
2
  "name": "@webpieces/ai-hook-rules",
3
- "version": "0.4.569",
3
+ "version": "0.4.570",
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",
7
7
  "bin": {
8
- "wp-ai-rules-hook": "./bin/wp-ai-rules-hook.js",
9
- "wp-ai-guards-hook": "./bin/wp-ai-guards-hook.js",
10
- "wp-install-ai-hooks": "./bin/wp-install-ai-hooks.js",
11
- "wp-upgrade-shim": "./bin/wp-upgrade-shim.js"
8
+ "wp-ai-rules-hook": "./src/adapters/rules-hook.js",
9
+ "wp-ai-guards-hook": "./src/adapters/guards-hook.js",
10
+ "wp-install-ai-hooks": "./src/bin/install-entry.js",
11
+ "wp-upgrade-shim": "./src/bin/upgrade-shim.js"
12
12
  },
13
13
  "exports": {
14
14
  ".": "./src/index.js",
@@ -19,7 +19,6 @@
19
19
  },
20
20
  "files": [
21
21
  "src/**/*",
22
- "bin/**/*",
23
22
  "templates/**/*",
24
23
  "openclaw.plugin.json",
25
24
  "README.md"
@@ -32,7 +31,7 @@
32
31
  "directory": "packages/tooling/ai-hook-rules"
33
32
  },
34
33
  "dependencies": {
35
- "@webpieces/rules-config": "0.4.569"
34
+ "@webpieces/rules-config": "0.4.570"
36
35
  },
37
36
  "publishConfig": {
38
37
  "access": "public"
@@ -1 +1,2 @@
1
+ #!/usr/bin/env node
1
2
  export declare function main(): Promise<void>;
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.main = main;
@@ -1 +1 @@
1
- {"version":3,"file":"guards-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/guards-hook.ts"],"names":[],"mappings":";;AAIA,oBAEC;AAND,mFAAmF;AACnF,gGAAgG;AAChG,2CAAsC;AAEtC,SAAgB,IAAI;IAChB,OAAO,IAAA,mBAAO,EAAC,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC1B,KAAK,IAAI,EAAE,CAAC;AAChB,CAAC","sourcesContent":["// Claude Code PreToolUse adapter for the GIT/PR/BRANCH GUARDS hook (matcher Bash).\n// File-edit payloads pass through untouched — code-style validation is the separate rules hook.\nimport { runMain } from './hook-core';\n\nexport function main(): Promise<void> {\n return runMain('guards');\n}\n\nif (require.main === module) {\n void main();\n}\n"]}
1
+ {"version":3,"file":"guards-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/guards-hook.ts"],"names":[],"mappings":";;;AAKA,oBAEC;AAND,mFAAmF;AACnF,gGAAgG;AAChG,2CAAsC;AAEtC,SAAgB,IAAI;IAChB,OAAO,IAAA,mBAAO,EAAC,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC1B,KAAK,IAAI,EAAE,CAAC;AAChB,CAAC","sourcesContent":["#!/usr/bin/env node\n// Claude Code PreToolUse adapter for the GIT/PR/BRANCH GUARDS hook (matcher Bash).\n// File-edit payloads pass through untouched — code-style validation is the separate rules hook.\nimport { runMain } from './hook-core';\n\nexport function main(): Promise<void> {\n return runMain('guards');\n}\n\nif (require.main === module) {\n void main();\n}\n"]}
@@ -1 +1,2 @@
1
+ #!/usr/bin/env node
1
2
  export declare function main(): Promise<void>;
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.main = main;
@@ -1 +1 @@
1
- {"version":3,"file":"rules-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/rules-hook.ts"],"names":[],"mappings":";;AAIA,oBAEC;AAND,+FAA+F;AAC/F,iGAAiG;AACjG,2CAAsC;AAEtC,SAAgB,IAAI;IAChB,OAAO,IAAA,mBAAO,EAAC,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC1B,KAAK,IAAI,EAAE,CAAC;AAChB,CAAC","sourcesContent":["// Claude Code PreToolUse adapter for the CODE-STYLE RULES hook (matcher Write|Edit|MultiEdit).\n// Bash payloads pass through untouched — branch/PR/merge protection is the separate guards hook.\nimport { runMain } from './hook-core';\n\nexport function main(): Promise<void> {\n return runMain('rules');\n}\n\nif (require.main === module) {\n void main();\n}\n"]}
1
+ {"version":3,"file":"rules-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/rules-hook.ts"],"names":[],"mappings":";;;AAKA,oBAEC;AAND,+FAA+F;AAC/F,iGAAiG;AACjG,2CAAsC;AAEtC,SAAgB,IAAI;IAChB,OAAO,IAAA,mBAAO,EAAC,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC1B,KAAK,IAAI,EAAE,CAAC;AAChB,CAAC","sourcesContent":["#!/usr/bin/env node\n// Claude Code PreToolUse adapter for the CODE-STYLE RULES hook (matcher Write|Edit|MultiEdit).\n// Bash payloads pass through untouched — branch/PR/merge protection is the separate guards hook.\nimport { runMain } from './hook-core';\n\nexport function main(): Promise<void> {\n return runMain('rules');\n}\n\nif (require.main === module) {\n void main();\n}\n"]}
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  export declare function recoveryNotice(detail: string, shimRefreshed: boolean): string[];
2
3
  export declare function isBrokenTreeError(error: Error): boolean;
3
4
  export declare function runInstaller(cwd: string): Promise<number>;
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.recoveryNotice = recoveryNotice;
@@ -1 +1 @@
1
- {"version":3,"file":"install-entry.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/bin/install-entry.ts"],"names":[],"mappings":";;AAiCA,wCAuBC;AAiBD,8CAEC;AAKD,oCAsBC;AAtGD,iCAA8D;AAC9D,+CAA2C;AAE3C,8EAA8E;AAC9E,kFAAkF;AAClF,EAAE;AACF,yFAAyF;AACzF,oGAAoG;AACpG,sGAAsG;AACtG,qGAAqG;AACrG,oGAAoG;AACpG,sGAAsG;AACtG,kGAAkG;AAClG,2FAA2F;AAC3F,EAAE;AACF,mGAAmG;AACnG,4FAA4F;AAC5F,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oGAAoG;AACpG,qGAAqG;AACrG,8FAA8F;AAC9F,8EAA8E;AAE9E,iGAAiG;AACjG,8CAA8C;AAC9C,MAAM,GAAG,GAAG,SAAS,CAAC;AACtB,MAAM,KAAK,GAAG,MAAM,CAAC;AAErB,sGAAsG;AACtG,+EAA+E;AAC/E,iOAAiO;AACjO,SAAgB,cAAc,CAAC,MAAc,EAAE,aAAsB;IACjE,MAAM,KAAK,GAAG;QACV,GAAG,GAAG,0EAA0E,KAAK,EAAE;QACvF,EAAE;QACF,KAAK,MAAM,EAAE;QACb,EAAE;QACF,8FAA8F;QAC9F,6FAA6F;QAC7F,mEAAmE;QACnE,EAAE;QACF,GAAG,GAAG,kCAAkC,KAAK,EAAE;QAC/C,GAAG,GAAG,OAAO,mBAAY,GAAG,KAAK,EAAE;QACnC,EAAE;KACL,CAAC;IACF,IAAI,aAAa,EAAE,CAAC;QAChB,8FAA8F;QAC9F,sFAAsF;QACtF,KAAK,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;QACpG,KAAK,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;IAChG,CAAC;SAAM,CAAC;QACJ,KAAK,CAAC,IAAI,CAAC,mFAAmF,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,0FAA0F;AAC1F,MAAM,gBAAiB,SAAQ,KAAK;IACvB,IAAI,CAAU;CAC1B;AAED,qGAAqG;AACrG,MAAM,WAAW;IACb,IAAI,CAAuB;CAC9B;AAED,uGAAuG;AACvG,qGAAqG;AACrG,uGAAuG;AACvG,oGAAoG;AACpG,iOAAiO;AACjO,SAAgB,iBAAiB,CAAC,KAAY;IAC1C,OAAQ,KAA0B,CAAC,IAAI,KAAK,kBAAkB,CAAC;AACnE,CAAC;AAED,iGAAiG;AACjG,kCAAkC;AAClC,iOAAiO;AAC1N,KAAK,UAAU,YAAY,CAAC,GAAW;IAC1C,6FAA6F;IAC7F,8FAA8F;IAC9F,MAAM,aAAa,GAAG,IAAA,mBAAY,EAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IACjD,IAAA,eAAQ,EAAC,GAAG,CAAC,CAAC;IAEd,kFAAkF;IAClF,gGAAgG;IAChG,kGAAkG;IAClG,8NAA8N;IAC9N,8DAA8D;IAC9D,IAAI,CAAC;QACD,iEAAiE;QACjE,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAgB,CAAC;QAChD,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,OAAO,CAAC,CAAC;IACb,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC,CAAG,mDAAmD;QACjG,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC;YAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpG,OAAO,CAAC,CAAC;IACb,CAAC;AACL,CAAC","sourcesContent":["import { healShim, findShimRoot, RECOVERY_CMD } from './shim';\nimport { toError } from '../core/to-error';\n\n// ---------------------------------------------------------------------------\n// The `wp-install-ai-hooks` entry point, deliberately kept FREE of heavy imports.\n//\n// THE BUG THIS FIXES — the installer was a victim of the very corruption it must repair.\n// setup.ts top-level-imports @webpieces/rules-config, which imports minimatch. When node_modules is\n// corrupt — a package half-written by an install that was killed mid-copy — node dies at REQUIRE time\n// with MODULE_NOT_FOUND, before a single line of the installer runs. So `pnpm wp-install-ai-hooks` —\n// the one command that can rewrite the fail-closed shim — died with a raw 30-line node loader trace\n// and repaired nothing. Worse, `pnpm install` cannot heal that corruption either (pnpm sees the right\n// version on disk, considers the package installed, and skips it), so the repo was wedged: guards\n// down, installer unable to reinstall them, and the only signal an unreadable stack trace.\n//\n// The seam: ./shim imports NOTHING but fs and path. Writing the shim never needed the rule engine.\n// So do the repair FIRST with the dependency-free module, and only then reach for setup.ts:\n//\n// 1. healShim(cwd) — refresh the committed shim. Works on a corrupt tree. This re-arms the\n// fail-closed gate, so guards-being-down becomes LOUD instead of silent.\n// 2. require('./setup') — the full install (config seeding, settings.json wiring). Needs the rule\n// engine, so it loads LAZILY: a corrupt tree can no longer preempt step 1.\n// 3. MODULE_NOT_FOUND — print the one command that actually repairs it, not a node stack.\n// ---------------------------------------------------------------------------\n\n// ANSI red — the recovery command is the whole point of this message, so it must not scroll past\n// unnoticed the way the old loader trace did.\nconst RED = '\u001b[31;1m';\nconst RESET = '\u001b[0m';\n\n// The human-facing recovery notice, printed to stderr INSTEAD of a raw node loader trace. Mirrors the\n// shim's deny reason so the terminal and the AI tell the human the same story.\n// webpieces-disable no-function-outside-class -- bin entry point: this module MUST load with only fs+path (see header). A DI-managed class would pull the container in and reintroduce the exact require-time crash being fixed.\nexport function recoveryNotice(detail: string, shimRefreshed: boolean): string[] {\n const lines = [\n `${RED}🛑 @webpieces: cannot run the installer — your node_modules is corrupt.${RESET}`,\n '',\n ` ${detail}`,\n '',\n ' A package is only partially written on disk (usually an install that was killed mid-copy).',\n ' A plain `pnpm install` will NOT fix it: pnpm sees the correct version in the package.json',\n ' already on disk, considers the package installed, and skips it.',\n '',\n `${RED} Run exactly this, then retry:${RESET}`,\n `${RED} ${RECOVERY_CMD}${RESET}`,\n '',\n ];\n if (shimRefreshed) {\n // The important half of the job still got done: the fail-closed gate is current, so the AI is\n // BLOCKED (citing this same command) rather than silently editing behind dead guards.\n lines.push(' The AI guard shim WAS refreshed, so tool calls are now BLOCKED until you repair the');\n lines.push(' tree. That is intentional — it is what stops the AI from working unguarded.');\n } else {\n lines.push(' No committed shim was found to refresh. Re-run this installer after the repair.');\n }\n return lines;\n}\n\n// Node stamps a `code` onto require() failures, which the base Error type does not carry.\nclass NodeRequireError extends Error {\n readonly code?: string;\n}\n\n// The lazily-required ./setup module. Named (not an inline literal) so the require cast stays typed.\nclass SetupModule {\n main!: () => Promise<void>;\n}\n\n// A require() failure from a corrupt / partially-written node_modules. Node reports a missing relative\n// specifier ('./assert-valid-pattern.js' — a package's own file absent from disk) and a missing bare\n// specifier ('@webpieces/rules-config' — package not installed at all) with the SAME code. Both mean a\n// broken tree from the installer's point of view, and both have the same cure, so both map to true.\n// webpieces-disable no-function-outside-class -- bin entry point: this module MUST load with only fs+path (see header). A DI-managed class would pull the container in and reintroduce the exact require-time crash being fixed.\nexport function isBrokenTreeError(error: Error): boolean {\n return (error as NodeRequireError).code === 'MODULE_NOT_FOUND';\n}\n\n// Returns the process exit code (0 = ok). Kept as a function (not top-level code) so it is unit-\n// testable without spawning node.\n// webpieces-disable no-function-outside-class -- bin entry point: this module MUST load with only fs+path (see header). A DI-managed class would pull the container in and reintroduce the exact require-time crash being fixed.\nexport async function runInstaller(cwd: string): Promise<number> {\n // STEP 1 — re-arm the fail-closed gate FIRST, via the dependency-free module. healShim never\n // throws and only rewrites a shim that ALREADY exists, so a global install is left untouched.\n const shimRefreshed = findShimRoot(cwd) !== null;\n healShim(cwd);\n\n // STEP 2 — the real install, loaded LAZILY ON PURPOSE. A static import would drag\n // @webpieces/rules-config → minimatch in at module-load time and a corrupt tree would kill this\n // file before step 1 ever ran — which is precisely the failure being fixed here. Do not hoist it.\n // webpieces-disable no-unmanaged-exceptions -- this IS the top-level chokepoint: the bin entry. It exists to turn a raw MODULE_NOT_FOUND loader trace into an actionable recovery message; a real bug is re-thrown untouched.\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const setup = require('./setup') as SetupModule;\n await setup.main();\n return 0;\n } catch (err: unknown) {\n const error = toError(err);\n if (!isBrokenTreeError(error)) throw error; // a real bug — never hide it behind a nice message\n for (const line of recoveryNotice(error.message.split('\\n')[0], shimRefreshed)) console.error(line);\n return 1;\n }\n}\n"]}
1
+ {"version":3,"file":"install-entry.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/bin/install-entry.ts"],"names":[],"mappings":";;;AAkCA,wCAuBC;AAiBD,8CAEC;AAKD,oCAsBC;AAtGD,iCAA8D;AAC9D,+CAA2C;AAE3C,8EAA8E;AAC9E,kFAAkF;AAClF,EAAE;AACF,yFAAyF;AACzF,oGAAoG;AACpG,sGAAsG;AACtG,qGAAqG;AACrG,oGAAoG;AACpG,sGAAsG;AACtG,kGAAkG;AAClG,2FAA2F;AAC3F,EAAE;AACF,mGAAmG;AACnG,4FAA4F;AAC5F,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oGAAoG;AACpG,qGAAqG;AACrG,8FAA8F;AAC9F,8EAA8E;AAE9E,iGAAiG;AACjG,8CAA8C;AAC9C,MAAM,GAAG,GAAG,SAAS,CAAC;AACtB,MAAM,KAAK,GAAG,MAAM,CAAC;AAErB,sGAAsG;AACtG,+EAA+E;AAC/E,iOAAiO;AACjO,SAAgB,cAAc,CAAC,MAAc,EAAE,aAAsB;IACjE,MAAM,KAAK,GAAG;QACV,GAAG,GAAG,0EAA0E,KAAK,EAAE;QACvF,EAAE;QACF,KAAK,MAAM,EAAE;QACb,EAAE;QACF,8FAA8F;QAC9F,6FAA6F;QAC7F,mEAAmE;QACnE,EAAE;QACF,GAAG,GAAG,kCAAkC,KAAK,EAAE;QAC/C,GAAG,GAAG,OAAO,mBAAY,GAAG,KAAK,EAAE;QACnC,EAAE;KACL,CAAC;IACF,IAAI,aAAa,EAAE,CAAC;QAChB,8FAA8F;QAC9F,sFAAsF;QACtF,KAAK,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;QACpG,KAAK,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;IAChG,CAAC;SAAM,CAAC;QACJ,KAAK,CAAC,IAAI,CAAC,mFAAmF,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,0FAA0F;AAC1F,MAAM,gBAAiB,SAAQ,KAAK;IACvB,IAAI,CAAU;CAC1B;AAED,qGAAqG;AACrG,MAAM,WAAW;IACb,IAAI,CAAuB;CAC9B;AAED,uGAAuG;AACvG,qGAAqG;AACrG,uGAAuG;AACvG,oGAAoG;AACpG,iOAAiO;AACjO,SAAgB,iBAAiB,CAAC,KAAY;IAC1C,OAAQ,KAA0B,CAAC,IAAI,KAAK,kBAAkB,CAAC;AACnE,CAAC;AAED,iGAAiG;AACjG,kCAAkC;AAClC,iOAAiO;AAC1N,KAAK,UAAU,YAAY,CAAC,GAAW;IAC1C,6FAA6F;IAC7F,8FAA8F;IAC9F,MAAM,aAAa,GAAG,IAAA,mBAAY,EAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IACjD,IAAA,eAAQ,EAAC,GAAG,CAAC,CAAC;IAEd,kFAAkF;IAClF,gGAAgG;IAChG,kGAAkG;IAClG,8NAA8N;IAC9N,8DAA8D;IAC9D,IAAI,CAAC;QACD,iEAAiE;QACjE,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAgB,CAAC;QAChD,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,OAAO,CAAC,CAAC;IACb,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC,CAAG,mDAAmD;QACjG,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC;YAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpG,OAAO,CAAC,CAAC;IACb,CAAC;AACL,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { healShim, findShimRoot, RECOVERY_CMD } from './shim';\nimport { toError } from '../core/to-error';\n\n// ---------------------------------------------------------------------------\n// The `wp-install-ai-hooks` entry point, deliberately kept FREE of heavy imports.\n//\n// THE BUG THIS FIXES — the installer was a victim of the very corruption it must repair.\n// setup.ts top-level-imports @webpieces/rules-config, which imports minimatch. When node_modules is\n// corrupt — a package half-written by an install that was killed mid-copy — node dies at REQUIRE time\n// with MODULE_NOT_FOUND, before a single line of the installer runs. So `pnpm wp-install-ai-hooks` —\n// the one command that can rewrite the fail-closed shim — died with a raw 30-line node loader trace\n// and repaired nothing. Worse, `pnpm install` cannot heal that corruption either (pnpm sees the right\n// version on disk, considers the package installed, and skips it), so the repo was wedged: guards\n// down, installer unable to reinstall them, and the only signal an unreadable stack trace.\n//\n// The seam: ./shim imports NOTHING but fs and path. Writing the shim never needed the rule engine.\n// So do the repair FIRST with the dependency-free module, and only then reach for setup.ts:\n//\n// 1. healShim(cwd) — refresh the committed shim. Works on a corrupt tree. This re-arms the\n// fail-closed gate, so guards-being-down becomes LOUD instead of silent.\n// 2. require('./setup') — the full install (config seeding, settings.json wiring). Needs the rule\n// engine, so it loads LAZILY: a corrupt tree can no longer preempt step 1.\n// 3. MODULE_NOT_FOUND — print the one command that actually repairs it, not a node stack.\n// ---------------------------------------------------------------------------\n\n// ANSI red — the recovery command is the whole point of this message, so it must not scroll past\n// unnoticed the way the old loader trace did.\nconst RED = '\u001b[31;1m';\nconst RESET = '\u001b[0m';\n\n// The human-facing recovery notice, printed to stderr INSTEAD of a raw node loader trace. Mirrors the\n// shim's deny reason so the terminal and the AI tell the human the same story.\n// webpieces-disable no-function-outside-class -- bin entry point: this module MUST load with only fs+path (see header). A DI-managed class would pull the container in and reintroduce the exact require-time crash being fixed.\nexport function recoveryNotice(detail: string, shimRefreshed: boolean): string[] {\n const lines = [\n `${RED}🛑 @webpieces: cannot run the installer — your node_modules is corrupt.${RESET}`,\n '',\n ` ${detail}`,\n '',\n ' A package is only partially written on disk (usually an install that was killed mid-copy).',\n ' A plain `pnpm install` will NOT fix it: pnpm sees the correct version in the package.json',\n ' already on disk, considers the package installed, and skips it.',\n '',\n `${RED} Run exactly this, then retry:${RESET}`,\n `${RED} ${RECOVERY_CMD}${RESET}`,\n '',\n ];\n if (shimRefreshed) {\n // The important half of the job still got done: the fail-closed gate is current, so the AI is\n // BLOCKED (citing this same command) rather than silently editing behind dead guards.\n lines.push(' The AI guard shim WAS refreshed, so tool calls are now BLOCKED until you repair the');\n lines.push(' tree. That is intentional — it is what stops the AI from working unguarded.');\n } else {\n lines.push(' No committed shim was found to refresh. Re-run this installer after the repair.');\n }\n return lines;\n}\n\n// Node stamps a `code` onto require() failures, which the base Error type does not carry.\nclass NodeRequireError extends Error {\n readonly code?: string;\n}\n\n// The lazily-required ./setup module. Named (not an inline literal) so the require cast stays typed.\nclass SetupModule {\n main!: () => Promise<void>;\n}\n\n// A require() failure from a corrupt / partially-written node_modules. Node reports a missing relative\n// specifier ('./assert-valid-pattern.js' — a package's own file absent from disk) and a missing bare\n// specifier ('@webpieces/rules-config' — package not installed at all) with the SAME code. Both mean a\n// broken tree from the installer's point of view, and both have the same cure, so both map to true.\n// webpieces-disable no-function-outside-class -- bin entry point: this module MUST load with only fs+path (see header). A DI-managed class would pull the container in and reintroduce the exact require-time crash being fixed.\nexport function isBrokenTreeError(error: Error): boolean {\n return (error as NodeRequireError).code === 'MODULE_NOT_FOUND';\n}\n\n// Returns the process exit code (0 = ok). Kept as a function (not top-level code) so it is unit-\n// testable without spawning node.\n// webpieces-disable no-function-outside-class -- bin entry point: this module MUST load with only fs+path (see header). A DI-managed class would pull the container in and reintroduce the exact require-time crash being fixed.\nexport async function runInstaller(cwd: string): Promise<number> {\n // STEP 1 — re-arm the fail-closed gate FIRST, via the dependency-free module. healShim never\n // throws and only rewrites a shim that ALREADY exists, so a global install is left untouched.\n const shimRefreshed = findShimRoot(cwd) !== null;\n healShim(cwd);\n\n // STEP 2 — the real install, loaded LAZILY ON PURPOSE. A static import would drag\n // @webpieces/rules-config → minimatch in at module-load time and a corrupt tree would kill this\n // file before step 1 ever ran — which is precisely the failure being fixed here. Do not hoist it.\n // webpieces-disable no-unmanaged-exceptions -- this IS the top-level chokepoint: the bin entry. It exists to turn a raw MODULE_NOT_FOUND loader trace into an actionable recovery message; a real bug is re-thrown untouched.\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const setup = require('./setup') as SetupModule;\n await setup.main();\n return 0;\n } catch (err: unknown) {\n const error = toError(err);\n if (!isBrokenTreeError(error)) throw error; // a real bug — never hide it behind a nice message\n for (const line of recoveryNotice(error.message.split('\\n')[0], shimRefreshed)) console.error(line);\n return 1;\n }\n}\n"]}
@@ -1 +1,2 @@
1
+ #!/usr/bin/env node
1
2
  export declare function runUpgradeShim(cwd: string): number;
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.runUpgradeShim = runUpgradeShim;
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade-shim.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/bin/upgrade-shim.ts"],"names":[],"mappings":";;AA0BA,wCAsBC;;AAhDD,+CAAyB;AAEzB,iCAA4D;AAC5D,+CAA2C;AAE3C,8EAA8E;AAC9E,kFAAkF;AAClF,EAAE;AACF,mGAAmG;AACnG,wGAAwG;AACxG,qGAAqG;AACrG,uGAAuG;AACvG,8FAA8F;AAC9F,EAAE;AACF,uGAAuG;AACvG,sGAAsG;AACtG,yGAAyG;AACzG,yGAAyG;AACzG,uFAAuF;AACvF,8EAA8E;AAC9E,MAAM,GAAG,GAAG,QAAQ,CAAC;AACrB,MAAM,KAAK,GAAG,KAAK,CAAC;AAEpB,yGAAyG;AACzG,yBAAyB;AACzB,uRAAuR;AACvR,SAAgB,cAAc,CAAC,GAAW;IACtC,MAAM,IAAI,GAAG,IAAA,mBAAY,EAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,gFAAgF,KAAK,EAAE,CAAC,CAAC;QAC7G,OAAO,CAAC,KAAK,CAAC,iHAAiH,CAAC,CAAC;QACjI,OAAO,CAAC,CAAC;IACb,CAAC;IACD,MAAM,MAAM,GAAG,IAAA,eAAQ,EAAC,IAAI,CAAC,CAAC;IAC9B,yMAAyM;IACzM,8DAA8D;IAC9D,IAAI,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAA,iBAAU,GAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACxD,8FAA8F;QAC9F,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,iDAAiD,MAAM,6BAA6B,CAAC,CAAC;QAClG,OAAO,CAAC,GAAG,CAAC,mFAAmF,CAAC,CAAC;QACjG,OAAO,CAAC,CAAC;IACb,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,kCAAkC,MAAM,KAAK,KAAK,CAAC,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC;QAC1F,OAAO,CAAC,CAAC;IACb,CAAC;AACL,CAAC","sourcesContent":["import * as fs from 'fs';\n\nimport { renderShim, shimPath, findShimRoot } from './shim';\nimport { toError } from '../core/to-error';\n\n// ---------------------------------------------------------------------------\n// The `wp-upgrade-shim` entry point — the CURE for the committed-shim self-guard.\n//\n// The committed .claude/webpieces/ai-hook.sh is webpieces-MANAGED: generated from renderShim() and\n// checked in only so the hook has a stable entry point when node_modules is absent. When it is reverted\n// or hand-edited it no longer matches the installed template, and the shim's self-guard fails CLOSED\n// (blocking every tool call) because stale escape-hatch logic must not run silently. That guard allows\n// exactly ONE command through — this one — so the assistant can re-arm it without a deadlock.\n//\n// Deliberately imports only ./shim (fs + path) + toError, exactly like install-entry: the whole job is\n// to rewrite the committed shim, which never needed the rule engine, and must stay runnable on a tree\n// too broken to load it. We write renderShim() — the single source of truth — which the shipped template\n// (templates/ai-hook.sh, byte-identical to renderShim() by a unit test) equals, so the self-guard, which\n// compares the committed shim against that installed template, clears after this runs.\n// ---------------------------------------------------------------------------\nconst RED = '[31;1m';\nconst RESET = '[0m';\n\n// Returns the process exit code (0 = ok). Kept as a function (not top-level code) so it is unit-testable\n// without spawning node.\n// webpieces-disable no-function-outside-class -- bin entry point: this module MUST load with only fs+path (see header), mirroring install-entry.ts. A DI-managed class would pull the container in and reintroduce the require-time crash this dependency-free path exists to survive.\nexport function runUpgradeShim(cwd: string): number {\n const root = findShimRoot(cwd);\n if (root === null) {\n console.error(`${RED}🛑 @webpieces: no committed .claude/webpieces/ai-hook.sh found to regenerate.${RESET}`);\n console.error(' Run this from a repo that installs @webpieces/ai-hook-rules, or run the installer (pnpm wp-install-ai-hooks).');\n return 1;\n }\n const target = shimPath(root);\n // webpieces-disable no-unmanaged-exceptions -- bin entry chokepoint: turn an fs error into an actionable line + non-zero exit rather than a raw node trace; there is no caller above a bin to handle it.\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n fs.writeFileSync(target, renderShim(), { mode: 0o755 });\n // writeFileSync's mode only applies on create; force it on overwrite too (matches writeShim).\n fs.chmodSync(target, 0o755);\n console.log(`✅ @webpieces: regenerated the managed shim at ${target} — tool calls are re-armed.`);\n console.log(' This file is generated + committed by webpieces; do not revert or hand-edit it.');\n return 0;\n } catch (err: unknown) {\n const error = toError(err);\n console.error(`${RED}🛑 @webpieces: could not write ${target}: ${error.message}${RESET}`);\n return 1;\n }\n}\n"]}
1
+ {"version":3,"file":"upgrade-shim.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/bin/upgrade-shim.ts"],"names":[],"mappings":";;;AA2BA,wCAsBC;;AAhDD,+CAAyB;AAEzB,iCAA4D;AAC5D,+CAA2C;AAE3C,8EAA8E;AAC9E,kFAAkF;AAClF,EAAE;AACF,mGAAmG;AACnG,wGAAwG;AACxG,qGAAqG;AACrG,uGAAuG;AACvG,8FAA8F;AAC9F,EAAE;AACF,uGAAuG;AACvG,sGAAsG;AACtG,yGAAyG;AACzG,yGAAyG;AACzG,uFAAuF;AACvF,8EAA8E;AAC9E,MAAM,GAAG,GAAG,QAAQ,CAAC;AACrB,MAAM,KAAK,GAAG,KAAK,CAAC;AAEpB,yGAAyG;AACzG,yBAAyB;AACzB,uRAAuR;AACvR,SAAgB,cAAc,CAAC,GAAW;IACtC,MAAM,IAAI,GAAG,IAAA,mBAAY,EAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,gFAAgF,KAAK,EAAE,CAAC,CAAC;QAC7G,OAAO,CAAC,KAAK,CAAC,iHAAiH,CAAC,CAAC;QACjI,OAAO,CAAC,CAAC;IACb,CAAC;IACD,MAAM,MAAM,GAAG,IAAA,eAAQ,EAAC,IAAI,CAAC,CAAC;IAC9B,yMAAyM;IACzM,8DAA8D;IAC9D,IAAI,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAA,iBAAU,GAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACxD,8FAA8F;QAC9F,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,iDAAiD,MAAM,6BAA6B,CAAC,CAAC;QAClG,OAAO,CAAC,GAAG,CAAC,mFAAmF,CAAC,CAAC;QACjG,OAAO,CAAC,CAAC;IACb,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,kCAAkC,MAAM,KAAK,KAAK,CAAC,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC;QAC1F,OAAO,CAAC,CAAC;IACb,CAAC;AACL,CAAC","sourcesContent":["#!/usr/bin/env node\nimport * as fs from 'fs';\n\nimport { renderShim, shimPath, findShimRoot } from './shim';\nimport { toError } from '../core/to-error';\n\n// ---------------------------------------------------------------------------\n// The `wp-upgrade-shim` entry point — the CURE for the committed-shim self-guard.\n//\n// The committed .claude/webpieces/ai-hook.sh is webpieces-MANAGED: generated from renderShim() and\n// checked in only so the hook has a stable entry point when node_modules is absent. When it is reverted\n// or hand-edited it no longer matches the installed template, and the shim's self-guard fails CLOSED\n// (blocking every tool call) because stale escape-hatch logic must not run silently. That guard allows\n// exactly ONE command through — this one — so the assistant can re-arm it without a deadlock.\n//\n// Deliberately imports only ./shim (fs + path) + toError, exactly like install-entry: the whole job is\n// to rewrite the committed shim, which never needed the rule engine, and must stay runnable on a tree\n// too broken to load it. We write renderShim() — the single source of truth — which the shipped template\n// (templates/ai-hook.sh, byte-identical to renderShim() by a unit test) equals, so the self-guard, which\n// compares the committed shim against that installed template, clears after this runs.\n// ---------------------------------------------------------------------------\nconst RED = '[31;1m';\nconst RESET = '[0m';\n\n// Returns the process exit code (0 = ok). Kept as a function (not top-level code) so it is unit-testable\n// without spawning node.\n// webpieces-disable no-function-outside-class -- bin entry point: this module MUST load with only fs+path (see header), mirroring install-entry.ts. A DI-managed class would pull the container in and reintroduce the require-time crash this dependency-free path exists to survive.\nexport function runUpgradeShim(cwd: string): number {\n const root = findShimRoot(cwd);\n if (root === null) {\n console.error(`${RED}🛑 @webpieces: no committed .claude/webpieces/ai-hook.sh found to regenerate.${RESET}`);\n console.error(' Run this from a repo that installs @webpieces/ai-hook-rules, or run the installer (pnpm wp-install-ai-hooks).');\n return 1;\n }\n const target = shimPath(root);\n // webpieces-disable no-unmanaged-exceptions -- bin entry chokepoint: turn an fs error into an actionable line + non-zero exit rather than a raw node trace; there is no caller above a bin to handle it.\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n fs.writeFileSync(target, renderShim(), { mode: 0o755 });\n // writeFileSync's mode only applies on create; force it on overwrite too (matches writeShim).\n fs.chmodSync(target, 0o755);\n console.log(`✅ @webpieces: regenerated the managed shim at ${target} — tool calls are re-armed.`);\n console.log(' This file is generated + committed by webpieces; do not revert or hand-edit it.');\n return 0;\n } catch (err: unknown) {\n const error = toError(err);\n console.error(`${RED}🛑 @webpieces: could not write ${target}: ${error.message}${RESET}`);\n return 1;\n }\n}\n"]}
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env node
2
- // Plain JS shim — delegates to compiled TypeScript.
3
- // Must NOT be converted to TypeScript (needs to exist pre-build for pnpm bin symlinks).
4
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
5
- 'use strict';
6
-
7
- const path = require('path');
8
- const fs = require('fs');
9
- const compiled = path.join(__dirname, '..', 'src', 'adapters', 'guards-hook.js');
10
-
11
- if (fs.existsSync(compiled)) {
12
- require(compiled).main();
13
- } else {
14
- console.error(' [ai-hook-rules] Package not built yet. Run the build first, or install from npm.');
15
- process.exit(1);
16
- }
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env node
2
- // Plain JS shim — delegates to compiled TypeScript.
3
- // Must NOT be converted to TypeScript (needs to exist pre-build for pnpm bin symlinks).
4
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
5
- 'use strict';
6
-
7
- const path = require('path');
8
- const fs = require('fs');
9
- const compiled = path.join(__dirname, '..', 'src', 'adapters', 'rules-hook.js');
10
-
11
- if (fs.existsSync(compiled)) {
12
- require(compiled).main();
13
- } else {
14
- console.error(' [ai-hook-rules] Package not built yet. Run the build first, or install from npm.');
15
- process.exit(1);
16
- }
@@ -1,23 +0,0 @@
1
- #!/usr/bin/env node
2
- // Plain JS shim — delegates to compiled TypeScript.
3
- // Must NOT be converted to TypeScript (needs to exist pre-build for pnpm bin symlinks).
4
- //
5
- // Points at install-entry.js, NOT setup.js, on purpose. setup.js top-level-imports
6
- // @webpieces/rules-config -> minimatch, so on a CORRUPT node_modules (a package half-written by an
7
- // install that was killed mid-copy) node died at require() time with a raw MODULE_NOT_FOUND loader
8
- // trace — before the installer could rewrite the fail-closed shim, which is the one thing that would
9
- // have made the breakage visible. install-entry.js imports only ./shim (fs + path), re-arms the shim
10
- // first, and only then loads setup.js lazily. See install-entry.ts for the full story.
11
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
12
- 'use strict';
13
-
14
- const path = require('path');
15
- const fs = require('fs');
16
- const compiled = path.join(__dirname, '..', 'src', 'bin', 'install-entry.js');
17
-
18
- if (fs.existsSync(compiled)) {
19
- require(compiled).runInstaller(process.cwd()).then((code) => process.exit(code));
20
- } else {
21
- console.error(' [ai-hook-rules] Package not built yet. Run the build first, or install from npm.');
22
- process.exit(1);
23
- }
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env node
2
- // Plain JS shim — delegates to compiled TypeScript.
3
- // Must NOT be converted to TypeScript (needs to exist pre-build for pnpm bin symlinks).
4
- //
5
- // Points at upgrade-shim.js, which imports only ./shim (fs + path) + toError — no rule engine — so it
6
- // stays runnable on a tree too broken to load setup.js. It is the CURE allowed through the committed
7
- // shim's self-guard: it rewrites .claude/webpieces/ai-hook.sh from renderShim() (the single source of
8
- // truth) when that committed file was reverted or hand-edited. See upgrade-shim.ts for the full story.
9
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
10
- 'use strict';
11
-
12
- const path = require('path');
13
- const fs = require('fs');
14
- const compiled = path.join(__dirname, '..', 'src', 'bin', 'upgrade-shim.js');
15
-
16
- if (fs.existsSync(compiled)) {
17
- process.exit(require(compiled).runUpgradeShim(process.cwd()));
18
- } else {
19
- console.error(' [ai-hook-rules] Package not built yet. Run the build first, or install from npm.');
20
- process.exit(1);
21
- }