@webpieces/hook-runtime 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/hook-runtime",
3
- "version": "0.4.847",
3
+ "version": "0.4.849",
4
4
  "description": "Agent hook protocol, normalized events, and fail-closed application boundary.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,16 +22,16 @@
22
22
  "dependencies": {
23
23
  "inversify": "7.10.4",
24
24
  "reflect-metadata": "0.2.2",
25
- "@webpieces/rules-config": "0.4.847",
26
- "@webpieces/tooling-common": "0.4.847",
25
+ "@webpieces/rules-config": "0.4.849",
26
+ "@webpieces/tooling-common": "0.4.849",
27
27
  "tslib": "2.8.1",
28
- "@webpieces/rules-sdk": "0.4.847"
28
+ "@webpieces/rules-sdk": "0.4.849"
29
29
  },
30
30
  "publishConfig": {
31
31
  "access": "public"
32
32
  },
33
33
  "devDependencies": {
34
- "@webpieces/tooling-testkit": "0.4.847"
34
+ "@webpieces/tooling-testkit": "0.4.849"
35
35
  },
36
36
  "types": "./src/index.d.ts"
37
37
  }
@@ -3,9 +3,9 @@ import { AbstractRule, RuleOptions } from '@webpieces/rules-config';
3
3
  import type { PlainRule, Rule, RuleScope, EditContext, FileContext, BashContext, Violation } from './types';
4
4
  import type { FixHint } from './fix-hint';
5
5
  /**
6
- * Wraps a custom rule loaded from a `rulesDir` (a plain object) so it satisfies the same
6
+ * Wraps a client policy returned by its declared runtime factory (a plain object) so it satisfies the same
7
7
  * runtime `Rule` contract as the built-in rule classes: it gains `shouldRun()` (driven by the
8
- * rule's config entry in webpieces.config.json) from AbstractRule, and it seeds `ctx.options`
8
+ * rule's validated config entry in its declared owner file) from AbstractRule, and it seeds `ctx.options`
9
9
  * with the merged option bag before delegating to the plain rule's `check()` — preserving the
10
10
  * exact behavior custom rules had under the old options-based runner.
11
11
  */
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CustomRuleAdapter = void 0;
4
4
  const rules_config_1 = require("@webpieces/rules-config");
5
5
  /**
6
- * Wraps a custom rule loaded from a `rulesDir` (a plain object) so it satisfies the same
6
+ * Wraps a client policy returned by its declared runtime factory (a plain object) so it satisfies the same
7
7
  * runtime `Rule` contract as the built-in rule classes: it gains `shouldRun()` (driven by the
8
- * rule's config entry in webpieces.config.json) from AbstractRule, and it seeds `ctx.options`
8
+ * rule's validated config entry in its declared owner file) from AbstractRule, and it seeds `ctx.options`
9
9
  * with the merged option bag before delegating to the plain rule's `check()` — preserving the
10
10
  * exact behavior custom rules had under the old options-based runner.
11
11
  */
@@ -18,7 +18,7 @@ class CustomRuleAdapter extends rules_config_1.AbstractRule {
18
18
  impl;
19
19
  rawConfig;
20
20
  constructor(impl, rawConfig) {
21
- // A custom rule from `rulesDir` is keyed in webpieces.config.json by its own `name`, so the
21
+ // A declared client policy uses its canonical owned ID as its config key, so the
22
22
  // two identities coincide. Stated twice rather than defaulted — see AbstractRule.
23
23
  super(rawConfig, impl.name, impl.name);
24
24
  this.impl = impl;
@@ -1 +1 @@
1
- {"version":3,"file":"custom-rule-adapter.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/custom-rule-adapter.ts"],"names":[],"mappings":";;;AACA,0DAAoE;AAKpE;;;;;;GAMG;AACH,MAAa,iBAAkB,SAAQ,2BAA4B;IACtD,KAAK,CAAY;IACjB,KAAK,CAAoB;IACzB,WAAW,CAAS;IACpB,OAAO,CAAU;IACjB,cAAc,CAAc;IACpB,IAAI,CAAY;IAChB,SAAS,CAAc;IAExC,YAAY,IAAe,EAAE,SAAsB;QAC/C,4FAA4F;QAC5F,kFAAkF;QAClF,KAAK,CAAC,SAA2B,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAEO,YAAY;QAChB,MAAM,GAAG,GAAgB,EAAE,CAAC;QAC5B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACxF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5C,kEAAkE;YAClE,IAAI,GAAG,KAAK,MAAM;gBAAE,SAAS;YAC7B,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IAED,KAAK,CAAC,GAA4C;QAC9C,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;CACJ;AArCD,8CAqCC","sourcesContent":["import { BaseRuleConfig } from '@webpieces/rules-sdk';\nimport { AbstractRule, RuleOptions } from '@webpieces/rules-config';\n\nimport type { PlainRule, Rule, RuleScope, EditContext, FileContext, BashContext, Violation } from './types';\nimport type { FixHint } from './fix-hint';\n\n/**\n * Wraps a custom rule loaded from a `rulesDir` (a plain object) so it satisfies the same\n * runtime `Rule` contract as the built-in rule classes: it gains `shouldRun()` (driven by the\n * rule's config entry in webpieces.config.json) from AbstractRule, and it seeds `ctx.options`\n * with the merged option bag before delegating to the plain rule's `check()` — preserving the\n * exact behavior custom rules had under the old options-based runner.\n */\nexport class CustomRuleAdapter extends AbstractRule<BaseRuleConfig> implements Rule {\n readonly scope: RuleScope;\n readonly files: readonly string[];\n readonly description: string;\n readonly fixHint: FixHint;\n readonly defaultOptions: RuleOptions;\n private readonly impl: PlainRule;\n private readonly rawConfig: RuleOptions;\n\n constructor(impl: PlainRule, rawConfig: RuleOptions) {\n // A custom rule from `rulesDir` is keyed in webpieces.config.json by its own `name`, so the\n // two identities coincide. Stated twice rather than defaulted — see AbstractRule.\n super(rawConfig as BaseRuleConfig, impl.name, impl.name);\n this.impl = impl;\n this.scope = impl.scope;\n this.files = impl.files;\n this.description = impl.description;\n this.fixHint = impl.fixHint;\n this.defaultOptions = impl.defaultOptions;\n this.rawConfig = rawConfig;\n }\n\n private buildOptions(): RuleOptions {\n const out: RuleOptions = {};\n for (const key of Object.keys(this.defaultOptions)) out[key] = this.defaultOptions[key];\n for (const key of Object.keys(this.rawConfig)) {\n // 'mode' is the framework-level on/off switch, not a rule option.\n if (key === 'mode') continue;\n out[key] = this.rawConfig[key];\n }\n return out;\n }\n\n check(ctx: EditContext | FileContext | BashContext): readonly Violation[] {\n ctx.options = this.buildOptions();\n return this.impl.check(ctx);\n }\n}\n"]}
1
+ {"version":3,"file":"custom-rule-adapter.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/custom-rule-adapter.ts"],"names":[],"mappings":";;;AACA,0DAAoE;AAapE;;;;;;GAMG;AACH,MAAa,iBAAkB,SAAQ,2BAA4B;IACtD,KAAK,CAAY;IACjB,KAAK,CAAoB;IACzB,WAAW,CAAS;IACpB,OAAO,CAAU;IACjB,cAAc,CAAc;IACpB,IAAI,CAAY;IAChB,SAAS,CAAc;IAExC,YAAY,IAAe,EAAE,SAAsB;QAC/C,iFAAiF;QACjF,kFAAkF;QAClF,KAAK,CAAC,SAA2B,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAEO,YAAY;QAChB,MAAM,GAAG,GAAgB,EAAE,CAAC;QAC5B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACxF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5C,kEAAkE;YAClE,IAAI,GAAG,KAAK,MAAM;gBAAE,SAAS;YAC7B,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IAED,KAAK,CAAC,GAA4C;QAC9C,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;CACJ;AArCD,8CAqCC","sourcesContent":["import { BaseRuleConfig } from '@webpieces/rules-sdk';\nimport { AbstractRule, RuleOptions } from '@webpieces/rules-config';\n\nimport type {\n PlainRule,\n Rule,\n RuleScope,\n EditContext,\n FileContext,\n BashContext,\n Violation,\n} from './types';\nimport type { FixHint } from './fix-hint';\n\n/**\n * Wraps a client policy returned by its declared runtime factory (a plain object) so it satisfies the same\n * runtime `Rule` contract as the built-in rule classes: it gains `shouldRun()` (driven by the\n * rule's validated config entry in its declared owner file) from AbstractRule, and it seeds `ctx.options`\n * with the merged option bag before delegating to the plain rule's `check()` — preserving the\n * exact behavior custom rules had under the old options-based runner.\n */\nexport class CustomRuleAdapter extends AbstractRule<BaseRuleConfig> implements Rule {\n readonly scope: RuleScope;\n readonly files: readonly string[];\n readonly description: string;\n readonly fixHint: FixHint;\n readonly defaultOptions: RuleOptions;\n private readonly impl: PlainRule;\n private readonly rawConfig: RuleOptions;\n\n constructor(impl: PlainRule, rawConfig: RuleOptions) {\n // A declared client policy uses its canonical owned ID as its config key, so the\n // two identities coincide. Stated twice rather than defaulted — see AbstractRule.\n super(rawConfig as BaseRuleConfig, impl.name, impl.name);\n this.impl = impl;\n this.scope = impl.scope;\n this.files = impl.files;\n this.description = impl.description;\n this.fixHint = impl.fixHint;\n this.defaultOptions = impl.defaultOptions;\n this.rawConfig = rawConfig;\n }\n\n private buildOptions(): RuleOptions {\n const out: RuleOptions = {};\n for (const key of Object.keys(this.defaultOptions)) out[key] = this.defaultOptions[key];\n for (const key of Object.keys(this.rawConfig)) {\n // 'mode' is the framework-level on/off switch, not a rule option.\n if (key === 'mode') continue;\n out[key] = this.rawConfig[key];\n }\n return out;\n }\n\n check(ctx: EditContext | FileContext | BashContext): readonly Violation[] {\n ctx.options = this.buildOptions();\n return this.impl.check(ctx);\n }\n}\n"]}
@@ -3,8 +3,7 @@ import { ToolKind, NormalizedToolInput } from '../protocol';
3
3
  import { Rule, BlockedResult } from './types';
4
4
  export declare class HookRuleSet {
5
5
  readonly rules: readonly Rule[];
6
- readonly configuredRules: readonly Rule[];
7
- constructor(rules: readonly Rule[], configuredRules: readonly Rule[]);
6
+ constructor(rules: readonly Rule[]);
8
7
  }
9
8
  export declare class HookFileEvaluation {
10
9
  evaluate(toolKind: ToolKind, input: NormalizedToolInput, loaded: LoadedConfig, contributions: HookRuleSet): BlockedResult | null;
@@ -14,10 +14,8 @@ const report_1 = require("./report");
14
14
  const root_manifest_1 = require("./root-manifest");
15
15
  class HookRuleSet {
16
16
  rules;
17
- configuredRules;
18
- constructor(rules, configuredRules) {
17
+ constructor(rules) {
19
18
  this.rules = rules;
20
- this.configuredRules = configuredRules;
21
19
  }
22
20
  }
23
21
  exports.HookRuleSet = HookRuleSet;
@@ -25,20 +23,23 @@ class HookFileEvaluation {
25
23
  evaluate(toolKind, input, loaded, contributions) {
26
24
  if (loaded.configPath === null)
27
25
  return null;
28
- if (path.resolve(input.filePath) === path.resolve(loaded.configPath)
29
- || (0, root_manifest_1.isRootManifest)(input.filePath) || new rules_config_1.HomeConfigService().isHomeConfigPath(input.filePath))
26
+ if (path.resolve(input.filePath) === path.resolve(loaded.configPath) ||
27
+ (0, root_manifest_1.isRootManifest)(input.filePath) ||
28
+ new rules_config_1.HomeConfigService().isHomeConfigPath(input.filePath))
30
29
  return null;
31
30
  const root = path.dirname(loaded.configPath);
32
31
  const governed = new target_tree_1.TargetTreeResolver().governedPath(input.filePath, root);
33
32
  const rules = new delete_scoped_rules_1.DeleteScopedRules().narrow(toolKind, (0, excluded_paths_1.filterByExcludedPaths)(contributions.rules, governed, loaded.excludePaths));
34
33
  if (rules.length === 0)
35
34
  return null;
36
- const sync = (0, rule_evaluation_1.checkConfigSync)(rules.filter((rule) => contributions.configuredRules.includes(rule)), loaded.rulesConfig, loaded.ruleRegistry);
37
- if (sync)
38
- return sync;
39
35
  const contexts = (0, build_context_1.buildContexts)(toolKind, input, root, governed);
40
- const groups = [...(0, rule_evaluation_1.runEditRules)(rules, contexts.editContexts), ...(0, rule_evaluation_1.runFileRules)(rules, contexts.fileContext)];
41
- return groups.length === 0 ? null : new types_1.BlockedResult((0, report_1.formatReport)(governed.relativePath, groups));
36
+ const groups = [
37
+ ...(0, rule_evaluation_1.runEditRules)(rules, contexts.editContexts),
38
+ ...(0, rule_evaluation_1.runFileRules)(rules, contexts.fileContext),
39
+ ];
40
+ return groups.length === 0
41
+ ? null
42
+ : new types_1.BlockedResult((0, report_1.formatReport)(governed.relativePath, groups));
42
43
  }
43
44
  }
44
45
  exports.HookFileEvaluation = HookFileEvaluation;
@@ -1 +1 @@
1
- {"version":3,"file":"file-evaluation.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/file-evaluation.ts"],"names":[],"mappings":";;;;AAAA,mDAA6B;AAC7B,0DAA0E;AAE1E,mCAA8C;AAE9C,+CAAmD;AACnD,+DAA0D;AAC1D,qDAAyD;AACzD,mDAAgD;AAChD,uDAAgF;AAChF,qCAAwC;AACxC,mDAAiD;AAEjD,MAAa,WAAW;IACC;IAAiC;IAAtD,YAAqB,KAAsB,EAAW,eAAgC;QAAjE,UAAK,GAAL,KAAK,CAAiB;QAAW,oBAAe,GAAf,eAAe,CAAiB;IAAG,CAAC;CAC7F;AAFD,kCAEC;AAED,MAAa,kBAAkB;IAC3B,QAAQ,CAAC,QAAkB,EAAE,KAA0B,EAAE,MAAoB,EAAE,aAA0B;QACrG,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC5C,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;eAC7D,IAAA,8BAAc,EAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,gCAAiB,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/G,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,gCAAkB,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7E,MAAM,KAAK,GAAG,IAAI,uCAAiB,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAA,sCAAqB,EAAC,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QAClI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,MAAM,IAAI,GAAG,IAAA,iCAAe,EAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,aAAa,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QAC3J,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,QAAQ,GAAG,IAAA,6BAAa,EAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,CAAC,GAAG,IAAA,8BAAY,EAAC,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,GAAG,IAAA,8BAAY,EAAC,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QAC7G,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,qBAAa,CAAC,IAAA,qBAAY,EAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IACvG,CAAC;CACJ;AAfD,gDAeC","sourcesContent":["import * as path from 'path';\nimport { LoadedConfig, HomeConfigService } from '@webpieces/rules-config';\nimport { ToolKind, NormalizedToolInput } from '../protocol';\nimport { Rule, BlockedResult } from './types';\n\nimport { TargetTreeResolver } from './target-tree';\nimport { DeleteScopedRules } from './delete-scoped-rules';\nimport { filterByExcludedPaths } from './excluded-paths';\nimport { buildContexts } from './build-context';\nimport { checkConfigSync, runEditRules, runFileRules } from './rule-evaluation';\nimport { formatReport } from './report';\nimport { isRootManifest } from './root-manifest';\n\nexport class HookRuleSet {\n constructor(readonly rules: readonly Rule[], readonly configuredRules: readonly Rule[]) {}\n}\n\nexport class HookFileEvaluation {\n evaluate(toolKind: ToolKind, input: NormalizedToolInput, loaded: LoadedConfig, contributions: HookRuleSet): BlockedResult | null {\n if (loaded.configPath === null) return null;\n if (path.resolve(input.filePath) === path.resolve(loaded.configPath)\n || isRootManifest(input.filePath) || new HomeConfigService().isHomeConfigPath(input.filePath)) return null;\n const root = path.dirname(loaded.configPath);\n const governed = new TargetTreeResolver().governedPath(input.filePath, root);\n const rules = new DeleteScopedRules().narrow(toolKind, filterByExcludedPaths(contributions.rules, governed, loaded.excludePaths));\n if (rules.length === 0) return null;\n const sync = checkConfigSync(rules.filter((rule: Rule): boolean => contributions.configuredRules.includes(rule)), loaded.rulesConfig, loaded.ruleRegistry);\n if (sync) return sync;\n const contexts = buildContexts(toolKind, input, root, governed);\n const groups = [...runEditRules(rules, contexts.editContexts), ...runFileRules(rules, contexts.fileContext)];\n return groups.length === 0 ? null : new BlockedResult(formatReport(governed.relativePath, groups));\n }\n}\n"]}
1
+ {"version":3,"file":"file-evaluation.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/file-evaluation.ts"],"names":[],"mappings":";;;;AAAA,mDAA6B;AAC7B,0DAA0E;AAE1E,mCAA8C;AAE9C,+CAAmD;AACnD,+DAA0D;AAC1D,qDAAyD;AACzD,mDAAgD;AAChD,uDAA+D;AAC/D,qCAAwC;AACxC,mDAAiD;AAEjD,MAAa,WAAW;IACC;IAArB,YAAqB,KAAsB;QAAtB,UAAK,GAAL,KAAK,CAAiB;IAAG,CAAC;CAClD;AAFD,kCAEC;AAED,MAAa,kBAAkB;IAC3B,QAAQ,CACJ,QAAkB,EAClB,KAA0B,EAC1B,MAAoB,EACpB,aAA0B;QAE1B,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC5C,IACI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;YAChE,IAAA,8BAAc,EAAC,KAAK,CAAC,QAAQ,CAAC;YAC9B,IAAI,gCAAiB,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC;YAExD,OAAO,IAAI,CAAC;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,gCAAkB,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7E,MAAM,KAAK,GAAG,IAAI,uCAAiB,EAAE,CAAC,MAAM,CACxC,QAAQ,EACR,IAAA,sCAAqB,EAAC,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,CAC5E,CAAC;QACF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAA,6BAAa,EAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG;YACX,GAAG,IAAA,8BAAY,EAAC,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC;YAC7C,GAAG,IAAA,8BAAY,EAAC,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC;SAC/C,CAAC;QACF,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC;YACtB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,qBAAa,CAAC,IAAA,qBAAY,EAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;CACJ;AA9BD,gDA8BC","sourcesContent":["import * as path from 'path';\nimport { LoadedConfig, HomeConfigService } from '@webpieces/rules-config';\nimport { ToolKind, NormalizedToolInput } from '../protocol';\nimport { Rule, BlockedResult } from './types';\n\nimport { TargetTreeResolver } from './target-tree';\nimport { DeleteScopedRules } from './delete-scoped-rules';\nimport { filterByExcludedPaths } from './excluded-paths';\nimport { buildContexts } from './build-context';\nimport { runEditRules, runFileRules } from './rule-evaluation';\nimport { formatReport } from './report';\nimport { isRootManifest } from './root-manifest';\n\nexport class HookRuleSet {\n constructor(readonly rules: readonly Rule[]) {}\n}\n\nexport class HookFileEvaluation {\n evaluate(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n loaded: LoadedConfig,\n contributions: HookRuleSet,\n ): BlockedResult | null {\n if (loaded.configPath === null) return null;\n if (\n path.resolve(input.filePath) === path.resolve(loaded.configPath) ||\n isRootManifest(input.filePath) ||\n new HomeConfigService().isHomeConfigPath(input.filePath)\n )\n return null;\n const root = path.dirname(loaded.configPath);\n const governed = new TargetTreeResolver().governedPath(input.filePath, root);\n const rules = new DeleteScopedRules().narrow(\n toolKind,\n filterByExcludedPaths(contributions.rules, governed, loaded.excludePaths),\n );\n if (rules.length === 0) return null;\n const contexts = buildContexts(toolKind, input, root, governed);\n const groups = [\n ...runEditRules(rules, contexts.editContexts),\n ...runFileRules(rules, contexts.fileContext),\n ];\n return groups.length === 0\n ? null\n : new BlockedResult(formatReport(governed.relativePath, groups));\n }\n}\n"]}
@@ -1,17 +1,4 @@
1
- import { RulePackRegistry, WebpiecesRulesConfig } from '@webpieces/rules-config';
2
- import { Rule, EditContext, FileContext, BashContext, Violation, RuleGroup, BlockedResult } from './types';
3
- /**
4
- * Fault Y — a loaded rule whose CONFIG KEY has no entry.
5
- *
6
- * Compared on `configKey`, not `name`. Eight of the loaded rules are classes behind two policy keys, so
7
- * comparing names here would demand entries named `feature-branch-guard`, `pr-merge-guard` and six
8
- * others — keys the validator then REJECTS as retired. That is the two-enforcement-paths deadlock this
9
- * repo has already hit once (see the narration in guards.spec.ts), and it blocks every Bash/Write/Edit.
10
- *
11
- * De-duplicated on the key too, so one missing policy entry reports ONE paste-ready snippet rather than
12
- * four copies of the same one under four different headings.
13
- */
14
- export declare function checkConfigSync(rules: readonly Rule[], config: WebpiecesRulesConfig, registry: RulePackRegistry): BlockedResult | null;
1
+ import { Rule, EditContext, FileContext, BashContext, Violation, RuleGroup } from './types';
15
2
  export declare function runRuleCheck(rule: Rule, ctx: EditContext | FileContext | BashContext): readonly Violation[];
16
3
  export declare function runBashRules(rules: readonly Rule[], bashContext: BashContext): readonly RuleGroup[];
17
4
  export declare function runEditRules(rules: readonly Rule[], editContexts: readonly EditContext[]): readonly RuleGroup[];
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkConfigSync = checkConfigSync;
4
3
  exports.runRuleCheck = runRuleCheck;
5
4
  exports.runBashRules = runBashRules;
6
5
  exports.runEditRules = runEditRules;
@@ -9,89 +8,13 @@ const rules_config_1 = require("@webpieces/rules-config");
9
8
  const to_error_1 = require("@webpieces/tooling-common/to-error");
10
9
  const types_1 = require("./types");
11
10
  const glob_1 = require("./glob");
12
- // The set of CONFIG KEYS explicitly present in webpieces.config.json (every key except rulesDir).
13
- // webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers
14
- function configuredRuleNames(config) {
15
- return new Set(Object.keys(config).filter((k) => k !== 'rulesDir'));
16
- }
17
- /**
18
- * Fault Y — a loaded rule whose CONFIG KEY has no entry.
19
- *
20
- * Compared on `configKey`, not `name`. Eight of the loaded rules are classes behind two policy keys, so
21
- * comparing names here would demand entries named `feature-branch-guard`, `pr-merge-guard` and six
22
- * others — keys the validator then REJECTS as retired. That is the two-enforcement-paths deadlock this
23
- * repo has already hit once (see the narration in guards.spec.ts), and it blocks every Bash/Write/Edit.
24
- *
25
- * De-duplicated on the key too, so one missing policy entry reports ONE paste-ready snippet rather than
26
- * four copies of the same one under four different headings.
27
- */
28
- // webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers
29
- function checkConfigSync(rules, config, registry) {
30
- const configured = configuredRuleNames(config);
31
- const seen = new Set();
32
- const unconfiguredRules = rules.filter((r) => {
33
- if (configured.has(r.configKey) || seen.has(r.configKey))
34
- return false;
35
- seen.add(r.configKey);
36
- return true;
37
- });
38
- if (unconfiguredRules.length === 0)
39
- return null;
40
- // ONE action, no menu, no escalation — the config-validation invariant (guards/L0-tooling.md): every
41
- // config problem cures to "make the file right", and editing it is never denied. This message used
42
- // to tell the agent to interview the human about each rule; agents did not do it, so the block just
43
- // stalled. Each rule now ships a paste-ready entry at its recommended mode.
44
- //
45
- // Note this is the CONFIG-BEHIND-CODE direction. The opposite one — the config names a rule the
46
- // installed validator has no schema for — is unknownRuleError() in rules-config/validate-config.ts
47
- // and surfaces in the validation banner, not here.
48
- const lines = [
49
- // Fault Y's header lives in ./l0-matrix beside the rest of the L0 fault table (same reason as
50
- // CONFIG_MISSING_REPORT: one place states what this fault is and what cures it).
51
- rules_config_1.CONFIG_OUT_OF_SYNC_HEADER,
52
- '',
53
- `Add an entry for each rule below to ${rules_config_1.CONFIG_FILENAME}. Editing that file is ALWAYS allowed through`,
54
- 'the guard — including right now, while this block is up — so paste the entries and retry.',
55
- '',
56
- 'Each entry below is ready to paste at its recommended mode; adjust the option values if your',
57
- 'project needs different ones.',
58
- '',
59
- `Do NOT delete a rule from ${rules_config_1.CONFIG_FILENAME} to silence it — an entry is REQUIRED for every rule,`,
60
- 'and "mode": "OFF" is how a rule is turned off.',
61
- '',
62
- ];
63
- for (const rule of unconfiguredRules) {
64
- lines.push(`--- ${rule.configKey} ---`);
65
- lines.push(`Description: ${rule.description}`);
66
- const opts = rule.defaultOptions;
67
- const optKeys = Object.keys(opts);
68
- if (optKeys.length > 0) {
69
- lines.push(`Available options (suggested defaults shown):`);
70
- for (const key of optKeys) {
71
- lines.push(` ${key}: ${JSON.stringify(opts[key])}`);
72
- }
73
- }
74
- else {
75
- lines.push('Available options: none beyond mode');
76
- }
77
- // The SAME entry the installer would seed: recommended mode, both hatches, and every other
78
- // schema-required field — so pasting it satisfies the loader in one pass.
79
- lines.push(`Entry to add to ${rules_config_1.CONFIG_FILENAME}:`);
80
- lines.push(` "${rule.configKey}": ${JSON.stringify((0, rules_config_1.seedEntryForRule)(rule.configKey, registry))}`);
81
- lines.push('');
82
- }
83
- // Fault Y, stamped for the audit trail — see configMissingBlock for why the producer names it.
84
- return new types_1.BlockedResult(lines.join('\n'), rules_config_1.L0_FAULT_CONFIG_OUT_OF_SYNC);
85
- }
86
- // N-legs pattern: each rule runs independently so one rule can never abort the others. A rule may
87
- // EITHER return Violation[] OR throw — both accumulate here into visible violations the AI sees:
88
- // - a thrown RuleFailError → an expected, well-formed violation (its line/snippet/fixOptions kept);
89
- // - a thrown plain Error → a "crashed" violation (a bug, surfaced not swallowed).
11
+ // Every entry was read from its declared owner file and validated before execution.
90
12
  // webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers
91
13
  function runRuleCheck(rule, ctx) {
92
14
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
93
15
  try {
94
16
  return rule.check(ctx);
17
+ // webpieces-disable no-any-unknown -- external JSON or runtime exports are validated before policy execution
95
18
  }
96
19
  catch (err) {
97
20
  const error = (0, to_error_1.toError)(err);
@@ -1 +1 @@
1
- {"version":3,"file":"rule-evaluation.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/rule-evaluation.ts"],"names":[],"mappings":"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","sourcesContent":["import { CONFIG_FILENAME, CONFIG_OUT_OF_SYNC_HEADER, L0_FAULT_CONFIG_OUT_OF_SYNC, seedEntryForRule, RulePackRegistry, WebpiecesRulesConfig, RuleFailError, renderRuleFailForAi } from '@webpieces/rules-config';\nimport { toError } from '@webpieces/tooling-common/to-error';\nimport { Rule, EditContext, FileContext, BashContext, Violation, RuleGroup, BlockedResult } from './types';\n\nimport { globMatches } from './glob';\n\n// The set of CONFIG KEYS explicitly present in webpieces.config.json (every key except rulesDir).\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nfunction configuredRuleNames(config: WebpiecesRulesConfig): ReadonlySet<string> {\n return new Set(Object.keys(config).filter((k: string) => k !== 'rulesDir'));\n}\n\n/**\n * Fault Y — a loaded rule whose CONFIG KEY has no entry.\n *\n * Compared on `configKey`, not `name`. Eight of the loaded rules are classes behind two policy keys, so\n * comparing names here would demand entries named `feature-branch-guard`, `pr-merge-guard` and six\n * others — keys the validator then REJECTS as retired. That is the two-enforcement-paths deadlock this\n * repo has already hit once (see the narration in guards.spec.ts), and it blocks every Bash/Write/Edit.\n *\n * De-duplicated on the key too, so one missing policy entry reports ONE paste-ready snippet rather than\n * four copies of the same one under four different headings.\n */\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function checkConfigSync(rules: readonly Rule[], config: WebpiecesRulesConfig, registry: RulePackRegistry): BlockedResult | null {\n const configured = configuredRuleNames(config);\n const seen = new Set<string>();\n const unconfiguredRules = rules.filter((r: Rule): boolean => {\n if (configured.has(r.configKey) || seen.has(r.configKey)) return false;\n seen.add(r.configKey);\n return true;\n });\n if (unconfiguredRules.length === 0) return null;\n\n // ONE action, no menu, no escalation — the config-validation invariant (guards/L0-tooling.md): every\n // config problem cures to \"make the file right\", and editing it is never denied. This message used\n // to tell the agent to interview the human about each rule; agents did not do it, so the block just\n // stalled. Each rule now ships a paste-ready entry at its recommended mode.\n //\n // Note this is the CONFIG-BEHIND-CODE direction. The opposite one — the config names a rule the\n // installed validator has no schema for — is unknownRuleError() in rules-config/validate-config.ts\n // and surfaces in the validation banner, not here.\n const lines = [\n // Fault Y's header lives in ./l0-matrix beside the rest of the L0 fault table (same reason as\n // CONFIG_MISSING_REPORT: one place states what this fault is and what cures it).\n CONFIG_OUT_OF_SYNC_HEADER,\n '',\n `Add an entry for each rule below to ${CONFIG_FILENAME}. Editing that file is ALWAYS allowed through`,\n 'the guard — including right now, while this block is up — so paste the entries and retry.',\n '',\n 'Each entry below is ready to paste at its recommended mode; adjust the option values if your',\n 'project needs different ones.',\n '',\n `Do NOT delete a rule from ${CONFIG_FILENAME} to silence it — an entry is REQUIRED for every rule,`,\n 'and \"mode\": \"OFF\" is how a rule is turned off.',\n '',\n ];\n\n for (const rule of unconfiguredRules) {\n lines.push(`--- ${rule.configKey} ---`);\n lines.push(`Description: ${rule.description}`);\n const opts = rule.defaultOptions;\n const optKeys = Object.keys(opts);\n if (optKeys.length > 0) {\n lines.push(`Available options (suggested defaults shown):`);\n for (const key of optKeys) {\n lines.push(` ${key}: ${JSON.stringify(opts[key])}`);\n }\n } else {\n lines.push('Available options: none beyond mode');\n }\n // The SAME entry the installer would seed: recommended mode, both hatches, and every other\n // schema-required field — so pasting it satisfies the loader in one pass.\n lines.push(`Entry to add to ${CONFIG_FILENAME}:`);\n lines.push(` \"${rule.configKey}\": ${JSON.stringify(seedEntryForRule(rule.configKey, registry))}`);\n lines.push('');\n }\n\n // Fault Y, stamped for the audit trail — see configMissingBlock for why the producer names it.\n return new BlockedResult(lines.join('\\n'), L0_FAULT_CONFIG_OUT_OF_SYNC);\n}\n\n// N-legs pattern: each rule runs independently so one rule can never abort the others. A rule may\n// EITHER return Violation[] OR throw — both accumulate here into visible violations the AI sees:\n// - a thrown RuleFailError → an expected, well-formed violation (its line/snippet/fixOptions kept);\n// - a thrown plain Error → a \"crashed\" violation (a bug, surfaced not swallowed).\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function runRuleCheck(rule: Rule, ctx: EditContext | FileContext | BashContext): readonly Violation[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return rule.check(ctx);\n } catch (err: unknown) {\n const error = toError(err);\n if (error instanceof RuleFailError) {\n return [violationFromRuleFail(error)];\n }\n return [new Violation(0, '', `Rule '${rule.name}' crashed: ${error.message}`)];\n }\n}\n\n// A thrown RuleFailError carries its own AI-facing message + optional location and cures. Fold the\n// cures into the message because Violation has no fixHint field (RuleGroup's fixHint comes from the\n// rule definition, not a per-throw value). The \"Fix Option N:\"/\"(preferred)\" labels come from the ONE\n// framework-owned renderer, exactly as report.ts renders a rule's static FixHint — a rule never\n// hand-numbers its own cures.\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nfunction violationFromRuleFail(error: RuleFailError): Violation {\n return new Violation(error.line ?? 0, error.snippet ?? '', renderRuleFailForAi(error));\n}\n\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nfunction ruleMatchesFile(rule: Rule, relativePath: string): boolean {\n for (const pattern of rule.files) {\n if (globMatches(pattern, relativePath)) return true;\n }\n return false;\n}\n\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function runBashRules(rules: readonly Rule[], bashContext: BashContext): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'bash') continue;\n if (!rule.shouldRun()) continue;\n const vs = runRuleCheck(rule, bashContext);\n if (vs.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, rule.fixHint, [...vs],\n ));\n }\n }\n return groups;\n}\n\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function runEditRules(rules: readonly Rule[], editContexts: readonly EditContext[]): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'edit') continue;\n if (!rule.shouldRun()) continue;\n const allViolations: Violation[] = [];\n for (const ctx of editContexts) {\n if (!ruleMatchesFile(rule, ctx.relativePath)) continue;\n const vs = runRuleCheck(rule, ctx);\n for (const v of vs) {\n const copy = new Violation(v.line, v.snippet, v.message);\n copy.editIndex = ctx.editIndex;\n copy.editCount = ctx.editCount;\n allViolations.push(copy);\n }\n }\n if (allViolations.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, rule.fixHint, allViolations,\n ));\n }\n }\n return groups;\n}\n\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function runFileRules(rules: readonly Rule[], fileContext: FileContext): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'file') continue;\n if (!rule.shouldRun()) continue;\n if (!ruleMatchesFile(rule, fileContext.relativePath)) continue;\n const vs = runRuleCheck(rule, fileContext);\n if (vs.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, rule.fixHint, [...vs],\n ));\n }\n }\n return groups;\n}\n"]}
1
+ {"version":3,"file":"rule-evaluation.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/rule-evaluation.ts"],"names":[],"mappings":"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","sourcesContent":["import { RuleFailError, renderRuleFailForAi } from '@webpieces/rules-config';\nimport { toError } from '@webpieces/tooling-common/to-error';\nimport {\n Rule,\n EditContext,\n FileContext,\n BashContext,\n Violation,\n RuleGroup,\n BlockedResult,\n} from './types';\n\nimport { globMatches } from './glob';\n\n// Every entry was read from its declared owner file and validated before execution.\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function runRuleCheck(\n rule: Rule,\n ctx: EditContext | FileContext | BashContext,\n): readonly Violation[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return rule.check(ctx);\n // webpieces-disable no-any-unknown -- external JSON or runtime exports are validated before policy execution\n } catch (err: unknown) {\n const error = toError(err);\n if (error instanceof RuleFailError) {\n return [violationFromRuleFail(error)];\n }\n return [new Violation(0, '', `Rule '${rule.name}' crashed: ${error.message}`)];\n }\n}\n\n// A thrown RuleFailError carries its own AI-facing message + optional location and cures. Fold the\n// cures into the message because Violation has no fixHint field (RuleGroup's fixHint comes from the\n// rule definition, not a per-throw value). The \"Fix Option N:\"/\"(preferred)\" labels come from the ONE\n// framework-owned renderer, exactly as report.ts renders a rule's static FixHint — a rule never\n// hand-numbers its own cures.\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nfunction violationFromRuleFail(error: RuleFailError): Violation {\n return new Violation(error.line ?? 0, error.snippet ?? '', renderRuleFailForAi(error));\n}\n\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nfunction ruleMatchesFile(rule: Rule, relativePath: string): boolean {\n for (const pattern of rule.files) {\n if (globMatches(pattern, relativePath)) return true;\n }\n return false;\n}\n\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function runBashRules(\n rules: readonly Rule[],\n bashContext: BashContext,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'bash') continue;\n if (!rule.shouldRun()) continue;\n const vs = runRuleCheck(rule, bashContext);\n if (vs.length > 0) {\n groups.push(new RuleGroup(rule.name, rule.description, rule.fixHint, [...vs]));\n }\n }\n return groups;\n}\n\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function runEditRules(\n rules: readonly Rule[],\n editContexts: readonly EditContext[],\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'edit') continue;\n if (!rule.shouldRun()) continue;\n const allViolations: Violation[] = [];\n for (const ctx of editContexts) {\n if (!ruleMatchesFile(rule, ctx.relativePath)) continue;\n const vs = runRuleCheck(rule, ctx);\n for (const v of vs) {\n const copy = new Violation(v.line, v.snippet, v.message);\n copy.editIndex = ctx.editIndex;\n copy.editCount = ctx.editCount;\n allViolations.push(copy);\n }\n }\n if (allViolations.length > 0) {\n groups.push(new RuleGroup(rule.name, rule.description, rule.fixHint, allViolations));\n }\n }\n return groups;\n}\n\n// webpieces-disable no-function-outside-class -- existing stateless evaluator helpers extracted intact for both hook providers\nexport function runFileRules(\n rules: readonly Rule[],\n fileContext: FileContext,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'file') continue;\n if (!rule.shouldRun()) continue;\n if (!ruleMatchesFile(rule, fileContext.relativePath)) continue;\n const vs = runRuleCheck(rule, fileContext);\n if (vs.length > 0) {\n groups.push(new RuleGroup(rule.name, rule.description, rule.fixHint, [...vs]));\n }\n }\n return groups;\n}\n"]}
@@ -95,7 +95,7 @@ export declare class FileContext {
95
95
  constructor(tool: ToolKind, filePath: string, relativePath: string, workspaceRoot: string, currentFileLines: number, linesAdded: number, linesRemoved: number, projectedFileLines: number, targetRoot?: string, targetRelativePath?: string);
96
96
  }
97
97
  /**
98
- * The shape of a custom rule loaded from a `rulesDir` (a plain object returned by require()).
98
+ * The shape of a client hook policy returned by its declared runtime factory.
99
99
  * It carries only metadata + a `check` method — no on/off logic of its own.
100
100
  */
101
101
  export interface PlainRule {
@@ -140,7 +140,7 @@ export declare class BlockedResult {
140
140
  * an ordinary rule block.
141
141
  *
142
142
  * It rides on the result rather than being re-derived from the report text because the block is
143
- * decided deep in the runner (fault C in configMissingBlock, fault Y in checkConfigSync) and
143
+ * decided deep in the runner (fault C in configMissingBlock, fault Y during owner-config validation) and
144
144
  * STAMPED by the adapter at the terminal boundary, several frames up. Scraping the report for a
145
145
  * fault would be a second answer to a question the producer already knows.
146
146
  */
package/src/core/types.js CHANGED
@@ -176,7 +176,7 @@ class BlockedResult {
176
176
  * an ordinary rule block.
177
177
  *
178
178
  * It rides on the result rather than being re-derived from the report text because the block is
179
- * decided deep in the runner (fault C in configMissingBlock, fault Y in checkConfigSync) and
179
+ * decided deep in the runner (fault C in configMissingBlock, fault Y during owner-config validation) and
180
180
  * STAMPED by the adapter at the terminal boundary, several frames up. Scraping the report for a
181
181
  * fault would be a second answer to a question the producer already knows.
182
182
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/types.ts"],"names":[],"mappings":"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","sourcesContent":["// ResolvedConfig / ResolvedRuleConfig / RuleOptions now live in @webpieces/rules-config\n// so ai-hooks and the Nx validate-code executor share one loader and one config file.\nimport { RuleOptions } from '@webpieces/rules-config';\nimport { HookMode, NormalizedBashInput, NormalizedEdit, NormalizedToolInput, ToolKind } from '../protocol';\nimport { FixHint } from './fix-hint';\nimport { L0_FAULT_NONE } from '@webpieces/rules-config';\n\n// 'Read' is a first-class member because read-stale-guard is a file-scoped guard that runs on the\n// Read fast path. It is deliberately NOT in HANDLED_FILE_TOOLS (adapters/claude-code-adapter.ts), so\n// ClaudeCodeAdapter.kindOf() maps it to the 'Read' routing kind and Read never enters the edit/file\n// rule pipeline — only the one guard that asks for it. Nothing switches exhaustively on this union;\n// it is carried for logging.\n//\n// 'Delete' arrives ONLY from a Codex `apply_patch` carrying a `*** Delete File:` directive — no Claude\n// Code tool produces it. Every existing rule DEFAULTS TO NOT FIRING on it (see DeleteScopedRules in\n// ./delete-scoped-rules.ts): a rule written to judge the bytes an edit ADDS has nothing to say about a file going\n// away, and inventing a verdict for it would be guessing. Rules for which a delete IS meaningful — a\n// barrel export disappearing, a doc that documents a deleted file — opt in there, deliberately.\nexport type RuleScope = 'edit' | 'file' | 'bash';\n// Which category of built-in rules a hook invocation runs: code-style 'rules', git/PR/branch\n// 'guards' (the hookGuards section), or 'all' (both categories — used by the openclaw plugin adapter,\n// which is a single before_tool_call hook rather than two split PreToolUse hooks).\nexport type IsLineDisabled = (lineNum: number, ruleName: string) => boolean;\n\nexport class Violation {\n readonly line: number;\n readonly snippet: string;\n // Optional per-occurrence override for the `→` line. When omitted, the report falls back\n // to the rule's `FixHint.violation`. Dynamic rules (param name, line count, marker path,\n // branch/PR) pass a specific message here; static rules omit it.\n readonly message: string | undefined;\n editIndex: number | undefined;\n editCount: number | undefined;\n\n constructor(line: number, snippet: string, message?: string) {\n this.line = line;\n this.snippet = snippet;\n this.message = message;\n this.editIndex = undefined;\n this.editCount = undefined;\n }\n}\n\nexport class EditContext {\n readonly tool: ToolKind;\n readonly editIndex: number;\n readonly editCount: number;\n readonly filePath: string;\n readonly relativePath: string;\n readonly workspaceRoot: string;\n readonly addedContent: string;\n readonly strippedContent: string;\n readonly lines: readonly string[];\n readonly strippedLines: readonly string[];\n readonly removedContent: string;\n readonly isLineDisabled: IsLineDisabled;\n options: RuleOptions;\n\n constructor(\n tool: ToolKind,\n editIndex: number,\n editCount: number,\n filePath: string,\n relativePath: string,\n workspaceRoot: string,\n addedContent: string,\n strippedContent: string,\n lines: readonly string[],\n strippedLines: readonly string[],\n removedContent: string,\n isLineDisabled: IsLineDisabled,\n ) {\n this.tool = tool;\n this.editIndex = editIndex;\n this.editCount = editCount;\n this.filePath = filePath;\n this.relativePath = relativePath;\n this.workspaceRoot = workspaceRoot;\n this.addedContent = addedContent;\n this.strippedContent = strippedContent;\n this.lines = lines;\n this.strippedLines = strippedLines;\n this.removedContent = removedContent;\n this.isLineDisabled = isLineDisabled;\n this.options = {};\n }\n}\n\n// A heredoc and its body: `<<EOF` / `<<'EOF'` / `<<-EOF` through the terminator line.\nconst HEREDOC_BODY = /<<-?\\s*(['\"]?)(\\w+)\\1[\\s\\S]*?^\\t*\\2\\s*$/gm;\n\n// A single- or double-quoted span.\nconst QUOTED_SPAN = /'([^']*)'|\"([^\"]*)\"/g;\n\n// Shell syntax that a quoted span is explicitly NOT: separators, pipes, subshells, redirection,\n// expansion. Content carrying any of these is data (a jq filter, a regex, a glob), and unquoting it\n// would hand the segment scanner syntax the shell never sees. See stripProse.\nconst SHELL_METACHARACTER = /[|;&()<>$`]/;\n\nexport class BashContext {\n readonly tool: 'Bash';\n readonly command: string;\n /**\n * `command` with heredoc bodies and prose-in-quotes removed — what a guard should MATCH on, while\n * `command` stays raw for the violation message (a human needs to see what they actually typed).\n *\n * A commit message that merely MENTIONS a blocked command is not that command. This repo's whole\n * subject matter is the git workflow, so its commit messages, docs and the guards' own fix-hint\n * text are full of `git push` / `gh pr …` strings; matching raw text blocks writing about the\n * tooling. A guard that cries wolf is a guard someone eventually turns OFF, which costs far more\n * than the theoretical bypass this opens (`sh -c '<blocked command>'` stops matching). That trade\n * is deliberate: these guards exist to catch a FORGETFUL agent, not an adversarial one.\n *\n * ONLY for blocklist-shaped guards (\"if the command matches X, block\"), where stripping can only\n * ever block LESS. Never use it in an allowlist-shaped guard such as merged-branch-bash-guard,\n * where stripping could turn a permitted recovery command into a blocked one and deadlock the\n * session — there, match on the raw `command`.\n */\n readonly commandCode: string;\n /**\n * The tree this command is JUDGED against — the root of the worktree it actually acts on, which\n * is NOT the shell's cwd whenever the command carries a leading `cd <path> &&` (see\n * EffectiveTreeResolver). Guards read git state, the main-sync cache and the decision log from\n * here, so a command aimed at a linked worktree is judged on that worktree's branch, never the\n * primary clone's.\n */\n readonly workspaceRoot: string;\n /** The directory the command really runs in (post-`cd`). Relative paths resolve against THIS. */\n readonly effectiveCwd: string;\n /** The root owning webpieces.config.json. Equals workspaceRoot except in a linked worktree. */\n readonly governedRoot: string;\n options: RuleOptions;\n\n constructor(command: string, workspaceRoot: string, effectiveCwd?: string, governedRoot?: string) {\n this.tool = 'Bash';\n this.command = command;\n this.commandCode = this.stripProse(command);\n this.workspaceRoot = workspaceRoot;\n this.effectiveCwd = effectiveCwd ?? workspaceRoot;\n this.governedRoot = governedRoot ?? workspaceRoot;\n this.options = {};\n }\n\n /**\n * A quoted span WITHOUT whitespace is kept: `git checkout \"main\"` is a real command with a quoted\n * argument, not prose. One containing whitespace is a sentence, and becomes a single space.\n *\n * ONE exception, and it is a false positive that actually fired: a whitespace-free span carrying\n * SHELL METACHARACTERS is dropped too. Keeping the content strips only the quotes, so a jq filter —\n * `--jq '[.[]|select(.title|test(\"x\"))]|length'` — was handed to the guards as bare shell syntax.\n * Every guard then split it on its `|`, `(` and `;` and saw a segment reading exactly `test`, which\n * whole-repo-build-guard classified as the workspace-wide test script and BLOCKED. There was no\n * build anywhere in that command; it polled npm and GitHub.\n *\n * Quoting is precisely how a shell says \"this is DATA, not syntax\", so honouring the quotes is the\n * correct reading. And because commandCode only ever feeds blocklist-shaped guards, dropping more\n * can only ever block LESS — never turn an allowed command into a refused one.\n */\n private stripProse(command: string): string {\n const withoutHeredocs = command.replace(HEREDOC_BODY, ' ');\n return withoutHeredocs.replace(QUOTED_SPAN, (match: string, single?: string, double?: string): string => {\n const content = single ?? double ?? '';\n if (/\\s/.test(content)) return ' ';\n return SHELL_METACHARACTER.test(content) ? ' ' : content;\n });\n }\n}\n\nexport class FileContext {\n readonly tool: ToolKind;\n readonly filePath: string;\n readonly relativePath: string;\n readonly workspaceRoot: string;\n /** The git tree that owns filePath. workspaceRoot remains the root that owns policy/config. */\n readonly targetRoot: string;\n /** filePath relative to targetRoot, independent of its governed-root spelling. */\n readonly targetRelativePath: string;\n readonly currentFileLines: number;\n readonly linesAdded: number;\n readonly linesRemoved: number;\n readonly projectedFileLines: number;\n options: RuleOptions;\n\n constructor(\n tool: ToolKind,\n filePath: string,\n relativePath: string,\n workspaceRoot: string,\n currentFileLines: number,\n linesAdded: number,\n linesRemoved: number,\n projectedFileLines: number,\n targetRoot: string = workspaceRoot,\n targetRelativePath: string = relativePath,\n ) {\n this.tool = tool;\n this.filePath = filePath;\n this.relativePath = relativePath;\n this.workspaceRoot = workspaceRoot;\n this.targetRoot = targetRoot;\n this.targetRelativePath = targetRelativePath;\n this.currentFileLines = currentFileLines;\n this.linesAdded = linesAdded;\n this.linesRemoved = linesRemoved;\n this.projectedFileLines = projectedFileLines;\n this.options = {};\n }\n}\n\n/**\n * The shape of a custom rule loaded from a `rulesDir` (a plain object returned by require()).\n * It carries only metadata + a `check` method — no on/off logic of its own.\n */\nexport interface PlainRule {\n readonly name: string;\n readonly description: string;\n readonly scope: RuleScope;\n readonly files: readonly string[];\n readonly defaultOptions: RuleOptions;\n readonly fixHint: FixHint;\n check(ctx: EditContext | FileContext | BashContext): readonly Violation[];\n}\n\n/**\n * The runtime contract the runner iterates. Both the built-in rule classes (which extend\n * EditRuleBase/FileRuleBase/BashRuleBase) and the custom-rule adapter satisfy this: they add\n * `shouldRun()` (mode + escape-hatch decision) on top of the PlainRule metadata.\n */\nexport interface Rule extends PlainRule {\n /**\n * The webpieces.config.json key whose entry configures this rule — NOT necessarily `name`.\n *\n * `name` is the operator identity (what a decision-log line and a deny report carry); `configKey`\n * is the switch. They differ wherever several classes implement one POLICY: all four branch-state\n * guards read `branch-state-guard`, all four PR-lifecycle guards read `pr-lifecycle-guard`. Every\n * consumer that asks \"which section does this live in?\" (filterByMode) or \"does this have an\n * entry?\" (the fault-Y config-sync check) must read THIS field, because asking for `name` there\n * demands entries under keys the validator rejects as retired.\n */\n readonly configKey: string;\n shouldRun(): boolean;\n}\n\nexport class RuleGroup {\n readonly ruleName: string;\n readonly ruleDescription: string;\n readonly fixHint: FixHint;\n readonly violations: readonly Violation[];\n\n constructor(\n ruleName: string,\n ruleDescription: string,\n fixHint: FixHint,\n violations: readonly Violation[],\n ) {\n this.ruleName = ruleName;\n this.ruleDescription = ruleDescription;\n this.fixHint = fixHint;\n this.violations = violations;\n }\n}\n\nexport class BlockedResult {\n readonly report: string;\n /**\n * The L0 fault this block IS, in the codebook's own letter (see core/l0-fault-codes.ts), or `-` for\n * an ordinary rule block.\n *\n * It rides on the result rather than being re-derived from the report text because the block is\n * decided deep in the runner (fault C in configMissingBlock, fault Y in checkConfigSync) and\n * STAMPED by the adapter at the terminal boundary, several frames up. Scraping the report for a\n * fault would be a second answer to a question the producer already knows.\n */\n readonly fault: string;\n\n constructor(report: string, fault: string = L0_FAULT_NONE) {\n this.report = report;\n this.fault = fault;\n }\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/types.ts"],"names":[],"mappings":"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","sourcesContent":["// ResolvedConfig / ResolvedRuleConfig / RuleOptions now live in @webpieces/rules-config\n// so ai-hooks and the Nx validate-code executor share one loader and one config file.\nimport { RuleOptions } from '@webpieces/rules-config';\nimport {\n HookMode,\n NormalizedBashInput,\n NormalizedEdit,\n NormalizedToolInput,\n ToolKind,\n} from '../protocol';\nimport { FixHint } from './fix-hint';\nimport { L0_FAULT_NONE } from '@webpieces/rules-config';\n\n// 'Read' is a first-class member because read-stale-guard is a file-scoped guard that runs on the\n// Read fast path. It is deliberately NOT in HANDLED_FILE_TOOLS (adapters/claude-code-adapter.ts), so\n// ClaudeCodeAdapter.kindOf() maps it to the 'Read' routing kind and Read never enters the edit/file\n// rule pipeline — only the one guard that asks for it. Nothing switches exhaustively on this union;\n// it is carried for logging.\n//\n// 'Delete' arrives ONLY from a Codex `apply_patch` carrying a `*** Delete File:` directive — no Claude\n// Code tool produces it. Every existing rule DEFAULTS TO NOT FIRING on it (see DeleteScopedRules in\n// ./delete-scoped-rules.ts): a rule written to judge the bytes an edit ADDS has nothing to say about a file going\n// away, and inventing a verdict for it would be guessing. Rules for which a delete IS meaningful — a\n// barrel export disappearing, a doc that documents a deleted file — opt in there, deliberately.\nexport type RuleScope = 'edit' | 'file' | 'bash';\n// Which category of built-in rules a hook invocation runs: code-style 'rules', git/PR/branch\n// 'guards' (the hookGuards section), or 'all' (both categories — used by the openclaw plugin adapter,\n// which is a single before_tool_call hook rather than two split PreToolUse hooks).\nexport type IsLineDisabled = (lineNum: number, ruleName: string) => boolean;\n\nexport class Violation {\n readonly line: number;\n readonly snippet: string;\n // Optional per-occurrence override for the `→` line. When omitted, the report falls back\n // to the rule's `FixHint.violation`. Dynamic rules (param name, line count, marker path,\n // branch/PR) pass a specific message here; static rules omit it.\n readonly message: string | undefined;\n editIndex: number | undefined;\n editCount: number | undefined;\n\n constructor(line: number, snippet: string, message?: string) {\n this.line = line;\n this.snippet = snippet;\n this.message = message;\n this.editIndex = undefined;\n this.editCount = undefined;\n }\n}\n\nexport class EditContext {\n readonly tool: ToolKind;\n readonly editIndex: number;\n readonly editCount: number;\n readonly filePath: string;\n readonly relativePath: string;\n readonly workspaceRoot: string;\n readonly addedContent: string;\n readonly strippedContent: string;\n readonly lines: readonly string[];\n readonly strippedLines: readonly string[];\n readonly removedContent: string;\n readonly isLineDisabled: IsLineDisabled;\n options: RuleOptions;\n\n constructor(\n tool: ToolKind,\n editIndex: number,\n editCount: number,\n filePath: string,\n relativePath: string,\n workspaceRoot: string,\n addedContent: string,\n strippedContent: string,\n lines: readonly string[],\n strippedLines: readonly string[],\n removedContent: string,\n isLineDisabled: IsLineDisabled,\n ) {\n this.tool = tool;\n this.editIndex = editIndex;\n this.editCount = editCount;\n this.filePath = filePath;\n this.relativePath = relativePath;\n this.workspaceRoot = workspaceRoot;\n this.addedContent = addedContent;\n this.strippedContent = strippedContent;\n this.lines = lines;\n this.strippedLines = strippedLines;\n this.removedContent = removedContent;\n this.isLineDisabled = isLineDisabled;\n this.options = {};\n }\n}\n\n// A heredoc and its body: `<<EOF` / `<<'EOF'` / `<<-EOF` through the terminator line.\nconst HEREDOC_BODY = /<<-?\\s*(['\"]?)(\\w+)\\1[\\s\\S]*?^\\t*\\2\\s*$/gm;\n\n// A single- or double-quoted span.\nconst QUOTED_SPAN = /'([^']*)'|\"([^\"]*)\"/g;\n\n// Shell syntax that a quoted span is explicitly NOT: separators, pipes, subshells, redirection,\n// expansion. Content carrying any of these is data (a jq filter, a regex, a glob), and unquoting it\n// would hand the segment scanner syntax the shell never sees. See stripProse.\nconst SHELL_METACHARACTER = /[|;&()<>$`]/;\n\nexport class BashContext {\n readonly tool: 'Bash';\n readonly command: string;\n /**\n * `command` with heredoc bodies and prose-in-quotes removed — what a guard should MATCH on, while\n * `command` stays raw for the violation message (a human needs to see what they actually typed).\n *\n * A commit message that merely MENTIONS a blocked command is not that command. This repo's whole\n * subject matter is the git workflow, so its commit messages, docs and the guards' own fix-hint\n * text are full of `git push` / `gh pr …` strings; matching raw text blocks writing about the\n * tooling. A guard that cries wolf is a guard someone eventually turns OFF, which costs far more\n * than the theoretical bypass this opens (`sh -c '<blocked command>'` stops matching). That trade\n * is deliberate: these guards exist to catch a FORGETFUL agent, not an adversarial one.\n *\n * ONLY for blocklist-shaped guards (\"if the command matches X, block\"), where stripping can only\n * ever block LESS. Never use it in an allowlist-shaped guard such as merged-branch-bash-guard,\n * where stripping could turn a permitted recovery command into a blocked one and deadlock the\n * session — there, match on the raw `command`.\n */\n readonly commandCode: string;\n /**\n * The tree this command is JUDGED against — the root of the worktree it actually acts on, which\n * is NOT the shell's cwd whenever the command carries a leading `cd <path> &&` (see\n * EffectiveTreeResolver). Guards read git state, the main-sync cache and the decision log from\n * here, so a command aimed at a linked worktree is judged on that worktree's branch, never the\n * primary clone's.\n */\n readonly workspaceRoot: string;\n /** The directory the command really runs in (post-`cd`). Relative paths resolve against THIS. */\n readonly effectiveCwd: string;\n /** The root owning webpieces.config.json. Equals workspaceRoot except in a linked worktree. */\n readonly governedRoot: string;\n options: RuleOptions;\n\n constructor(\n command: string,\n workspaceRoot: string,\n effectiveCwd?: string,\n governedRoot?: string,\n ) {\n this.tool = 'Bash';\n this.command = command;\n this.commandCode = this.stripProse(command);\n this.workspaceRoot = workspaceRoot;\n this.effectiveCwd = effectiveCwd ?? workspaceRoot;\n this.governedRoot = governedRoot ?? workspaceRoot;\n this.options = {};\n }\n\n /**\n * A quoted span WITHOUT whitespace is kept: `git checkout \"main\"` is a real command with a quoted\n * argument, not prose. One containing whitespace is a sentence, and becomes a single space.\n *\n * ONE exception, and it is a false positive that actually fired: a whitespace-free span carrying\n * SHELL METACHARACTERS is dropped too. Keeping the content strips only the quotes, so a jq filter —\n * `--jq '[.[]|select(.title|test(\"x\"))]|length'` — was handed to the guards as bare shell syntax.\n * Every guard then split it on its `|`, `(` and `;` and saw a segment reading exactly `test`, which\n * whole-repo-build-guard classified as the workspace-wide test script and BLOCKED. There was no\n * build anywhere in that command; it polled npm and GitHub.\n *\n * Quoting is precisely how a shell says \"this is DATA, not syntax\", so honouring the quotes is the\n * correct reading. And because commandCode only ever feeds blocklist-shaped guards, dropping more\n * can only ever block LESS — never turn an allowed command into a refused one.\n */\n private stripProse(command: string): string {\n const withoutHeredocs = command.replace(HEREDOC_BODY, ' ');\n return withoutHeredocs.replace(\n QUOTED_SPAN,\n (match: string, single?: string, double?: string): string => {\n const content = single ?? double ?? '';\n if (/\\s/.test(content)) return ' ';\n return SHELL_METACHARACTER.test(content) ? ' ' : content;\n },\n );\n }\n}\n\nexport class FileContext {\n readonly tool: ToolKind;\n readonly filePath: string;\n readonly relativePath: string;\n readonly workspaceRoot: string;\n /** The git tree that owns filePath. workspaceRoot remains the root that owns policy/config. */\n readonly targetRoot: string;\n /** filePath relative to targetRoot, independent of its governed-root spelling. */\n readonly targetRelativePath: string;\n readonly currentFileLines: number;\n readonly linesAdded: number;\n readonly linesRemoved: number;\n readonly projectedFileLines: number;\n options: RuleOptions;\n\n constructor(\n tool: ToolKind,\n filePath: string,\n relativePath: string,\n workspaceRoot: string,\n currentFileLines: number,\n linesAdded: number,\n linesRemoved: number,\n projectedFileLines: number,\n targetRoot: string = workspaceRoot,\n targetRelativePath: string = relativePath,\n ) {\n this.tool = tool;\n this.filePath = filePath;\n this.relativePath = relativePath;\n this.workspaceRoot = workspaceRoot;\n this.targetRoot = targetRoot;\n this.targetRelativePath = targetRelativePath;\n this.currentFileLines = currentFileLines;\n this.linesAdded = linesAdded;\n this.linesRemoved = linesRemoved;\n this.projectedFileLines = projectedFileLines;\n this.options = {};\n }\n}\n\n/**\n * The shape of a client hook policy returned by its declared runtime factory.\n * It carries only metadata + a `check` method — no on/off logic of its own.\n */\nexport interface PlainRule {\n readonly name: string;\n readonly description: string;\n readonly scope: RuleScope;\n readonly files: readonly string[];\n readonly defaultOptions: RuleOptions;\n readonly fixHint: FixHint;\n check(ctx: EditContext | FileContext | BashContext): readonly Violation[];\n}\n\n/**\n * The runtime contract the runner iterates. Both the built-in rule classes (which extend\n * EditRuleBase/FileRuleBase/BashRuleBase) and the custom-rule adapter satisfy this: they add\n * `shouldRun()` (mode + escape-hatch decision) on top of the PlainRule metadata.\n */\nexport interface Rule extends PlainRule {\n /**\n * The webpieces.config.json key whose entry configures this rule — NOT necessarily `name`.\n *\n * `name` is the operator identity (what a decision-log line and a deny report carry); `configKey`\n * is the switch. They differ wherever several classes implement one POLICY: all four branch-state\n * guards read `branch-state-guard`, all four PR-lifecycle guards read `pr-lifecycle-guard`. Every\n * consumer that asks \"which section does this live in?\" (filterByMode) or \"does this have an\n * entry?\" (the fault-Y config-sync check) must read THIS field, because asking for `name` there\n * demands entries under keys the validator rejects as retired.\n */\n readonly configKey: string;\n shouldRun(): boolean;\n}\n\nexport class RuleGroup {\n readonly ruleName: string;\n readonly ruleDescription: string;\n readonly fixHint: FixHint;\n readonly violations: readonly Violation[];\n\n constructor(\n ruleName: string,\n ruleDescription: string,\n fixHint: FixHint,\n violations: readonly Violation[],\n ) {\n this.ruleName = ruleName;\n this.ruleDescription = ruleDescription;\n this.fixHint = fixHint;\n this.violations = violations;\n }\n}\n\nexport class BlockedResult {\n readonly report: string;\n /**\n * The L0 fault this block IS, in the codebook's own letter (see core/l0-fault-codes.ts), or `-` for\n * an ordinary rule block.\n *\n * It rides on the result rather than being re-derived from the report text because the block is\n * decided deep in the runner (fault C in configMissingBlock, fault Y during owner-config validation) and\n * STAMPED by the adapter at the terminal boundary, several frames up. Scraping the report for a\n * fault would be a second answer to a question the producer already knows.\n */\n readonly fault: string;\n\n constructor(report: string, fault: string = L0_FAULT_NONE) {\n this.report = report;\n this.fault = fault;\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { denyJson } from './response';
4
4
  export { HookStdinSource, HookStdoutSink, HookProcessExit } from './hook-ports';
5
5
  export { HookEvaluator } from './hook-evaluator';
6
6
  export { HookApp, HookBootFailure } from './hook-app';
7
- export { RuleScope, IsLineDisabled, Violation, EditContext, FileContext, BashContext, Rule, PlainRule, RuleGroup, BlockedResult } from './core/types';
7
+ export { RuleScope, IsLineDisabled, Violation, EditContext, FileContext, BashContext, Rule, PlainRule, RuleGroup, BlockedResult, } from './core/types';
8
8
  export * from './core/fix-hint';
9
9
  export * from './core/rule-base';
10
10
  export * from './core/custom-rule-adapter';
@@ -29,3 +29,6 @@ export * from './core/root-manifest';
29
29
  export * from './core/rule-evaluation';
30
30
  export * from './core/rules/shell-segment-scan';
31
31
  export * from './core/file-evaluation';
32
+ export { HookPolicyRuntimeRequest } from './policy-runtime';
33
+ export type { HookRuleRuntime } from './policy-runtime';
34
+ export { HookPolicyContributions } from './policy-contributions';
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BlockedResult = exports.RuleGroup = exports.BashContext = exports.FileContext = exports.EditContext = exports.Violation = exports.HookBootFailure = exports.HookApp = exports.HookEvaluator = exports.HookProcessExit = exports.HookStdoutSink = exports.HookStdinSource = exports.denyJson = exports.HookTerminated = exports.HookArgs = exports.HookOutcome = exports.AgentHookEvent = exports.FileOperation = exports.NormalizedBashInput = exports.NormalizedToolInput = exports.NormalizedEdit = exports.AI_TYPE_UNKNOWN = exports.AI_TYPES = void 0;
3
+ exports.HookPolicyContributions = exports.HookPolicyRuntimeRequest = exports.BlockedResult = exports.RuleGroup = exports.BashContext = exports.FileContext = exports.EditContext = exports.Violation = exports.HookBootFailure = exports.HookApp = exports.HookEvaluator = exports.HookProcessExit = exports.HookStdoutSink = exports.HookStdinSource = exports.denyJson = exports.HookTerminated = exports.HookArgs = exports.HookOutcome = exports.AgentHookEvent = exports.FileOperation = exports.NormalizedBashInput = exports.NormalizedToolInput = exports.NormalizedEdit = exports.AI_TYPE_UNKNOWN = exports.AI_TYPES = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  var protocol_1 = require("./protocol");
6
6
  Object.defineProperty(exports, "AI_TYPES", { enumerable: true, get: function () { return protocol_1.AI_TYPES; } });
@@ -56,4 +56,8 @@ tslib_1.__exportStar(require("./core/root-manifest"), exports);
56
56
  tslib_1.__exportStar(require("./core/rule-evaluation"), exports);
57
57
  tslib_1.__exportStar(require("./core/rules/shell-segment-scan"), exports);
58
58
  tslib_1.__exportStar(require("./core/file-evaluation"), exports);
59
+ var policy_runtime_1 = require("./policy-runtime");
60
+ Object.defineProperty(exports, "HookPolicyRuntimeRequest", { enumerable: true, get: function () { return policy_runtime_1.HookPolicyRuntimeRequest; } });
61
+ var policy_contributions_1 = require("./policy-contributions");
62
+ Object.defineProperty(exports, "HookPolicyContributions", { enumerable: true, get: function () { return policy_contributions_1.HookPolicyContributions; } });
59
63
  //# sourceMappingURL=index.js.map
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/index.ts"],"names":[],"mappings":";;;;AAAA,uCAGoB;AAFR,oGAAA,QAAQ,OAAA;AAAE,2GAAA,eAAe,OAAA;AACjC,0GAAA,cAAc,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,yGAAA,aAAa,OAAA;AAAE,0GAAA,cAAc,OAAA;AAE3F,qCAAkE;AAAzD,sGAAA,WAAW,OAAA;AAAE,mGAAA,QAAQ,OAAA;AAAE,yGAAA,cAAc,OAAA;AAC9C,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA;AACjB,2CAAgF;AAAvE,6GAAA,eAAe,OAAA;AAAE,4GAAA,cAAc,OAAA;AAAE,6GAAA,eAAe,OAAA;AACzD,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AACtB,uCAAsD;AAA7C,mGAAA,OAAO,OAAA;AAAE,2GAAA,eAAe,OAAA;AAEjC,sCAAsJ;AAAlH,kGAAA,SAAS,OAAA;AAAE,oGAAA,WAAW,OAAA;AAAE,oGAAA,WAAW,OAAA;AAAE,oGAAA,WAAW,OAAA;AAAmB,kGAAA,SAAS,OAAA;AAAE,sGAAA,aAAa,OAAA;AAC/H,0DAAgC;AAChC,2DAAiC;AACjC,qEAA2C;AAC3C,gEAAsC;AACtC,oEAA0C;AAC1C,+DAAqC;AACrC,wDAA8B;AAC9B,gEAAsC;AACtC,8DAAoC;AACpC,6DAAmC;AACnC,gEAAsC;AACtC,qEAA2C;AAC3C,mEAAyC;AACzC,mEAAyC;AACzC,sDAA4B;AAC5B,mEAAyC;AACzC,+DAAqC;AACrC,oEAA0C;AAC1C,yEAA+C;AAC/C,mEAAyC;AACzC,+DAAqC;AACrC,iEAAuC;AACvC,0EAAgD;AAChD,iEAAuC","sourcesContent":["export {\n AiType, AI_TYPES, AI_TYPE_UNKNOWN, HookMode, ToolKind, AgentEventKind,\n NormalizedEdit, NormalizedToolInput, NormalizedBashInput, FileOperation, AgentHookEvent,\n} from './protocol';\nexport { HookOutcome, HookArgs, HookTerminated } from './outcome';\nexport { denyJson } from './response';\nexport { HookStdinSource, HookStdoutSink, HookProcessExit } from './hook-ports';\nexport { HookEvaluator } from './hook-evaluator';\nexport { HookApp, HookBootFailure } from './hook-app';\n\nexport { RuleScope, IsLineDisabled, Violation, EditContext, FileContext, BashContext, Rule, PlainRule, RuleGroup, BlockedResult } from './core/types';\nexport * from './core/fix-hint';\nexport * from './core/rule-base';\nexport * from './core/custom-rule-adapter';\nexport * from './core/strip-ts-noise';\nexport * from './core/disable-directives';\nexport * from './core/build-context';\nexport * from './core/report';\nexport * from './core/effective-tree';\nexport * from './core/command-scan';\nexport * from './core/target-tree';\nexport * from './core/excluded-paths';\nexport * from './core/delete-scoped-rules';\nexport * from './core/apply-patch-parse';\nexport * from './core/shell-read-parity';\nexport * from './core/glob';\nexport * from './adapters/agent-payload';\nexport * from './adapters/detect-ai';\nexport * from './adapters/agent-adapters';\nexport * from './adapters/claude-code-adapter';\nexport * from './adapters/codex-adapter';\nexport * from './core/root-manifest';\nexport * from './core/rule-evaluation';\nexport * from './core/rules/shell-segment-scan';\nexport * from './core/file-evaluation';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/index.ts"],"names":[],"mappings":";;;;AAAA,uCAYoB;AAVhB,oGAAA,QAAQ,OAAA;AACR,2GAAA,eAAe,OAAA;AAIf,0GAAA,cAAc,OAAA;AACd,+GAAA,mBAAmB,OAAA;AACnB,+GAAA,mBAAmB,OAAA;AACnB,yGAAA,aAAa,OAAA;AACb,0GAAA,cAAc,OAAA;AAElB,qCAAkE;AAAzD,sGAAA,WAAW,OAAA;AAAE,mGAAA,QAAQ,OAAA;AAAE,yGAAA,cAAc,OAAA;AAC9C,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA;AACjB,2CAAgF;AAAvE,6GAAA,eAAe,OAAA;AAAE,4GAAA,cAAc,OAAA;AAAE,6GAAA,eAAe,OAAA;AACzD,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AACtB,uCAAsD;AAA7C,mGAAA,OAAO,OAAA;AAAE,2GAAA,eAAe,OAAA;AAEjC,sCAWsB;AARlB,kGAAA,SAAS,OAAA;AACT,oGAAA,WAAW,OAAA;AACX,oGAAA,WAAW,OAAA;AACX,oGAAA,WAAW,OAAA;AAGX,kGAAA,SAAS,OAAA;AACT,sGAAA,aAAa,OAAA;AAEjB,0DAAgC;AAChC,2DAAiC;AACjC,qEAA2C;AAC3C,gEAAsC;AACtC,oEAA0C;AAC1C,+DAAqC;AACrC,wDAA8B;AAC9B,gEAAsC;AACtC,8DAAoC;AACpC,6DAAmC;AACnC,gEAAsC;AACtC,qEAA2C;AAC3C,mEAAyC;AACzC,mEAAyC;AACzC,sDAA4B;AAC5B,mEAAyC;AACzC,+DAAqC;AACrC,oEAA0C;AAC1C,yEAA+C;AAC/C,mEAAyC;AACzC,+DAAqC;AACrC,iEAAuC;AACvC,0EAAgD;AAChD,iEAAuC;AAEvC,mDAA4D;AAAnD,0HAAA,wBAAwB,OAAA;AAEjC,+DAAiE;AAAxD,+HAAA,uBAAuB,OAAA","sourcesContent":["export {\n AiType,\n AI_TYPES,\n AI_TYPE_UNKNOWN,\n HookMode,\n ToolKind,\n AgentEventKind,\n NormalizedEdit,\n NormalizedToolInput,\n NormalizedBashInput,\n FileOperation,\n AgentHookEvent,\n} from './protocol';\nexport { HookOutcome, HookArgs, HookTerminated } from './outcome';\nexport { denyJson } from './response';\nexport { HookStdinSource, HookStdoutSink, HookProcessExit } from './hook-ports';\nexport { HookEvaluator } from './hook-evaluator';\nexport { HookApp, HookBootFailure } from './hook-app';\n\nexport {\n RuleScope,\n IsLineDisabled,\n Violation,\n EditContext,\n FileContext,\n BashContext,\n Rule,\n PlainRule,\n RuleGroup,\n BlockedResult,\n} from './core/types';\nexport * from './core/fix-hint';\nexport * from './core/rule-base';\nexport * from './core/custom-rule-adapter';\nexport * from './core/strip-ts-noise';\nexport * from './core/disable-directives';\nexport * from './core/build-context';\nexport * from './core/report';\nexport * from './core/effective-tree';\nexport * from './core/command-scan';\nexport * from './core/target-tree';\nexport * from './core/excluded-paths';\nexport * from './core/delete-scoped-rules';\nexport * from './core/apply-patch-parse';\nexport * from './core/shell-read-parity';\nexport * from './core/glob';\nexport * from './adapters/agent-payload';\nexport * from './adapters/detect-ai';\nexport * from './adapters/agent-adapters';\nexport * from './adapters/claude-code-adapter';\nexport * from './adapters/codex-adapter';\nexport * from './core/root-manifest';\nexport * from './core/rule-evaluation';\nexport * from './core/rules/shell-segment-scan';\nexport * from './core/file-evaluation';\n\nexport { HookPolicyRuntimeRequest } from './policy-runtime';\nexport type { HookRuleRuntime } from './policy-runtime';\nexport { HookPolicyContributions } from './policy-contributions';\n"]}
@@ -0,0 +1,12 @@
1
+ import { ExecutionKind } from '@webpieces/rules-sdk';
2
+ import { LoadedConfig } from '@webpieces/rules-config';
3
+ import { HookRuleRuntime } from './policy-runtime';
4
+ import { Rule } from './core/types';
5
+ /** Composition may supply its own public runtime directly; all other declared modules use Node transport. */
6
+ export declare class HookPolicyContributions {
7
+ private readonly composed;
8
+ constructor(composed: ReadonlyMap<string, HookRuleRuntime>);
9
+ load(loaded: LoadedConfig, kind: Extract<ExecutionKind, 'source-hook' | 'workflow-guard'>): readonly Rule[];
10
+ private runtime;
11
+ private validate;
12
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HookPolicyContributions = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("node:path"));
6
+ const rules_config_1 = require("@webpieces/rules-config");
7
+ const tooling_common_1 = require("@webpieces/tooling-common");
8
+ const policy_runtime_1 = require("./policy-runtime");
9
+ const fix_hint_1 = require("./core/fix-hint");
10
+ /** The framework enforces explicit OFF and escape hatches even for client-authored implementations. */
11
+ class ConfiguredHookPolicy extends rules_config_1.AbstractRule {
12
+ implementation;
13
+ description;
14
+ scope;
15
+ files;
16
+ defaultOptions;
17
+ fixHint;
18
+ constructor(config, implementation, configFile) {
19
+ super(config, implementation.name, implementation.configKey);
20
+ this.implementation = implementation;
21
+ this.description = implementation.description;
22
+ this.scope = implementation.scope;
23
+ this.files = implementation.files;
24
+ this.defaultOptions = implementation.defaultOptions;
25
+ const hint = implementation.fixHint;
26
+ this.fixHint = new fix_hint_1.FixHint(hint.violation.replaceAll('{configFile}', configFile), hint.mainMessage.replaceAll('{configFile}', configFile), hint.fixOptions.map((option) => new rules_config_1.Option(option.text.replaceAll('{configFile}', configFile), option.preferred)), hint.escape);
27
+ }
28
+ shouldRun() {
29
+ return super.shouldRun() && this.implementation.shouldRun();
30
+ }
31
+ check(context) {
32
+ return this.implementation.check(context);
33
+ }
34
+ }
35
+ /** Composition may supply its own public runtime directly; all other declared modules use Node transport. */
36
+ class HookPolicyContributions {
37
+ composed;
38
+ constructor(composed) {
39
+ this.composed = composed;
40
+ }
41
+ load(loaded, kind) {
42
+ if (loaded.configPath === null)
43
+ return [];
44
+ const root = path.dirname(loaded.configPath), modules = new rules_config_1.RuleRuntimeModules();
45
+ const loader = new rules_config_1.NodeRuleRuntimeModuleLoader(root), result = [];
46
+ for (const group of modules.groups(loaded.ruleRegistry, kind)) {
47
+ const runtime = this.composed.get(group.implementationModule) ??
48
+ this.runtime(modules.load(group, loaded.ruleRegistry.manifestModules, loader), group);
49
+ const request = modules.request(root, loaded.rulesConfig, group);
50
+ const rules = runtime.create(new policy_runtime_1.HookPolicyRuntimeRequest(request, loaded.commands));
51
+ this.validate(rules, group);
52
+ for (const rule of rules) {
53
+ const filename = loaded.policyFiles?.targetFiles.get(rule.configKey);
54
+ if (!filename)
55
+ throw new tooling_common_1.InformAiError(`Missing declared owner config path for ${rule.configKey}. Repair the root declaration.`);
56
+ result.push(new ConfiguredHookPolicy(request.configs[rule.configKey], rule, filename));
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+ // webpieces-disable no-any-unknown -- the selected execution-family module is validated before invocation
62
+ runtime(exports, group) {
63
+ const name = group.executionKind === 'source-hook' ? 'sourceRuleRuntime' : 'workflowRuleRuntime';
64
+ if (!exports || typeof exports !== 'object' || !(name in exports)) {
65
+ throw new tooling_common_1.InformAiError(`${group.implementationModule} must export ${name}.create(request). Repair the declared public runtime module.`);
66
+ }
67
+ // webpieces-disable no-any-unknown -- external JSON or runtime exports are validated before policy execution
68
+ const runtime = exports[name];
69
+ if (!runtime || typeof runtime.create !== 'function')
70
+ throw new tooling_common_1.InformAiError(`Invalid ${name}.create in ${group.implementationModule}.`);
71
+ return runtime;
72
+ }
73
+ validate(rules, group) {
74
+ if (!Array.isArray(rules))
75
+ throw new tooling_common_1.InformAiError(`${group.implementationModule} must return an array of configured hook policies.`);
76
+ const covered = new Set();
77
+ for (const rule of rules) {
78
+ if (!rule ||
79
+ !group.ruleIds.includes(rule.configKey) ||
80
+ typeof rule.name !== 'string' ||
81
+ !rule.name ||
82
+ typeof rule.description !== 'string' ||
83
+ !['edit', 'file', 'bash'].includes(rule.scope) ||
84
+ !Array.isArray(rule.files) ||
85
+ rule.files.some((file) => typeof file !== 'string') ||
86
+ !rule.defaultOptions ||
87
+ !rule.fixHint ||
88
+ typeof rule.check !== 'function' ||
89
+ typeof rule.shouldRun !== 'function') {
90
+ throw new tooling_common_1.InformAiError(`Invalid hook policy returned by ${group.implementationModule}. Supply the declared configKey and complete hook contract.`);
91
+ }
92
+ covered.add(rule.configKey);
93
+ }
94
+ for (const id of group.ruleIds) {
95
+ if (!covered.has(id))
96
+ throw new tooling_common_1.InformAiError(`${group.implementationModule} omitted declared hook policy ${id}. Supply its actual implementation.`);
97
+ }
98
+ }
99
+ }
100
+ exports.HookPolicyContributions = HookPolicyContributions;
101
+ //# sourceMappingURL=policy-contributions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-contributions.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/policy-contributions.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'node:path';\nimport { BaseRuleConfig, ConfigObject, ExecutionKind } from '@webpieces/rules-sdk';\nimport {\n AbstractRule,\n LoadedConfig,\n Option,\n NodeRuleRuntimeModuleLoader,\n RuleRuntimeModules,\n RuntimeContributionGroup,\n} from '@webpieces/rules-config';\nimport { InformAiError } from '@webpieces/tooling-common';\nimport { HookRuleRuntime, HookPolicyRuntimeRequest } from './policy-runtime';\nimport { Rule, EditContext, FileContext, BashContext, Violation } from './core/types';\nimport { FixHint } from './core/fix-hint';\n\n/** The framework enforces explicit OFF and escape hatches even for client-authored implementations. */\nclass ConfiguredHookPolicy extends AbstractRule<BaseRuleConfig> implements Rule {\n readonly description: string;\n readonly scope: Rule['scope'];\n readonly files: readonly string[];\n readonly defaultOptions: Rule['defaultOptions'];\n readonly fixHint: FixHint;\n\n constructor(\n config: BaseRuleConfig,\n private readonly implementation: Rule,\n configFile: string,\n ) {\n super(config, implementation.name, implementation.configKey);\n this.description = implementation.description;\n this.scope = implementation.scope;\n this.files = implementation.files;\n this.defaultOptions = implementation.defaultOptions;\n const hint = implementation.fixHint;\n this.fixHint = new FixHint(\n hint.violation.replaceAll('{configFile}', configFile),\n hint.mainMessage.replaceAll('{configFile}', configFile),\n hint.fixOptions.map(\n (option: Option) =>\n new Option(\n option.text.replaceAll('{configFile}', configFile),\n option.preferred,\n ),\n ),\n hint.escape,\n );\n }\n\n override shouldRun(): boolean {\n return super.shouldRun() && this.implementation.shouldRun();\n }\n check(context: EditContext | FileContext | BashContext): readonly Violation[] {\n return this.implementation.check(context);\n }\n}\n\n/** Composition may supply its own public runtime directly; all other declared modules use Node transport. */\nexport class HookPolicyContributions {\n constructor(private readonly composed: ReadonlyMap<string, HookRuleRuntime>) {}\n\n load(\n loaded: LoadedConfig,\n kind: Extract<ExecutionKind, 'source-hook' | 'workflow-guard'>,\n ): readonly Rule[] {\n if (loaded.configPath === null) return [];\n const root = path.dirname(loaded.configPath),\n modules = new RuleRuntimeModules();\n const loader = new NodeRuleRuntimeModuleLoader(root),\n result: Rule[] = [];\n for (const group of modules.groups(loaded.ruleRegistry, kind)) {\n const runtime =\n this.composed.get(group.implementationModule) ??\n this.runtime(\n modules.load(group, loaded.ruleRegistry.manifestModules, loader),\n group,\n );\n const request = modules.request(\n root,\n loaded.rulesConfig as Record<string, ConfigObject>,\n group,\n );\n const rules = runtime.create(new HookPolicyRuntimeRequest(request, loaded.commands));\n this.validate(rules, group);\n for (const rule of rules) {\n const filename = loaded.policyFiles?.targetFiles.get(rule.configKey);\n if (!filename)\n throw new InformAiError(\n `Missing declared owner config path for ${rule.configKey}. Repair the root declaration.`,\n );\n result.push(\n new ConfiguredHookPolicy(\n request.configs[rule.configKey] as BaseRuleConfig,\n rule,\n filename,\n ),\n );\n }\n }\n return result;\n }\n\n // webpieces-disable no-any-unknown -- the selected execution-family module is validated before invocation\n private runtime(exports: unknown, group: RuntimeContributionGroup): HookRuleRuntime {\n const name =\n group.executionKind === 'source-hook' ? 'sourceRuleRuntime' : 'workflowRuleRuntime';\n if (!exports || typeof exports !== 'object' || !(name in exports)) {\n throw new InformAiError(\n `${group.implementationModule} must export ${name}.create(request). Repair the declared public runtime module.`,\n );\n }\n // webpieces-disable no-any-unknown -- external JSON or runtime exports are validated before policy execution\n const runtime = (exports as Record<string, unknown>)[name] as HookRuleRuntime;\n if (!runtime || typeof runtime.create !== 'function')\n throw new InformAiError(`Invalid ${name}.create in ${group.implementationModule}.`);\n return runtime;\n }\n\n private validate(rules: readonly Rule[], group: RuntimeContributionGroup): void {\n if (!Array.isArray(rules))\n throw new InformAiError(\n `${group.implementationModule} must return an array of configured hook policies.`,\n );\n const covered = new Set<string>();\n for (const rule of rules) {\n if (\n !rule ||\n !group.ruleIds.includes(rule.configKey) ||\n typeof rule.name !== 'string' ||\n !rule.name ||\n typeof rule.description !== 'string' ||\n !['edit', 'file', 'bash'].includes(rule.scope) ||\n !Array.isArray(rule.files) ||\n rule.files.some((file: string) => typeof file !== 'string') ||\n !rule.defaultOptions ||\n !rule.fixHint ||\n typeof rule.check !== 'function' ||\n typeof rule.shouldRun !== 'function'\n ) {\n throw new InformAiError(\n `Invalid hook policy returned by ${group.implementationModule}. Supply the declared configKey and complete hook contract.`,\n );\n }\n covered.add(rule.configKey);\n }\n for (const id of group.ruleIds) {\n if (!covered.has(id))\n throw new InformAiError(\n `${group.implementationModule} omitted declared hook policy ${id}. Supply its actual implementation.`,\n );\n }\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { PolicyRuntimeRequest } from '@webpieces/rules-sdk';
2
+ import { CommandsConfig } from '@webpieces/rules-config';
3
+ import { Rule } from './core/types';
4
+ /** Hook mechanics receive the resolved generic command configuration separately from owner settings. */
5
+ export declare class HookPolicyRuntimeRequest extends PolicyRuntimeRequest {
6
+ readonly commands: CommandsConfig;
7
+ constructor(request: PolicyRuntimeRequest, commands: CommandsConfig);
8
+ }
9
+ export interface HookRuleRuntime {
10
+ create(request: HookPolicyRuntimeRequest): readonly Rule[];
11
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HookPolicyRuntimeRequest = void 0;
4
+ const rules_sdk_1 = require("@webpieces/rules-sdk");
5
+ /** Hook mechanics receive the resolved generic command configuration separately from owner settings. */
6
+ class HookPolicyRuntimeRequest extends rules_sdk_1.PolicyRuntimeRequest {
7
+ commands;
8
+ constructor(request, commands) {
9
+ super(request.workspaceRoot, request.configs, request.ruleIds);
10
+ this.commands = commands;
11
+ }
12
+ }
13
+ exports.HookPolicyRuntimeRequest = HookPolicyRuntimeRequest;
14
+ //# sourceMappingURL=policy-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-runtime.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/policy-runtime.ts"],"names":[],"mappings":";;;AAAA,oDAA4D;AAI5D,wGAAwG;AACxG,MAAa,wBAAyB,SAAQ,gCAAoB;IAGjD;IAFb,YACI,OAA6B,EACpB,QAAwB;QAEjC,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAFtD,aAAQ,GAAR,QAAQ,CAAgB;IAGrC,CAAC;CACJ;AAPD,4DAOC","sourcesContent":["import { PolicyRuntimeRequest } from '@webpieces/rules-sdk';\nimport { CommandsConfig } from '@webpieces/rules-config';\nimport { Rule } from './core/types';\n\n/** Hook mechanics receive the resolved generic command configuration separately from owner settings. */\nexport class HookPolicyRuntimeRequest extends PolicyRuntimeRequest {\n constructor(\n request: PolicyRuntimeRequest,\n readonly commands: CommandsConfig,\n ) {\n super(request.workspaceRoot, request.configs, request.ruleIds);\n }\n}\n\nexport interface HookRuleRuntime {\n create(request: HookPolicyRuntimeRequest): readonly Rule[];\n}\n"]}