@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.NoSymbolDiTokensRule = 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 SYMBOL_DI_REGEX = /=\s*Symbol(?:\.for)?\(/;
5
8
  const TEST_PATHS = [/\.test\.ts$/, /\.spec\.ts$/, /__tests__\//];
6
9
  function globToRegex(pattern) {
@@ -40,20 +43,19 @@ function isAllowedPath(relativePath, allowedPaths) {
40
43
  return true;
41
44
  return allowedPaths.some((pattern) => globToRegex(pattern).test(relativePath));
42
45
  }
43
- const noSymbolDiTokensRule = {
44
- name: 'no-symbol-di-tokens',
45
- description: 'Disallow Symbol() DI tokens outside explicitly configured paths. Use @provideSingleton() + inject-by-type instead.',
46
- scope: 'edit',
47
- files: ['**/*.ts', '**/*.tsx'],
48
- defaultOptions: { allowedPaths: [] },
49
- fixHint: [
46
+ class NoSymbolDiTokensRule extends rule_base_1.EditRuleBase {
47
+ constructor(config) { super(config, 'no-symbol-di-tokens'); }
48
+ description = 'Disallow Symbol() DI tokens outside explicitly configured paths. Use @provideSingleton() + inject-by-type instead.';
49
+ files = ['**/*.ts', '**/*.tsx'];
50
+ defaultOptions = { allowedPaths: [] };
51
+ fixHint = [
50
52
  'Option 1: Use @provideSingleton() on the class and inject by type — no Symbol needed.',
51
53
  'Option 2: Implement an API interface — import the Symbol from the API definition and use @provideSingletonAs(TOKEN).',
52
54
  'Option 3: External lib class (DataSource, Anthropic, etc.) — bind<Cls>(Cls).toDynamicValue(...).inSingletonScope() — no Symbol.',
53
55
  'Option 4 (last resort): // webpieces-disable no-symbol-di-tokens -- <reason>',
54
- ],
56
+ ];
55
57
  check(ctx) {
56
- const allowedPaths = ctx.options['allowedPaths'] ?? [];
58
+ const allowedPaths = this.config.allowedPaths ?? [];
57
59
  if (isAllowedPath(ctx.relativePath, allowedPaths))
58
60
  return [];
59
61
  const violations = [];
@@ -62,12 +64,12 @@ const noSymbolDiTokensRule = {
62
64
  if (!SYMBOL_DI_REGEX.test(stripped ?? ''))
63
65
  continue;
64
66
  const lineNum = i + 1;
65
- if (ctx.isLineDisabled(lineNum, 'no-symbol-di-tokens'))
67
+ if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.NO_SYMBOL_DI_TOKENS))
66
68
  continue;
67
69
  violations.push(new types_1.Violation(lineNum, ctx.lines[i]?.trim() ?? '', 'Symbol() used as a DI token. Mostly we avoid Symbol if we can — see fix options below.'));
68
70
  }
69
71
  return violations;
70
- },
71
- };
72
- exports.default = noSymbolDiTokensRule;
72
+ }
73
+ }
74
+ exports.NoSymbolDiTokensRule = NoSymbolDiTokensRule;
73
75
  //# sourceMappingURL=no-symbol-di-tokens.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-symbol-di-tokens.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-symbol-di-tokens.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst SYMBOL_DI_REGEX = /=\\s*Symbol(?:\\.for)?\\(/;\n\nconst TEST_PATHS: RegExp[] = [/\\.test\\.ts$/, /\\.spec\\.ts$/, /__tests__\\//];\n\nfunction globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*') {\n if (pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n}\n\nfunction isAllowedPath(relativePath: string, allowedPaths: readonly string[]): boolean {\n if (TEST_PATHS.some((re: RegExp) => re.test(relativePath))) return true;\n return allowedPaths.some((pattern: string) => globToRegex(pattern).test(relativePath));\n}\n\nconst noSymbolDiTokensRule: EditRule = {\n name: 'no-symbol-di-tokens',\n description: 'Disallow Symbol() DI tokens outside explicitly configured paths. Use @provideSingleton() + inject-by-type instead.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: { allowedPaths: [] },\n fixHint: [\n 'Option 1: Use @provideSingleton() on the class and inject by type — no Symbol needed.',\n 'Option 2: Implement an API interface — import the Symbol from the API definition and use @provideSingletonAs(TOKEN).',\n 'Option 3: External lib class (DataSource, Anthropic, etc.) — bind<Cls>(Cls).toDynamicValue(...).inSingletonScope() — no Symbol.',\n 'Option 4 (last resort): // webpieces-disable no-symbol-di-tokens -- <reason>',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const allowedPaths = (ctx.options['allowedPaths'] as string[] | undefined) ?? [];\n if (isAllowedPath(ctx.relativePath, allowedPaths)) return [];\n\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!SYMBOL_DI_REGEX.test(stripped ?? '')) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, 'no-symbol-di-tokens')) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i]?.trim() ?? '',\n 'Symbol() used as a DI token. Mostly we avoid Symbol if we can — see fix options below.',\n ));\n }\n return violations;\n },\n};\n\nexport default noSymbolDiTokensRule;\n"]}
1
+ {"version":3,"file":"no-symbol-di-tokens.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-symbol-di-tokens.ts"],"names":[],"mappings":"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","sourcesContent":["import { NoSymbolDiTokensConfig, RULE_NAMES } from '@webpieces/rules-config';\n\nimport type { EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { EditRuleBase } from '../rule-base';\n\nconst SYMBOL_DI_REGEX = /=\\s*Symbol(?:\\.for)?\\(/;\n\nconst TEST_PATHS: RegExp[] = [/\\.test\\.ts$/, /\\.spec\\.ts$/, /__tests__\\//];\n\nfunction globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*') {\n if (pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n}\n\nfunction isAllowedPath(relativePath: string, allowedPaths: readonly string[]): boolean {\n if (TEST_PATHS.some((re: RegExp) => re.test(relativePath))) return true;\n return allowedPaths.some((pattern: string) => globToRegex(pattern).test(relativePath));\n}\n\nexport class NoSymbolDiTokensRule extends EditRuleBase<NoSymbolDiTokensConfig> {\n constructor(config: NoSymbolDiTokensConfig) { super(config, 'no-symbol-di-tokens'); }\n\n readonly description = 'Disallow Symbol() DI tokens outside explicitly configured paths. Use @provideSingleton() + inject-by-type instead.';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n override readonly defaultOptions = { allowedPaths: [] };\n readonly fixHint = [\n 'Option 1: Use @provideSingleton() on the class and inject by type — no Symbol needed.',\n 'Option 2: Implement an API interface — import the Symbol from the API definition and use @provideSingletonAs(TOKEN).',\n 'Option 3: External lib class (DataSource, Anthropic, etc.) — bind<Cls>(Cls).toDynamicValue(...).inSingletonScope() — no Symbol.',\n 'Option 4 (last resort): // webpieces-disable no-symbol-di-tokens -- <reason>',\n ];\n\n check(ctx: EditContext): readonly Violation[] {\n const allowedPaths = this.config.allowedPaths ?? [];\n if (isAllowedPath(ctx.relativePath, allowedPaths)) return [];\n\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!SYMBOL_DI_REGEX.test(stripped ?? '')) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.NO_SYMBOL_DI_TOKENS)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i]?.trim() ?? '',\n 'Symbol() used as a DI token. Mostly we avoid Symbol if we can — see fix options below.',\n ));\n }\n return violations;\n }\n}\n"]}
@@ -1,3 +1,10 @@
1
- import type { EditRule } from '../types';
2
- declare const noUnmanagedExceptionsRule: EditRule;
3
- export default noUnmanagedExceptionsRule;
1
+ import { NoUnmanagedExceptionsConfig } from '@webpieces/rules-config';
2
+ import type { EditContext, Violation } from '../types';
3
+ import { EditRuleBase } from '../rule-base';
4
+ export declare class NoUnmanagedExceptionsRule extends EditRuleBase<NoUnmanagedExceptionsConfig> {
5
+ constructor(config: NoUnmanagedExceptionsConfig);
6
+ readonly description = "try/catch is generally not allowed. Only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.";
7
+ readonly files: string[];
8
+ readonly fixHint: string[];
9
+ check(ctx: EditContext): readonly Violation[];
10
+ }
@@ -1,17 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoUnmanagedExceptionsRule = 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
  const TRY_PATTERN = /\btry\s*\{/;
6
9
  // Both webpieces-disable and the existing ESLint directive suppress this rule
7
10
  const DISABLE_PATTERN = /@webpieces\/no-unmanaged-exceptions|webpieces-disable\s+(?:[\w-]+,\s*)*no-unmanaged-exceptions/;
8
- const noUnmanagedExceptionsRule = {
9
- name: 'no-unmanaged-exceptions',
10
- description: 'try/catch is generally not allowed. Only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.',
11
- scope: 'edit',
12
- files: ['**/*.ts', '**/*.tsx'],
13
- defaultOptions: {},
14
- fixHint: [
11
+ function hasPrecedingDisable(lines, idx) {
12
+ if (idx === 0)
13
+ return false;
14
+ const prevLine = lines[idx - 1];
15
+ return DISABLE_PATTERN.test(prevLine);
16
+ }
17
+ class NoUnmanagedExceptionsRule extends rule_base_1.EditRuleBase {
18
+ constructor(config) { super(config, 'no-unmanaged-exceptions'); }
19
+ description = 'try/catch is generally not allowed. Only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.';
20
+ files = ['**/*.ts', '**/*.tsx'];
21
+ fixHint = [
15
22
  'Fix Option 1 (preferred): Remove the try/catch — let the exception bubble to the top-level chokepoint (filter, globalErrorHandler) where it is already logged and handled.',
16
23
  'Fix Option 2 (ask the human first): If you genuinely believe this IS a chokepoint, STOP and tell the human:',
17
24
  ' - What exception could be thrown here',
@@ -20,7 +27,7 @@ const noUnmanagedExceptionsRule = {
20
27
  ' Then ask: "Should I add a disable comment or remove the try/catch?"',
21
28
  ' Only add // webpieces-disable no-unmanaged-exceptions -- <reason> if the human says yes.',
22
29
  'NOTE: If the code is calling an external process (execSync, fs, network), the correct answer is almost always Option 1 — let it throw. Hooks have a top-level runner that reports errors properly.',
23
- ],
30
+ ];
24
31
  check(ctx) {
25
32
  const violations = [];
26
33
  for (let i = 0; i < ctx.strippedLines.length; i += 1) {
@@ -28,7 +35,7 @@ const noUnmanagedExceptionsRule = {
28
35
  if (!TRY_PATTERN.test(stripped))
29
36
  continue;
30
37
  const lineNum = i + 1;
31
- if (ctx.isLineDisabled(lineNum, 'no-unmanaged-exceptions'))
38
+ if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.NO_UNMANAGED_EXCEPTIONS))
32
39
  continue;
33
40
  if (hasPrecedingDisable(ctx.lines, i))
34
41
  continue;
@@ -37,13 +44,7 @@ const noUnmanagedExceptionsRule = {
37
44
  if (violations.length > 0)
38
45
  (0, instruct_ai_writer_1.writeTemplateIfMissing)(ctx.workspaceRoot, 'webpieces.exceptions.md');
39
46
  return violations;
40
- },
41
- };
42
- function hasPrecedingDisable(lines, idx) {
43
- if (idx === 0)
44
- return false;
45
- const prevLine = lines[idx - 1];
46
- return DISABLE_PATTERN.test(prevLine);
47
+ }
47
48
  }
48
- exports.default = noUnmanagedExceptionsRule;
49
+ exports.NoUnmanagedExceptionsRule = NoUnmanagedExceptionsRule;
49
50
  //# sourceMappingURL=no-unmanaged-exceptions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-unmanaged-exceptions.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-unmanaged-exceptions.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAC1C,8DAA+D;AAE/D,MAAM,WAAW,GAAG,YAAY,CAAC;AAEjC,8EAA8E;AAC9E,MAAM,eAAe,GAAG,gGAAgG,CAAC;AAEzH,MAAM,yBAAyB,GAAa;IACxC,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EAAE,uHAAuH;IACpI,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,4KAA4K;QAC5K,6GAA6G;QAC7G,yCAAyC;QACzC,2FAA2F;QAC3F,0EAA0E;QAC1E,uEAAuE;QACvE,4FAA4F;QAC5F,oMAAoM;KACvM;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAyB,CAAC;gBAAE,SAAS;YACrE,IAAI,mBAAmB,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;gBAAE,SAAS;YAChD,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,gLAAgL,CACnL,CAAC,CAAC;QACP,CAAC;QACD,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,SAAS,mBAAmB,CAAC,KAAwB,EAAE,GAAW;IAC9D,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAChC,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,kBAAe,yBAAyB,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { writeTemplateIfMissing } from '../instruct-ai-writer';\n\nconst TRY_PATTERN = /\\btry\\s*\\{/;\n\n// Both webpieces-disable and the existing ESLint directive suppress this rule\nconst DISABLE_PATTERN = /@webpieces\\/no-unmanaged-exceptions|webpieces-disable\\s+(?:[\\w-]+,\\s*)*no-unmanaged-exceptions/;\n\nconst noUnmanagedExceptionsRule: EditRule = {\n name: 'no-unmanaged-exceptions',\n description: 'try/catch is generally not allowed. Only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'Fix Option 1 (preferred): Remove the try/catch — let the exception bubble to the top-level chokepoint (filter, globalErrorHandler) where it is already logged and handled.',\n 'Fix Option 2 (ask the human first): If you genuinely believe this IS a chokepoint, STOP and tell the human:',\n ' - What exception could be thrown here',\n ' - What the current top-level chokepoint is (the try/catch at the top of the call stack)',\n ' - Why throwing to that chokepoint would be wrong in this specific case',\n ' Then ask: \"Should I add a disable comment or remove the try/catch?\"',\n ' Only add // webpieces-disable no-unmanaged-exceptions -- <reason> if the human says yes.',\n 'NOTE: If the code is calling an external process (execSync, fs, network), the correct answer is almost always Option 1 — let it throw. Hooks have a top-level runner that reports errors properly.',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!TRY_PATTERN.test(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, 'no-unmanaged-exceptions')) continue;\n if (hasPrecedingDisable(ctx.lines, i)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'try/catch is generally not allowed. READ .webpieces/instruct-ai/webpieces.exceptions.md to understand why. Only chokepoints (filter, globalErrorHandler) may catch exceptions.',\n ));\n }\n if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');\n return violations;\n },\n};\n\nfunction hasPrecedingDisable(lines: readonly string[], idx: number): boolean {\n if (idx === 0) return false;\n const prevLine = lines[idx - 1];\n return DISABLE_PATTERN.test(prevLine);\n}\n\nexport default noUnmanagedExceptionsRule;\n"]}
1
+ {"version":3,"file":"no-unmanaged-exceptions.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-unmanaged-exceptions.ts"],"names":[],"mappings":";;;AAAA,0DAAkF;AAGlF,oCAA0C;AAC1C,4CAA4C;AAC5C,8DAA+D;AAE/D,MAAM,WAAW,GAAG,YAAY,CAAC;AAEjC,8EAA8E;AAC9E,MAAM,eAAe,GAAG,gGAAgG,CAAC;AAEzH,SAAS,mBAAmB,CAAC,KAAwB,EAAE,GAAW;IAC9D,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAChC,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,MAAa,yBAA0B,SAAQ,wBAAyC;IACpF,YAAY,MAAmC,IAAI,KAAK,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC;IAErF,WAAW,GAAG,uHAAuH,CAAC;IAC7H,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACzC,OAAO,GAAG;QACf,4KAA4K;QAC5K,6GAA6G;QAC7G,yCAAyC;QACzC,2FAA2F;QAC3F,0EAA0E;QAC1E,uEAAuE;QACvE,4FAA4F;QAC5F,oMAAoM;KACvM,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAU,CAAC,uBAAuB,CAAC;gBAAE,SAAS;YAC9E,IAAI,mBAAmB,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;gBAAE,SAAS;YAChD,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,gLAAgL,CACnL,CAAC,CAAC;QACP,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,IAAA,2CAAsB,EAAC,GAAG,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC;QAChG,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ;AAjCD,8DAiCC","sourcesContent":["import { NoUnmanagedExceptionsConfig, 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\nconst TRY_PATTERN = /\\btry\\s*\\{/;\n\n// Both webpieces-disable and the existing ESLint directive suppress this rule\nconst DISABLE_PATTERN = /@webpieces\\/no-unmanaged-exceptions|webpieces-disable\\s+(?:[\\w-]+,\\s*)*no-unmanaged-exceptions/;\n\nfunction hasPrecedingDisable(lines: readonly string[], idx: number): boolean {\n if (idx === 0) return false;\n const prevLine = lines[idx - 1];\n return DISABLE_PATTERN.test(prevLine);\n}\n\nexport class NoUnmanagedExceptionsRule extends EditRuleBase<NoUnmanagedExceptionsConfig> {\n constructor(config: NoUnmanagedExceptionsConfig) { super(config, 'no-unmanaged-exceptions'); }\n\n readonly description = 'try/catch is generally not allowed. Only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n readonly fixHint = [\n 'Fix Option 1 (preferred): Remove the try/catch — let the exception bubble to the top-level chokepoint (filter, globalErrorHandler) where it is already logged and handled.',\n 'Fix Option 2 (ask the human first): If you genuinely believe this IS a chokepoint, STOP and tell the human:',\n ' - What exception could be thrown here',\n ' - What the current top-level chokepoint is (the try/catch at the top of the call stack)',\n ' - Why throwing to that chokepoint would be wrong in this specific case',\n ' Then ask: \"Should I add a disable comment or remove the try/catch?\"',\n ' Only add // webpieces-disable no-unmanaged-exceptions -- <reason> if the human says yes.',\n 'NOTE: If the code is calling an external process (execSync, fs, network), the correct answer is almost always Option 1 — let it throw. Hooks have a top-level runner that reports errors properly.',\n ];\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!TRY_PATTERN.test(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.NO_UNMANAGED_EXCEPTIONS)) continue;\n if (hasPrecedingDisable(ctx.lines, i)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'try/catch is generally not allowed. READ .webpieces/instruct-ai/webpieces.exceptions.md to understand why. Only chokepoints (filter, globalErrorHandler) may catch exceptions.',\n ));\n }\n if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');\n return violations;\n }\n}\n"]}
@@ -1,4 +1,9 @@
1
- import type { BashRule } from '../types';
2
- declare const prCreationGuard: BashRule;
3
- export declare function extractPrBody(command: string, workspaceRoot: string): string | null;
4
- export default prCreationGuard;
1
+ import { PrCreationGuardConfig } from '@webpieces/rules-config';
2
+ import type { BashContext, Violation } from '../types';
3
+ import { BashRuleBase } from '../rule-base';
4
+ export declare class PrCreationGuardRule extends BashRuleBase<PrCreationGuardConfig> {
5
+ constructor(config: PrCreationGuardConfig);
6
+ readonly description = "Block direct PR creation/edit (gh pr / gh api / curl) so PRs go only through pnpm wp-upsert-pr.";
7
+ readonly fixHint: readonly string[];
8
+ check(ctx: BashContext): readonly Violation[];
9
+ }
@@ -1,84 +1,48 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractPrBody = extractPrBody;
4
- const child_process_1 = require("child_process");
5
- const fs_1 = require("fs");
6
- const path_1 = require("path");
3
+ exports.PrCreationGuardRule = void 0;
7
4
  const types_1 = require("../types");
5
+ const rule_base_1 = require("../rule-base");
8
6
  const FIX_HINT = [
9
- 'Branch is not up-to-date with origin/main.',
10
- 'Run the squash-update process to sync with main first:',
11
- ' pnpm wp-git-update',
12
- 'Then retry: gh pr create ...',
13
- '',
14
- 'Do NOT use "git merge origin/main" or "git rebase" — these break the 3-point fork-point system.',
15
- 'See docs/git-workflow.md for the full squash-merge update process.',
7
+ 'Direct PR creation is blocked. Create or update a PR ONLY via the gated command:',
8
+ ' pnpm wp-upsert-pr',
9
+ 'It updates the branch from main (3-point merge), runs the real build (nx affected), and',
10
+ 'assembles the PR dashboard then creates/updates the PR itself. A failing build = no PR.',
11
+ 'There is nothing to paste or attest to; the command does the work.',
16
12
  ];
17
- const prCreationGuard = {
18
- name: 'pr-creation-guard',
19
- description: 'Block PR creation when the branch has not been updated from origin/main via the squash-merge process.',
20
- scope: 'bash',
21
- files: [],
22
- defaultOptions: { buildCommand: '', requireTextInPr: '' },
23
- fixHint: FIX_HINT,
24
- check(ctx) {
25
- if (!/gh\s+pr\s+create/.test(ctx.command))
26
- return [];
27
- (0, child_process_1.execSync)('git fetch origin main --quiet', { cwd: ctx.workspaceRoot, encoding: 'utf8' });
28
- // spawnSync is used here because exit code 1 means "not an ancestor" (not an error)
29
- const result = (0, child_process_1.spawnSync)('git', ['merge-base', '--is-ancestor', 'origin/main', 'HEAD'], { cwd: ctx.workspaceRoot });
30
- if (result.status !== 0) {
31
- return [new types_1.Violation(1, truncate(ctx.command), [
32
- 'Branch is not up-to-date with origin/main.',
33
- 'Run the squash-update process first:',
34
- ' pnpm wp-git-update',
35
- 'Do NOT use "git merge origin/main" or "git rebase" — these break the 3-point fork-point system.',
36
- 'See docs/git-workflow.md for details.',
37
- 'Then retry: gh pr create',
38
- ].join('\n'))];
39
- }
40
- const buildCmd = ctx.options['buildCommand'];
41
- const requireText = ctx.options['requireTextInPr'];
42
- if (buildCmd && requireText) {
43
- const body = extractPrBody(ctx.command, ctx.workspaceRoot);
44
- if (body !== null && !body.includes(requireText)) {
45
- return [new types_1.Violation(1, truncate(ctx.command), [
46
- 'PR body is missing the required CI confirmation.',
47
- 'Before creating a PR you must run:',
48
- ` ${buildCmd}`,
49
- 'After it passes, add this exact phrase to your PR description:',
50
- ` "${requireText}"`,
51
- ].join('\n'))];
52
- }
53
- }
54
- return [];
55
- },
56
- };
57
- function extractPrBody(command, workspaceRoot) {
58
- const fileMatch = /--body-file\s+(\S+)/.exec(command);
59
- if (fileMatch) {
60
- const resolved = (0, path_1.isAbsolute)(fileMatch[1]) ? fileMatch[1] : (0, path_1.join)(workspaceRoot, fileMatch[1]);
61
- if (!(0, fs_1.existsSync)(resolved))
62
- return null;
63
- return (0, fs_1.readFileSync)(resolved, 'utf8');
13
+ // Detect every way an agent could open/update a PR directly, so the ONLY path left is
14
+ // `pnpm wp-upsert-pr` (whose internal `gh pr create` runs as a child process the hook
15
+ // never sees). Read-only `gh pr list` / `gh api .../pulls` GET are intentionally allowed.
16
+ function isDirectPrCreation(cmd) {
17
+ if (/\bgh\s+pr\s+(create|edit)\b/.test(cmd))
18
+ return true;
19
+ const ghApiPulls = /\bgh\s+api\b[^\n]*\/pulls\b/.test(cmd);
20
+ if (ghApiPulls && (/--method\s+POST/i.test(cmd) || /-X\s+POST/i.test(cmd) || /\s-f\b/.test(cmd) || /\s-F\b/.test(cmd) || /--field\b/.test(cmd))) {
21
+ return true;
64
22
  }
65
- // Greedy + newline-spanning ([\s\S]*) so an inline body that itself contains
66
- // quote characters (e.g. a `--body "$(cat <<EOF ... )"` heredoc with prose like
67
- // "not installed") is captured in full up to the LAST quote, instead of being
68
- // truncated at the first embedded quote. Truncation used to drop the required CI
69
- // phrase and produce a false "missing CI confirmation" block. For bodies built by
70
- // command substitution from a separate file, prefer --body-file (handled above).
71
- const doubleQuoteMatch = /--body\s+"([\s\S]*)"/.exec(command);
72
- if (doubleQuoteMatch)
73
- return doubleQuoteMatch[1];
74
- const singleQuoteMatch = /--body\s+'([\s\S]*)'/.exec(command);
75
- if (singleQuoteMatch)
76
- return singleQuoteMatch[1];
77
- return null;
23
+ const curlPulls = /\bcurl\b[^\n]*api\.github\.com[^\n]*\/pulls\b/.test(cmd);
24
+ if (curlPulls && (/-X\s*POST/i.test(cmd) || /--request\s+POST/i.test(cmd) || /(\s-d\b|--data\b)/.test(cmd))) {
25
+ return true;
26
+ }
27
+ return false;
78
28
  }
79
29
  function truncate(s) {
80
30
  const MAX = 120;
81
31
  return s.length <= MAX ? s : s.slice(0, MAX) + '…';
82
32
  }
83
- exports.default = prCreationGuard;
33
+ class PrCreationGuardRule extends rule_base_1.BashRuleBase {
34
+ constructor(config) { super(config, 'pr-creation-guard'); }
35
+ description = 'Block direct PR creation/edit (gh pr / gh api / curl) so PRs go only through pnpm wp-upsert-pr.';
36
+ fixHint = FIX_HINT;
37
+ check(ctx) {
38
+ if (!isDirectPrCreation(ctx.command))
39
+ return [];
40
+ return [new types_1.Violation(1, truncate(ctx.command), [
41
+ 'Direct PR creation/update is blocked.',
42
+ 'Use the gated command instead — it runs the build and builds the dashboard:',
43
+ ' pnpm wp-upsert-pr',
44
+ ].join('\n'))];
45
+ }
46
+ }
47
+ exports.PrCreationGuardRule = PrCreationGuardRule;
84
48
  //# sourceMappingURL=pr-creation-guard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pr-creation-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-creation-guard.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync, spawnSync } from 'child_process';\nimport { readFileSync, existsSync } from 'fs';\nimport { isAbsolute, join } from 'path';\nimport type { BashRule, BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst FIX_HINT: readonly string[] = [\n 'Branch is not up-to-date with origin/main.',\n 'Run the squash-update process to sync with main first:',\n ' pnpm wp-git-update',\n 'Then retry: gh pr create ...',\n '',\n 'Do NOT use \"git merge origin/main\" or \"git rebase\" — these break the 3-point fork-point system.',\n 'See docs/git-workflow.md for the full squash-merge update process.',\n];\n\nconst prCreationGuard: BashRule = {\n name: 'pr-creation-guard',\n description: 'Block PR creation when the branch has not been updated from origin/main via the squash-merge process.',\n scope: 'bash',\n files: [],\n defaultOptions: { buildCommand: '', requireTextInPr: '' },\n fixHint: FIX_HINT,\n\n check(ctx: BashContext): readonly Violation[] {\n if (!/gh\\s+pr\\s+create/.test(ctx.command)) return [];\n\n execSync('git fetch origin main --quiet', { cwd: ctx.workspaceRoot, encoding: 'utf8' });\n\n // spawnSync is used here because exit code 1 means \"not an ancestor\" (not an error)\n const result = spawnSync(\n 'git', ['merge-base', '--is-ancestor', 'origin/main', 'HEAD'],\n { cwd: ctx.workspaceRoot },\n );\n\n if (result.status !== 0) {\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'Branch is not up-to-date with origin/main.',\n 'Run the squash-update process first:',\n ' pnpm wp-git-update',\n 'Do NOT use \"git merge origin/main\" or \"git rebase\" these break the 3-point fork-point system.',\n 'See docs/git-workflow.md for details.',\n 'Then retry: gh pr create',\n ].join('\\n'),\n )];\n }\n\n const buildCmd = ctx.options['buildCommand'] as string;\n const requireText = ctx.options['requireTextInPr'] as string;\n if (buildCmd && requireText) {\n const body = extractPrBody(ctx.command, ctx.workspaceRoot);\n if (body !== null && !body.includes(requireText)) {\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'PR body is missing the required CI confirmation.',\n 'Before creating a PR you must run:',\n ` ${buildCmd}`,\n 'After it passes, add this exact phrase to your PR description:',\n ` \"${requireText}\"`,\n ].join('\\n'),\n )];\n }\n }\n return [];\n },\n};\n\nexport function extractPrBody(command: string, workspaceRoot: string): string | null {\n const fileMatch = /--body-file\\s+(\\S+)/.exec(command);\n if (fileMatch) {\n const resolved = isAbsolute(fileMatch[1]) ? fileMatch[1] : join(workspaceRoot, fileMatch[1]);\n if (!existsSync(resolved)) return null;\n return readFileSync(resolved, 'utf8');\n }\n // Greedy + newline-spanning ([\\s\\S]*) so an inline body that itself contains\n // quote characters (e.g. a `--body \"$(cat <<EOF ... )\"` heredoc with prose like\n // \"not installed\") is captured in full up to the LAST quote, instead of being\n // truncated at the first embedded quote. Truncation used to drop the required CI\n // phrase and produce a false \"missing CI confirmation\" block. For bodies built by\n // command substitution from a separate file, prefer --body-file (handled above).\n const doubleQuoteMatch = /--body\\s+\"([\\s\\S]*)\"/.exec(command);\n if (doubleQuoteMatch) return doubleQuoteMatch[1];\n const singleQuoteMatch = /--body\\s+'([\\s\\S]*)'/.exec(command);\n if (singleQuoteMatch) return singleQuoteMatch[1];\n return null;\n}\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '';\n}\n\nexport default prCreationGuard;\n"]}
1
+ {"version":3,"file":"pr-creation-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-creation-guard.ts"],"names":[],"mappings":";;;AAGA,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,QAAQ,GAAsB;IAChC,kFAAkF;IAClF,qBAAqB;IACrB,yFAAyF;IACzF,2FAA2F;IAC3F,oEAAoE;CACvE,CAAC;AAEF,sFAAsF;AACtF,sFAAsF;AACtF,0FAA0F;AAC1F,SAAS,kBAAkB,CAAC,GAAW;IACnC,IAAI,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzD,MAAM,UAAU,GAAG,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3D,IAAI,UAAU,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC9I,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,SAAS,GAAG,+CAA+C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5E,IAAI,SAAS,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC1G,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,MAAa,mBAAoB,SAAQ,wBAAmC;IACxE,YAAY,MAA6B,IAAI,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;IAEzE,WAAW,GAAG,iGAAiG,CAAC;IAChH,OAAO,GAAG,QAAQ,CAAC;IAE5B,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAChD,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,uCAAuC;gBACvC,6EAA6E;gBAC7E,qBAAqB;aACxB,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ;AAlBD,kDAkBC","sourcesContent":["import { PrCreationGuardConfig } from '@webpieces/rules-config';\n\nimport type { BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { BashRuleBase } from '../rule-base';\n\nconst FIX_HINT: readonly string[] = [\n 'Direct PR creation is blocked. Create or update a PR ONLY via the gated command:',\n ' pnpm wp-upsert-pr',\n 'It updates the branch from main (3-point merge), runs the real build (nx affected), and',\n 'assembles the PR dashboard then creates/updates the PR itself. A failing build = no PR.',\n 'There is nothing to paste or attest to; the command does the work.',\n];\n\n// Detect every way an agent could open/update a PR directly, so the ONLY path left is\n// `pnpm wp-upsert-pr` (whose internal `gh pr create` runs as a child process the hook\n// never sees). Read-only `gh pr list` / `gh api .../pulls` GET are intentionally allowed.\nfunction isDirectPrCreation(cmd: string): boolean {\n if (/\\bgh\\s+pr\\s+(create|edit)\\b/.test(cmd)) return true;\n\n const ghApiPulls = /\\bgh\\s+api\\b[^\\n]*\\/pulls\\b/.test(cmd);\n if (ghApiPulls && (/--method\\s+POST/i.test(cmd) || /-X\\s+POST/i.test(cmd) || /\\s-f\\b/.test(cmd) || /\\s-F\\b/.test(cmd) || /--field\\b/.test(cmd))) {\n return true;\n }\n\n const curlPulls = /\\bcurl\\b[^\\n]*api\\.github\\.com[^\\n]*\\/pulls\\b/.test(cmd);\n if (curlPulls && (/-X\\s*POST/i.test(cmd) || /--request\\s+POST/i.test(cmd) || /(\\s-d\\b|--data\\b)/.test(cmd))) {\n return true;\n }\n return false;\n}\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '';\n}\n\nexport class PrCreationGuardRule extends BashRuleBase<PrCreationGuardConfig> {\n constructor(config: PrCreationGuardConfig) { super(config, 'pr-creation-guard'); }\n\n readonly description = 'Block direct PR creation/edit (gh pr / gh api / curl) so PRs go only through pnpm wp-upsert-pr.';\n readonly fixHint = FIX_HINT;\n\n check(ctx: BashContext): readonly Violation[] {\n if (!isDirectPrCreation(ctx.command)) return [];\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'Direct PR creation/update is blocked.',\n 'Use the gated command instead it runs the build and builds the dashboard:',\n ' pnpm wp-upsert-pr',\n ].join('\\n'),\n )];\n }\n}\n"]}
@@ -1,3 +1,9 @@
1
- import type { BashRule } from '../types';
2
- declare const prMergeCleanup: BashRule;
3
- export default prMergeCleanup;
1
+ import { PrMergeCleanupConfig } from '@webpieces/rules-config';
2
+ import type { BashContext, Violation } from '../types';
3
+ import { BashRuleBase } from '../rule-base';
4
+ export declare class PrMergeCleanupRule extends BashRuleBase<PrMergeCleanupConfig> {
5
+ constructor(config: PrMergeCleanupConfig);
6
+ readonly description = "After merging a PR, require switching to main, pulling, and deleting the local branch.";
7
+ readonly fixHint: string[];
8
+ check(ctx: BashContext): readonly Violation[];
9
+ }
@@ -1,18 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PrMergeCleanupRule = void 0;
3
4
  const child_process_1 = require("child_process");
4
5
  const types_1 = require("../types");
5
- const prMergeCleanup = {
6
- name: 'pr-merge-cleanup',
7
- description: 'After merging a PR, require switching to main, pulling, and deleting the local branch.',
8
- scope: 'bash',
9
- files: [],
10
- defaultOptions: {},
11
- fixHint: [
6
+ const rule_base_1 = require("../rule-base");
7
+ function truncate(s) {
8
+ const MAX = 120;
9
+ return s.length <= MAX ? s : s.slice(0, MAX) + '';
10
+ }
11
+ class PrMergeCleanupRule extends rule_base_1.BashRuleBase {
12
+ constructor(config) { super(config, 'pr-merge-cleanup'); }
13
+ description = 'After merging a PR, require switching to main, pulling, and deleting the local branch.';
14
+ fixHint = [
12
15
  'After merging a PR you must clean up the local branch.',
13
16
  'Run this combined command instead:',
14
17
  ' gh pr merge --squash && git checkout main && git pull && git branch -d <current-branch>',
15
- ],
18
+ ];
16
19
  check(ctx) {
17
20
  if (!/gh\s+pr\s+merge/.test(ctx.command))
18
21
  return [];
@@ -29,11 +32,7 @@ const prMergeCleanup = {
29
32
  'Run this combined command instead:',
30
33
  ` gh pr merge --squash && git checkout main && git pull && git branch -d ${currentBranch}`,
31
34
  ].join('\n'))];
32
- },
33
- };
34
- function truncate(s) {
35
- const MAX = 120;
36
- return s.length <= MAX ? s : s.slice(0, MAX) + '…';
35
+ }
37
36
  }
38
- exports.default = prMergeCleanup;
37
+ exports.PrMergeCleanupRule = PrMergeCleanupRule;
39
38
  //# sourceMappingURL=pr-merge-cleanup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pr-merge-cleanup.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-merge-cleanup.ts"],"names":[],"mappings":";;AAAA,iDAAyC;AAEzC,oCAA0C;AAE1C,MAAM,cAAc,GAAa;IAC7B,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,wFAAwF;IACrG,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,EAAE;IACT,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,wDAAwD;QACxD,oCAAoC;QACpC,2FAA2F;KAC9F;IAED,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAEpD,MAAM,WAAW,GAAG,gCAAgC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE3D,IAAI,WAAW,IAAI,SAAS;YAAE,OAAO,EAAE,CAAC;QAExC,MAAM,aAAa,GAAG,IAAA,wBAAQ,EAAC,iCAAiC,EAAE;YAC9D,GAAG,EAAE,GAAG,CAAC,aAAa;YACtB,QAAQ,EAAE,MAAM;SACnB,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,2EAA2E;gBAC3E,oCAAoC;gBACpC,4EAA4E,aAAa,EAAE;aAC9F,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ,CAAC;AAEF,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,kBAAe,cAAc,CAAC","sourcesContent":["import { execSync } from 'child_process';\nimport type { BashRule, BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst prMergeCleanup: BashRule = {\n name: 'pr-merge-cleanup',\n description: 'After merging a PR, require switching to main, pulling, and deleting the local branch.',\n scope: 'bash',\n files: [],\n defaultOptions: {},\n fixHint: [\n 'After merging a PR you must clean up the local branch.',\n 'Run this combined command instead:',\n ' gh pr merge --squash && git checkout main && git pull && git branch -d <current-branch>',\n ],\n\n check(ctx: BashContext): readonly Violation[] {\n if (!/gh\\s+pr\\s+merge/.test(ctx.command)) return [];\n\n const hasCheckout = /git\\s+(checkout|switch)\\s+main/.test(ctx.command);\n const hasDelete = /git\\s+branch\\s+-[dD]/.test(ctx.command);\n\n if (hasCheckout && hasDelete) return [];\n\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd: ctx.workspaceRoot,\n encoding: 'utf8',\n }).trim();\n\n return [new V(\n 1,\n truncate(ctx.command),\n [\n '[pr-merge-cleanup] After merging a PR you must clean up the local branch.',\n 'Run this combined command instead:',\n ` gh pr merge --squash && git checkout main && git pull && git branch -d ${currentBranch}`,\n ].join('\\n'),\n )];\n },\n};\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport default prMergeCleanup;\n"]}
1
+ {"version":3,"file":"pr-merge-cleanup.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-merge-cleanup.ts"],"names":[],"mappings":";;;AAAA,iDAAyC;AAKzC,oCAA0C;AAC1C,4CAA4C;AAE5C,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,MAAa,kBAAmB,SAAQ,wBAAkC;IACtE,YAAY,MAA4B,IAAI,KAAK,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAEvE,WAAW,GAAG,wFAAwF,CAAC;IACvG,OAAO,GAAG;QACf,wDAAwD;QACxD,oCAAoC;QACpC,2FAA2F;KAC9F,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAEpD,MAAM,WAAW,GAAG,gCAAgC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE3D,IAAI,WAAW,IAAI,SAAS;YAAE,OAAO,EAAE,CAAC;QAExC,MAAM,aAAa,GAAG,IAAA,wBAAQ,EAAC,iCAAiC,EAAE;YAC9D,GAAG,EAAE,GAAG,CAAC,aAAa;YACtB,QAAQ,EAAE,MAAM;SACnB,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,2EAA2E;gBAC3E,oCAAoC;gBACpC,4EAA4E,aAAa,EAAE;aAC9F,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ;AAjCD,gDAiCC","sourcesContent":["import { execSync } from 'child_process';\n\nimport { PrMergeCleanupConfig } from '@webpieces/rules-config';\n\nimport type { BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { BashRuleBase } from '../rule-base';\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport class PrMergeCleanupRule extends BashRuleBase<PrMergeCleanupConfig> {\n constructor(config: PrMergeCleanupConfig) { super(config, 'pr-merge-cleanup'); }\n\n readonly description = 'After merging a PR, require switching to main, pulling, and deleting the local branch.';\n readonly fixHint = [\n 'After merging a PR you must clean up the local branch.',\n 'Run this combined command instead:',\n ' gh pr merge --squash && git checkout main && git pull && git branch -d <current-branch>',\n ];\n\n check(ctx: BashContext): readonly Violation[] {\n if (!/gh\\s+pr\\s+merge/.test(ctx.command)) return [];\n\n const hasCheckout = /git\\s+(checkout|switch)\\s+main/.test(ctx.command);\n const hasDelete = /git\\s+branch\\s+-[dD]/.test(ctx.command);\n\n if (hasCheckout && hasDelete) return [];\n\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd: ctx.workspaceRoot,\n encoding: 'utf8',\n }).trim();\n\n return [new V(\n 1,\n truncate(ctx.command),\n [\n '[pr-merge-cleanup] After merging a PR you must clean up the local branch.',\n 'Run this combined command instead:',\n ` gh pr merge --squash && git checkout main && git pull && git branch -d ${currentBranch}`,\n ].join('\\n'),\n )];\n }\n}\n"]}
@@ -1,3 +1,10 @@
1
- import type { EditRule } from '../types';
2
- declare const requireReturnTypeRule: EditRule;
3
- export default requireReturnTypeRule;
1
+ import { RequireReturnTypeConfig } from '@webpieces/rules-config';
2
+ import type { EditContext, Violation } from '../types';
3
+ import { EditRuleBase } from '../rule-base';
4
+ export declare class RequireReturnTypeRule extends EditRuleBase<RequireReturnTypeConfig> {
5
+ constructor(config: RequireReturnTypeConfig);
6
+ readonly description = "Every function and method must declare its return type.";
7
+ readonly files: string[];
8
+ readonly fixHint: string[];
9
+ check(ctx: EditContext): readonly Violation[];
10
+ }
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequireReturnTypeRule = 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
  // Matches function/method signatures that don't have `: ReturnType` before the `{` body opener.
5
8
  // Pattern: function name(<params>) { — missing `: Type` between `)` and `{`
6
9
  const FUNC_DECL_MISSING = /\bfunction\s+\w+\s*(?:<[^>]*>)?\s*\([^)]*\)\s*\{/;
@@ -12,16 +15,26 @@ const ARROW_MISSING = /\bconst\s+\w+\s*=\s*(?:async\s+)?(?:<[^>]*>)?\s*\([^)]*\)
12
15
  const HAS_RETURN_TYPE = /\)\s*:\s*\S/;
13
16
  // Skip constructors, getters, setters, and control flow keywords
14
17
  const SKIP_PATTERN = /\b(?:constructor|get\s+\w+|set\s+\w+|if|else|while|for|switch|catch|return)\s*\(/;
15
- const requireReturnTypeRule = {
16
- name: 'require-return-type',
17
- description: 'Every function and method must declare its return type.',
18
- scope: 'edit',
19
- files: ['**/*.ts', '**/*.tsx'],
20
- defaultOptions: {},
21
- fixHint: [
18
+ function isMissingReturnType(line) {
19
+ if (SKIP_PATTERN.test(line))
20
+ return false;
21
+ const isFuncLike = FUNC_DECL_MISSING.test(line) ||
22
+ METHOD_MISSING.test(line) ||
23
+ ARROW_MISSING.test(line);
24
+ if (!isFuncLike)
25
+ return false;
26
+ if (HAS_RETURN_TYPE.test(line))
27
+ return false;
28
+ return true;
29
+ }
30
+ class RequireReturnTypeRule extends rule_base_1.EditRuleBase {
31
+ constructor(config) { super(config, 'require-return-type'); }
32
+ description = 'Every function and method must declare its return type.';
33
+ files = ['**/*.ts', '**/*.tsx'];
34
+ fixHint = [
22
35
  'Add return type: function foo(x: T): ReturnType { ... }',
23
36
  '// webpieces-disable require-return-type -- <reason>',
24
- ],
37
+ ];
25
38
  check(ctx) {
26
39
  const violations = [];
27
40
  for (let i = 0; i < ctx.strippedLines.length; i += 1) {
@@ -29,24 +42,12 @@ const requireReturnTypeRule = {
29
42
  if (!isMissingReturnType(stripped))
30
43
  continue;
31
44
  const lineNum = i + 1;
32
- if (ctx.isLineDisabled(lineNum, 'require-return-type'))
45
+ if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.REQUIRE_RETURN_TYPE))
33
46
  continue;
34
47
  violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), 'Missing return type annotation.'));
35
48
  }
36
49
  return violations;
37
- },
38
- };
39
- function isMissingReturnType(line) {
40
- if (SKIP_PATTERN.test(line))
41
- return false;
42
- const isFuncLike = FUNC_DECL_MISSING.test(line) ||
43
- METHOD_MISSING.test(line) ||
44
- ARROW_MISSING.test(line);
45
- if (!isFuncLike)
46
- return false;
47
- if (HAS_RETURN_TYPE.test(line))
48
- return false;
49
- return true;
50
+ }
50
51
  }
51
- exports.default = requireReturnTypeRule;
52
+ exports.RequireReturnTypeRule = RequireReturnTypeRule;
52
53
  //# sourceMappingURL=require-return-type.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"require-return-type.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/require-return-type.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAE1C,gGAAgG;AAChG,4EAA4E;AAC5E,MAAM,iBAAiB,GAAG,kDAAkD,CAAC;AAE7E,8EAA8E;AAC9E,MAAM,cAAc,GAAG,yDAAyD,CAAC;AAEjF,qFAAqF;AACrF,MAAM,aAAa,GAAG,gEAAgE,CAAC;AAEvF,qEAAqE;AACrE,MAAM,eAAe,GAAG,aAAa,CAAC;AAEtC,iEAAiE;AACjE,MAAM,YAAY,GAAG,kFAAkF,CAAC;AAExG,MAAM,qBAAqB,GAAa;IACpC,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,yDAAyD;IACtE,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,yDAAyD;QACzD,sDAAsD;KACzD;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,qBAAqB,CAAC;gBAAE,SAAS;YACjE,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,iCAAiC,CACpC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,SAAS,mBAAmB,CAAC,IAAY;IACrC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,UAAU,GACZ,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9B,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,kBAAe,qBAAqB,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\n// Matches function/method signatures that don't have `: ReturnType` before the `{` body opener.\n// Pattern: function name(<params>) { — missing `: Type` between `)` and `{`\nconst FUNC_DECL_MISSING = /\\bfunction\\s+\\w+\\s*(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*\\{/;\n\n// Matches class method signatures: indented, optional async, name(<params>) {\nconst METHOD_MISSING = /^\\s{2,}(?:async\\s+)?\\w+\\s*(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*\\{/;\n\n// Arrow function: const name = (async)? (<params>) => — missing `: Type` before `=>`\nconst ARROW_MISSING = /\\bconst\\s+\\w+\\s*=\\s*(?:async\\s+)?(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*=>/;\n\n// Lines that have ): ReturnType before { or => — these are COMPLIANT\nconst HAS_RETURN_TYPE = /\\)\\s*:\\s*\\S/;\n\n// Skip constructors, getters, setters, and control flow keywords\nconst SKIP_PATTERN = /\\b(?:constructor|get\\s+\\w+|set\\s+\\w+|if|else|while|for|switch|catch|return)\\s*\\(/;\n\nconst requireReturnTypeRule: EditRule = {\n name: 'require-return-type',\n description: 'Every function and method must declare its return type.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'Add return type: function foo(x: T): ReturnType { ... }',\n '// webpieces-disable require-return-type -- <reason>',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!isMissingReturnType(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, 'require-return-type')) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Missing return type annotation.',\n ));\n }\n return violations;\n },\n};\n\nfunction isMissingReturnType(line: string): boolean {\n if (SKIP_PATTERN.test(line)) return false;\n const isFuncLike =\n FUNC_DECL_MISSING.test(line) ||\n METHOD_MISSING.test(line) ||\n ARROW_MISSING.test(line);\n if (!isFuncLike) return false;\n if (HAS_RETURN_TYPE.test(line)) return false;\n return true;\n}\n\nexport default requireReturnTypeRule;\n"]}
1
+ {"version":3,"file":"require-return-type.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/require-return-type.ts"],"names":[],"mappings":";;;AAAA,0DAA8E;AAG9E,oCAA0C;AAC1C,4CAA4C;AAE5C,gGAAgG;AAChG,4EAA4E;AAC5E,MAAM,iBAAiB,GAAG,kDAAkD,CAAC;AAE7E,8EAA8E;AAC9E,MAAM,cAAc,GAAG,yDAAyD,CAAC;AAEjF,qFAAqF;AACrF,MAAM,aAAa,GAAG,gEAAgE,CAAC;AAEvF,qEAAqE;AACrE,MAAM,eAAe,GAAG,aAAa,CAAC;AAEtC,iEAAiE;AACjE,MAAM,YAAY,GAAG,kFAAkF,CAAC;AAExG,SAAS,mBAAmB,CAAC,IAAY;IACrC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,UAAU,GACZ,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9B,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,MAAa,qBAAsB,SAAQ,wBAAqC;IAC5E,YAAY,MAA+B,IAAI,KAAK,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAE7E,WAAW,GAAG,yDAAyD,CAAC;IAC/D,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACzC,OAAO,GAAG;QACf,yDAAyD;QACzD,sDAAsD;KACzD,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAU,CAAC,mBAAmB,CAAC;gBAAE,SAAS;YAC1E,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,iCAAiC,CACpC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ;AAzBD,sDAyBC","sourcesContent":["import { RequireReturnTypeConfig, RULE_NAMES } from '@webpieces/rules-config';\n\nimport type { EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { EditRuleBase } from '../rule-base';\n\n// Matches function/method signatures that don't have `: ReturnType` before the `{` body opener.\n// Pattern: function name(<params>) { — missing `: Type` between `)` and `{`\nconst FUNC_DECL_MISSING = /\\bfunction\\s+\\w+\\s*(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*\\{/;\n\n// Matches class method signatures: indented, optional async, name(<params>) {\nconst METHOD_MISSING = /^\\s{2,}(?:async\\s+)?\\w+\\s*(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*\\{/;\n\n// Arrow function: const name = (async)? (<params>) => — missing `: Type` before `=>`\nconst ARROW_MISSING = /\\bconst\\s+\\w+\\s*=\\s*(?:async\\s+)?(?:<[^>]*>)?\\s*\\([^)]*\\)\\s*=>/;\n\n// Lines that have ): ReturnType before { or => — these are COMPLIANT\nconst HAS_RETURN_TYPE = /\\)\\s*:\\s*\\S/;\n\n// Skip constructors, getters, setters, and control flow keywords\nconst SKIP_PATTERN = /\\b(?:constructor|get\\s+\\w+|set\\s+\\w+|if|else|while|for|switch|catch|return)\\s*\\(/;\n\nfunction isMissingReturnType(line: string): boolean {\n if (SKIP_PATTERN.test(line)) return false;\n const isFuncLike =\n FUNC_DECL_MISSING.test(line) ||\n METHOD_MISSING.test(line) ||\n ARROW_MISSING.test(line);\n if (!isFuncLike) return false;\n if (HAS_RETURN_TYPE.test(line)) return false;\n return true;\n}\n\nexport class RequireReturnTypeRule extends EditRuleBase<RequireReturnTypeConfig> {\n constructor(config: RequireReturnTypeConfig) { super(config, 'require-return-type'); }\n\n readonly description = 'Every function and method must declare its return type.';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n readonly fixHint = [\n 'Add return type: function foo(x: T): ReturnType { ... }',\n '// webpieces-disable require-return-type -- <reason>',\n ];\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!isMissingReturnType(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.REQUIRE_RETURN_TYPE)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Missing return type annotation.',\n ));\n }\n return violations;\n }\n}\n"]}
@@ -1,3 +1,10 @@
1
- import type { EditRule } from '../types';
2
- declare const throwCauseRequiredRule: EditRule;
3
- export default throwCauseRequiredRule;
1
+ import { ThrowCauseRequiredConfig } from '@webpieces/rules-config';
2
+ import type { EditContext, Violation } from '../types';
3
+ import { EditRuleBase } from '../rule-base';
4
+ export declare class ThrowCauseRequiredRule extends EditRuleBase<ThrowCauseRequiredConfig> {
5
+ constructor(config: ThrowCauseRequiredConfig);
6
+ readonly description = "When rethrowing with added context, chain the original exception: throw new Error(\"msg\", { cause: error })";
7
+ readonly files: string[];
8
+ readonly fixHint: string[];
9
+ check(ctx: EditContext): readonly Violation[];
10
+ }