@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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BashRuleBase = exports.FileRuleBase = exports.EditRuleBase = exports.EmptyRuleConfig = void 0;
|
|
4
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
5
|
+
/**
|
|
6
|
+
* A concrete, instantiable BaseRuleConfig used as a fallback when a built-in rule has no
|
|
7
|
+
* entry in webpieces.config.json yet. It carries no values, so the rule's `description`,
|
|
8
|
+
* `fixHint`, and `defaultOptions` (which are config-independent) are still readable for the
|
|
9
|
+
* out-of-sync report — the rule never actually runs in that state (the sync check blocks first).
|
|
10
|
+
*/
|
|
11
|
+
class EmptyRuleConfig extends rules_config_1.BaseRuleConfig {
|
|
12
|
+
}
|
|
13
|
+
exports.EmptyRuleConfig = EmptyRuleConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Scope-specific base for edit rules. Extends the shared AbstractRule (which owns `name`,
|
|
16
|
+
* the typed `config`, and `shouldRun()`), and adds the ai-hook execution surface.
|
|
17
|
+
*/
|
|
18
|
+
class EditRuleBase extends rules_config_1.AbstractRule {
|
|
19
|
+
scope = 'edit';
|
|
20
|
+
files = [];
|
|
21
|
+
defaultOptions = {};
|
|
22
|
+
}
|
|
23
|
+
exports.EditRuleBase = EditRuleBase;
|
|
24
|
+
class FileRuleBase extends rules_config_1.AbstractRule {
|
|
25
|
+
scope = 'file';
|
|
26
|
+
files = [];
|
|
27
|
+
defaultOptions = {};
|
|
28
|
+
}
|
|
29
|
+
exports.FileRuleBase = FileRuleBase;
|
|
30
|
+
class BashRuleBase extends rules_config_1.AbstractRule {
|
|
31
|
+
scope = 'bash';
|
|
32
|
+
files = [];
|
|
33
|
+
defaultOptions = {};
|
|
34
|
+
}
|
|
35
|
+
exports.BashRuleBase = BashRuleBase;
|
|
36
|
+
//# sourceMappingURL=rule-base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-base.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/rule-base.ts"],"names":[],"mappings":";;;AAAA,0DAAoF;AAIpF;;;;;GAKG;AACH,MAAa,eAAgB,SAAQ,6BAAc;CAAG;AAAtD,0CAAsD;AAEtD;;;GAGG;AACH,MAAsB,YAAuC,SAAQ,2BAAe;IACvE,KAAK,GAAG,MAAe,CAAC;IACxB,KAAK,GAAsB,EAAE,CAAC;IAC9B,cAAc,GAAgB,EAAE,CAAC;CAK7C;AARD,oCAQC;AAED,MAAsB,YAAuC,SAAQ,2BAAe;IACvE,KAAK,GAAG,MAAe,CAAC;IACxB,KAAK,GAAsB,EAAE,CAAC;IAC9B,cAAc,GAAgB,EAAE,CAAC;CAK7C;AARD,oCAQC;AAED,MAAsB,YAAuC,SAAQ,2BAAe;IACvE,KAAK,GAAG,MAAe,CAAC;IACxB,KAAK,GAAsB,EAAE,CAAC;IAC9B,cAAc,GAAgB,EAAE,CAAC;CAK7C;AARD,oCAQC","sourcesContent":["import { AbstractRule, BaseRuleConfig, RuleOptions } from '@webpieces/rules-config';\n\nimport type { EditContext, BashContext, FileContext, Violation, Rule } from './types';\n\n/**\n * A concrete, instantiable BaseRuleConfig used as a fallback when a built-in rule has no\n * entry in webpieces.config.json yet. It carries no values, so the rule's `description`,\n * `fixHint`, and `defaultOptions` (which are config-independent) are still readable for the\n * out-of-sync report — the rule never actually runs in that state (the sync check blocks first).\n */\nexport class EmptyRuleConfig extends BaseRuleConfig {}\n\n/**\n * Scope-specific base for edit rules. Extends the shared AbstractRule (which owns `name`,\n * the typed `config`, and `shouldRun()`), and adds the ai-hook execution surface.\n */\nexport abstract class EditRuleBase<C extends BaseRuleConfig> extends AbstractRule<C> implements Rule {\n readonly scope = 'edit' as const;\n readonly files: readonly string[] = [];\n readonly defaultOptions: RuleOptions = {};\n abstract readonly description: string;\n abstract readonly fixHint: readonly string[];\n\n abstract check(ctx: EditContext): readonly Violation[];\n}\n\nexport abstract class FileRuleBase<C extends BaseRuleConfig> extends AbstractRule<C> implements Rule {\n readonly scope = 'file' as const;\n readonly files: readonly string[] = [];\n readonly defaultOptions: RuleOptions = {};\n abstract readonly description: string;\n abstract readonly fixHint: readonly string[];\n\n abstract check(ctx: FileContext): readonly Violation[];\n}\n\nexport abstract class BashRuleBase<C extends BaseRuleConfig> extends AbstractRule<C> implements Rule {\n readonly scope = 'bash' as const;\n readonly files: readonly string[] = [];\n readonly defaultOptions: RuleOptions = {};\n abstract readonly description: string;\n abstract readonly fixHint: readonly string[];\n\n abstract check(ctx: BashContext): readonly Violation[];\n}\n"]}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { BranchCreationGuardConfig } from '@webpieces/rules-config';
|
|
2
|
+
import type { BashContext, Violation } from '../types';
|
|
3
|
+
import { BashRuleBase } from '../rule-base';
|
|
4
|
+
export declare class BranchCreationGuardRule extends BashRuleBase<BranchCreationGuardConfig> {
|
|
5
|
+
constructor(config: BranchCreationGuardConfig);
|
|
6
|
+
readonly description = "Block new-branch creation when main is stale, or when not on main.";
|
|
7
|
+
readonly defaultOptions: {
|
|
8
|
+
subBranchNaming: string;
|
|
9
|
+
};
|
|
10
|
+
readonly fixHint: readonly string[];
|
|
11
|
+
check(ctx: BashContext): readonly Violation[];
|
|
12
|
+
}
|
|
@@ -1,47 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BranchCreationGuardRule = void 0;
|
|
3
4
|
const child_process_1 = require("child_process");
|
|
4
5
|
const types_1 = require("../types");
|
|
6
|
+
const rule_base_1 = require("../rule-base");
|
|
5
7
|
const FIX_HINT = [
|
|
6
8
|
"Run 'git checkout main && git pull origin main', then create your branch from main",
|
|
7
9
|
"If you truly need a sub-branch (requires human approval), name it using the convention in webpieces.config.json 'branch-creation-guard.subBranchNaming'",
|
|
8
10
|
];
|
|
9
|
-
const branchCreationGuard = {
|
|
10
|
-
name: 'branch-creation-guard',
|
|
11
|
-
description: 'Block new-branch creation when main is stale, or when not on main.',
|
|
12
|
-
scope: 'bash',
|
|
13
|
-
files: [],
|
|
14
|
-
defaultOptions: { subBranchNaming: 'feature/<ticket>/<short-description>' },
|
|
15
|
-
fixHint: FIX_HINT,
|
|
16
|
-
check(ctx) {
|
|
17
|
-
const requestedName = extractBranchName(ctx.command);
|
|
18
|
-
if (!requestedName)
|
|
19
|
-
return [];
|
|
20
|
-
const currentBranch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
|
|
21
|
-
cwd: ctx.workspaceRoot,
|
|
22
|
-
encoding: 'utf8',
|
|
23
|
-
}).trim();
|
|
24
|
-
if (currentBranch === 'main') {
|
|
25
|
-
return checkMainIsUpToDate(ctx, requestedName);
|
|
26
|
-
}
|
|
27
|
-
return [new types_1.Violation(1, truncate(ctx.command), `You are on '${currentBranch}', not main. Branches must be created from main.`)];
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
function checkMainIsUpToDate(ctx, requestedName) {
|
|
31
|
-
(0, child_process_1.execSync)('git fetch origin main --quiet', {
|
|
32
|
-
cwd: ctx.workspaceRoot,
|
|
33
|
-
encoding: 'utf8',
|
|
34
|
-
});
|
|
35
|
-
const countStr = (0, child_process_1.execSync)('git rev-list HEAD..origin/main --count', {
|
|
36
|
-
cwd: ctx.workspaceRoot,
|
|
37
|
-
encoding: 'utf8',
|
|
38
|
-
}).trim();
|
|
39
|
-
const count = parseInt(countStr, 10);
|
|
40
|
-
if (count > 0) {
|
|
41
|
-
return [new types_1.Violation(1, truncate(ctx.command), `Local main is ${count} commit(s) behind origin/main. Run 'git pull origin main' first, then retry creating branch '${requestedName}'.`)];
|
|
42
|
-
}
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
11
|
const BRANCH_PATTERNS = [
|
|
46
12
|
/git\s+checkout\s+-[bB]\s+([^\s]+)/,
|
|
47
13
|
/git\s+switch\s+-[cC]\s+([^\s]+)/,
|
|
@@ -59,5 +25,39 @@ function truncate(s) {
|
|
|
59
25
|
const MAX = 120;
|
|
60
26
|
return s.length <= MAX ? s : s.slice(0, MAX) + '…';
|
|
61
27
|
}
|
|
62
|
-
|
|
28
|
+
function checkMainIsUpToDate(ctx, requestedName) {
|
|
29
|
+
(0, child_process_1.execSync)('git fetch origin main --quiet', {
|
|
30
|
+
cwd: ctx.workspaceRoot,
|
|
31
|
+
encoding: 'utf8',
|
|
32
|
+
});
|
|
33
|
+
const countStr = (0, child_process_1.execSync)('git rev-list HEAD..origin/main --count', {
|
|
34
|
+
cwd: ctx.workspaceRoot,
|
|
35
|
+
encoding: 'utf8',
|
|
36
|
+
}).trim();
|
|
37
|
+
const count = parseInt(countStr, 10);
|
|
38
|
+
if (count > 0) {
|
|
39
|
+
return [new types_1.Violation(1, truncate(ctx.command), `Local main is ${count} commit(s) behind origin/main. Run 'git pull origin main' first, then retry creating branch '${requestedName}'.`)];
|
|
40
|
+
}
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
class BranchCreationGuardRule extends rule_base_1.BashRuleBase {
|
|
44
|
+
constructor(config) { super(config, 'branch-creation-guard'); }
|
|
45
|
+
description = 'Block new-branch creation when main is stale, or when not on main.';
|
|
46
|
+
defaultOptions = { subBranchNaming: 'feature/<ticket>/<short-description>' };
|
|
47
|
+
fixHint = FIX_HINT;
|
|
48
|
+
check(ctx) {
|
|
49
|
+
const requestedName = extractBranchName(ctx.command);
|
|
50
|
+
if (!requestedName)
|
|
51
|
+
return [];
|
|
52
|
+
const currentBranch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
|
|
53
|
+
cwd: ctx.workspaceRoot,
|
|
54
|
+
encoding: 'utf8',
|
|
55
|
+
}).trim();
|
|
56
|
+
if (currentBranch === 'main') {
|
|
57
|
+
return checkMainIsUpToDate(ctx, requestedName);
|
|
58
|
+
}
|
|
59
|
+
return [new types_1.Violation(1, truncate(ctx.command), `You are on '${currentBranch}', not main. Branches must be created from main.`)];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.BranchCreationGuardRule = BranchCreationGuardRule;
|
|
63
63
|
//# sourceMappingURL=branch-creation-guard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-creation-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/branch-creation-guard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"branch-creation-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/branch-creation-guard.ts"],"names":[],"mappings":";;;AAAA,iDAAyC;AAKzC,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,QAAQ,GAAsB;IAChC,oFAAoF;IACpF,yJAAyJ;CAC5J,CAAC;AAEF,MAAM,eAAe,GAAa;IAC9B,mCAAmC;IACnC,iCAAiC;IACjC,8CAA8C;CACjD,CAAC;AAEF,SAAS,iBAAiB,CAAC,OAAe;IACtC,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,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,SAAS,mBAAmB,CAAC,GAAgB,EAAE,aAAqB;IAChE,IAAA,wBAAQ,EAAC,+BAA+B,EAAE;QACtC,GAAG,EAAE,GAAG,CAAC,aAAa;QACtB,QAAQ,EAAE,MAAM;KACnB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,IAAA,wBAAQ,EAAC,wCAAwC,EAAE;QAChE,GAAG,EAAE,GAAG,CAAC,aAAa;QACtB,QAAQ,EAAE,MAAM;KACnB,CAAC,CAAC,IAAI,EAAE,CAAC;IACV,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB,iBAAiB,KAAK,gGAAgG,aAAa,IAAI,CAC1I,CAAC,CAAC;IACP,CAAC;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED,MAAa,uBAAwB,SAAQ,wBAAuC;IAChF,YAAY,MAAiC,IAAI,KAAK,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC;IAEjF,WAAW,GAAG,oEAAoE,CAAC;IAC1E,cAAc,GAAG,EAAE,eAAe,EAAE,sCAAsC,EAAE,CAAC;IACtF,OAAO,GAAG,QAAQ,CAAC;IAE5B,KAAK,CAAC,GAAgB;QAClB,MAAM,aAAa,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrD,IAAI,CAAC,aAAa;YAAE,OAAO,EAAE,CAAC;QAE9B,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,IAAI,aAAa,KAAK,MAAM,EAAE,CAAC;YAC3B,OAAO,mBAAmB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB,eAAe,aAAa,kDAAkD,CACjF,CAAC,CAAC;IACP,CAAC;CACJ;AA1BD,0DA0BC","sourcesContent":["import { execSync } from 'child_process';\n\nimport { BranchCreationGuardConfig } 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 \"Run 'git checkout main && git pull origin main', then create your branch from main\",\n \"If you truly need a sub-branch (requires human approval), name it using the convention in webpieces.config.json 'branch-creation-guard.subBranchNaming'\",\n];\n\nconst BRANCH_PATTERNS: RegExp[] = [\n /git\\s+checkout\\s+-[bB]\\s+([^\\s]+)/,\n /git\\s+switch\\s+-[cC]\\s+([^\\s]+)/,\n /git\\s+branch\\s+(?!-[dDmMrRla])([^\\s-][^\\s]*)/,\n];\n\nfunction extractBranchName(command: string): string | null {\n for (const pattern of BRANCH_PATTERNS) {\n const m = pattern.exec(command);\n if (m) return m[1];\n }\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\nfunction checkMainIsUpToDate(ctx: BashContext, requestedName: string): readonly Violation[] {\n execSync('git fetch origin main --quiet', {\n cwd: ctx.workspaceRoot,\n encoding: 'utf8',\n });\n const countStr = execSync('git rev-list HEAD..origin/main --count', {\n cwd: ctx.workspaceRoot,\n encoding: 'utf8',\n }).trim();\n const count = parseInt(countStr, 10);\n if (count > 0) {\n return [new V(\n 1,\n truncate(ctx.command),\n `Local main is ${count} commit(s) behind origin/main. Run 'git pull origin main' first, then retry creating branch '${requestedName}'.`,\n )];\n }\n return [];\n}\n\nexport class BranchCreationGuardRule extends BashRuleBase<BranchCreationGuardConfig> {\n constructor(config: BranchCreationGuardConfig) { super(config, 'branch-creation-guard'); }\n\n readonly description = 'Block new-branch creation when main is stale, or when not on main.';\n override readonly defaultOptions = { subBranchNaming: 'feature/<ticket>/<short-description>' };\n readonly fixHint = FIX_HINT;\n\n check(ctx: BashContext): readonly Violation[] {\n const requestedName = extractBranchName(ctx.command);\n if (!requestedName) return [];\n\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd: ctx.workspaceRoot,\n encoding: 'utf8',\n }).trim();\n\n if (currentBranch === 'main') {\n return checkMainIsUpToDate(ctx, requestedName);\n }\n\n return [new V(\n 1,\n truncate(ctx.command),\n `You are on '${currentBranch}', not main. Branches must be created from main.`,\n )];\n }\n}\n"]}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { CatchErrorPatternConfig } from '@webpieces/rules-config';
|
|
2
|
+
import type { EditContext, Violation } from '../types';
|
|
3
|
+
import { EditRuleBase } from '../rule-base';
|
|
4
|
+
export declare class CatchErrorPatternRule extends EditRuleBase<CatchErrorPatternConfig> {
|
|
5
|
+
constructor(config: CatchErrorPatternConfig);
|
|
6
|
+
readonly description = "Catch blocks must use: catch (err: unknown) { const error = toError(err); }";
|
|
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.CatchErrorPatternRule = void 0;
|
|
4
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
3
5
|
const types_1 = require("../types");
|
|
6
|
+
const rule_base_1 = require("../rule-base");
|
|
4
7
|
const instruct_ai_writer_1 = require("../instruct-ai-writer");
|
|
5
8
|
/**
|
|
6
9
|
* Matches a catch clause opening: } catch (paramName: typeAnnotation) {
|
|
@@ -12,18 +15,31 @@ const CATCH_PATTERN = /\bcatch\s*\(\s*(\w+)(?:\s*:\s*(\w+))?\s*\)/;
|
|
|
12
15
|
* Group 1 = variable name, group 2 = param passed to toError
|
|
13
16
|
*/
|
|
14
17
|
const TO_ERROR_PATTERN = /^\s*(?:\/\/\s*)?const\s+(\w+)\s*=\s*toError\(\s*(\w+)\s*\)\s*;?\s*$/;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
function findToErrorStatement(lines, startIndex) {
|
|
19
|
+
for (let j = startIndex; j < lines.length; j += 1) {
|
|
20
|
+
const line = lines[j].trim();
|
|
21
|
+
if (line === '' || line === '{')
|
|
22
|
+
continue;
|
|
23
|
+
const match = TO_ERROR_PATTERN.exec(line);
|
|
24
|
+
if (match) {
|
|
25
|
+
return { varName: match[1], paramName: match[2], lineIndex: j };
|
|
26
|
+
}
|
|
27
|
+
// First non-blank line is not a toError call
|
|
28
|
+
return 'not-found';
|
|
29
|
+
}
|
|
30
|
+
// Ran off the end of the edit content — can't validate further
|
|
31
|
+
return 'end-of-content';
|
|
32
|
+
}
|
|
33
|
+
class CatchErrorPatternRule extends rule_base_1.EditRuleBase {
|
|
34
|
+
constructor(config) { super(config, 'catch-error-pattern'); }
|
|
35
|
+
description = 'Catch blocks must use: catch (err: unknown) { const error = toError(err); }'; // webpieces-disable catch-error-pattern -- example text in a description string
|
|
36
|
+
files = ['**/*.ts', '**/*.tsx'];
|
|
37
|
+
fixHint = [
|
|
22
38
|
'Add as first statement inside the catch block: const error = toError(err);',
|
|
23
39
|
'To explicitly ignore the error: //const error = toError(err);',
|
|
24
|
-
'For nested catches use numbered names: catch (err2: unknown) { const error2 = toError(err2); }',
|
|
40
|
+
'For nested catches use numbered names: catch (err2: unknown) { const error2 = toError(err2); }', // webpieces-disable catch-error-pattern -- example text in a hint string
|
|
25
41
|
'To suppress this rule: // webpieces-disable catch-error-pattern -- <reason>',
|
|
26
|
-
]
|
|
42
|
+
];
|
|
27
43
|
check(ctx) {
|
|
28
44
|
const violations = [];
|
|
29
45
|
const lines = ctx.strippedLines;
|
|
@@ -33,7 +49,7 @@ const catchErrorPatternRule = {
|
|
|
33
49
|
if (!catchMatch)
|
|
34
50
|
continue;
|
|
35
51
|
const lineNum = i + 1;
|
|
36
|
-
if (ctx.isLineDisabled(lineNum,
|
|
52
|
+
if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.CATCH_ERROR_PATTERN))
|
|
37
53
|
continue;
|
|
38
54
|
const actualParam = catchMatch[1];
|
|
39
55
|
const typeAnnotation = catchMatch[2];
|
|
@@ -73,22 +89,7 @@ const catchErrorPatternRule = {
|
|
|
73
89
|
if (violations.length > 0)
|
|
74
90
|
(0, instruct_ai_writer_1.writeTemplateIfMissing)(ctx.workspaceRoot, 'webpieces.exceptions.md');
|
|
75
91
|
return violations;
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
function findToErrorStatement(lines, startIndex) {
|
|
79
|
-
for (let j = startIndex; j < lines.length; j += 1) {
|
|
80
|
-
const line = lines[j].trim();
|
|
81
|
-
if (line === '' || line === '{')
|
|
82
|
-
continue;
|
|
83
|
-
const match = TO_ERROR_PATTERN.exec(line);
|
|
84
|
-
if (match) {
|
|
85
|
-
return { varName: match[1], paramName: match[2], lineIndex: j };
|
|
86
|
-
}
|
|
87
|
-
// First non-blank line is not a toError call
|
|
88
|
-
return 'not-found';
|
|
89
92
|
}
|
|
90
|
-
// Ran off the end of the edit content — can't validate further
|
|
91
|
-
return 'end-of-content';
|
|
92
93
|
}
|
|
93
|
-
exports.
|
|
94
|
+
exports.CatchErrorPatternRule = CatchErrorPatternRule;
|
|
94
95
|
//# sourceMappingURL=catch-error-pattern.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catch-error-pattern.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/catch-error-pattern.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"catch-error-pattern.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/catch-error-pattern.ts"],"names":[],"mappings":"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","sourcesContent":["import { CatchErrorPatternConfig, RULE_NAMES } from '@webpieces/rules-config';\n\nimport type { EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { EditRuleBase } from '../rule-base';\nimport { writeTemplateIfMissing } from '../instruct-ai-writer';\n\n/**\n * Matches a catch clause opening: } catch (paramName: typeAnnotation) {\n * Captures: group 1 = param name, group 2 = type annotation (if present)\n */\nconst CATCH_PATTERN = /\\bcatch\\s*\\(\\s*(\\w+)(?:\\s*:\\s*(\\w+))?\\s*\\)/;\n\n/**\n * Matches the required toError first statement (with or without comment-out).\n * Group 1 = variable name, group 2 = param passed to toError\n */\nconst TO_ERROR_PATTERN = /^\\s*(?:\\/\\/\\s*)?const\\s+(\\w+)\\s*=\\s*toError\\(\\s*(\\w+)\\s*\\)\\s*;?\\s*$/;\n\ninterface ToErrorMatch {\n varName: string;\n paramName: string;\n lineIndex: number;\n}\n\nfunction findToErrorStatement(lines: readonly string[], startIndex: number): ToErrorMatch | 'not-found' | 'end-of-content' {\n for (let j = startIndex; j < lines.length; j += 1) {\n const line = lines[j].trim();\n if (line === '' || line === '{') continue;\n\n const match = TO_ERROR_PATTERN.exec(line);\n if (match) {\n return { varName: match[1], paramName: match[2], lineIndex: j };\n }\n // First non-blank line is not a toError call\n return 'not-found';\n }\n // Ran off the end of the edit content — can't validate further\n return 'end-of-content';\n}\n\nexport class CatchErrorPatternRule extends EditRuleBase<CatchErrorPatternConfig> {\n constructor(config: CatchErrorPatternConfig) { super(config, 'catch-error-pattern'); }\n\n readonly description = 'Catch blocks must use: catch (err: unknown) { const error = toError(err); }'; // webpieces-disable catch-error-pattern -- example text in a description string\n override readonly files = ['**/*.ts', '**/*.tsx'];\n readonly fixHint = [\n 'Add as first statement inside the catch block: const error = toError(err);',\n 'To explicitly ignore the error: //const error = toError(err);',\n 'For nested catches use numbered names: catch (err2: unknown) { const error2 = toError(err2); }', // webpieces-disable catch-error-pattern -- example text in a hint string\n 'To suppress this rule: // webpieces-disable catch-error-pattern -- <reason>',\n ];\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n const lines = ctx.strippedLines;\n\n for (let i = 0; i < lines.length; i += 1) {\n const stripped = lines[i];\n const catchMatch = CATCH_PATTERN.exec(stripped);\n if (!catchMatch) continue;\n\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.CATCH_ERROR_PATTERN)) continue;\n\n const actualParam = catchMatch[1];\n const typeAnnotation = catchMatch[2];\n\n // Determine expected names from suffix on the actual param (err, err2, err3...)\n const suffixMatch = actualParam.match(/^err(\\d*)$/);\n const suffix = suffixMatch ? suffixMatch[1] : '';\n const expectedParam = 'err' + suffix;\n const expectedVar = 'error' + suffix;\n\n // Check parameter name\n if (actualParam !== expectedParam) {\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n `Catch parameter must be named \"${expectedParam}\" (or \"err2\", \"err3\" for nested catches), got \"${actualParam}\"`,\n ));\n }\n\n // Check type annotation is unknown\n if (typeAnnotation !== 'unknown') {\n const msg = typeAnnotation\n ? `Catch parameter must be typed as \"unknown\": catch (${expectedParam}: unknown), got \"${typeAnnotation}\"`\n : `Catch parameter must be typed as \"unknown\": catch (${expectedParam}: unknown)`;\n violations.push(new V(lineNum, ctx.lines[i].trim(), msg));\n }\n\n // Find next non-blank line after the catch opening to check for toError\n const toErrorResult = findToErrorStatement(lines, i + 1);\n if (toErrorResult === 'not-found') {\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n `Catch block must call toError(${actualParam}) as first statement: const ${expectedVar} = toError(${actualParam}); or //const ${expectedVar} = toError(${actualParam});`,\n ));\n } else if (toErrorResult !== 'end-of-content') {\n // Validate variable name and param match\n if (toErrorResult.varName !== expectedVar) {\n const toErrorLineNum = toErrorResult.lineIndex + 1;\n violations.push(new V(\n toErrorLineNum,\n ctx.lines[toErrorResult.lineIndex].trim(),\n `Error variable must be named \"${expectedVar}\", got \"${toErrorResult.varName}\"`,\n ));\n }\n if (toErrorResult.paramName !== actualParam) {\n const toErrorLineNum = toErrorResult.lineIndex + 1;\n violations.push(new V(\n toErrorLineNum,\n ctx.lines[toErrorResult.lineIndex].trim(),\n `toError() must be called with \"${actualParam}\", got \"${toErrorResult.paramName}\"`,\n ));\n }\n }\n }\n if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');\n return violations;\n }\n}\n"]}
|
package/src/core/rules/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/index.ts"],"names":[],"mappings":";;;AAAa,QAAA,gBAAgB,GAAsB;IAC/C,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,oBAAoB;IACpB,aAAa;IACb,gBAAgB;IAChB,qBAAqB;IACrB,yBAAyB;IACzB,qBAAqB;IACrB,sBAAsB;IACtB,uBAAuB;IACvB,qBAAqB;IACrB,uBAAuB;IACvB,mBAAmB;IACnB,kBAAkB;IAClB,uBAAuB;IACvB,iBAAiB;CACpB,CAAC","sourcesContent":["export const builtInRuleNames: readonly string[] = [\n 'no-any-unknown',\n 'no-implicit-any',\n 'max-file-lines',\n 'validate-ts-in-src',\n 'no-js-files',\n 'no-destructure',\n 'require-return-type',\n 'no-unmanaged-exceptions',\n 'catch-error-pattern',\n 'throw-cause-required',\n 'no-shell-substitution',\n 'no-symbol-di-tokens',\n 'branch-creation-guard',\n 'pr-creation-guard',\n 'pr-merge-cleanup',\n 'no-direct-main-update',\n 'no-edit-on-main',\n];\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/index.ts"],"names":[],"mappings":";;;AAAa,QAAA,gBAAgB,GAAsB;IAC/C,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,oBAAoB;IACpB,aAAa;IACb,gBAAgB;IAChB,qBAAqB;IACrB,yBAAyB;IACzB,qBAAqB;IACrB,sBAAsB;IACtB,uBAAuB;IACvB,qBAAqB;IACrB,uBAAuB;IACvB,mBAAmB;IACnB,yBAAyB;IACzB,kBAAkB;IAClB,uBAAuB;IACvB,iBAAiB;CACpB,CAAC","sourcesContent":["export const builtInRuleNames: readonly string[] = [\n 'no-any-unknown',\n 'no-implicit-any',\n 'max-file-lines',\n 'validate-ts-in-src',\n 'no-js-files',\n 'no-destructure',\n 'require-return-type',\n 'no-unmanaged-exceptions',\n 'catch-error-pattern',\n 'throw-cause-required',\n 'no-shell-substitution',\n 'no-symbol-di-tokens',\n 'branch-creation-guard',\n 'pr-creation-guard',\n 'merge-in-progress-guard',\n 'pr-merge-cleanup',\n 'no-direct-main-update',\n 'no-edit-on-main',\n];\n"]}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { MaxFileLinesConfig } from '@webpieces/rules-config';
|
|
2
|
+
import type { FileContext, Violation } from '../types';
|
|
3
|
+
import { FileRuleBase } from '../rule-base';
|
|
4
|
+
export declare class MaxFileLinesRule extends FileRuleBase<MaxFileLinesConfig> {
|
|
5
|
+
constructor(config: MaxFileLinesConfig);
|
|
6
|
+
readonly description = "Cap file length at a configured line limit.";
|
|
7
|
+
readonly files: string[];
|
|
8
|
+
readonly defaultOptions: {
|
|
9
|
+
limit: number;
|
|
10
|
+
};
|
|
11
|
+
readonly fixHint: string[];
|
|
12
|
+
check(ctx: FileContext): readonly Violation[];
|
|
13
|
+
}
|
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
3
|
+
exports.MaxFileLinesRule = void 0;
|
|
4
4
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
const rule_base_1 = require("../rule-base");
|
|
5
7
|
const DEFAULT_LIMIT = 900;
|
|
6
8
|
const INSTRUCT_FILE = 'webpieces.filesize.md';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
description
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
fixHint: [
|
|
9
|
+
class MaxFileLinesRule extends rule_base_1.FileRuleBase {
|
|
10
|
+
constructor(config) { super(config, 'max-file-lines'); }
|
|
11
|
+
description = 'Cap file length at a configured line limit.';
|
|
12
|
+
files = ['**/*.ts', '**/*.tsx'];
|
|
13
|
+
defaultOptions = { limit: DEFAULT_LIMIT };
|
|
14
|
+
fixHint = [
|
|
14
15
|
'READ .webpieces/instruct-ai/webpieces.filesize.md for step-by-step refactoring guidance.',
|
|
15
16
|
'// eslint-disable-next-line @webpieces/max-file-lines (also suppresses the eslint rule)',
|
|
16
|
-
]
|
|
17
|
+
];
|
|
17
18
|
check(ctx) {
|
|
18
|
-
const limit =
|
|
19
|
-
? ctx.options['limit']
|
|
20
|
-
: DEFAULT_LIMIT;
|
|
19
|
+
const limit = this.config.limit ?? DEFAULT_LIMIT;
|
|
21
20
|
if (ctx.projectedFileLines <= limit)
|
|
22
21
|
return [];
|
|
23
22
|
(0, rules_config_1.writeTemplateIfMissing)(ctx.workspaceRoot, INSTRUCT_FILE);
|
|
24
23
|
return [new types_1.Violation(1, `(projected ${String(ctx.projectedFileLines)} lines)`, `File will be ${String(ctx.projectedFileLines)} lines, exceeding the ${String(limit)}-line limit. See .webpieces/instruct-ai/webpieces.filesize.md for detailed refactoring instructions.`)];
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
exports.
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.MaxFileLinesRule = MaxFileLinesRule;
|
|
28
27
|
//# sourceMappingURL=max-file-lines.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"max-file-lines.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/max-file-lines.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"max-file-lines.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/max-file-lines.ts"],"names":[],"mappings":";;;AAAA,0DAAqF;AAGrF,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAE9C,MAAa,gBAAiB,SAAQ,wBAAgC;IAClE,YAAY,MAA0B,IAAI,KAAK,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAEnE,WAAW,GAAG,6CAA6C,CAAC;IACnD,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChC,cAAc,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;IACnD,OAAO,GAAG;QACf,0FAA0F;QAC1F,0FAA0F;KAC7F,CAAC;IAEF,KAAK,CAAC,GAAgB;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,aAAa,CAAC;QACjD,IAAI,GAAG,CAAC,kBAAkB,IAAI,KAAK;YAAE,OAAO,EAAE,CAAC;QAC/C,IAAA,qCAAsB,EAAC,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QACzD,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,cAAc,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,SAAS,EACrD,gBAAgB,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,sGAAsG,CAC7L,CAAC,CAAC;IACP,CAAC;CACJ;AArBD,4CAqBC","sourcesContent":["import { MaxFileLinesConfig, writeTemplateIfMissing } from '@webpieces/rules-config';\n\nimport type { FileContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { FileRuleBase } from '../rule-base';\n\nconst DEFAULT_LIMIT = 900;\nconst INSTRUCT_FILE = 'webpieces.filesize.md';\n\nexport class MaxFileLinesRule extends FileRuleBase<MaxFileLinesConfig> {\n constructor(config: MaxFileLinesConfig) { super(config, 'max-file-lines'); }\n\n readonly description = 'Cap file length at a configured line limit.';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n override readonly defaultOptions = { limit: DEFAULT_LIMIT };\n readonly fixHint = [\n 'READ .webpieces/instruct-ai/webpieces.filesize.md for step-by-step refactoring guidance.',\n '// eslint-disable-next-line @webpieces/max-file-lines (also suppresses the eslint rule)',\n ];\n\n check(ctx: FileContext): readonly Violation[] {\n const limit = this.config.limit ?? DEFAULT_LIMIT;\n if (ctx.projectedFileLines <= limit) return [];\n writeTemplateIfMissing(ctx.workspaceRoot, INSTRUCT_FILE);\n return [new V(\n 1,\n `(projected ${String(ctx.projectedFileLines)} lines)`,\n `File will be ${String(ctx.projectedFileLines)} lines, exceeding the ${String(limit)}-line limit. See .webpieces/instruct-ai/webpieces.filesize.md for detailed refactoring instructions.`,\n )];\n }\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MergeInProgressGuardConfig } from '@webpieces/rules-config';
|
|
2
|
+
import type { BashContext, Violation } from '../types';
|
|
3
|
+
import { BashRuleBase } from '../rule-base';
|
|
4
|
+
export declare class MergeInProgressGuardRule extends BashRuleBase<MergeInProgressGuardConfig> {
|
|
5
|
+
constructor(config: MergeInProgressGuardConfig);
|
|
6
|
+
readonly description = "Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-git-merge-complete.";
|
|
7
|
+
readonly fixHint: readonly string[];
|
|
8
|
+
check(ctx: BashContext): readonly Violation[];
|
|
9
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MergeInProgressGuardRule = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const path = tslib_1.__importStar(require("path"));
|
|
7
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
const rule_base_1 = require("../rule-base");
|
|
10
|
+
const FIX_HINT = [
|
|
11
|
+
'A 3-point merge is in progress and not yet validated.',
|
|
12
|
+
'Resolve the remaining conflicts in the working tree, then run:',
|
|
13
|
+
' pnpm wp-git-merge-complete',
|
|
14
|
+
'That scans for leftover conflict markers and runs the build; only when green does it',
|
|
15
|
+
'commit and unblock commit/push/PR. Then run: pnpm wp-upsert-pr',
|
|
16
|
+
];
|
|
17
|
+
// Returns the path of the first UNVALIDATED merge marker found, or null. We detect validation
|
|
18
|
+
// by a raw substring (no JSON.parse) so a malformed marker can never crash the guard.
|
|
19
|
+
function findUnvalidatedMerge(workspaceRoot) {
|
|
20
|
+
const tmpDir = path.join(workspaceRoot, rules_config_1.WEBPIECES_TMP_DIR);
|
|
21
|
+
if (!fs.existsSync(tmpDir))
|
|
22
|
+
return null;
|
|
23
|
+
for (const entry of fs.readdirSync(tmpDir)) {
|
|
24
|
+
if (!entry.startsWith(rules_config_1.MERGE_DIR_PREFIX))
|
|
25
|
+
continue;
|
|
26
|
+
const marker = path.join(tmpDir, entry, rules_config_1.MERGE_IN_PROGRESS_FILE);
|
|
27
|
+
if (!fs.existsSync(marker))
|
|
28
|
+
continue;
|
|
29
|
+
const raw = fs.readFileSync(marker, 'utf8');
|
|
30
|
+
if (!/"validated"\s*:\s*true/.test(raw))
|
|
31
|
+
return marker;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// Operations that would let an agent route around the merge gate.
|
|
36
|
+
function isBlockedDuringMerge(cmd) {
|
|
37
|
+
return /\bgit\s+commit\b/.test(cmd)
|
|
38
|
+
|| /\bgit\s+push\b/.test(cmd)
|
|
39
|
+
|| /\bgit\s+merge\b/.test(cmd)
|
|
40
|
+
|| /\bgit\s+rebase\b/.test(cmd)
|
|
41
|
+
|| /\bgh\s+pr\s+(create|edit|merge)\b/.test(cmd);
|
|
42
|
+
}
|
|
43
|
+
function truncate(s) {
|
|
44
|
+
const MAX = 120;
|
|
45
|
+
return s.length <= MAX ? s : s.slice(0, MAX) + '…';
|
|
46
|
+
}
|
|
47
|
+
class MergeInProgressGuardRule extends rule_base_1.BashRuleBase {
|
|
48
|
+
constructor(config) { super(config, 'merge-in-progress-guard'); }
|
|
49
|
+
description = 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-git-merge-complete.';
|
|
50
|
+
fixHint = FIX_HINT;
|
|
51
|
+
check(ctx) {
|
|
52
|
+
if (!isBlockedDuringMerge(ctx.command))
|
|
53
|
+
return [];
|
|
54
|
+
const marker = findUnvalidatedMerge(ctx.workspaceRoot);
|
|
55
|
+
if (!marker)
|
|
56
|
+
return [];
|
|
57
|
+
return [new types_1.Violation(1, truncate(ctx.command), [
|
|
58
|
+
'A merge is in progress and not yet validated — this command is blocked.',
|
|
59
|
+
`Marker: ${marker}`,
|
|
60
|
+
'Finish resolving conflicts, then run: pnpm wp-git-merge-complete',
|
|
61
|
+
].join('\n'))];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.MergeInProgressGuardRule = MergeInProgressGuardRule;
|
|
65
|
+
//# sourceMappingURL=merge-in-progress-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-in-progress-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/merge-in-progress-guard.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAE7B,0DAAkI;AAGlI,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,QAAQ,GAAsB;IAChC,uDAAuD;IACvD,gEAAgE;IAChE,8BAA8B;IAC9B,sFAAsF;IACtF,gEAAgE;CACnE,CAAC;AAEF,8FAA8F;AAC9F,sFAAsF;AACtF,SAAS,oBAAoB,CAAC,aAAqB;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,gCAAiB,CAAC,CAAC;IAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,+BAAgB,CAAC;YAAE,SAAS;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,qCAAsB,CAAC,CAAC;QAChE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,SAAS;QACrC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,MAAM,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,kEAAkE;AAClE,SAAS,oBAAoB,CAAC,GAAW;IACrC,OAAO,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC5B,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC1B,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC3B,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC5B,mCAAmC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzD,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,wBAAyB,SAAQ,wBAAwC;IAClF,YAAY,MAAkC,IAAI,KAAK,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC;IAEpF,WAAW,GAAG,6GAA6G,CAAC;IAC5H,OAAO,GAAG,QAAQ,CAAC;IAE5B,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,yEAAyE;gBACzE,WAAW,MAAM,EAAE;gBACnB,mEAAmE;aACtE,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ;AApBD,4DAoBC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { WEBPIECES_TMP_DIR, MERGE_DIR_PREFIX, MERGE_IN_PROGRESS_FILE, MergeInProgressGuardConfig } 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 'A 3-point merge is in progress and not yet validated.',\n 'Resolve the remaining conflicts in the working tree, then run:',\n ' pnpm wp-git-merge-complete',\n 'That scans for leftover conflict markers and runs the build; only when green does it',\n 'commit and unblock commit/push/PR. Then run: pnpm wp-upsert-pr',\n];\n\n// Returns the path of the first UNVALIDATED merge marker found, or null. We detect validation\n// by a raw substring (no JSON.parse) so a malformed marker can never crash the guard.\nfunction findUnvalidatedMerge(workspaceRoot: string): string | null {\n const tmpDir = path.join(workspaceRoot, WEBPIECES_TMP_DIR);\n if (!fs.existsSync(tmpDir)) return null;\n for (const entry of fs.readdirSync(tmpDir)) {\n if (!entry.startsWith(MERGE_DIR_PREFIX)) continue;\n const marker = path.join(tmpDir, entry, MERGE_IN_PROGRESS_FILE);\n if (!fs.existsSync(marker)) continue;\n const raw = fs.readFileSync(marker, 'utf8');\n if (!/\"validated\"\\s*:\\s*true/.test(raw)) return marker;\n }\n return null;\n}\n\n// Operations that would let an agent route around the merge gate.\nfunction isBlockedDuringMerge(cmd: string): boolean {\n return /\\bgit\\s+commit\\b/.test(cmd)\n || /\\bgit\\s+push\\b/.test(cmd)\n || /\\bgit\\s+merge\\b/.test(cmd)\n || /\\bgit\\s+rebase\\b/.test(cmd)\n || /\\bgh\\s+pr\\s+(create|edit|merge)\\b/.test(cmd);\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 MergeInProgressGuardRule extends BashRuleBase<MergeInProgressGuardConfig> {\n constructor(config: MergeInProgressGuardConfig) { super(config, 'merge-in-progress-guard'); }\n\n readonly description = 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-git-merge-complete.';\n readonly fixHint = FIX_HINT;\n\n check(ctx: BashContext): readonly Violation[] {\n if (!isBlockedDuringMerge(ctx.command)) return [];\n const marker = findUnvalidatedMerge(ctx.workspaceRoot);\n if (!marker) return [];\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'A merge is in progress and not yet validated — this command is blocked.',\n `Marker: ${marker}`,\n 'Finish resolving conflicts, then run: pnpm wp-git-merge-complete',\n ].join('\\n'),\n )];\n }\n}\n"]}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { NoAnyUnknownConfig } from '@webpieces/rules-config';
|
|
2
|
+
import type { EditContext, Violation } from '../types';
|
|
3
|
+
import { EditRuleBase } from '../rule-base';
|
|
4
|
+
export declare class NoAnyUnknownRule extends EditRuleBase<NoAnyUnknownConfig> {
|
|
5
|
+
constructor(config: NoAnyUnknownConfig);
|
|
6
|
+
readonly description = "Disallow the `any` keyword. Use concrete types or interfaces.";
|
|
7
|
+
readonly files: string[];
|
|
8
|
+
readonly fixHint: string[];
|
|
9
|
+
check(ctx: EditContext): readonly Violation[];
|
|
10
|
+
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoAnyUnknownRule = void 0;
|
|
4
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
3
5
|
const types_1 = require("../types");
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
fixHint
|
|
6
|
+
const rule_base_1 = require("../rule-base");
|
|
7
|
+
// This regex literal matches the token as text, not a TS type.
|
|
8
|
+
const ANY_PATTERN = /(?::\s*any\b|\bas\s+any\b|<any>|any\[\]|Array<any>|Promise<any>|Map<[^,<>]+,\s*any\s*>|Record<[^,<>]+,\s*any\s*>|Set<any>)/; // webpieces-disable no-any-unknown -- regex literal, not a type
|
|
9
|
+
class NoAnyUnknownRule extends rule_base_1.EditRuleBase {
|
|
10
|
+
constructor(config) { super(config, 'no-any-unknown'); }
|
|
11
|
+
description = 'Disallow the `any` keyword. Use concrete types or interfaces.';
|
|
12
|
+
files = ['**/*.ts', '**/*.tsx'];
|
|
13
|
+
fixHint = [
|
|
12
14
|
'Prefer: interface MyData { ... } or class MyData { ... }',
|
|
13
15
|
'// webpieces-disable no-any-unknown -- <one-line reason>',
|
|
14
|
-
]
|
|
16
|
+
];
|
|
15
17
|
check(ctx) {
|
|
16
18
|
const violations = [];
|
|
17
19
|
for (let i = 0; i < ctx.strippedLines.length; i += 1) {
|
|
@@ -19,12 +21,12 @@ const noAnyRule = {
|
|
|
19
21
|
if (!ANY_PATTERN.test(stripped))
|
|
20
22
|
continue;
|
|
21
23
|
const lineNum = i + 1;
|
|
22
|
-
if (ctx.isLineDisabled(lineNum,
|
|
24
|
+
if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.NO_ANY_UNKNOWN))
|
|
23
25
|
continue;
|
|
24
26
|
violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), '`any` erases type information. Use a concrete type, an interface, or `unknown` with type guards.'));
|
|
25
27
|
}
|
|
26
28
|
return violations;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
exports.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.NoAnyUnknownRule = NoAnyUnknownRule;
|
|
30
32
|
//# sourceMappingURL=no-any-unknown.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-any-unknown.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-any-unknown.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-any-unknown.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-any-unknown.ts"],"names":[],"mappings":";;;AAAA,0DAAyE;AAGzE,oCAA0C;AAC1C,4CAA4C;AAE5C,+DAA+D;AAC/D,MAAM,WAAW,GACb,4HAA4H,CAAC,CAAC,gEAAgE;AAElM,MAAa,gBAAiB,SAAQ,wBAAgC;IAClE,YAAY,MAA0B,IAAI,KAAK,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAEnE,WAAW,GAAG,+DAA+D,CAAC;IACrE,KAAK,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACzC,OAAO,GAAG;QACf,8DAA8D;QAC9D,0DAA0D;KAC7D,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,cAAc,CAAC;gBAAE,SAAS;YACrE,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,kGAAkG,CACrG,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ;AAzBD,4CAyBC","sourcesContent":["import { NoAnyUnknownConfig, 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// This regex literal matches the token as text, not a TS type.\nconst ANY_PATTERN =\n /(?::\\s*any\\b|\\bas\\s+any\\b|<any>|any\\[\\]|Array<any>|Promise<any>|Map<[^,<>]+,\\s*any\\s*>|Record<[^,<>]+,\\s*any\\s*>|Set<any>)/; // webpieces-disable no-any-unknown -- regex literal, not a type\n\nexport class NoAnyUnknownRule extends EditRuleBase<NoAnyUnknownConfig> {\n constructor(config: NoAnyUnknownConfig) { super(config, 'no-any-unknown'); }\n\n readonly description = 'Disallow the `any` keyword. Use concrete types or interfaces.';\n override readonly files = ['**/*.ts', '**/*.tsx'];\n readonly fixHint = [\n 'Prefer: interface MyData { ... } or class MyData { ... }',\n '// webpieces-disable no-any-unknown -- <one-line 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 (!ANY_PATTERN.test(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.NO_ANY_UNKNOWN)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n '`any` erases type information. Use a concrete type, an interface, or `unknown` with type guards.',\n ));\n }\n return violations;\n }\n}\n"]}
|