@webpieces/ai-hook-rules 0.3.184 → 0.3.186

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 (104) hide show
  1. package/package.json +4 -8
  2. package/src/adapters/global-hook.js +3 -3
  3. package/src/adapters/global-hook.js.map +1 -1
  4. package/src/core/custom-rule-adapter.d.ts +21 -0
  5. package/src/core/custom-rule-adapter.js +48 -0
  6. package/src/core/custom-rule-adapter.js.map +1 -0
  7. package/src/core/disable-directives.d.ts +1 -2
  8. package/src/core/disable-directives.js +17 -31
  9. package/src/core/disable-directives.js.map +1 -1
  10. package/src/core/load-rules.d.ts +3 -2
  11. package/src/core/load-rules.js +71 -48
  12. package/src/core/load-rules.js.map +1 -1
  13. package/src/core/rule-base.d.ts +38 -0
  14. package/src/core/rule-base.js +36 -0
  15. package/src/core/rule-base.js.map +1 -0
  16. package/src/core/rules/branch-creation-guard.d.ts +12 -3
  17. package/src/core/rules/branch-creation-guard.js +37 -37
  18. package/src/core/rules/branch-creation-guard.js.map +1 -1
  19. package/src/core/rules/catch-error-pattern.d.ts +10 -3
  20. package/src/core/rules/catch-error-pattern.js +27 -26
  21. package/src/core/rules/catch-error-pattern.js.map +1 -1
  22. package/src/core/rules/index.js +1 -0
  23. package/src/core/rules/index.js.map +1 -1
  24. package/src/core/rules/max-file-lines.d.ts +13 -3
  25. package/src/core/rules/max-file-lines.js +14 -15
  26. package/src/core/rules/max-file-lines.js.map +1 -1
  27. package/src/core/rules/merge-in-progress-guard.d.ts +9 -0
  28. package/src/core/rules/merge-in-progress-guard.js +65 -0
  29. package/src/core/rules/merge-in-progress-guard.js.map +1 -0
  30. package/src/core/rules/no-any-unknown.d.ts +10 -3
  31. package/src/core/rules/no-any-unknown.js +15 -13
  32. package/src/core/rules/no-any-unknown.js.map +1 -1
  33. package/src/core/rules/no-destructure.d.ts +13 -3
  34. package/src/core/rules/no-destructure.js +14 -12
  35. package/src/core/rules/no-destructure.js.map +1 -1
  36. package/src/core/rules/no-direct-main-update.d.ts +9 -3
  37. package/src/core/rules/no-direct-main-update.js +12 -13
  38. package/src/core/rules/no-direct-main-update.js.map +1 -1
  39. package/src/core/rules/no-edit-on-main.d.ts +13 -3
  40. package/src/core/rules/no-edit-on-main.js +14 -14
  41. package/src/core/rules/no-edit-on-main.js.map +1 -1
  42. package/src/core/rules/no-implicit-any.d.ts +10 -3
  43. package/src/core/rules/no-implicit-any.js +13 -12
  44. package/src/core/rules/no-implicit-any.js.map +1 -1
  45. package/src/core/rules/no-js-files.d.ts +13 -3
  46. package/src/core/rules/no-js-files.js +13 -16
  47. package/src/core/rules/no-js-files.js.map +1 -1
  48. package/src/core/rules/no-shell-substitution.d.ts +9 -3
  49. package/src/core/rules/no-shell-substitution.js +22 -23
  50. package/src/core/rules/no-shell-substitution.js.map +1 -1
  51. package/src/core/rules/no-symbol-di-tokens.d.ts +13 -3
  52. package/src/core/rules/no-symbol-di-tokens.js +15 -13
  53. package/src/core/rules/no-symbol-di-tokens.js.map +1 -1
  54. package/src/core/rules/no-unmanaged-exceptions.d.ts +10 -3
  55. package/src/core/rules/no-unmanaged-exceptions.js +18 -17
  56. package/src/core/rules/no-unmanaged-exceptions.js.map +1 -1
  57. package/src/core/rules/pr-creation-guard.d.ts +9 -4
  58. package/src/core/rules/pr-creation-guard.js +36 -72
  59. package/src/core/rules/pr-creation-guard.js.map +1 -1
  60. package/src/core/rules/pr-merge-cleanup.d.ts +9 -3
  61. package/src/core/rules/pr-merge-cleanup.js +13 -14
  62. package/src/core/rules/pr-merge-cleanup.js.map +1 -1
  63. package/src/core/rules/require-return-type.d.ts +10 -3
  64. package/src/core/rules/require-return-type.js +24 -23
  65. package/src/core/rules/require-return-type.js.map +1 -1
  66. package/src/core/rules/throw-cause-required.d.ts +10 -3
  67. package/src/core/rules/throw-cause-required.js +13 -12
  68. package/src/core/rules/throw-cause-required.js.map +1 -1
  69. package/src/core/rules/validate-ts-in-src.d.ts +14 -3
  70. package/src/core/rules/validate-ts-in-src.js +15 -18
  71. package/src/core/rules/validate-ts-in-src.js.map +1 -1
  72. package/src/core/runner.js +38 -67
  73. package/src/core/runner.js.map +1 -1
  74. package/src/core/to-error.js +0 -1
  75. package/src/core/to-error.js.map +1 -1
  76. package/src/core/types.d.ts +14 -13
  77. package/src/core/types.js.map +1 -1
  78. package/src/index.d.ts +21 -1
  79. package/src/index.js +47 -1
  80. package/src/index.js.map +1 -1
  81. package/src/core/configs/default.d.ts +0 -2
  82. package/src/core/configs/default.js +0 -22
  83. package/src/core/configs/default.js.map +0 -1
  84. package/src/scripts/git-gatherInfo.d.ts +0 -2
  85. package/src/scripts/git-gatherInfo.js +0 -98
  86. package/src/scripts/git-gatherInfo.js.map +0 -1
  87. package/src/scripts/git-mergeComplete.d.ts +0 -2
  88. package/src/scripts/git-mergeComplete.js +0 -58
  89. package/src/scripts/git-mergeComplete.js.map +0 -1
  90. package/src/scripts/git-updateFromMain.d.ts +0 -2
  91. package/src/scripts/git-updateFromMain.js +0 -290
  92. package/src/scripts/git-updateFromMain.js.map +0 -1
  93. package/src/scripts/workflow/cleanTmp.d.ts +0 -1
  94. package/src/scripts/workflow/cleanTmp.js +0 -57
  95. package/src/scripts/workflow/cleanTmp.js.map +0 -1
  96. package/src/scripts/workflow/git-findForkPoint.d.ts +0 -2
  97. package/src/scripts/workflow/git-findForkPoint.js +0 -120
  98. package/src/scripts/workflow/git-findForkPoint.js.map +0 -1
  99. package/src/scripts/workflow/git-readAiBranchName.d.ts +0 -2
  100. package/src/scripts/workflow/git-readAiBranchName.js +0 -20
  101. package/src/scripts/workflow/git-readAiBranchName.js.map +0 -1
  102. package/src/scripts/workflow/git-validateUpToDate.d.ts +0 -1
  103. package/src/scripts/workflow/git-validateUpToDate.js +0 -72
  104. package/src/scripts/workflow/git-validateUpToDate.js.map +0 -1
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ThrowCauseRequiredRule = void 0;
4
+ const rules_config_1 = require("@webpieces/rules-config");
3
5
  const types_1 = require("../types");
6
+ const rule_base_1 = require("../rule-base");
4
7
  const instruct_ai_writer_1 = require("../instruct-ai-writer");
5
8
  /**
6
9
  * Matches: throw new SomeClass(
@@ -14,18 +17,16 @@ const ERROR_MESSAGE_PATTERN = /\berror\d*\.message\b/;
14
17
  * Matches: cause: error or cause: error2 etc. (the good pattern)
15
18
  */
16
19
  const CAUSE_PATTERN = /\bcause\s*:\s*error\d*\b/;
17
- const throwCauseRequiredRule = {
18
- name: 'throw-cause-required',
19
- description: 'When rethrowing with added context, chain the original exception: throw new Error("msg", { cause: error })',
20
- scope: 'edit',
21
- files: ['**/*.ts', '**/*.tsx'],
22
- defaultOptions: {},
23
- fixHint: [
20
+ class ThrowCauseRequiredRule extends rule_base_1.EditRuleBase {
21
+ constructor(config) { super(config, 'throw-cause-required'); }
22
+ description = 'When rethrowing with added context, chain the original exception: throw new Error("msg", { cause: error })';
23
+ files = ['**/*.ts', '**/*.tsx'];
24
+ fixHint = [
24
25
  'Option 1 — Remove the try-catch entirely. Letting the original exception bubble is usually the best option.',
25
26
  'Option 2 — throw new Error("add more info here", { cause: error }); chains the error and preserves the full stack trace.',
26
27
  'Option 3 — throw new SpecificError("add info here", { cause: error }); e.g. new InformAiError("what happened for AI", { cause: error }).',
27
28
  '[Only if disableAllowed:true] Option 4 — // webpieces-disable throw-cause-required -- <reason>',
28
- ],
29
+ ];
29
30
  check(ctx) {
30
31
  const violations = [];
31
32
  const lines = ctx.strippedLines;
@@ -38,14 +39,14 @@ const throwCauseRequiredRule = {
38
39
  if (CAUSE_PATTERN.test(stripped))
39
40
  continue;
40
41
  const lineNum = i + 1;
41
- if (ctx.isLineDisabled(lineNum, 'throw-cause-required'))
42
+ if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.THROW_CAUSE_REQUIRED))
42
43
  continue;
43
44
  violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), 'Rethrowing with error.message loses the original stack trace. Use { cause: error } to chain it.'));
44
45
  }
45
46
  if (violations.length > 0)
46
47
  (0, instruct_ai_writer_1.writeTemplateIfMissing)(ctx.workspaceRoot, 'webpieces.exceptions.md');
47
48
  return violations;
48
- },
49
- };
50
- exports.default = throwCauseRequiredRule;
49
+ }
50
+ }
51
+ exports.ThrowCauseRequiredRule = ThrowCauseRequiredRule;
51
52
  //# sourceMappingURL=throw-cause-required.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"throw-cause-required.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/throw-cause-required.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAC1C,8DAA+D;AAE/D;;GAEG;AACH,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAErD;;GAEG;AACH,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAEtD;;GAEG;AACH,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAEjD,MAAM,sBAAsB,GAAa;IACrC,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EAAE,4GAA4G;IACzH,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,6GAA6G;QAC7G,0HAA0H;QAC1H,0IAA0I;QAC1I,gGAAgG;KACnG;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC;QAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAChD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACpD,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAE3C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,sBAAsB,CAAC;gBAAE,SAAS;YAElE,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,iGAAiG,CACpG,CAAC,CAAC;QACP,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,IAAA,2CAAsB,EAAC,GAAG,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC;QAChG,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,kBAAe,sBAAsB,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { writeTemplateIfMissing } from '../instruct-ai-writer';\n\n/**\n * Matches: throw new SomeClass(\n */\nconst THROW_NEW_PATTERN = /\\bthrow\\s+new\\s+\\w+\\s*\\(/;\n\n/**\n * Matches: error.message or error2.message or error3.message\n */\nconst ERROR_MESSAGE_PATTERN = /\\berror\\d*\\.message\\b/;\n\n/**\n * Matches: cause: error or cause: error2 etc. (the good pattern)\n */\nconst CAUSE_PATTERN = /\\bcause\\s*:\\s*error\\d*\\b/;\n\nconst throwCauseRequiredRule: EditRule = {\n name: 'throw-cause-required',\n description: 'When rethrowing with added context, chain the original exception: throw new Error(\"msg\", { cause: error })',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'Option 1 — Remove the try-catch entirely. Letting the original exception bubble is usually the best option.',\n 'Option 2 — throw new Error(\"add more info here\", { cause: error }); chains the error and preserves the full stack trace.',\n 'Option 3 — throw new SpecificError(\"add info here\", { cause: error }); e.g. new InformAiError(\"what happened for AI\", { cause: error }).',\n '[Only if disableAllowed:true] Option 4 — // webpieces-disable throw-cause-required -- <reason>',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n const lines = ctx.strippedLines;\n\n for (let i = 0; i < lines.length; i += 1) {\n const stripped = lines[i];\n if (!THROW_NEW_PATTERN.test(stripped)) continue;\n if (!ERROR_MESSAGE_PATTERN.test(stripped)) continue;\n if (CAUSE_PATTERN.test(stripped)) continue;\n\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, 'throw-cause-required')) continue;\n\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Rethrowing with error.message loses the original stack trace. Use { cause: error } to chain it.',\n ));\n }\n\n if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');\n return violations;\n },\n};\n\nexport default throwCauseRequiredRule;\n"]}
1
+ {"version":3,"file":"throw-cause-required.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/throw-cause-required.ts"],"names":[],"mappings":";;;AAAA,0DAA+E;AAG/E,oCAA0C;AAC1C,4CAA4C;AAC5C,8DAA+D;AAE/D;;GAEG;AACH,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAErD;;GAEG;AACH,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAEtD;;GAEG;AACH,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAEjD,MAAa,sBAAuB,SAAQ,wBAAsC;IAC9E,YAAY,MAAgC,IAAI,KAAK,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAE/E,WAAW,GAAG,4GAA4G,CAAC;IAClH,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACzC,OAAO,GAAG;QACf,6GAA6G;QAC7G,0HAA0H;QAC1H,0IAA0I;QAC1I,gGAAgG;KACnG,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC;QAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAChD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACpD,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAE3C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAU,CAAC,oBAAoB,CAAC;gBAAE,SAAS;YAE3E,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,iGAAiG,CACpG,CAAC,CAAC;QACP,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,IAAA,2CAAsB,EAAC,GAAG,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC;QAChG,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ;AAnCD,wDAmCC","sourcesContent":["import { ThrowCauseRequiredConfig, RULE_NAMES } from '@webpieces/rules-config';\n\nimport type { EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { EditRuleBase } from '../rule-base';\nimport { writeTemplateIfMissing } from '../instruct-ai-writer';\n\n/**\n * Matches: throw new SomeClass(\n */\nconst THROW_NEW_PATTERN = /\\bthrow\\s+new\\s+\\w+\\s*\\(/;\n\n/**\n * Matches: error.message or error2.message or error3.message\n */\nconst ERROR_MESSAGE_PATTERN = /\\berror\\d*\\.message\\b/;\n\n/**\n * Matches: cause: error or cause: error2 etc. (the good pattern)\n */\nconst CAUSE_PATTERN = /\\bcause\\s*:\\s*error\\d*\\b/;\n\nexport class ThrowCauseRequiredRule extends EditRuleBase<ThrowCauseRequiredConfig> {\n constructor(config: ThrowCauseRequiredConfig) { super(config, 'throw-cause-required'); }\n\n readonly description = 'When rethrowing with added context, chain the original exception: throw new Error(\"msg\", { cause: error })';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n readonly fixHint = [\n 'Option 1 — Remove the try-catch entirely. Letting the original exception bubble is usually the best option.',\n 'Option 2 — throw new Error(\"add more info here\", { cause: error }); chains the error and preserves the full stack trace.',\n 'Option 3 — throw new SpecificError(\"add info here\", { cause: error }); e.g. new InformAiError(\"what happened for AI\", { cause: error }).',\n '[Only if disableAllowed:true] Option 4 — // webpieces-disable throw-cause-required -- <reason>',\n ];\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n const lines = ctx.strippedLines;\n\n for (let i = 0; i < lines.length; i += 1) {\n const stripped = lines[i];\n if (!THROW_NEW_PATTERN.test(stripped)) continue;\n if (!ERROR_MESSAGE_PATTERN.test(stripped)) continue;\n if (CAUSE_PATTERN.test(stripped)) continue;\n\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.THROW_CAUSE_REQUIRED)) continue;\n\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Rethrowing with error.message loses the original stack trace. Use { cause: error } to chain it.',\n ));\n }\n\n if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');\n return violations;\n }\n}\n"]}
@@ -1,3 +1,14 @@
1
- import type { FileRule } from '../types';
2
- declare const validateTsInSrcRule: FileRule;
3
- export default validateTsInSrcRule;
1
+ import { ValidateTsInSrcConfig } from '@webpieces/rules-config';
2
+ import type { FileContext, Violation } from '../types';
3
+ import { FileRuleBase } from '../rule-base';
4
+ export declare class ValidateTsInSrcRule extends FileRuleBase<ValidateTsInSrcConfig> {
5
+ constructor(config: ValidateTsInSrcConfig);
6
+ readonly description = "Every .ts file must belong to a project's src/ directory.";
7
+ readonly files: string[];
8
+ readonly defaultOptions: {
9
+ excludePaths: string[];
10
+ allowedRootFiles: string[];
11
+ };
12
+ readonly fixHint: string[];
13
+ check(ctx: FileContext): readonly Violation[];
14
+ }
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidateTsInSrcRule = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  const fs = tslib_1.__importStar(require("fs"));
5
6
  const path = tslib_1.__importStar(require("path"));
6
7
  const rules_config_1 = require("@webpieces/rules-config");
7
8
  const types_1 = require("../types");
9
+ const rule_base_1 = require("../rule-base");
8
10
  const DEFAULT_EXCLUDE_PATHS = [
9
11
  'node_modules', 'dist', '.nx', '.git',
10
12
  '**/*.d.ts', '**/jest.config.ts',
@@ -19,28 +21,23 @@ function findProjectRoot(filePath, workspaceRoot) {
19
21
  }
20
22
  return null;
21
23
  }
22
- const validateTsInSrcRule = {
23
- name: 'validate-ts-in-src',
24
- description: 'Every .ts file must belong to a project\'s src/ directory.',
25
- scope: 'file',
26
- files: ['**/*.ts', '**/*.tsx'],
27
- defaultOptions: {
24
+ class ValidateTsInSrcRule extends rule_base_1.FileRuleBase {
25
+ constructor(config) { super(config, 'validate-ts-in-src'); }
26
+ description = 'Every .ts file must belong to a project\'s src/ directory.';
27
+ files = ['**/*.ts', '**/*.tsx'];
28
+ defaultOptions = {
28
29
  excludePaths: DEFAULT_EXCLUDE_PATHS,
29
30
  allowedRootFiles: DEFAULT_ALLOWED_ROOT_FILES,
30
- },
31
- fixHint: [
31
+ };
32
+ fixHint = [
32
33
  'Move the file into an existing project\'s src/ directory, or create a new project with project.json that owns the directory.',
33
34
  'Add a dir or glob (e.g. "**/codegen.ts") to validate-ts-in-src.excludePaths in webpieces.config.json',
34
- ],
35
+ ];
35
36
  check(ctx) {
36
37
  if (ctx.tool !== 'Write')
37
38
  return [];
38
- const excludePaths = Array.isArray(ctx.options['excludePaths'])
39
- ? ctx.options['excludePaths']
40
- : DEFAULT_EXCLUDE_PATHS;
41
- const allowedRootFiles = Array.isArray(ctx.options['allowedRootFiles'])
42
- ? ctx.options['allowedRootFiles']
43
- : DEFAULT_ALLOWED_ROOT_FILES;
39
+ const excludePaths = this.config.excludePaths ?? DEFAULT_EXCLUDE_PATHS;
40
+ const allowedRootFiles = this.config.allowedRootFiles ?? DEFAULT_ALLOWED_ROOT_FILES;
44
41
  // Holistic exclusion (Layer 1 + Layer 2): bare dir names + globs.
45
42
  if ((0, rules_config_1.isPathExcluded)(ctx.relativePath, excludePaths))
46
43
  return [];
@@ -57,7 +54,7 @@ const validateTsInSrcRule = {
57
54
  return [new types_1.Violation(1, ctx.relativePath, `File is inside project \`${projectName}\` but outside its src/ directory. Move it into src/.`)];
58
55
  }
59
56
  return [];
60
- },
61
- };
62
- exports.default = validateTsInSrcRule;
57
+ }
58
+ }
59
+ exports.ValidateTsInSrcRule = ValidateTsInSrcRule;
63
60
  //# sourceMappingURL=validate-ts-in-src.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-ts-in-src.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/validate-ts-in-src.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { isPathExcluded } from '@webpieces/rules-config';\n\nimport type { FileRule, FileContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst DEFAULT_EXCLUDE_PATHS = [\n 'node_modules', 'dist', '.nx', '.git',\n '**/*.d.ts', '**/jest.config.ts',\n];\nconst DEFAULT_ALLOWED_ROOT_FILES = ['jest.setup.ts'];\n\nfunction findProjectRoot(filePath: string, workspaceRoot: string): string | null {\n let dir = path.dirname(filePath);\n while (dir !== workspaceRoot && dir.startsWith(workspaceRoot)) {\n if (fs.existsSync(path.join(dir, 'project.json'))) return dir;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nconst validateTsInSrcRule: FileRule = {\n name: 'validate-ts-in-src',\n description: 'Every .ts file must belong to a project\\'s src/ directory.',\n scope: 'file',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {\n excludePaths: DEFAULT_EXCLUDE_PATHS,\n allowedRootFiles: DEFAULT_ALLOWED_ROOT_FILES,\n },\n fixHint: [\n 'Move the file into an existing project\\'s src/ directory, or create a new project with project.json that owns the directory.',\n 'Add a dir or glob (e.g. \"**/codegen.ts\") to validate-ts-in-src.excludePaths in webpieces.config.json',\n ],\n\n check(ctx: FileContext): readonly Violation[] {\n if (ctx.tool !== 'Write') return [];\n\n const excludePaths = Array.isArray(ctx.options['excludePaths'])\n ? ctx.options['excludePaths'] as string[]\n : DEFAULT_EXCLUDE_PATHS;\n const allowedRootFiles = Array.isArray(ctx.options['allowedRootFiles'])\n ? ctx.options['allowedRootFiles'] as string[]\n : DEFAULT_ALLOWED_ROOT_FILES;\n\n // Holistic exclusion (Layer 1 + Layer 2): bare dir names + globs.\n if (isPathExcluded(ctx.relativePath, excludePaths)) return [];\n\n const relParts = ctx.relativePath.split(path.sep);\n if (relParts.length === 1 && allowedRootFiles.indexOf(relParts[0] ?? '') >= 0) return [];\n\n const projectRoot = findProjectRoot(ctx.filePath, ctx.workspaceRoot);\n\n if (!projectRoot) {\n return [new V(\n 1,\n ctx.relativePath,\n 'File is not inside any Nx project. Move it into a project\\'s src/ directory.',\n )];\n }\n\n const relToProject = path.relative(projectRoot, ctx.filePath);\n if (!relToProject.startsWith('src' + path.sep) && relToProject !== 'src') {\n const projectName = path.relative(ctx.workspaceRoot, projectRoot);\n return [new V(\n 1,\n ctx.relativePath,\n `File is inside project \\`${projectName}\\` but outside its src/ directory. Move it into src/.`,\n )];\n }\n\n return [];\n },\n};\n\nexport default validateTsInSrcRule;\n"]}
1
+ {"version":3,"file":"validate-ts-in-src.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/validate-ts-in-src.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAE7B,0DAAgF;AAGhF,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,qBAAqB,GAAG;IAC1B,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IACrC,WAAW,EAAE,mBAAmB;CACnC,CAAC;AACF,MAAM,0BAA0B,GAAG,CAAC,eAAe,CAAC,CAAC;AAErD,SAAS,eAAe,CAAC,QAAgB,EAAE,aAAqB;IAC5D,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,OAAO,GAAG,KAAK,aAAa,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC5D,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC9D,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,MAAa,mBAAoB,SAAQ,wBAAmC;IACxE,YAAY,MAA6B,IAAI,KAAK,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;IAE1E,WAAW,GAAG,4DAA4D,CAAC;IAClE,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChC,cAAc,GAAG;QAC/B,YAAY,EAAE,qBAAqB;QACnC,gBAAgB,EAAE,0BAA0B;KAC/C,CAAC;IACO,OAAO,GAAG;QACf,8HAA8H;QAC9H,sGAAsG;KACzG,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,EAAE,CAAC;QAEpC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,qBAAqB,CAAC;QACvE,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,IAAI,0BAA0B,CAAC;QAEpF,kEAAkE;QAClE,IAAI,IAAA,6BAAc,EAAC,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC;YAAE,OAAO,EAAE,CAAC;QAE9D,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAEzF,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;QAErE,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,GAAG,CAAC,YAAY,EAChB,8EAA8E,CACjF,CAAC,CAAC;QACP,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,KAAK,KAAK,EAAE,CAAC;YACvE,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,GAAG,CAAC,YAAY,EAChB,4BAA4B,WAAW,uDAAuD,CACjG,CAAC,CAAC;QACP,CAAC;QAED,OAAO,EAAE,CAAC;IACd,CAAC;CACJ;AAhDD,kDAgDC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { ValidateTsInSrcConfig, isPathExcluded } from '@webpieces/rules-config';\n\nimport type { FileContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { FileRuleBase } from '../rule-base';\n\nconst DEFAULT_EXCLUDE_PATHS = [\n 'node_modules', 'dist', '.nx', '.git',\n '**/*.d.ts', '**/jest.config.ts',\n];\nconst DEFAULT_ALLOWED_ROOT_FILES = ['jest.setup.ts'];\n\nfunction findProjectRoot(filePath: string, workspaceRoot: string): string | null {\n let dir = path.dirname(filePath);\n while (dir !== workspaceRoot && dir.startsWith(workspaceRoot)) {\n if (fs.existsSync(path.join(dir, 'project.json'))) return dir;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nexport class ValidateTsInSrcRule extends FileRuleBase<ValidateTsInSrcConfig> {\n constructor(config: ValidateTsInSrcConfig) { super(config, 'validate-ts-in-src'); }\n\n readonly description = 'Every .ts file must belong to a project\\'s src/ directory.';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n override readonly defaultOptions = {\n excludePaths: DEFAULT_EXCLUDE_PATHS,\n allowedRootFiles: DEFAULT_ALLOWED_ROOT_FILES,\n };\n readonly fixHint = [\n 'Move the file into an existing project\\'s src/ directory, or create a new project with project.json that owns the directory.',\n 'Add a dir or glob (e.g. \"**/codegen.ts\") to validate-ts-in-src.excludePaths in webpieces.config.json',\n ];\n\n check(ctx: FileContext): readonly Violation[] {\n if (ctx.tool !== 'Write') return [];\n\n const excludePaths = this.config.excludePaths ?? DEFAULT_EXCLUDE_PATHS;\n const allowedRootFiles = this.config.allowedRootFiles ?? DEFAULT_ALLOWED_ROOT_FILES;\n\n // Holistic exclusion (Layer 1 + Layer 2): bare dir names + globs.\n if (isPathExcluded(ctx.relativePath, excludePaths)) return [];\n\n const relParts = ctx.relativePath.split(path.sep);\n if (relParts.length === 1 && allowedRootFiles.indexOf(relParts[0] ?? '') >= 0) return [];\n\n const projectRoot = findProjectRoot(ctx.filePath, ctx.workspaceRoot);\n\n if (!projectRoot) {\n return [new V(\n 1,\n ctx.relativePath,\n 'File is not inside any Nx project. Move it into a project\\'s src/ directory.',\n )];\n }\n\n const relToProject = path.relative(projectRoot, ctx.filePath);\n if (!relToProject.startsWith('src' + path.sep) && relToProject !== 'src') {\n const projectName = path.relative(ctx.workspaceRoot, projectRoot);\n return [new V(\n 1,\n ctx.relativePath,\n `File is inside project \\`${projectName}\\` but outside its src/ directory. Move it into src/.`,\n )];\n }\n\n return [];\n }\n}\n"]}
@@ -6,36 +6,35 @@ const tslib_1 = require("tslib");
6
6
  const path = tslib_1.__importStar(require("path"));
7
7
  const rules_config_1 = require("@webpieces/rules-config");
8
8
  const build_context_1 = require("./build-context");
9
- const load_config_1 = require("./load-config");
10
9
  const load_rules_1 = require("./load-rules");
11
10
  const to_error_1 = require("./to-error");
12
11
  const report_1 = require("./report");
13
12
  const types_1 = require("./types");
13
+ const CONFIG_MISSING_REPORT = 'webpieces.config.json not found.\n' +
14
+ 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\n' +
15
+ 'Do not proceed until the human has done this.';
14
16
  function run(toolKind, input, cwd) {
15
17
  return runInternal(toolKind, input, cwd);
16
18
  }
17
19
  function runInternal(toolKind, input, cwd) {
18
- const config = (0, load_config_1.loadConfig)(cwd);
19
- if (!config.configPath) {
20
- return new types_1.BlockedResult('webpieces.config.json not found.\n' +
21
- 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\n' +
22
- 'Do not proceed until the human has done this.');
23
- }
24
- const workspaceRoot = path.dirname(config.configPath);
20
+ const loaded = (0, rules_config_1.loadWebpiecesRulesConfig)(cwd);
21
+ if (!loaded)
22
+ return new types_1.BlockedResult(CONFIG_MISSING_REPORT);
23
+ const workspaceRoot = path.dirname(loaded.configPath);
25
24
  // Always allow edits to webpieces.config.json — it's the fix target when out of sync
26
- if (path.resolve(input.filePath) === path.resolve(config.configPath)) {
25
+ if (path.resolve(input.filePath) === path.resolve(loaded.configPath)) {
27
26
  return null;
28
27
  }
29
- const rules = (0, load_rules_1.loadRules)(config, workspaceRoot);
28
+ const rules = (0, load_rules_1.loadRules)(loaded.config, workspaceRoot);
30
29
  if (rules.length === 0)
31
30
  return null;
32
- const outOfSync = checkConfigSync(rules, config);
31
+ const outOfSync = checkConfigSync(rules, loaded.config);
33
32
  if (outOfSync)
34
33
  return outOfSync;
35
34
  const contexts = (0, build_context_1.buildContexts)(toolKind, input, workspaceRoot);
36
35
  const relativePath = path.relative(workspaceRoot, input.filePath);
37
- const editGroups = runEditRules(rules, contexts.editContexts, config);
38
- const fileGroups = runFileRules(rules, contexts.fileContext, config);
36
+ const editGroups = runEditRules(rules, contexts.editContexts);
37
+ const fileGroups = runFileRules(rules, contexts.fileContext);
39
38
  const allGroups = [...editGroups, ...fileGroups];
40
39
  if (allGroups.length === 0)
41
40
  return null;
@@ -46,28 +45,30 @@ function runBash(command, cwd) {
46
45
  return runBashInternal(command, cwd);
47
46
  }
48
47
  function runBashInternal(command, cwd) {
49
- const config = (0, load_config_1.loadConfig)(cwd);
50
- if (!config.configPath) {
51
- return new types_1.BlockedResult('webpieces.config.json not found.\n' +
52
- 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\n' +
53
- 'Do not proceed until the human has done this.');
54
- }
55
- const workspaceRoot = path.dirname(config.configPath);
56
- const rules = (0, load_rules_1.loadRules)(config, workspaceRoot);
48
+ const loaded = (0, rules_config_1.loadWebpiecesRulesConfig)(cwd);
49
+ if (!loaded)
50
+ return new types_1.BlockedResult(CONFIG_MISSING_REPORT);
51
+ const workspaceRoot = path.dirname(loaded.configPath);
52
+ const rules = (0, load_rules_1.loadRules)(loaded.config, workspaceRoot);
57
53
  if (rules.length === 0)
58
54
  return null;
59
- const outOfSync = checkConfigSync(rules, config);
55
+ const outOfSync = checkConfigSync(rules, loaded.config);
60
56
  if (outOfSync)
61
57
  return outOfSync;
62
58
  const ctx = (0, build_context_1.buildBashContext)(command, workspaceRoot);
63
- const groups = runBashRules(rules, ctx, config);
59
+ const groups = runBashRules(rules, ctx);
64
60
  if (groups.length === 0)
65
61
  return null;
66
62
  const report = (0, report_1.formatReport)('<bash>', groups);
67
63
  return new types_1.BlockedResult(report);
68
64
  }
65
+ // The set of rule names explicitly present in webpieces.config.json (every key except rulesDir).
66
+ function configuredRuleNames(config) {
67
+ return new Set(Object.keys(config).filter((k) => k !== 'rulesDir'));
68
+ }
69
69
  function checkConfigSync(rules, config) {
70
- const unconfiguredRules = rules.filter((r) => !config.userConfiguredRuleNames.has(r.name));
70
+ const configured = configuredRuleNames(config);
71
+ const unconfiguredRules = rules.filter((r) => !configured.has(r.name));
71
72
  if (unconfiguredRules.length === 0)
72
73
  return null;
73
74
  const lines = [
@@ -112,17 +113,20 @@ function runRuleCheck(rule, ctx) {
112
113
  return [new types_1.Violation(0, '', `Rule '${rule.name}' crashed: ${error.message}`)];
113
114
  }
114
115
  }
115
- function runBashRules(rules, bashContext, config) {
116
+ function ruleMatchesFile(rule, relativePath) {
117
+ for (const pattern of rule.files) {
118
+ if ((0, load_rules_1.globMatches)(pattern, relativePath))
119
+ return true;
120
+ }
121
+ return false;
122
+ }
123
+ function runBashRules(rules, bashContext) {
116
124
  const groups = [];
117
125
  for (const rule of rules) {
118
126
  if (rule.scope !== 'bash')
119
127
  continue;
120
- const ruleConfig = config.rules.get(rule.name);
121
- if (!ruleConfig || ruleConfig.isOff)
122
- continue;
123
- if (isRuleSkipped(ruleConfig))
128
+ if (!rule.shouldRun())
124
129
  continue;
125
- bashContext.options = mergeOptions(rule.defaultOptions, ruleConfig);
126
130
  const vs = runRuleCheck(rule, bashContext);
127
131
  if (vs.length > 0) {
128
132
  groups.push(new types_1.RuleGroup(rule.name, rule.description, [...rule.fixHint], [...vs]));
@@ -130,46 +134,17 @@ function runBashRules(rules, bashContext, config) {
130
134
  }
131
135
  return groups;
132
136
  }
133
- // Every rule honors the universal escape hatches: skip while on a named branch
134
- // (ignoreRuleWhileOnBranch) or until an epoch passes (ignoreModifiedUntilEpoch).
135
- function isRuleSkipped(ruleConfig) {
136
- return (0, rules_config_1.shouldSkipRule)(ruleConfig.options['ignoreModifiedUntilEpoch'], ruleConfig.options['ignoreRuleWhileOnBranch']).skip;
137
- }
138
- function ruleMatchesFile(rule, relativePath) {
139
- for (const pattern of rule.files) {
140
- if ((0, load_rules_1.globMatches)(pattern, relativePath))
141
- return true;
142
- }
143
- return false;
144
- }
145
- function mergeOptions(defaultOptions, ruleConfig) {
146
- // webpieces-disable no-any-unknown -- building an options bag from opaque RuleOptions
147
- const out = {};
148
- for (const key of Object.keys(defaultOptions))
149
- out[key] = defaultOptions[key];
150
- for (const key of Object.keys(ruleConfig.options)) {
151
- // 'mode' is the framework-level on/off switch, not a rule option.
152
- if (key === 'mode')
153
- continue;
154
- out[key] = ruleConfig.options[key];
155
- }
156
- return out;
157
- }
158
- function runEditRules(rules, editContexts, config) {
137
+ function runEditRules(rules, editContexts) {
159
138
  const groups = [];
160
139
  for (const rule of rules) {
161
140
  if (rule.scope !== 'edit')
162
141
  continue;
163
- const ruleConfig = config.rules.get(rule.name);
164
- if (!ruleConfig || ruleConfig.isOff)
165
- continue;
166
- if (isRuleSkipped(ruleConfig))
142
+ if (!rule.shouldRun())
167
143
  continue;
168
144
  const allViolations = [];
169
145
  for (const ctx of editContexts) {
170
146
  if (!ruleMatchesFile(rule, ctx.relativePath))
171
147
  continue;
172
- ctx.options = mergeOptions(rule.defaultOptions, ruleConfig);
173
148
  const vs = runRuleCheck(rule, ctx);
174
149
  for (const v of vs) {
175
150
  const copy = new types_1.Violation(v.line, v.snippet, v.message);
@@ -184,19 +159,15 @@ function runEditRules(rules, editContexts, config) {
184
159
  }
185
160
  return groups;
186
161
  }
187
- function runFileRules(rules, fileContext, config) {
162
+ function runFileRules(rules, fileContext) {
188
163
  const groups = [];
189
164
  for (const rule of rules) {
190
165
  if (rule.scope !== 'file')
191
166
  continue;
192
- const ruleConfig = config.rules.get(rule.name);
193
- if (!ruleConfig || ruleConfig.isOff)
194
- continue;
195
- if (isRuleSkipped(ruleConfig))
167
+ if (!rule.shouldRun())
196
168
  continue;
197
169
  if (!ruleMatchesFile(rule, fileContext.relativePath))
198
170
  continue;
199
- fileContext.options = mergeOptions(rule.defaultOptions, ruleConfig);
200
171
  const vs = runRuleCheck(rule, fileContext);
201
172
  if (vs.length > 0) {
202
173
  groups.push(new types_1.RuleGroup(rule.name, rule.description, [...rule.fixHint], [...vs]));
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\n\nimport { shouldSkipRule } from '@webpieces/rules-config';\n\nimport { buildContexts, buildBashContext } from './build-context';\nimport { loadConfig } from './load-config';\nimport { loadRules, globMatches } from './load-rules';\nimport { toError } from './to-error';\nimport { formatReport } from './report';\nimport {\n ToolKind, NormalizedToolInput, BlockedResult,\n Rule, EditRule, FileRule, BashRule, Violation, RuleGroup,\n EditContext, FileContext, BashContext,\n ResolvedConfig, ResolvedRuleConfig, RuleOptions,\n} from './types';\n\nexport function run(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n cwd: string,\n): BlockedResult | null {\n return runInternal(toolKind, input, cwd);\n}\n\nfunction runInternal(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n cwd: string,\n): BlockedResult | null {\n const config = loadConfig(cwd);\n if (!config.configPath) {\n return new BlockedResult(\n 'webpieces.config.json not found.\\n' +\n 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\\n' +\n 'Do not proceed until the human has done this.',\n );\n }\n\n const workspaceRoot = path.dirname(config.configPath);\n\n // Always allow edits to webpieces.config.json — it's the fix target when out of sync\n if (path.resolve(input.filePath) === path.resolve(config.configPath)) {\n return null;\n }\n\n const rules = loadRules(config, workspaceRoot);\n if (rules.length === 0) return null;\n\n const outOfSync = checkConfigSync(rules, config);\n if (outOfSync) return outOfSync;\n\n const contexts = buildContexts(toolKind, input, workspaceRoot);\n const relativePath = path.relative(workspaceRoot, input.filePath);\n\n const editGroups = runEditRules(rules, contexts.editContexts, config);\n const fileGroups = runFileRules(rules, contexts.fileContext, config);\n const allGroups = [...editGroups, ...fileGroups];\n\n if (allGroups.length === 0) return null;\n\n const report = formatReport(relativePath, allGroups);\n return new BlockedResult(report);\n}\n\nexport function runBash(command: string, cwd: string): BlockedResult | null {\n return runBashInternal(command, cwd);\n}\n\nfunction runBashInternal(command: string, cwd: string): BlockedResult | null {\n const config = loadConfig(cwd);\n if (!config.configPath) {\n return new BlockedResult(\n 'webpieces.config.json not found.\\n' +\n 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\\n' +\n 'Do not proceed until the human has done this.',\n );\n }\n\n const workspaceRoot = path.dirname(config.configPath);\n const rules = loadRules(config, workspaceRoot);\n if (rules.length === 0) return null;\n\n const outOfSync = checkConfigSync(rules, config);\n if (outOfSync) return outOfSync;\n\n const ctx = buildBashContext(command, workspaceRoot);\n const groups = runBashRules(rules, ctx, config);\n if (groups.length === 0) return null;\n\n const report = formatReport('<bash>', groups);\n return new BlockedResult(report);\n}\n\nfunction checkConfigSync(rules: readonly Rule[], config: ResolvedConfig): BlockedResult | null {\n const unconfiguredRules = rules.filter((r: Rule) => !config.userConfiguredRuleNames.has(r.name));\n if (unconfiguredRules.length === 0) return null;\n\n const lines = [\n 'webpieces.config.json is out of sync — new built-in rules are present that have no entry in webpieces.config.json.',\n '',\n 'Tell the human: the following rules need to be configured. Ask for each one:',\n ' - Should this rule be ON, OFF, MODIFIED_CODE, or MODIFIED_FILES?',\n ' - What values do you want for the options listed below?',\n 'Then update webpieces.config.json and retry.',\n '',\n 'Do NOT proceed until webpieces.config.json has an entry for every rule below.',\n '',\n ];\n\n for (const rule of unconfiguredRules) {\n lines.push(`--- ${rule.name} ---`);\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 lines.push(`Example entry for webpieces.config.json:`);\n lines.push(` \"${rule.name}\": { \"mode\": \"ON\" }`);\n lines.push('');\n }\n\n return new BlockedResult(lines.join('\\n'));\n}\n\n// N-legs pattern: each rule runs independently; crash → visible violation so AI sees it, not silent []\nfunction runRuleCheck(rule: Rule, ctx: EditContext | FileContext | BashContext): readonly Violation[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return (rule as EditRule | FileRule | BashRule).check(ctx as never);\n } catch (err: unknown) {\n const error = toError(err);\n return [new Violation(0, '', `Rule '${rule.name}' crashed: ${error.message}`)];\n }\n}\n\nfunction runBashRules(\n rules: readonly Rule[],\n bashContext: BashContext,\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'bash') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n if (isRuleSkipped(ruleConfig)) continue;\n bashContext.options = mergeOptions(rule.defaultOptions, ruleConfig);\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// Every rule honors the universal escape hatches: skip while on a named branch\n// (ignoreRuleWhileOnBranch) or until an epoch passes (ignoreModifiedUntilEpoch).\nfunction isRuleSkipped(ruleConfig: ResolvedRuleConfig): boolean {\n return shouldSkipRule(\n ruleConfig.options['ignoreModifiedUntilEpoch'] as number | undefined,\n ruleConfig.options['ignoreRuleWhileOnBranch'] as string | undefined,\n ).skip;\n}\n\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\nfunction mergeOptions(defaultOptions: RuleOptions, ruleConfig: ResolvedRuleConfig): RuleOptions {\n // webpieces-disable no-any-unknown -- building an options bag from opaque RuleOptions\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(defaultOptions)) out[key] = defaultOptions[key];\n for (const key of Object.keys(ruleConfig.options)) {\n // 'mode' is the framework-level on/off switch, not a rule option.\n if (key === 'mode') continue;\n out[key] = ruleConfig.options[key];\n }\n return out;\n}\n\nfunction runEditRules(\n rules: readonly Rule[],\n editContexts: readonly EditContext[],\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'edit') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n if (isRuleSkipped(ruleConfig)) continue;\n const allViolations: Violation[] = [];\n for (const ctx of editContexts) {\n if (!ruleMatchesFile(rule, ctx.relativePath)) continue;\n ctx.options = mergeOptions(rule.defaultOptions, ruleConfig);\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\nfunction runFileRules(\n rules: readonly Rule[],\n fileContext: FileContext,\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'file') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n if (isRuleSkipped(ruleConfig)) continue;\n if (!ruleMatchesFile(rule, fileContext.relativePath)) continue;\n fileContext.options = mergeOptions(rule.defaultOptions, ruleConfig);\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":"runner.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\n\nimport { loadWebpiecesRulesConfig, WebpiecesRulesConfig } from '@webpieces/rules-config';\n\nimport { buildContexts, buildBashContext } from './build-context';\nimport { loadRules, globMatches } from './load-rules';\nimport { toError } from './to-error';\nimport { formatReport } from './report';\nimport {\n ToolKind, NormalizedToolInput, BlockedResult,\n Rule, Violation, RuleGroup,\n EditContext, FileContext, BashContext,\n} from './types';\n\nconst CONFIG_MISSING_REPORT =\n 'webpieces.config.json not found.\\n' +\n 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\\n' +\n 'Do not proceed until the human has done this.';\n\nexport function run(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n cwd: string,\n): BlockedResult | null {\n return runInternal(toolKind, input, cwd);\n}\n\nfunction runInternal(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n cwd: string,\n): BlockedResult | null {\n const loaded = loadWebpiecesRulesConfig(cwd);\n if (!loaded) return new BlockedResult(CONFIG_MISSING_REPORT);\n\n const workspaceRoot = path.dirname(loaded.configPath);\n\n // Always allow edits to webpieces.config.json — it's the fix target when out of sync\n if (path.resolve(input.filePath) === path.resolve(loaded.configPath)) {\n return null;\n }\n\n const rules = loadRules(loaded.config, workspaceRoot);\n if (rules.length === 0) return null;\n\n const outOfSync = checkConfigSync(rules, loaded.config);\n if (outOfSync) return outOfSync;\n\n const contexts = buildContexts(toolKind, input, workspaceRoot);\n const relativePath = path.relative(workspaceRoot, input.filePath);\n\n const editGroups = runEditRules(rules, contexts.editContexts);\n const fileGroups = runFileRules(rules, contexts.fileContext);\n const allGroups = [...editGroups, ...fileGroups];\n\n if (allGroups.length === 0) return null;\n\n const report = formatReport(relativePath, allGroups);\n return new BlockedResult(report);\n}\n\nexport function runBash(command: string, cwd: string): BlockedResult | null {\n return runBashInternal(command, cwd);\n}\n\nfunction runBashInternal(command: string, cwd: string): BlockedResult | null {\n const loaded = loadWebpiecesRulesConfig(cwd);\n if (!loaded) return new BlockedResult(CONFIG_MISSING_REPORT);\n\n const workspaceRoot = path.dirname(loaded.configPath);\n const rules = loadRules(loaded.config, workspaceRoot);\n if (rules.length === 0) return null;\n\n const outOfSync = checkConfigSync(rules, loaded.config);\n if (outOfSync) return outOfSync;\n\n const ctx = buildBashContext(command, workspaceRoot);\n const groups = runBashRules(rules, ctx);\n if (groups.length === 0) return null;\n\n const report = formatReport('<bash>', groups);\n return new BlockedResult(report);\n}\n\n// The set of rule names explicitly present in webpieces.config.json (every key except rulesDir).\nfunction configuredRuleNames(config: WebpiecesRulesConfig): ReadonlySet<string> {\n return new Set(Object.keys(config).filter((k: string) => k !== 'rulesDir'));\n}\n\nfunction checkConfigSync(rules: readonly Rule[], config: WebpiecesRulesConfig): BlockedResult | null {\n const configured = configuredRuleNames(config);\n const unconfiguredRules = rules.filter((r: Rule) => !configured.has(r.name));\n if (unconfiguredRules.length === 0) return null;\n\n const lines = [\n 'webpieces.config.json is out of sync — new built-in rules are present that have no entry in webpieces.config.json.',\n '',\n 'Tell the human: the following rules need to be configured. Ask for each one:',\n ' - Should this rule be ON, OFF, MODIFIED_CODE, or MODIFIED_FILES?',\n ' - What values do you want for the options listed below?',\n 'Then update webpieces.config.json and retry.',\n '',\n 'Do NOT proceed until webpieces.config.json has an entry for every rule below.',\n '',\n ];\n\n for (const rule of unconfiguredRules) {\n lines.push(`--- ${rule.name} ---`);\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 lines.push(`Example entry for webpieces.config.json:`);\n lines.push(` \"${rule.name}\": { \"mode\": \"ON\" }`);\n lines.push('');\n }\n\n return new BlockedResult(lines.join('\\n'));\n}\n\n// N-legs pattern: each rule runs independently; crash → visible violation so AI sees it, not silent []\nfunction 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 return [new Violation(0, '', `Rule '${rule.name}' crashed: ${error.message}`)];\n }\n}\n\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\nfunction 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\nfunction 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\nfunction 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,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toError = toError;
4
- // ai-hook-disable-file require-return-type -- toError is a utility copied from dev-config; function return type is on line 6
5
4
  /**
6
5
  * Lightweight duplicate of @webpieces/core-util toError.
7
6
  * ai-hooks is a standalone package and cannot depend on core-util or dev-config.
@@ -1 +1 @@
1
- {"version":3,"file":"to-error.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/to-error.ts"],"names":[],"mappings":";;AAMA,0BA4BC;AAlCD,6HAA6H;AAC7H;;;GAGG;AACH,wHAAwH;AACxH,SAAgB,OAAO,CAAC,GAAY;IAChC,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC;IACf,CAAC;IAED,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACjC,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YAC7C,IAAI,OAAO,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAClD,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;YAC5B,CAAC;YACD,IAAI,MAAM,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAChD,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YAC1B,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,8DAA8D;QAC9D,IAAI,CAAC;YACD,OAAO,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,6BAA6B;YAC7B,KAAK,GAAG,CAAC;YACT,OAAO,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvE,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC","sourcesContent":["// ai-hook-disable-file require-return-type -- toError is a utility copied from dev-config; function return type is on line 6\n/**\n * Lightweight duplicate of @webpieces/core-util toError.\n * ai-hooks is a standalone package and cannot depend on core-util or dev-config.\n */\n// webpieces-disable no-any-unknown -- toError intentionally accepts unknown to safely convert any thrown value to Error\nexport function toError(err: unknown): Error {\n if (err instanceof Error) {\n return err;\n }\n\n if (err && typeof err === 'object') {\n if ('message' in err) {\n const error = new Error(String(err.message));\n if ('stack' in err && typeof err.stack === 'string') {\n error.stack = err.stack;\n }\n if ('name' in err && typeof err.name === 'string') {\n error.name = err.name;\n }\n return error;\n }\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return new Error(`Non-Error object thrown: ${JSON.stringify(err)}`);\n } catch (err: unknown) {\n //const error = toError(err);\n void err;\n return new Error('Non-Error object thrown (unable to stringify)');\n }\n }\n\n const message = err == null ? 'Null or undefined thrown' : String(err);\n return new Error(message);\n}\n"]}
1
+ {"version":3,"file":"to-error.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/to-error.ts"],"names":[],"mappings":";;AAKA,0BA4BC;AAjCD;;;GAGG;AACH,wHAAwH;AACxH,SAAgB,OAAO,CAAC,GAAY;IAChC,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC;IACf,CAAC;IAED,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACjC,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YAC7C,IAAI,OAAO,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAClD,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;YAC5B,CAAC;YACD,IAAI,MAAM,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAChD,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YAC1B,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,8DAA8D;QAC9D,IAAI,CAAC;YACD,OAAO,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,6BAA6B;YAC7B,KAAK,GAAG,CAAC;YACT,OAAO,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvE,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC","sourcesContent":["/**\n * Lightweight duplicate of @webpieces/core-util toError.\n * ai-hooks is a standalone package and cannot depend on core-util or dev-config.\n */\n// webpieces-disable no-any-unknown -- toError intentionally accepts unknown to safely convert any thrown value to Error\nexport function toError(err: unknown): Error {\n if (err instanceof Error) {\n return err;\n }\n\n if (err && typeof err === 'object') {\n if ('message' in err) {\n const error = new Error(String(err.message));\n if ('stack' in err && typeof err.stack === 'string') {\n error.stack = err.stack;\n }\n if ('name' in err && typeof err.name === 'string') {\n error.name = err.name;\n }\n return error;\n }\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return new Error(`Non-Error object thrown: ${JSON.stringify(err)}`);\n } catch (err: unknown) {\n //const error = toError(err);\n void err;\n return new Error('Non-Error object thrown (unable to stringify)');\n }\n }\n\n const message = err == null ? 'Null or undefined thrown' : String(err);\n return new Error(message);\n}\n"]}
@@ -60,26 +60,27 @@ export declare class FileContext {
60
60
  options: RuleOptions;
61
61
  constructor(tool: ToolKind, filePath: string, relativePath: string, workspaceRoot: string, currentFileLines: number, linesAdded: number, linesRemoved: number, projectedFileLines: number);
62
62
  }
63
- interface RuleBase {
63
+ /**
64
+ * The shape of a custom rule loaded from a `rulesDir` (a plain object returned by require()).
65
+ * It carries only metadata + a `check` method — no on/off logic of its own.
66
+ */
67
+ export interface PlainRule {
64
68
  readonly name: string;
65
69
  readonly description: string;
70
+ readonly scope: RuleScope;
66
71
  readonly files: readonly string[];
67
72
  readonly defaultOptions: RuleOptions;
68
73
  readonly fixHint: readonly string[];
74
+ check(ctx: EditContext | FileContext | BashContext): readonly Violation[];
69
75
  }
70
- export interface EditRule extends RuleBase {
71
- readonly scope: 'edit';
72
- check(ctx: EditContext): readonly Violation[];
76
+ /**
77
+ * The runtime contract the runner iterates. Both the built-in rule classes (which extend
78
+ * EditRuleBase/FileRuleBase/BashRuleBase) and the custom-rule adapter satisfy this: they add
79
+ * `shouldRun()` (mode + escape-hatch decision) on top of the PlainRule metadata.
80
+ */
81
+ export interface Rule extends PlainRule {
82
+ shouldRun(): boolean;
73
83
  }
74
- export interface FileRule extends RuleBase {
75
- readonly scope: 'file';
76
- check(ctx: FileContext): readonly Violation[];
77
- }
78
- export interface BashRule extends RuleBase {
79
- readonly scope: 'bash';
80
- check(ctx: BashContext): readonly Violation[];
81
- }
82
- export type Rule = EditRule | FileRule | BashRule;
83
84
  export declare class RuleGroup {
84
85
  readonly ruleName: string;
85
86
  readonly ruleDescription: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/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';\nexport { ResolvedConfig, ResolvedRuleConfig, RuleOptions, InformAiError } from '@webpieces/rules-config';\n\nexport type ToolKind = 'Write' | 'Edit' | 'MultiEdit';\nexport type RuleScope = 'edit' | 'file' | 'bash';\nexport type IsLineDisabled = (lineNum: number, ruleName: string) => boolean;\n\nexport class Violation {\n readonly line: number;\n readonly snippet: string;\n readonly message: string;\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 NormalizedEdit {\n readonly oldString: string;\n readonly newString: string;\n\n constructor(oldString: string, newString: string) {\n this.oldString = oldString;\n this.newString = newString;\n }\n}\n\nexport class NormalizedToolInput {\n readonly filePath: string;\n readonly edits: readonly NormalizedEdit[];\n\n constructor(filePath: string, edits: readonly NormalizedEdit[]) {\n this.filePath = filePath;\n this.edits = edits;\n }\n}\n\nexport class NormalizedBashInput {\n readonly command: string;\n\n constructor(command: string) {\n this.command = command;\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\nexport class BashContext {\n readonly tool: 'Bash';\n readonly command: string;\n readonly workspaceRoot: string;\n options: RuleOptions;\n\n constructor(command: string, workspaceRoot: string) {\n this.tool = 'Bash';\n this.command = command;\n this.workspaceRoot = workspaceRoot;\n this.options = {};\n }\n}\n\nexport class FileContext {\n readonly tool: ToolKind;\n readonly filePath: string;\n readonly relativePath: string;\n readonly workspaceRoot: 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 ) {\n this.tool = tool;\n this.filePath = filePath;\n this.relativePath = relativePath;\n this.workspaceRoot = workspaceRoot;\n this.currentFileLines = currentFileLines;\n this.linesAdded = linesAdded;\n this.linesRemoved = linesRemoved;\n this.projectedFileLines = projectedFileLines;\n this.options = {};\n }\n}\n\ninterface RuleBase {\n readonly name: string;\n readonly description: string;\n readonly files: readonly string[];\n readonly defaultOptions: RuleOptions;\n readonly fixHint: readonly string[];\n}\n\nexport interface EditRule extends RuleBase {\n readonly scope: 'edit';\n check(ctx: EditContext): readonly Violation[];\n}\n\nexport interface FileRule extends RuleBase {\n readonly scope: 'file';\n check(ctx: FileContext): readonly Violation[];\n}\n\nexport interface BashRule extends RuleBase {\n readonly scope: 'bash';\n check(ctx: BashContext): readonly Violation[];\n}\n\nexport type Rule = EditRule | FileRule | BashRule;\n\nexport class RuleGroup {\n readonly ruleName: string;\n readonly ruleDescription: string;\n readonly fixHint: readonly string[];\n readonly violations: readonly Violation[];\n\n constructor(\n ruleName: string,\n ruleDescription: string,\n fixHint: readonly string[],\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 constructor(report: string) {\n this.report = report;\n }\n}\n\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/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';\nexport { ResolvedConfig, ResolvedRuleConfig, RuleOptions, InformAiError } from '@webpieces/rules-config';\n\nexport type ToolKind = 'Write' | 'Edit' | 'MultiEdit';\nexport type RuleScope = 'edit' | 'file' | 'bash';\nexport type IsLineDisabled = (lineNum: number, ruleName: string) => boolean;\n\nexport class Violation {\n readonly line: number;\n readonly snippet: string;\n readonly message: string;\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 NormalizedEdit {\n readonly oldString: string;\n readonly newString: string;\n\n constructor(oldString: string, newString: string) {\n this.oldString = oldString;\n this.newString = newString;\n }\n}\n\nexport class NormalizedToolInput {\n readonly filePath: string;\n readonly edits: readonly NormalizedEdit[];\n\n constructor(filePath: string, edits: readonly NormalizedEdit[]) {\n this.filePath = filePath;\n this.edits = edits;\n }\n}\n\nexport class NormalizedBashInput {\n readonly command: string;\n\n constructor(command: string) {\n this.command = command;\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\nexport class BashContext {\n readonly tool: 'Bash';\n readonly command: string;\n readonly workspaceRoot: string;\n options: RuleOptions;\n\n constructor(command: string, workspaceRoot: string) {\n this.tool = 'Bash';\n this.command = command;\n this.workspaceRoot = workspaceRoot;\n this.options = {};\n }\n}\n\nexport class FileContext {\n readonly tool: ToolKind;\n readonly filePath: string;\n readonly relativePath: string;\n readonly workspaceRoot: 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 ) {\n this.tool = tool;\n this.filePath = filePath;\n this.relativePath = relativePath;\n this.workspaceRoot = workspaceRoot;\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: readonly string[];\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 shouldRun(): boolean;\n}\n\nexport class RuleGroup {\n readonly ruleName: string;\n readonly ruleDescription: string;\n readonly fixHint: readonly string[];\n readonly violations: readonly Violation[];\n\n constructor(\n ruleName: string,\n ruleDescription: string,\n fixHint: readonly string[],\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 constructor(report: string) {\n this.report = report;\n }\n}\n\n"]}
package/src/index.d.ts CHANGED
@@ -1,4 +1,24 @@
1
- export { ToolKind, RuleScope, RuleOptions, IsLineDisabled, Violation, NormalizedEdit, NormalizedToolInput, EditContext, FileContext, Rule, EditRule, FileRule, RuleGroup, BlockedResult, ResolvedConfig, ResolvedRuleConfig, } from './core/types';
1
+ export { ToolKind, RuleScope, RuleOptions, IsLineDisabled, Violation, NormalizedEdit, NormalizedToolInput, EditContext, FileContext, BashContext, Rule, PlainRule, RuleGroup, BlockedResult, ResolvedConfig, ResolvedRuleConfig, } from './core/types';
2
+ export { EditRuleBase, FileRuleBase, BashRuleBase, EmptyRuleConfig } from './core/rule-base';
3
+ export { CustomRuleAdapter } from './core/custom-rule-adapter';
4
+ export { NoAnyUnknownRule } from './core/rules/no-any-unknown';
5
+ export { NoImplicitAnyRule } from './core/rules/no-implicit-any';
6
+ export { MaxFileLinesRule } from './core/rules/max-file-lines';
7
+ export { ValidateTsInSrcRule } from './core/rules/validate-ts-in-src';
8
+ export { NoDestructureRule } from './core/rules/no-destructure';
9
+ export { RequireReturnTypeRule } from './core/rules/require-return-type';
10
+ export { NoUnmanagedExceptionsRule } from './core/rules/no-unmanaged-exceptions';
11
+ export { CatchErrorPatternRule } from './core/rules/catch-error-pattern';
12
+ export { ThrowCauseRequiredRule } from './core/rules/throw-cause-required';
13
+ export { NoShellSubstitutionRule } from './core/rules/no-shell-substitution';
14
+ export { NoSymbolDiTokensRule } from './core/rules/no-symbol-di-tokens';
15
+ export { BranchCreationGuardRule } from './core/rules/branch-creation-guard';
16
+ export { PrCreationGuardRule } from './core/rules/pr-creation-guard';
17
+ export { MergeInProgressGuardRule } from './core/rules/merge-in-progress-guard';
18
+ export { PrMergeCleanupRule } from './core/rules/pr-merge-cleanup';
19
+ export { NoDirectMainUpdateRule } from './core/rules/no-direct-main-update';
20
+ export { NoJsFilesRule } from './core/rules/no-js-files';
21
+ export { NoEditOnMainRule } from './core/rules/no-edit-on-main';
2
22
  export { run } from './core/runner';
3
23
  export { stripTsNoise } from './core/strip-ts-noise';
4
24
  export { parseDirectives, DirectiveIndex, createIsLineDisabled } from './core/disable-directives';