@webpieces/ai-hook-rules 0.4.847 → 0.4.849

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.
Files changed (39) hide show
  1. package/package.json +8 -7
  2. package/src/adapters/hook-app-fixtures.d.ts +2 -2
  3. package/src/adapters/hook-app-fixtures.js +60 -18
  4. package/src/adapters/hook-app-fixtures.js.map +1 -1
  5. package/src/core/load-rules.d.ts +2 -5
  6. package/src/core/load-rules.js +2 -148
  7. package/src/core/load-rules.js.map +1 -1
  8. package/src/core/rules/index.d.ts +13 -2
  9. package/src/core/rules/index.js +27 -17
  10. package/src/core/rules/index.js.map +1 -1
  11. package/src/core/rules/max-file-lines.d.ts +1 -1
  12. package/src/core/rules/max-file-lines.js +11 -4
  13. package/src/core/rules/max-file-lines.js.map +1 -1
  14. package/src/core/rules/no-custom-css.d.ts +1 -1
  15. package/src/core/rules/no-custom-css.js +2 -2
  16. package/src/core/rules/no-custom-css.js.map +1 -1
  17. package/src/core/rules/no-destructure.d.ts +1 -1
  18. package/src/core/rules/no-destructure.js +5 -3
  19. package/src/core/rules/no-destructure.js.map +1 -1
  20. package/src/core/rules/no-js-files.d.ts +1 -1
  21. package/src/core/rules/no-js-files.js +4 -2
  22. package/src/core/rules/no-js-files.js.map +1 -1
  23. package/src/core/rules/validate-ts-in-src.d.ts +1 -1
  24. package/src/core/rules/validate-ts-in-src.js +18 -8
  25. package/src/core/rules/validate-ts-in-src.js.map +1 -1
  26. package/src/core/runner.d.ts +3 -3
  27. package/src/core/runner.js +16 -12
  28. package/src/core/runner.js.map +1 -1
  29. package/src/rule-help.d.ts +3 -0
  30. package/src/rule-help.js +11 -0
  31. package/src/rule-help.js.map +1 -0
  32. package/src/rule-pack.js +2 -21
  33. package/src/rule-pack.js.map +1 -1
  34. package/src/rule-runtime.d.ts +7 -0
  35. package/src/rule-runtime.js +27 -0
  36. package/src/rule-runtime.js.map +1 -0
  37. package/src/source-policy-registry.d.ts +10 -0
  38. package/src/source-policy-registry.js +40 -0
  39. package/src/source-policy-registry.js.map +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/ai-hook-rules",
3
- "version": "0.4.847",
3
+ "version": "0.4.849",
4
4
  "description": "Independent source edit rules and source hook for AI coding agents.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -8,7 +8,8 @@
8
8
  ".": "./src/index.js",
9
9
  "./package.json": "./package.json",
10
10
  "./claude-code-rules": "./src/adapters/rules-hook.js",
11
- "./rule-pack": "./src/rule-pack.js"
11
+ "./rule-pack": "./src/rule-pack.js",
12
+ "./rule-runtime": "./src/rule-runtime.js"
12
13
  },
13
14
  "files": [
14
15
  "src/**/*",
@@ -22,18 +23,18 @@
22
23
  "directory": "packages/tooling/ai-hook-rules"
23
24
  },
24
25
  "dependencies": {
25
- "@webpieces/hook-runtime": "0.4.847",
26
- "@webpieces/rules-config": "0.4.847",
26
+ "@webpieces/hook-runtime": "0.4.849",
27
+ "@webpieces/rules-config": "0.4.849",
27
28
  "inversify": "7.10.4",
28
29
  "reflect-metadata": "0.2.2",
29
- "@webpieces/tooling-common": "0.4.847",
30
- "@webpieces/rules-sdk": "0.4.847"
30
+ "@webpieces/tooling-common": "0.4.849",
31
+ "@webpieces/rules-sdk": "0.4.849"
31
32
  },
32
33
  "publishConfig": {
33
34
  "access": "public"
34
35
  },
35
36
  "devDependencies": {
36
- "@webpieces/tooling-testkit": "0.4.847"
37
+ "@webpieces/tooling-testkit": "0.4.849"
37
38
  },
38
39
  "types": "./src/index.d.ts",
39
40
  "bin": {
@@ -23,8 +23,8 @@ export declare class GoldenFixture {
23
23
  */
24
24
  readonly stdin: string;
25
25
  /** True ⇒ the fixture repo also gets a `rulesDir` whose one module throws when required. */
26
- readonly crashingRulesDir: boolean;
27
- constructor(name: string, mode: HookMode, stdin: string, crashingRulesDir?: boolean);
26
+ readonly crashingRuntime: boolean;
27
+ constructor(name: string, mode: HookMode, stdin: string, crashingRuntime?: boolean);
28
28
  }
29
29
  /** A built fixture repo: where it lives, and the stdin bytes with `<REPO>` resolved into it. */
30
30
  export declare class PreparedFixture {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GoldenRepoBuilder = exports.GOLDEN_FIXTURES = exports.REPO_TOKEN = exports.PreparedFixture = exports.GoldenFixture = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const rules_sdk_1 = require("@webpieces/rules-sdk");
5
6
  const rule_pack_1 = require("../rule-pack");
6
7
  const tooling_testkit_1 = require("@webpieces/tooling-testkit");
7
8
  const tooling_testkit_2 = require("@webpieces/tooling-testkit");
@@ -32,12 +33,12 @@ class GoldenFixture {
32
33
  */
33
34
  stdin;
34
35
  /** True ⇒ the fixture repo also gets a `rulesDir` whose one module throws when required. */
35
- crashingRulesDir;
36
- constructor(name, mode, stdin, crashingRulesDir = false) {
36
+ crashingRuntime;
37
+ constructor(name, mode, stdin, crashingRuntime = false) {
37
38
  this.name = name;
38
39
  this.mode = mode;
39
40
  this.stdin = stdin;
40
- this.crashingRulesDir = crashingRulesDir;
41
+ this.crashingRuntime = crashingRuntime;
41
42
  }
42
43
  }
43
44
  exports.GoldenFixture = GoldenFixture;
@@ -61,14 +62,27 @@ exports.PreparedFixture = PreparedFixture;
61
62
  * a golden nobody else could run.
62
63
  */
63
64
  exports.REPO_TOKEN = '<REPO>';
64
- const CLAUDE_ENVELOPE = { hook_event_name: 'PreToolUse', session_id: 'sess-golden', transcript_path: '/dev/null', cwd: exports.REPO_TOKEN };
65
+ const CLAUDE_ENVELOPE = {
66
+ hook_event_name: 'PreToolUse',
67
+ session_id: 'sess-golden',
68
+ transcript_path: '/dev/null',
69
+ cwd: exports.REPO_TOKEN,
70
+ };
65
71
  /**
66
72
  * The Codex additions, MEASURED from codex-cli 0.151.0: it uses Claude's key names and merely ADDS
67
73
  * `model`, `turn_id`, `tool_use_id` and `permission_mode`. `turn_id` is the one discriminator (see
68
74
  * detect-ai.ts). `agent_id` empty ⇒ the coordinator, populated ⇒ a subagent — identical semantics in
69
75
  * both harnesses.
70
76
  */
71
- const CODEX_ENVELOPE = { ...CLAUDE_ENVELOPE, model: 'gpt-5-codex', turn_id: 'turn-golden', tool_use_id: 'call_1', permission_mode: 'default', agent_type: 'default', agent_id: '' };
77
+ const CODEX_ENVELOPE = {
78
+ ...CLAUDE_ENVELOPE,
79
+ model: 'gpt-5-codex',
80
+ turn_id: 'turn-golden',
81
+ tool_use_id: 'call_1',
82
+ permission_mode: 'default',
83
+ agent_type: 'default',
84
+ agent_id: '',
85
+ };
72
86
  // Codex's edit tool. MEASURED: the tool is named `apply_patch`, it carries `tool_input.command` (not
73
87
  // file_path), hunk headers are a bare `@@`, and ONE patch may carry many files with mixed operations.
74
88
  const PATCH_ADD_JS = '*** Begin Patch\n*** Add File: src/foo.js\n+var x = 1;\n*** End Patch\n';
@@ -80,7 +94,11 @@ const PATCH_ADD_TS = '*** Begin Patch\n*** Add File: scripts/added.ts\n+export c
80
94
  */
81
95
  class WirePayload {
82
96
  // webpieces-disable no-any-unknown -- these objects ARE the wire envelope; JSON.stringify of a plain object is the payload under test, and naming a type for it would assert a shape the fixtures exist to state literally
83
- write(envelope, toolName, toolInput) {
97
+ write(
98
+ // webpieces-disable no-any-unknown -- external JSON or runtime exports are validated before policy execution
99
+ envelope, toolName,
100
+ // webpieces-disable no-any-unknown -- external JSON or runtime exports are validated before policy execution
101
+ toolInput) {
84
102
  return JSON.stringify({ ...envelope, tool_name: toolName, tool_input: toolInput });
85
103
  }
86
104
  }
@@ -96,11 +114,27 @@ exports.GOLDEN_FIXTURES = [
96
114
  // state — and a Bash deny is the ONE case that carries the red `systemMessage`.
97
115
  // The read-only tool: log-and-allow, and the only guard that can deny it is a stale `main`.
98
116
  // Write / Edit / MultiEdit denies — all three must emit NO systemMessage.
99
- new GoldenFixture('claude/write-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', { file_path: `${exports.REPO_TOKEN}/src/foo.js`, content: 'var x = 1;\n' })),
100
- new GoldenFixture('claude/edit-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Edit', { file_path: `${exports.REPO_TOKEN}/scripts/ok.ts`, old_string: 'const a = 1;', new_string: 'const { a } = b;' })),
101
- new GoldenFixture('claude/multiedit-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'MultiEdit', { file_path: `${exports.REPO_TOKEN}/scripts/ok.ts`, edits: [{ old_string: 'const a = 1;', new_string: 'const { a } = b;' }] })),
102
- new GoldenFixture('claude/write-allow', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', { file_path: `${exports.REPO_TOKEN}/scripts/ok.ts`, content: 'export const a = 1;\n' })),
103
- new GoldenFixture('claude/crash', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', { file_path: `${exports.REPO_TOKEN}/scripts/ok.ts`, content: 'export const a = 1;\n' }), true),
117
+ new GoldenFixture('claude/write-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', {
118
+ file_path: `${exports.REPO_TOKEN}/src/foo.js`,
119
+ content: 'var x = 1;\n',
120
+ })),
121
+ new GoldenFixture('claude/edit-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Edit', {
122
+ file_path: `${exports.REPO_TOKEN}/scripts/ok.ts`,
123
+ old_string: 'const a = 1;',
124
+ new_string: 'const { a } = b;',
125
+ })),
126
+ new GoldenFixture('claude/multiedit-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'MultiEdit', {
127
+ file_path: `${exports.REPO_TOKEN}/scripts/ok.ts`,
128
+ edits: [{ old_string: 'const a = 1;', new_string: 'const { a } = b;' }],
129
+ })),
130
+ new GoldenFixture('claude/write-allow', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', {
131
+ file_path: `${exports.REPO_TOKEN}/scripts/ok.ts`,
132
+ content: 'export const a = 1;\n',
133
+ })),
134
+ new GoldenFixture('claude/crash', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', {
135
+ file_path: `${exports.REPO_TOKEN}/scripts/ok.ts`,
136
+ content: 'export const a = 1;\n',
137
+ }), true),
104
138
  // ── Codex ──────────────────────────────────────────────────────────────────────────────────────
105
139
  // Codex has no Read tool: a read arrives as `Bash` running a pager, which read parity turns into a
106
140
  // read-scoped verdict ON TOP of the bash guards.
@@ -137,14 +171,22 @@ class GoldenRepoBuilder {
137
171
  this.git(repo, 'config core.hooksPath /dev/null');
138
172
  this.git(repo, 'config user.email t@t.co');
139
173
  this.git(repo, 'config user.name tester');
140
- fs.writeFileSync(path.join(repo, 'webpieces.config.json'), this.repoConfig(fixture));
141
- tooling_testkit_1.policyFixture.declareIn(repo, tooling_testkit_1.policyFixture.manifests().map(pack => pack.packageName === rule_pack_1.rulePackManifest.packageName ? rule_pack_1.rulePackManifest : pack));
174
+ const manifests = tooling_testkit_1.policyFixture
175
+ .manifests()
176
+ .map((pack) => pack.packageName === rule_pack_1.rulePackManifest.packageName ? rule_pack_1.rulePackManifest : pack);
177
+ if (fixture.crashingRuntime) {
178
+ const seed = Object.assign(new rules_sdk_1.ConfigObject(), { mode: 'ON' });
179
+ const definition = new rules_sdk_1.OwnedRuleDefinition('client-crash', { mode: new rules_sdk_1.FieldDef('string', ['ON', 'OFF']) }, 1, {}, seed, 'rules', new rules_sdk_1.RuleHelp('Client failure fixture', 'Repair {configFile}.'));
180
+ manifests.push(new rules_sdk_1.RulePackManifest('@client/crash', '1.0.0-fixture', 3, [definition], [
181
+ new rules_sdk_1.RuleContribution('client-crash', '@client/crash', 'source-hook', './crash-runtime.cjs'),
182
+ ], [], []));
183
+ }
184
+ tooling_testkit_1.policyFixture.writeOwnerConfig(repo, JSON.parse(this.repoConfig(fixture)), manifests);
142
185
  fs.writeFileSync(path.join(repo, 'f.txt'), 'hello\n');
143
186
  fs.mkdirSync(path.join(repo, 'scripts'));
144
187
  fs.writeFileSync(path.join(repo, 'scripts', 'ok.ts'), 'const a = 1;\n');
145
- if (fixture.crashingRulesDir) {
146
- fs.mkdirSync(path.join(repo, 'wprules'));
147
- fs.writeFileSync(path.join(repo, 'wprules', 'crash.js'), CRASHING_RULE_MODULE);
188
+ if (fixture.crashingRuntime) {
189
+ fs.writeFileSync(path.join(repo, 'crash-runtime.cjs'), CRASHING_RULE_MODULE);
148
190
  }
149
191
  this.git(repo, 'add -A');
150
192
  this.git(repo, 'commit -qm init');
@@ -155,11 +197,11 @@ class GoldenRepoBuilder {
155
197
  return new PreparedFixture(repo, root, fixture.stdin.split(exports.REPO_TOKEN).join(repo));
156
198
  }
157
199
  repoConfig(fixture) {
158
- if (!fixture.crashingRulesDir)
200
+ if (!fixture.crashingRuntime)
159
201
  return this.configJson;
160
202
  // webpieces-disable no-any-unknown -- the frozen fixture config is data on disk; re-parsing it into a named type here would be a second declaration of a file whose whole point is being literal
161
203
  const parsed = JSON.parse(this.configJson);
162
- parsed['rulesDir'] = ['wprules'];
204
+ parsed['rules']['client-crash'] = Object.assign(new rules_sdk_1.ConfigObject(), { mode: 'ON' });
163
205
  return JSON.stringify(parsed, null, 4);
164
206
  }
165
207
  git(repo, args) {
@@ -1 +1 @@
1
- {"version":3,"file":"hook-app-fixtures.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/hook-app-fixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import { rulePackManifest } from '../rule-pack';\nimport { policyFixture } from '@webpieces/tooling-testkit';\nimport { specTempDirs } from '@webpieces/tooling-testkit';\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\n\nimport { HookMode } from '@webpieces/hook-runtime';\n\n\n/**\n * THE WIRE BYTES the golden tests drive, and the throwaway repo they are judged against.\n *\n * Everything here exists so a reader can see EXACTLY what a harness sends: the payloads are built from\n * the measured envelope key sets (see the fixture docs on GoldenFixture below), not from anything the\n * hook itself produces, and the repo is a real `git init` with a frozen webpieces.config.json rather\n * than whatever tree the suite happens to be running in. A golden computed against the live repo would\n * change verdict with the branch you are standing on.\n *\n * This is NOT a spec file, deliberately: `tsconfig.lib.json` excludes `*.spec.ts`, so a payload builder\n * living in one would never be type-checked by the build.\n */\n\n/** How one PreToolUse call is presented to the hook. Data class per CLAUDE.md rule 1. */\nexport class GoldenFixture {\n /** Stable key into `__goldens__/hook-app-goldens.json`. */\n readonly name: string;\n /** Which hook binary's mode — `guards` or `rules`. */\n readonly mode: HookMode;\n /**\n * The raw stdin bytes. A STRING, never an object, because malformed stdin is one of the fixtures\n * and a shape that cannot express \"not JSON\" would quietly drop the case that matters most.\n */\n readonly stdin: string;\n /** True ⇒ the fixture repo also gets a `rulesDir` whose one module throws when required. */\n readonly crashingRulesDir: boolean;\n\n constructor(name: string, mode: HookMode, stdin: string, crashingRulesDir: boolean = false) {\n this.name = name;\n this.mode = mode;\n this.stdin = stdin;\n this.crashingRulesDir = crashingRulesDir;\n }\n}\n\n/** A built fixture repo: where it lives, and the stdin bytes with `<REPO>` resolved into it. */\nexport class PreparedFixture {\n readonly repo: string;\n readonly root: string;\n readonly stdin: string;\n\n constructor(repo: string, root: string, stdin: string) {\n this.repo = repo;\n this.root = root;\n this.stdin = stdin;\n }\n}\n\n/**\n * The placeholder that stands for the fixture repo's absolute path, in BOTH directions: payloads are\n * written with it and it is substituted in before the run; golden bytes are compared with the real\n * path substituted back out. Guard reports legitimately name absolute paths (the git-workflow doc\n * pointer, the blocked file), and a golden that hard-coded one machine's `/var/folders/...` would be\n * a golden nobody else could run.\n */\nexport const REPO_TOKEN = '<REPO>';\n\nconst CLAUDE_ENVELOPE = { hook_event_name: 'PreToolUse', session_id: 'sess-golden', transcript_path: '/dev/null', cwd: REPO_TOKEN };\n\n/**\n * The Codex additions, MEASURED from codex-cli 0.151.0: it uses Claude's key names and merely ADDS\n * `model`, `turn_id`, `tool_use_id` and `permission_mode`. `turn_id` is the one discriminator (see\n * detect-ai.ts). `agent_id` empty ⇒ the coordinator, populated ⇒ a subagent — identical semantics in\n * both harnesses.\n */\nconst CODEX_ENVELOPE = { ...CLAUDE_ENVELOPE, model: 'gpt-5-codex', turn_id: 'turn-golden', tool_use_id: 'call_1', permission_mode: 'default', agent_type: 'default', agent_id: '' };\n\n// Codex's edit tool. MEASURED: the tool is named `apply_patch`, it carries `tool_input.command` (not\n// file_path), hunk headers are a bare `@@`, and ONE patch may carry many files with mixed operations.\nconst PATCH_ADD_JS = '*** Begin Patch\\n*** Add File: src/foo.js\\n+var x = 1;\\n*** End Patch\\n';\nconst PATCH_ADD_TS = '*** Begin Patch\\n*** Add File: scripts/added.ts\\n+export const b = 2;\\n*** End Patch\\n';\n\n/**\n * Serializes ONE PreToolUse envelope to the bytes a harness would put on stdin. A class rather than a\n * module-scope function because that is the repo rule, and the one instance below is built at module\n * load so the fixture table can stay a plain literal list.\n */\nclass WirePayload {\n // webpieces-disable no-any-unknown -- these objects ARE the wire envelope; JSON.stringify of a plain object is the payload under test, and naming a type for it would assert a shape the fixtures exist to state literally\n write(envelope: Record<string, unknown>, toolName: string, toolInput: Record<string, unknown>): string {\n return JSON.stringify({ ...envelope, tool_name: toolName, tool_input: toolInput });\n }\n}\n\nconst WIRE = new WirePayload();\n\n/**\n * ONE fixture per row of the coverage the composed pipeline had none of before: for BOTH harnesses a\n * Bash deny (the ANSI-red systemMessage), a file-tool deny (NO systemMessage), an allow, a read-only\n * tool, malformed stdin, and a fail-closed crash.\n */\nexport const GOLDEN_FIXTURES: readonly GoldenFixture[] = [\n // ── Claude Code ────────────────────────────────────────────────────────────────────────────────\n // A Bash deny. `git merge` is blocked on every branch, so this verdict does not depend on repo\n // state — and a Bash deny is the ONE case that carries the red `systemMessage`.\n // The read-only tool: log-and-allow, and the only guard that can deny it is a stale `main`.\n // Write / Edit / MultiEdit denies — all three must emit NO systemMessage.\n new GoldenFixture('claude/write-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', { file_path: `${REPO_TOKEN}/src/foo.js`, content: 'var x = 1;\\n' })),\n new GoldenFixture('claude/edit-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Edit', { file_path: `${REPO_TOKEN}/scripts/ok.ts`, old_string: 'const a = 1;', new_string: 'const { a } = b;' })),\n new GoldenFixture('claude/multiedit-deny', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'MultiEdit', { file_path: `${REPO_TOKEN}/scripts/ok.ts`, edits: [{ old_string: 'const a = 1;', new_string: 'const { a } = b;' }] })),\n new GoldenFixture('claude/write-allow', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', { file_path: `${REPO_TOKEN}/scripts/ok.ts`, content: 'export const a = 1;\\n' })),\n new GoldenFixture('claude/crash', 'rules', WIRE.write(CLAUDE_ENVELOPE, 'Write', { file_path: `${REPO_TOKEN}/scripts/ok.ts`, content: 'export const a = 1;\\n' }), true),\n\n // ── Codex ──────────────────────────────────────────────────────────────────────────────────────\n // Codex has no Read tool: a read arrives as `Bash` running a pager, which read parity turns into a\n // read-scoped verdict ON TOP of the bash guards.\n new GoldenFixture('codex/apply-patch-deny', 'rules', WIRE.write(CODEX_ENVELOPE, 'apply_patch', { command: PATCH_ADD_JS })),\n new GoldenFixture('codex/apply-patch-allow', 'rules', WIRE.write(CODEX_ENVELOPE, 'apply_patch', { command: PATCH_ADD_TS })),\n new GoldenFixture('codex/crash', 'rules', WIRE.write(CODEX_ENVELOPE, 'apply_patch', { command: PATCH_ADD_TS }), true),\n];\n\n// A rules module that blows up the moment it is required — the shortest honest way to reach the hook's\n// fail-closed boundary from OUTSIDE the hook, i.e. without stubbing anything the pipeline owns.\nconst CRASHING_RULE_MODULE = \"throw new Error('boom from a custom rule module');\\n\";\n\n/**\n * Builds ONE throwaway git repo per fixture and returns it with the payload's `<REPO>` resolved.\n *\n * A FRESH repo per fixture, not one shared: the hook writes `.webpieces/` state (the decision log, the\n * main-sync cache) as it runs, so a shared tree would let fixture N's leftovers decide fixture N+1's\n * verdict — an order-dependent suite, which is the one kind of golden test worse than none.\n *\n * `realpathSync` matters on macOS: `os.tmpdir()` hands back `/var/folders/...` which resolves to\n * `/private/var/...`, and the L1 location guard compares the payload's cwd against the resolved repo\n * root. Without it every fixture denies with \"run git from the repo root\" instead of the verdict under\n * test.\n */\nexport class GoldenRepoBuilder {\n private readonly configJson: string;\n\n constructor() {\n this.configJson = fs.readFileSync(path.join(__dirname, '__goldens__', 'fixture-webpieces.config.json'), 'utf8');\n }\n\n build(fixture: GoldenFixture): PreparedFixture {\n const root = specTempDirs.makeReal('wp-hook-golden-');\n const repo = path.join(root, 'repo');\n fs.mkdirSync(repo);\n this.git(repo, 'init -q -b main');\n // The developer's own commit hooks would otherwise fire inside this throwaway repo.\n this.git(repo, 'config core.hooksPath /dev/null');\n this.git(repo, 'config user.email t@t.co');\n this.git(repo, 'config user.name tester');\n fs.writeFileSync(path.join(repo, 'webpieces.config.json'), this.repoConfig(fixture));\n policyFixture.declareIn(repo, policyFixture.manifests().map(pack => pack.packageName === rulePackManifest.packageName ? rulePackManifest : pack));\n fs.writeFileSync(path.join(repo, 'f.txt'), 'hello\\n');\n fs.mkdirSync(path.join(repo, 'scripts'));\n fs.writeFileSync(path.join(repo, 'scripts', 'ok.ts'), 'const a = 1;\\n');\n if (fixture.crashingRulesDir) {\n fs.mkdirSync(path.join(repo, 'wprules'));\n fs.writeFileSync(path.join(repo, 'wprules', 'crash.js'), CRASHING_RULE_MODULE);\n }\n this.git(repo, 'add -A');\n this.git(repo, 'commit -qm init');\n // A local origin/main, so `origin/main..<branch>` resolves exactly as it does in a real clone.\n this.git(repo, 'update-ref refs/remotes/origin/main HEAD');\n // A feature branch, because that is where an agent actually works.\n this.git(repo, 'checkout -q -b dean/fixture');\n return new PreparedFixture(repo, root, fixture.stdin.split(REPO_TOKEN).join(repo));\n }\n\n private repoConfig(fixture: GoldenFixture): string {\n if (!fixture.crashingRulesDir) return this.configJson;\n // webpieces-disable no-any-unknown -- the frozen fixture config is data on disk; re-parsing it into a named type here would be a second declaration of a file whose whole point is being literal\n const parsed = JSON.parse(this.configJson) as Record<string, unknown>;\n parsed['rulesDir'] = ['wprules'];\n return JSON.stringify(parsed, null, 4);\n }\n\n private git(repo: string, args: string): void {\n execSync(`git ${args}`, { cwd: repo, encoding: 'utf8' });\n }\n}\n"]}
1
+ {"version":3,"file":"hook-app-fixtures.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/hook-app-fixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ConfigObject,\n FieldDef,\n OwnedRuleDefinition,\n RuleContribution,\n RuleHelp,\n RulePackManifest,\n} from '@webpieces/rules-sdk';\nimport { rulePackManifest } from '../rule-pack';\nimport { policyFixture } from '@webpieces/tooling-testkit';\nimport { specTempDirs } from '@webpieces/tooling-testkit';\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\n\nimport { HookMode } from '@webpieces/hook-runtime';\n\n/**\n * THE WIRE BYTES the golden tests drive, and the throwaway repo they are judged against.\n *\n * Everything here exists so a reader can see EXACTLY what a harness sends: the payloads are built from\n * the measured envelope key sets (see the fixture docs on GoldenFixture below), not from anything the\n * hook itself produces, and the repo is a real `git init` with a frozen webpieces.config.json rather\n * than whatever tree the suite happens to be running in. A golden computed against the live repo would\n * change verdict with the branch you are standing on.\n *\n * This is NOT a spec file, deliberately: `tsconfig.lib.json` excludes `*.spec.ts`, so a payload builder\n * living in one would never be type-checked by the build.\n */\n\n/** How one PreToolUse call is presented to the hook. Data class per CLAUDE.md rule 1. */\nexport class GoldenFixture {\n /** Stable key into `__goldens__/hook-app-goldens.json`. */\n readonly name: string;\n /** Which hook binary's mode — `guards` or `rules`. */\n readonly mode: HookMode;\n /**\n * The raw stdin bytes. A STRING, never an object, because malformed stdin is one of the fixtures\n * and a shape that cannot express \"not JSON\" would quietly drop the case that matters most.\n */\n readonly stdin: string;\n /** True ⇒ the fixture repo also gets a `rulesDir` whose one module throws when required. */\n readonly crashingRuntime: boolean;\n\n constructor(name: string, mode: HookMode, stdin: string, crashingRuntime: boolean = false) {\n this.name = name;\n this.mode = mode;\n this.stdin = stdin;\n this.crashingRuntime = crashingRuntime;\n }\n}\n\n/** A built fixture repo: where it lives, and the stdin bytes with `<REPO>` resolved into it. */\nexport class PreparedFixture {\n readonly repo: string;\n readonly root: string;\n readonly stdin: string;\n\n constructor(repo: string, root: string, stdin: string) {\n this.repo = repo;\n this.root = root;\n this.stdin = stdin;\n }\n}\n\n/**\n * The placeholder that stands for the fixture repo's absolute path, in BOTH directions: payloads are\n * written with it and it is substituted in before the run; golden bytes are compared with the real\n * path substituted back out. Guard reports legitimately name absolute paths (the git-workflow doc\n * pointer, the blocked file), and a golden that hard-coded one machine's `/var/folders/...` would be\n * a golden nobody else could run.\n */\nexport const REPO_TOKEN = '<REPO>';\n\nconst CLAUDE_ENVELOPE = {\n hook_event_name: 'PreToolUse',\n session_id: 'sess-golden',\n transcript_path: '/dev/null',\n cwd: REPO_TOKEN,\n};\n\n/**\n * The Codex additions, MEASURED from codex-cli 0.151.0: it uses Claude's key names and merely ADDS\n * `model`, `turn_id`, `tool_use_id` and `permission_mode`. `turn_id` is the one discriminator (see\n * detect-ai.ts). `agent_id` empty ⇒ the coordinator, populated ⇒ a subagent — identical semantics in\n * both harnesses.\n */\nconst CODEX_ENVELOPE = {\n ...CLAUDE_ENVELOPE,\n model: 'gpt-5-codex',\n turn_id: 'turn-golden',\n tool_use_id: 'call_1',\n permission_mode: 'default',\n agent_type: 'default',\n agent_id: '',\n};\n\n// Codex's edit tool. MEASURED: the tool is named `apply_patch`, it carries `tool_input.command` (not\n// file_path), hunk headers are a bare `@@`, and ONE patch may carry many files with mixed operations.\nconst PATCH_ADD_JS = '*** Begin Patch\\n*** Add File: src/foo.js\\n+var x = 1;\\n*** End Patch\\n';\nconst PATCH_ADD_TS =\n '*** Begin Patch\\n*** Add File: scripts/added.ts\\n+export const b = 2;\\n*** End Patch\\n';\n\n/**\n * Serializes ONE PreToolUse envelope to the bytes a harness would put on stdin. A class rather than a\n * module-scope function because that is the repo rule, and the one instance below is built at module\n * load so the fixture table can stay a plain literal list.\n */\nclass WirePayload {\n // webpieces-disable no-any-unknown -- these objects ARE the wire envelope; JSON.stringify of a plain object is the payload under test, and naming a type for it would assert a shape the fixtures exist to state literally\n write(\n // webpieces-disable no-any-unknown -- external JSON or runtime exports are validated before policy execution\n envelope: Record<string, unknown>,\n toolName: string,\n // webpieces-disable no-any-unknown -- external JSON or runtime exports are validated before policy execution\n toolInput: Record<string, unknown>,\n ): string {\n return JSON.stringify({ ...envelope, tool_name: toolName, tool_input: toolInput });\n }\n}\n\nconst WIRE = new WirePayload();\n\n/**\n * ONE fixture per row of the coverage the composed pipeline had none of before: for BOTH harnesses a\n * Bash deny (the ANSI-red systemMessage), a file-tool deny (NO systemMessage), an allow, a read-only\n * tool, malformed stdin, and a fail-closed crash.\n */\nexport const GOLDEN_FIXTURES: readonly GoldenFixture[] = [\n // ── Claude Code ────────────────────────────────────────────────────────────────────────────────\n // A Bash deny. `git merge` is blocked on every branch, so this verdict does not depend on repo\n // state — and a Bash deny is the ONE case that carries the red `systemMessage`.\n // The read-only tool: log-and-allow, and the only guard that can deny it is a stale `main`.\n // Write / Edit / MultiEdit denies — all three must emit NO systemMessage.\n new GoldenFixture(\n 'claude/write-deny',\n 'rules',\n WIRE.write(CLAUDE_ENVELOPE, 'Write', {\n file_path: `${REPO_TOKEN}/src/foo.js`,\n content: 'var x = 1;\\n',\n }),\n ),\n new GoldenFixture(\n 'claude/edit-deny',\n 'rules',\n WIRE.write(CLAUDE_ENVELOPE, 'Edit', {\n file_path: `${REPO_TOKEN}/scripts/ok.ts`,\n old_string: 'const a = 1;',\n new_string: 'const { a } = b;',\n }),\n ),\n new GoldenFixture(\n 'claude/multiedit-deny',\n 'rules',\n WIRE.write(CLAUDE_ENVELOPE, 'MultiEdit', {\n file_path: `${REPO_TOKEN}/scripts/ok.ts`,\n edits: [{ old_string: 'const a = 1;', new_string: 'const { a } = b;' }],\n }),\n ),\n new GoldenFixture(\n 'claude/write-allow',\n 'rules',\n WIRE.write(CLAUDE_ENVELOPE, 'Write', {\n file_path: `${REPO_TOKEN}/scripts/ok.ts`,\n content: 'export const a = 1;\\n',\n }),\n ),\n new GoldenFixture(\n 'claude/crash',\n 'rules',\n WIRE.write(CLAUDE_ENVELOPE, 'Write', {\n file_path: `${REPO_TOKEN}/scripts/ok.ts`,\n content: 'export const a = 1;\\n',\n }),\n true,\n ),\n\n // ── Codex ──────────────────────────────────────────────────────────────────────────────────────\n // Codex has no Read tool: a read arrives as `Bash` running a pager, which read parity turns into a\n // read-scoped verdict ON TOP of the bash guards.\n new GoldenFixture(\n 'codex/apply-patch-deny',\n 'rules',\n WIRE.write(CODEX_ENVELOPE, 'apply_patch', { command: PATCH_ADD_JS }),\n ),\n new GoldenFixture(\n 'codex/apply-patch-allow',\n 'rules',\n WIRE.write(CODEX_ENVELOPE, 'apply_patch', { command: PATCH_ADD_TS }),\n ),\n new GoldenFixture(\n 'codex/crash',\n 'rules',\n WIRE.write(CODEX_ENVELOPE, 'apply_patch', { command: PATCH_ADD_TS }),\n true,\n ),\n];\n\n// A rules module that blows up the moment it is required — the shortest honest way to reach the hook's\n// fail-closed boundary from OUTSIDE the hook, i.e. without stubbing anything the pipeline owns.\nconst CRASHING_RULE_MODULE = \"throw new Error('boom from a custom rule module');\\n\";\n\n/**\n * Builds ONE throwaway git repo per fixture and returns it with the payload's `<REPO>` resolved.\n *\n * A FRESH repo per fixture, not one shared: the hook writes `.webpieces/` state (the decision log, the\n * main-sync cache) as it runs, so a shared tree would let fixture N's leftovers decide fixture N+1's\n * verdict — an order-dependent suite, which is the one kind of golden test worse than none.\n *\n * `realpathSync` matters on macOS: `os.tmpdir()` hands back `/var/folders/...` which resolves to\n * `/private/var/...`, and the L1 location guard compares the payload's cwd against the resolved repo\n * root. Without it every fixture denies with \"run git from the repo root\" instead of the verdict under\n * test.\n */\nexport class GoldenRepoBuilder {\n private readonly configJson: string;\n\n constructor() {\n this.configJson = fs.readFileSync(\n path.join(__dirname, '__goldens__', 'fixture-webpieces.config.json'),\n 'utf8',\n );\n }\n\n build(fixture: GoldenFixture): PreparedFixture {\n const root = specTempDirs.makeReal('wp-hook-golden-');\n const repo = path.join(root, 'repo');\n fs.mkdirSync(repo);\n this.git(repo, 'init -q -b main');\n // The developer's own commit hooks would otherwise fire inside this throwaway repo.\n this.git(repo, 'config core.hooksPath /dev/null');\n this.git(repo, 'config user.email t@t.co');\n this.git(repo, 'config user.name tester');\n const manifests = policyFixture\n .manifests()\n .map((pack: RulePackManifest) =>\n pack.packageName === rulePackManifest.packageName ? rulePackManifest : pack,\n );\n if (fixture.crashingRuntime) {\n const seed = Object.assign(new ConfigObject(), { mode: 'ON' });\n const definition = new OwnedRuleDefinition(\n 'client-crash',\n { mode: new FieldDef('string', ['ON', 'OFF']) },\n 1,\n {},\n seed,\n 'rules',\n new RuleHelp('Client failure fixture', 'Repair {configFile}.'),\n );\n manifests.push(\n new RulePackManifest(\n '@client/crash',\n '1.0.0-fixture',\n 3,\n [definition],\n [\n new RuleContribution(\n 'client-crash',\n '@client/crash',\n 'source-hook',\n './crash-runtime.cjs',\n ),\n ],\n [],\n [],\n ),\n );\n }\n policyFixture.writeOwnerConfig(repo, JSON.parse(this.repoConfig(fixture)), manifests);\n fs.writeFileSync(path.join(repo, 'f.txt'), 'hello\\n');\n fs.mkdirSync(path.join(repo, 'scripts'));\n fs.writeFileSync(path.join(repo, 'scripts', 'ok.ts'), 'const a = 1;\\n');\n if (fixture.crashingRuntime) {\n fs.writeFileSync(path.join(repo, 'crash-runtime.cjs'), CRASHING_RULE_MODULE);\n }\n this.git(repo, 'add -A');\n this.git(repo, 'commit -qm init');\n // A local origin/main, so `origin/main..<branch>` resolves exactly as it does in a real clone.\n this.git(repo, 'update-ref refs/remotes/origin/main HEAD');\n // A feature branch, because that is where an agent actually works.\n this.git(repo, 'checkout -q -b dean/fixture');\n return new PreparedFixture(repo, root, fixture.stdin.split(REPO_TOKEN).join(repo));\n }\n\n private repoConfig(fixture: GoldenFixture): string {\n if (!fixture.crashingRuntime) return this.configJson;\n // webpieces-disable no-any-unknown -- the frozen fixture config is data on disk; re-parsing it into a named type here would be a second declaration of a file whose whole point is being literal\n const parsed = JSON.parse(this.configJson) as Record<string, unknown>;\n (parsed['rules'] as Record<string, ConfigObject>)['client-crash'] = Object.assign(\n new ConfigObject(),\n { mode: 'ON' },\n );\n return JSON.stringify(parsed, null, 4);\n }\n\n private git(repo: string, args: string): void {\n execSync(`git ${args}`, { cwd: repo, encoding: 'utf8' });\n }\n}\n"]}
@@ -1,6 +1,3 @@
1
- import { WebpiecesRulesConfig, MatchRuleConfig } from "@webpieces/rules-config";
2
- import type { Rule } from '@webpieces/hook-runtime';
3
- export declare function loadRules(config: WebpiecesRulesConfig, workspaceRoot: string): readonly Rule[];
1
+ import { MatchRuleConfig } from '@webpieces/rules-config';
2
+ import { Rule } from '@webpieces/hook-runtime';
4
3
  export declare function loadMatchRules(matchRules: readonly MatchRuleConfig[]): Rule[];
5
- export declare function loadBuiltInRules(config: WebpiecesRulesConfig): Rule[];
6
- export declare function loadCustomRules(config: WebpiecesRulesConfig, workspaceRoot: string): Rule[];
@@ -1,155 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadRules = loadRules;
4
3
  exports.loadMatchRules = loadMatchRules;
5
- exports.loadBuiltInRules = loadBuiltInRules;
6
- exports.loadCustomRules = loadCustomRules;
7
- const tslib_1 = require("tslib");
8
- const fs = tslib_1.__importStar(require("fs"));
9
- const path = tslib_1.__importStar(require("path"));
10
- const tooling_common_1 = require("@webpieces/tooling-common");
11
- const to_error_1 = require("@webpieces/tooling-common/to-error");
12
- const hook_runtime_1 = require("@webpieces/hook-runtime");
13
- const hook_runtime_2 = require("@webpieces/hook-runtime");
14
- const index_1 = require("./rules/index");
15
- const no_any_unknown_1 = require("./rules/no-any-unknown");
16
- const no_implicit_any_1 = require("./rules/no-implicit-any");
17
- const max_file_lines_1 = require("./rules/max-file-lines");
18
- const validate_ts_in_src_1 = require("./rules/validate-ts-in-src");
19
- const no_destructure_1 = require("./rules/no-destructure");
20
- const require_return_type_1 = require("./rules/require-return-type");
21
- const no_unmanaged_exceptions_1 = require("./rules/no-unmanaged-exceptions");
22
- const catch_error_pattern_1 = require("./rules/catch-error-pattern");
23
- const throw_cause_required_1 = require("./rules/throw-cause-required");
24
- const no_symbol_di_tokens_1 = require("./rules/no-symbol-di-tokens");
25
- const no_custom_css_1 = require("./rules/no-custom-css");
26
- const no_process_exit_outside_main_1 = require("./rules/no-process-exit-outside-main");
27
- const no_js_files_1 = require("./rules/no-js-files");
28
4
  const match_rule_1 = require("./rules/match-rule");
29
- const REQUIRED_FIELDS = ['name', 'description', 'scope', 'files', 'check'];
30
- const VALID_SCOPES = new Set(['edit', 'file', 'bash']);
31
- const BUILT_IN_RULE_MAP = {
32
- 'no-any-unknown': (c) => [new no_any_unknown_1.NoAnyUnknownRule(c)],
33
- 'no-implicit-any': (c) => [new no_implicit_any_1.NoImplicitAnyRule(c)],
34
- 'max-file-lines': (c) => [new max_file_lines_1.MaxFileLinesRule(c)],
35
- 'validate-ts-in-src': (c) => [new validate_ts_in_src_1.ValidateTsInSrcRule(c)],
36
- 'no-destructure': (c) => [new no_destructure_1.NoDestructureRule(c)],
37
- 'require-return-type': (c) => [new require_return_type_1.RequireReturnTypeRule(c)],
38
- 'no-unmanaged-exceptions': (c) => [new no_unmanaged_exceptions_1.NoUnmanagedExceptionsRule(c)],
39
- 'catch-error-pattern': (c) => [new catch_error_pattern_1.CatchErrorPatternRule(c)],
40
- 'throw-cause-required': (c) => [new throw_cause_required_1.ThrowCauseRequiredRule(c)],
41
- 'no-symbol-di-tokens': (c) => [new no_symbol_di_tokens_1.NoSymbolDiTokensRule(c)],
42
- 'no-custom-css': (c) => [new no_custom_css_1.NoCustomCssRule(c)],
43
- 'no-process-exit-outside-main': (c) => [new no_process_exit_outside_main_1.NoProcessExitOutsideMainRule(c)],
44
- 'no-js-files': (c) => [new no_js_files_1.NoJsFilesRule(c)],
45
- };
46
- // Index the typed config by rule name. Each value is the rule's *Config (a plain object from
47
- // JSON), or undefined when the rule has no entry yet (the sync check reports those).
48
- function asConfigMap(config) {
49
- // webpieces-disable no-any-unknown -- index the typed config by dynamic rule name
50
- return config;
51
- }
52
- // webpieces-disable no-function-outside-class -- the module's entry point, beside loadMatchRules/loadKeylessBashRules; this whole loader is module-scope functions and a lone class for one of them would break the file's shape
53
- function loadRules(config, workspaceRoot) {
54
- const builtIns = loadBuiltInRules(config);
55
- const custom = loadCustomRules(config, workspaceRoot);
56
- return [...builtIns, ...custom];
57
- }
58
- // One MatchRule per entry of the `match-rules` array. Kept separate from loadRules (built-ins/custom)
59
- // because match-rules live in their own validated section — they must NOT flow through the
60
- // config-sync check, which compares rule names against the `rules`/`hookGuards` map.
5
+ // webpieces-disable no-function-outside-class -- public module entry point for explicit client-authored match rules
61
6
  function loadMatchRules(matchRules) {
62
- return matchRules.map((c) => new match_rule_1.MatchRule(c));
63
- }
64
- // Iterates CONFIG KEYS, not rule names — one entry can yield several rules (see BUILT_IN_RULE_MAP).
65
- // webpieces-disable no-function-outside-class -- the body of loadRules above, in the same module of loader functions
66
- function loadBuiltInRules(config) {
67
- const map = asConfigMap(config);
68
- const rules = [];
69
- for (const configKey of index_1.builtInConfigKeys) {
70
- const factory = BUILT_IN_RULE_MAP[configKey];
71
- if (!factory) {
72
- process.stderr.write(`[ai-hooks] unknown built-in config key: ${configKey}\n`);
73
- continue;
74
- }
75
- const ruleConfig = map[configKey] ?? new hook_runtime_1.EmptyRuleConfig();
76
- rules.push(...factory(ruleConfig));
77
- }
78
- return rules;
79
- }
80
- // webpieces-disable no-function-outside-class -- existing source registry helper contributed to both source and umbrella runners
81
- function loadCustomRules(config, workspaceRoot) {
82
- const dirs = config.rulesDir ?? [];
83
- // webpieces-disable no-any-unknown -- index the typed config by dynamic custom-rule name
84
- const map = config;
85
- const rules = [];
86
- for (const plain of loadCustomPlainRules(dirs, workspaceRoot)) {
87
- const rawConfig = map[plain.name] ?? {};
88
- rules.push(new hook_runtime_2.CustomRuleAdapter(plain, rawConfig));
89
- }
90
- return rules;
91
- }
92
- function loadCustomPlainRules(rulesDirs, workspaceRoot) {
93
- const modules = [];
94
- for (const dir of rulesDirs) {
95
- const absDir = path.isAbsolute(dir) ? dir : path.join(workspaceRoot, dir);
96
- if (!fs.existsSync(absDir)) {
97
- process.stderr.write(`[ai-hooks] rulesDir not found: ${absDir}\n`);
98
- continue;
99
- }
100
- let entries;
101
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
102
- try {
103
- entries = fs.readdirSync(absDir).filter((e) => e.endsWith('.js'));
104
- }
105
- catch (err) {
106
- const error = (0, to_error_1.toError)(err);
107
- throw new tooling_common_1.InformAiError(`Cannot read custom rules directory '${absDir}'`, { cause: error });
108
- }
109
- for (const entry of entries) {
110
- const full = path.join(absDir, entry);
111
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
112
- try {
113
- const mod = require(full);
114
- const candidate = mod.default || mod;
115
- if (validateRule(candidate))
116
- modules.push(candidate);
117
- }
118
- catch (err) {
119
- const error = (0, to_error_1.toError)(err);
120
- throw new tooling_common_1.InformAiError(`Cannot load custom rule '${full}'`, { cause: error });
121
- }
122
- }
123
- }
124
- return modules;
125
- }
126
- // webpieces-disable no-any-unknown -- validates untrusted require() output at system boundary
127
- function validateRule(rule) {
128
- if (!rule || typeof rule !== 'object') {
129
- process.stderr.write('[ai-hooks] rule is not an object, skipping\n');
130
- return false;
131
- }
132
- // webpieces-disable no-any-unknown -- narrowing from unknown at system boundary
133
- const obj = rule;
134
- for (const field of REQUIRED_FIELDS) {
135
- if (obj[field] === undefined) {
136
- const name = typeof obj['name'] === 'string' ? obj['name'] : '<unnamed>';
137
- process.stderr.write(`[ai-hooks] rule "${name}" missing required field: ${field}\n`);
138
- return false;
139
- }
140
- }
141
- if (!VALID_SCOPES.has(obj['scope'])) {
142
- process.stderr.write(`[ai-hooks] rule "${obj['name']}" has invalid scope: ${String(obj['scope'])}\n`);
143
- return false;
144
- }
145
- if (!Array.isArray(obj['files'])) {
146
- process.stderr.write(`[ai-hooks] rule "${obj['name']}" files must be an array\n`);
147
- return false;
148
- }
149
- if (typeof obj['check'] !== 'function') {
150
- process.stderr.write(`[ai-hooks] rule "${obj['name']}" check must be a function\n`);
151
- return false;
152
- }
153
- return true;
7
+ return matchRules.map((config) => new match_rule_1.MatchRule(config));
154
8
  }
155
9
  //# sourceMappingURL=load-rules.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-rules.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/load-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import { SourceContributionConfig } from \"./source-contribution-config\";\nimport { BaseRuleConfig } from '@webpieces/rules-sdk';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nimport { RuleOptions, WebpiecesRulesConfig, MatchRuleConfig } from \"@webpieces/rules-config\";\nimport { ValidateTsInSrcConfig, ThrowCauseRequiredConfig, NoJsFilesConfig } from \"../configs/rule-configs\";\n\nimport type { Rule, PlainRule } from '@webpieces/hook-runtime';\nimport { InformAiError } from '@webpieces/tooling-common';\nimport { toError } from '@webpieces/tooling-common/to-error';\nimport { EmptyRuleConfig } from '@webpieces/hook-runtime';\nimport { CustomRuleAdapter } from '@webpieces/hook-runtime';\nimport { builtInConfigKeys } from './rules/index';\nimport { NoAnyUnknownRule } from './rules/no-any-unknown';\nimport { NoImplicitAnyRule } from './rules/no-implicit-any';\nimport { MaxFileLinesRule } from './rules/max-file-lines';\nimport { ValidateTsInSrcRule } from './rules/validate-ts-in-src';\nimport { NoDestructureRule } from './rules/no-destructure';\nimport { RequireReturnTypeRule } from './rules/require-return-type';\nimport { NoUnmanagedExceptionsRule } from './rules/no-unmanaged-exceptions';\nimport { CatchErrorPatternRule } from './rules/catch-error-pattern';\nimport { ThrowCauseRequiredRule } from './rules/throw-cause-required';\nimport { NoSymbolDiTokensRule } from './rules/no-symbol-di-tokens';\nimport { NoCustomCssRule } from './rules/no-custom-css';\nimport { NoProcessExitOutsideMainRule } from './rules/no-process-exit-outside-main';\nimport { NoJsFilesRule } from './rules/no-js-files';\nimport { MatchRule } from './rules/match-rule';\n\nconst REQUIRED_FIELDS: readonly string[] = ['name', 'description', 'scope', 'files', 'check'];\nconst VALID_SCOPES = new Set(['edit', 'file', 'bash']);\n\n/**\n * ONE CONFIG KEY → N RULES.\n *\n * Each built-in rule is constructed from its typed *Config (the entry in webpieces.config.json). The\n * config arrives as a plain object structurally typed as the *Config class, so the `as` narrows the\n * shared BaseRuleConfig param back to the concrete config the rule consumes.\n *\n * This registry contains source-rule factories only. Each accepts BaseRuleConfig and returns an\n * array of source rule instances for its config key. Workflow policies and command hints are owned\n * by agent-workflow-rules and are not inputs to this loader.\n */\ntype RuleFactory = (config: BaseRuleConfig) => readonly Rule[];\n\nconst BUILT_IN_RULE_MAP: Record<string, RuleFactory> = {\n 'no-any-unknown': (c: BaseRuleConfig) => [new NoAnyUnknownRule(c as SourceContributionConfig)],\n 'no-implicit-any': (c: BaseRuleConfig) => [new NoImplicitAnyRule(c as SourceContributionConfig)],\n 'max-file-lines': (c: BaseRuleConfig) => [new MaxFileLinesRule(c as SourceContributionConfig)],\n 'validate-ts-in-src': (c: BaseRuleConfig) => [new ValidateTsInSrcRule(c as ValidateTsInSrcConfig)],\n 'no-destructure': (c: BaseRuleConfig) => [new NoDestructureRule(c as SourceContributionConfig)],\n 'require-return-type': (c: BaseRuleConfig) => [new RequireReturnTypeRule(c as SourceContributionConfig)],\n 'no-unmanaged-exceptions': (c: BaseRuleConfig) => [new NoUnmanagedExceptionsRule(c as SourceContributionConfig)],\n 'catch-error-pattern': (c: BaseRuleConfig) => [new CatchErrorPatternRule(c as SourceContributionConfig)],\n 'throw-cause-required': (c: BaseRuleConfig) => [new ThrowCauseRequiredRule(c as ThrowCauseRequiredConfig)],\n 'no-symbol-di-tokens': (c: BaseRuleConfig) => [new NoSymbolDiTokensRule(c as SourceContributionConfig)],\n 'no-custom-css': (c: BaseRuleConfig) => [new NoCustomCssRule(c as SourceContributionConfig)],\n 'no-process-exit-outside-main': (c: BaseRuleConfig) => [new NoProcessExitOutsideMainRule(c as SourceContributionConfig)],\n 'no-js-files': (c: BaseRuleConfig) => [new NoJsFilesRule(c as NoJsFilesConfig)],\n\n};\n\n// Index the typed config by rule name. Each value is the rule's *Config (a plain object from\n// JSON), or undefined when the rule has no entry yet (the sync check reports those).\nfunction asConfigMap(config: WebpiecesRulesConfig): Record<string, BaseRuleConfig | undefined> {\n // webpieces-disable no-any-unknown -- index the typed config by dynamic rule name\n return config as unknown as Record<string, BaseRuleConfig | undefined>;\n}\n\n// webpieces-disable no-function-outside-class -- the module's entry point, beside loadMatchRules/loadKeylessBashRules; this whole loader is module-scope functions and a lone class for one of them would break the file's shape\nexport function loadRules(\n config: WebpiecesRulesConfig,\n workspaceRoot: string,\n): readonly Rule[] {\n const builtIns = loadBuiltInRules(config);\n const custom = loadCustomRules(config, workspaceRoot);\n return [...builtIns, ...custom];\n}\n\n// One MatchRule per entry of the `match-rules` array. Kept separate from loadRules (built-ins/custom)\n// because match-rules live in their own validated section — they must NOT flow through the\n// config-sync check, which compares rule names against the `rules`/`hookGuards` map.\nexport function loadMatchRules(matchRules: readonly MatchRuleConfig[]): Rule[] {\n return matchRules.map((c: MatchRuleConfig) => new MatchRule(c));\n}\n\n// Iterates CONFIG KEYS, not rule names — one entry can yield several rules (see BUILT_IN_RULE_MAP).\n// webpieces-disable no-function-outside-class -- the body of loadRules above, in the same module of loader functions\nexport function loadBuiltInRules(config: WebpiecesRulesConfig): Rule[] {\n const map = asConfigMap(config);\n const rules: Rule[] = [];\n for (const configKey of builtInConfigKeys) {\n const factory = BUILT_IN_RULE_MAP[configKey];\n if (!factory) {\n process.stderr.write(`[ai-hooks] unknown built-in config key: ${configKey}\\n`);\n continue;\n }\n const ruleConfig = map[configKey] ?? new EmptyRuleConfig();\n rules.push(...factory(ruleConfig));\n }\n return rules;\n}\n\n// webpieces-disable no-function-outside-class -- existing source registry helper contributed to both source and umbrella runners\nexport function loadCustomRules(config: WebpiecesRulesConfig, workspaceRoot: string): Rule[] {\n const dirs = config.rulesDir ?? [];\n // webpieces-disable no-any-unknown -- index the typed config by dynamic custom-rule name\n const map = config as unknown as Record<string, RuleOptions | undefined>;\n const rules: Rule[] = [];\n for (const plain of loadCustomPlainRules(dirs, workspaceRoot)) {\n const rawConfig = map[plain.name] ?? {};\n rules.push(new CustomRuleAdapter(plain, rawConfig));\n }\n return rules;\n}\n\nfunction loadCustomPlainRules(rulesDirs: readonly string[], workspaceRoot: string): PlainRule[] {\n const modules: PlainRule[] = [];\n for (const dir of rulesDirs) {\n const absDir = path.isAbsolute(dir) ? dir : path.join(workspaceRoot, dir);\n if (!fs.existsSync(absDir)) {\n process.stderr.write(`[ai-hooks] rulesDir not found: ${absDir}\\n`);\n continue;\n }\n let entries: string[];\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n entries = fs.readdirSync(absDir).filter((e: string) => e.endsWith('.js'));\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(`Cannot read custom rules directory '${absDir}'`, { cause: error });\n }\n for (const entry of entries) {\n const full = path.join(absDir, entry);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mod = require(full);\n const candidate = mod.default || mod;\n if (validateRule(candidate)) modules.push(candidate);\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(`Cannot load custom rule '${full}'`, { cause: error });\n }\n }\n }\n return modules;\n}\n\n// webpieces-disable no-any-unknown -- validates untrusted require() output at system boundary\nfunction validateRule(rule: unknown): rule is PlainRule {\n if (!rule || typeof rule !== 'object') {\n process.stderr.write('[ai-hooks] rule is not an object, skipping\\n');\n return false;\n }\n // webpieces-disable no-any-unknown -- narrowing from unknown at system boundary\n const obj = rule as Record<string, unknown>;\n for (const field of REQUIRED_FIELDS) {\n if (obj[field] === undefined) {\n const name = typeof obj['name'] === 'string' ? obj['name'] : '<unnamed>';\n process.stderr.write(`[ai-hooks] rule \"${name}\" missing required field: ${field}\\n`);\n return false;\n }\n }\n if (!VALID_SCOPES.has(obj['scope'] as string)) {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" has invalid scope: ${String(obj['scope'])}\\n`);\n return false;\n }\n if (!Array.isArray(obj['files'])) {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" files must be an array\\n`);\n return false;\n }\n if (typeof obj['check'] !== 'function') {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" check must be a function\\n`);\n return false;\n }\n return true;\n}\n"]}
1
+ {"version":3,"file":"load-rules.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/load-rules.ts"],"names":[],"mappings":";;AAKA,wCAEC;AALD,mDAA+C;AAE/C,oHAAoH;AACpH,SAAgB,cAAc,CAAC,UAAsC;IACjE,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,MAAuB,EAAE,EAAE,CAAC,IAAI,sBAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["import { MatchRuleConfig } from '@webpieces/rules-config';\nimport { Rule } from '@webpieces/hook-runtime';\nimport { MatchRule } from './rules/match-rule';\n\n// webpieces-disable no-function-outside-class -- public module entry point for explicit client-authored match rules\nexport function loadMatchRules(matchRules: readonly MatchRuleConfig[]): Rule[] {\n return matchRules.map((config: MatchRuleConfig) => new MatchRule(config));\n}\n"]}
@@ -1,2 +1,13 @@
1
- /** The thirteen source configuration keys owned by this rule provider. */
2
- export declare const builtInConfigKeys: readonly string[];
1
+ export { NoAnyUnknownRule } from './no-any-unknown';
2
+ export { NoImplicitAnyRule } from './no-implicit-any';
3
+ export { MaxFileLinesRule } from './max-file-lines';
4
+ export { ValidateTsInSrcRule } from './validate-ts-in-src';
5
+ export { NoDestructureRule } from './no-destructure';
6
+ export { RequireReturnTypeRule } from './require-return-type';
7
+ export { NoUnmanagedExceptionsRule } from './no-unmanaged-exceptions';
8
+ export { CatchErrorPatternRule } from './catch-error-pattern';
9
+ export { ThrowCauseRequiredRule } from './throw-cause-required';
10
+ export { NoSymbolDiTokensRule } from './no-symbol-di-tokens';
11
+ export { NoCustomCssRule } from './no-custom-css';
12
+ export { NoProcessExitOutsideMainRule } from './no-process-exit-outside-main';
13
+ export { NoJsFilesRule } from './no-js-files';
@@ -1,20 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.builtInConfigKeys = void 0;
4
- /** The thirteen source configuration keys owned by this rule provider. */
5
- exports.builtInConfigKeys = [
6
- 'no-any-unknown',
7
- 'no-implicit-any',
8
- 'max-file-lines',
9
- 'validate-ts-in-src',
10
- 'no-js-files',
11
- 'no-destructure',
12
- 'require-return-type',
13
- 'no-unmanaged-exceptions',
14
- 'catch-error-pattern',
15
- 'throw-cause-required',
16
- 'no-symbol-di-tokens',
17
- 'no-custom-css',
18
- 'no-process-exit-outside-main',
19
- ];
3
+ exports.NoJsFilesRule = exports.NoProcessExitOutsideMainRule = exports.NoCustomCssRule = exports.NoSymbolDiTokensRule = exports.ThrowCauseRequiredRule = exports.CatchErrorPatternRule = exports.NoUnmanagedExceptionsRule = exports.RequireReturnTypeRule = exports.NoDestructureRule = exports.ValidateTsInSrcRule = exports.MaxFileLinesRule = exports.NoImplicitAnyRule = exports.NoAnyUnknownRule = void 0;
4
+ var no_any_unknown_1 = require("./no-any-unknown");
5
+ Object.defineProperty(exports, "NoAnyUnknownRule", { enumerable: true, get: function () { return no_any_unknown_1.NoAnyUnknownRule; } });
6
+ var no_implicit_any_1 = require("./no-implicit-any");
7
+ Object.defineProperty(exports, "NoImplicitAnyRule", { enumerable: true, get: function () { return no_implicit_any_1.NoImplicitAnyRule; } });
8
+ var max_file_lines_1 = require("./max-file-lines");
9
+ Object.defineProperty(exports, "MaxFileLinesRule", { enumerable: true, get: function () { return max_file_lines_1.MaxFileLinesRule; } });
10
+ var validate_ts_in_src_1 = require("./validate-ts-in-src");
11
+ Object.defineProperty(exports, "ValidateTsInSrcRule", { enumerable: true, get: function () { return validate_ts_in_src_1.ValidateTsInSrcRule; } });
12
+ var no_destructure_1 = require("./no-destructure");
13
+ Object.defineProperty(exports, "NoDestructureRule", { enumerable: true, get: function () { return no_destructure_1.NoDestructureRule; } });
14
+ var require_return_type_1 = require("./require-return-type");
15
+ Object.defineProperty(exports, "RequireReturnTypeRule", { enumerable: true, get: function () { return require_return_type_1.RequireReturnTypeRule; } });
16
+ var no_unmanaged_exceptions_1 = require("./no-unmanaged-exceptions");
17
+ Object.defineProperty(exports, "NoUnmanagedExceptionsRule", { enumerable: true, get: function () { return no_unmanaged_exceptions_1.NoUnmanagedExceptionsRule; } });
18
+ var catch_error_pattern_1 = require("./catch-error-pattern");
19
+ Object.defineProperty(exports, "CatchErrorPatternRule", { enumerable: true, get: function () { return catch_error_pattern_1.CatchErrorPatternRule; } });
20
+ var throw_cause_required_1 = require("./throw-cause-required");
21
+ Object.defineProperty(exports, "ThrowCauseRequiredRule", { enumerable: true, get: function () { return throw_cause_required_1.ThrowCauseRequiredRule; } });
22
+ var no_symbol_di_tokens_1 = require("./no-symbol-di-tokens");
23
+ Object.defineProperty(exports, "NoSymbolDiTokensRule", { enumerable: true, get: function () { return no_symbol_di_tokens_1.NoSymbolDiTokensRule; } });
24
+ var no_custom_css_1 = require("./no-custom-css");
25
+ Object.defineProperty(exports, "NoCustomCssRule", { enumerable: true, get: function () { return no_custom_css_1.NoCustomCssRule; } });
26
+ var no_process_exit_outside_main_1 = require("./no-process-exit-outside-main");
27
+ Object.defineProperty(exports, "NoProcessExitOutsideMainRule", { enumerable: true, get: function () { return no_process_exit_outside_main_1.NoProcessExitOutsideMainRule; } });
28
+ var no_js_files_1 = require("./no-js-files");
29
+ Object.defineProperty(exports, "NoJsFilesRule", { enumerable: true, get: function () { return no_js_files_1.NoJsFilesRule; } });
20
30
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/index.ts"],"names":[],"mappings":";;;AAAA,0EAA0E;AAC7D,QAAA,iBAAiB,GAAsB;IAChD,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,oBAAoB;IACpB,aAAa;IACb,gBAAgB;IAChB,qBAAqB;IACrB,yBAAyB;IACzB,qBAAqB;IACrB,sBAAsB;IACtB,qBAAqB;IACrB,eAAe;IACf,8BAA8B;CACjC,CAAC","sourcesContent":["/** The thirteen source configuration keys owned by this rule provider. */\nexport const builtInConfigKeys: readonly string[] = [\n 'no-any-unknown',\n 'no-implicit-any',\n 'max-file-lines',\n 'validate-ts-in-src',\n 'no-js-files',\n 'no-destructure',\n 'require-return-type',\n 'no-unmanaged-exceptions',\n 'catch-error-pattern',\n 'throw-cause-required',\n 'no-symbol-di-tokens',\n 'no-custom-css',\n 'no-process-exit-outside-main',\n];\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/index.ts"],"names":[],"mappings":";;;AAAA,mDAAoD;AAA3C,kHAAA,gBAAgB,OAAA;AACzB,qDAAsD;AAA7C,oHAAA,iBAAiB,OAAA;AAC1B,mDAAoD;AAA3C,kHAAA,gBAAgB,OAAA;AACzB,2DAA2D;AAAlD,yHAAA,mBAAmB,OAAA;AAC5B,mDAAqD;AAA5C,mHAAA,iBAAiB,OAAA;AAC1B,6DAA8D;AAArD,4HAAA,qBAAqB,OAAA;AAC9B,qEAAsE;AAA7D,oIAAA,yBAAyB,OAAA;AAClC,6DAA8D;AAArD,4HAAA,qBAAqB,OAAA;AAC9B,+DAAgE;AAAvD,8HAAA,sBAAsB,OAAA;AAC/B,6DAA6D;AAApD,2HAAA,oBAAoB,OAAA;AAC7B,iDAAkD;AAAzC,gHAAA,eAAe,OAAA;AACxB,+EAA8E;AAArE,4IAAA,4BAA4B,OAAA;AACrC,6CAA8C;AAArC,4GAAA,aAAa,OAAA","sourcesContent":["export { NoAnyUnknownRule } from './no-any-unknown';\nexport { NoImplicitAnyRule } from './no-implicit-any';\nexport { MaxFileLinesRule } from './max-file-lines';\nexport { ValidateTsInSrcRule } from './validate-ts-in-src';\nexport { NoDestructureRule } from './no-destructure';\nexport { RequireReturnTypeRule } from './require-return-type';\nexport { NoUnmanagedExceptionsRule } from './no-unmanaged-exceptions';\nexport { CatchErrorPatternRule } from './catch-error-pattern';\nexport { ThrowCauseRequiredRule } from './throw-cause-required';\nexport { NoSymbolDiTokensRule } from './no-symbol-di-tokens';\nexport { NoCustomCssRule } from './no-custom-css';\nexport { NoProcessExitOutsideMainRule } from './no-process-exit-outside-main';\nexport { NoJsFilesRule } from './no-js-files';\n"]}
@@ -1,4 +1,4 @@
1
- import { SourceContributionConfig } from "../source-contribution-config";
1
+ import { SourceContributionConfig } from '../source-contribution-config';
2
2
  import type { FileContext, Violation } from '@webpieces/hook-runtime';
3
3
  import { FileRuleBase } from '@webpieces/hook-runtime';
4
4
  import { FixHint } from '@webpieces/hook-runtime';