@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.
- package/package.json +4 -8
- package/src/adapters/global-hook.js +3 -3
- package/src/adapters/global-hook.js.map +1 -1
- package/src/core/custom-rule-adapter.d.ts +21 -0
- package/src/core/custom-rule-adapter.js +48 -0
- package/src/core/custom-rule-adapter.js.map +1 -0
- package/src/core/disable-directives.d.ts +1 -2
- package/src/core/disable-directives.js +17 -31
- package/src/core/disable-directives.js.map +1 -1
- package/src/core/load-rules.d.ts +3 -2
- package/src/core/load-rules.js +71 -48
- package/src/core/load-rules.js.map +1 -1
- package/src/core/rule-base.d.ts +38 -0
- package/src/core/rule-base.js +36 -0
- package/src/core/rule-base.js.map +1 -0
- package/src/core/rules/branch-creation-guard.d.ts +12 -3
- package/src/core/rules/branch-creation-guard.js +37 -37
- package/src/core/rules/branch-creation-guard.js.map +1 -1
- package/src/core/rules/catch-error-pattern.d.ts +10 -3
- package/src/core/rules/catch-error-pattern.js +27 -26
- package/src/core/rules/catch-error-pattern.js.map +1 -1
- package/src/core/rules/index.js +1 -0
- package/src/core/rules/index.js.map +1 -1
- package/src/core/rules/max-file-lines.d.ts +13 -3
- package/src/core/rules/max-file-lines.js +14 -15
- package/src/core/rules/max-file-lines.js.map +1 -1
- package/src/core/rules/merge-in-progress-guard.d.ts +9 -0
- package/src/core/rules/merge-in-progress-guard.js +65 -0
- package/src/core/rules/merge-in-progress-guard.js.map +1 -0
- package/src/core/rules/no-any-unknown.d.ts +10 -3
- package/src/core/rules/no-any-unknown.js +15 -13
- package/src/core/rules/no-any-unknown.js.map +1 -1
- package/src/core/rules/no-destructure.d.ts +13 -3
- package/src/core/rules/no-destructure.js +14 -12
- package/src/core/rules/no-destructure.js.map +1 -1
- package/src/core/rules/no-direct-main-update.d.ts +9 -3
- package/src/core/rules/no-direct-main-update.js +12 -13
- package/src/core/rules/no-direct-main-update.js.map +1 -1
- package/src/core/rules/no-edit-on-main.d.ts +13 -3
- package/src/core/rules/no-edit-on-main.js +14 -14
- package/src/core/rules/no-edit-on-main.js.map +1 -1
- package/src/core/rules/no-implicit-any.d.ts +10 -3
- package/src/core/rules/no-implicit-any.js +13 -12
- package/src/core/rules/no-implicit-any.js.map +1 -1
- package/src/core/rules/no-js-files.d.ts +13 -3
- package/src/core/rules/no-js-files.js +13 -16
- package/src/core/rules/no-js-files.js.map +1 -1
- package/src/core/rules/no-shell-substitution.d.ts +9 -3
- package/src/core/rules/no-shell-substitution.js +22 -23
- package/src/core/rules/no-shell-substitution.js.map +1 -1
- package/src/core/rules/no-symbol-di-tokens.d.ts +13 -3
- package/src/core/rules/no-symbol-di-tokens.js +15 -13
- package/src/core/rules/no-symbol-di-tokens.js.map +1 -1
- package/src/core/rules/no-unmanaged-exceptions.d.ts +10 -3
- package/src/core/rules/no-unmanaged-exceptions.js +18 -17
- package/src/core/rules/no-unmanaged-exceptions.js.map +1 -1
- package/src/core/rules/pr-creation-guard.d.ts +9 -4
- package/src/core/rules/pr-creation-guard.js +36 -72
- package/src/core/rules/pr-creation-guard.js.map +1 -1
- package/src/core/rules/pr-merge-cleanup.d.ts +9 -3
- package/src/core/rules/pr-merge-cleanup.js +13 -14
- package/src/core/rules/pr-merge-cleanup.js.map +1 -1
- package/src/core/rules/require-return-type.d.ts +10 -3
- package/src/core/rules/require-return-type.js +24 -23
- package/src/core/rules/require-return-type.js.map +1 -1
- package/src/core/rules/throw-cause-required.d.ts +10 -3
- package/src/core/rules/throw-cause-required.js +13 -12
- package/src/core/rules/throw-cause-required.js.map +1 -1
- package/src/core/rules/validate-ts-in-src.d.ts +14 -3
- package/src/core/rules/validate-ts-in-src.js +15 -18
- package/src/core/rules/validate-ts-in-src.js.map +1 -1
- package/src/core/runner.js +38 -67
- package/src/core/runner.js.map +1 -1
- package/src/core/to-error.js +0 -1
- package/src/core/to-error.js.map +1 -1
- package/src/core/types.d.ts +14 -13
- package/src/core/types.js.map +1 -1
- package/src/index.d.ts +21 -1
- package/src/index.js +47 -1
- package/src/index.js.map +1 -1
- package/src/core/configs/default.d.ts +0 -2
- package/src/core/configs/default.js +0 -22
- package/src/core/configs/default.js.map +0 -1
- package/src/scripts/git-gatherInfo.d.ts +0 -2
- package/src/scripts/git-gatherInfo.js +0 -98
- package/src/scripts/git-gatherInfo.js.map +0 -1
- package/src/scripts/git-mergeComplete.d.ts +0 -2
- package/src/scripts/git-mergeComplete.js +0 -58
- package/src/scripts/git-mergeComplete.js.map +0 -1
- package/src/scripts/git-updateFromMain.d.ts +0 -2
- package/src/scripts/git-updateFromMain.js +0 -290
- package/src/scripts/git-updateFromMain.js.map +0 -1
- package/src/scripts/workflow/cleanTmp.d.ts +0 -1
- package/src/scripts/workflow/cleanTmp.js +0 -57
- package/src/scripts/workflow/cleanTmp.js.map +0 -1
- package/src/scripts/workflow/git-findForkPoint.d.ts +0 -2
- package/src/scripts/workflow/git-findForkPoint.js +0 -120
- package/src/scripts/workflow/git-findForkPoint.js.map +0 -1
- package/src/scripts/workflow/git-readAiBranchName.d.ts +0 -2
- package/src/scripts/workflow/git-readAiBranchName.js +0 -20
- package/src/scripts/workflow/git-readAiBranchName.js.map +0 -1
- package/src/scripts/workflow/git-validateUpToDate.d.ts +0 -1
- package/src/scripts/workflow/git-validateUpToDate.js +0 -72
- 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
|
-
|
|
44
|
-
|
|
45
|
-
description
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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 =
|
|
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,
|
|
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.
|
|
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":"
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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,
|
|
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.
|
|
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":"
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
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.
|
|
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
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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":"
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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.
|
|
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":"
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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,
|
|
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.
|
|
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":"
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
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
|
+
}
|