@webpieces/ai-hook-rules 0.3.183 → 0.3.185
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 +3 -6
- package/src/adapters/global-hook.js +3 -3
- package/src/adapters/global-hook.js.map +1 -1
- 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.js +2 -0
- package/src/core/load-rules.js.map +1 -1
- package/src/core/rules/catch-error-pattern.js +2 -1
- 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/merge-in-progress-guard.d.ts +3 -0
- package/src/core/rules/merge-in-progress-guard.js +66 -0
- package/src/core/rules/merge-in-progress-guard.js.map +1 -0
- package/src/core/rules/no-any-unknown.js +2 -1
- package/src/core/rules/no-any-unknown.js.map +1 -1
- package/src/core/rules/no-destructure.js +2 -1
- package/src/core/rules/no-destructure.js.map +1 -1
- package/src/core/rules/no-implicit-any.js +2 -1
- package/src/core/rules/no-implicit-any.js.map +1 -1
- package/src/core/rules/no-symbol-di-tokens.js +2 -1
- package/src/core/rules/no-symbol-di-tokens.js.map +1 -1
- package/src/core/rules/no-unmanaged-exceptions.js +2 -1
- package/src/core/rules/no-unmanaged-exceptions.js.map +1 -1
- package/src/core/rules/pr-creation-guard.js +29 -57
- package/src/core/rules/pr-creation-guard.js.map +1 -1
- package/src/core/rules/require-return-type.js +2 -1
- package/src/core/rules/require-return-type.js.map +1 -1
- package/src/core/rules/throw-cause-required.js +2 -1
- package/src/core/rules/throw-cause-required.js.map +1 -1
- package/src/core/to-error.js +0 -1
- package/src/core/to-error.js.map +1 -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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/ai-hook-rules",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.185",
|
|
4
4
|
"description": "Pluggable write-time validation framework for AI coding agents (@webpieces/ai-hook-rules). Claude Code PreToolUse + openclaw before_tool_call adapters share one rule engine.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -9,10 +9,7 @@
|
|
|
9
9
|
"wp-setup-ai-hooks": "./bin/wp-setup-ai-hooks.js",
|
|
10
10
|
"wp-setup-global-ai-hooks": "./bin/wp-setup-global-ai-hooks.js",
|
|
11
11
|
"wp-dev-hook-install": "./bin/wp-dev-hook-install.js",
|
|
12
|
-
"wp-dev-hook-uninstall": "./bin/wp-dev-hook-uninstall.js"
|
|
13
|
-
"wp-git-update": "./src/scripts/git-updateFromMain.js",
|
|
14
|
-
"wp-git-gather": "./src/scripts/git-gatherInfo.js",
|
|
15
|
-
"wp-git-merge-complete": "./src/scripts/git-mergeComplete.js"
|
|
12
|
+
"wp-dev-hook-uninstall": "./bin/wp-dev-hook-uninstall.js"
|
|
16
13
|
},
|
|
17
14
|
"exports": {
|
|
18
15
|
".": "./src/index.js",
|
|
@@ -36,7 +33,7 @@
|
|
|
36
33
|
"directory": "packages/tooling/ai-hook-rules"
|
|
37
34
|
},
|
|
38
35
|
"dependencies": {
|
|
39
|
-
"@webpieces/rules-config": "0.3.
|
|
36
|
+
"@webpieces/rules-config": "0.3.185"
|
|
40
37
|
},
|
|
41
38
|
"publishConfig": {
|
|
42
39
|
"access": "public"
|
|
@@ -83,11 +83,11 @@ function run(rawInput) {
|
|
|
83
83
|
'Present these THREE options to the human verbatim and wait for them to pick one.\n' +
|
|
84
84
|
'Each option is a command the human can copy-paste into their own terminal:\n' +
|
|
85
85
|
'\n' +
|
|
86
|
-
' A) PREFERRED — install the hook.\n' +
|
|
86
|
+
' A) PREFERRED — install the webpieces rules bundle (one package: hook + all rules + pr-gate).\n' +
|
|
87
87
|
' If this is a pnpm workspace, run from the monorepo root:\n' +
|
|
88
|
-
' pnpm add -Dw @webpieces/
|
|
88
|
+
' pnpm add -Dw @webpieces/nx-webpieces-rules\n' +
|
|
89
89
|
' Otherwise, run from the project root:\n' +
|
|
90
|
-
' npm install --save-dev @webpieces/
|
|
90
|
+
' npm install --save-dev @webpieces/nx-webpieces-rules\n' +
|
|
91
91
|
'\n' +
|
|
92
92
|
' B) Skip hooks temporarily (auto-expires). Ask the human how long, then have them run\n' +
|
|
93
93
|
' ONE of these from the project root:\n' +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/global-hook.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"global-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/global-hook.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'fs';\nimport { join, dirname, parse } from 'path';\nimport { spawnSync } from 'child_process';\n\ninterface SkipHooksFile {\n expires: number | null;\n reason?: string;\n}\n\n/**\n * Walk up from `startDir` until we find the directory containing `.git`, which marks\n * the root of the repo. Claude can `cd` into a sub-package before running a Bash tool,\n * which moves process.cwd() away from the repo root; without this traversal the hook\n * would look for `.webpieces/skiphooks` and `node_modules/.bin/wp-ai-hook` in the wrong\n * place and wrongly report webpieces as \"not installed\". `.git` can be a directory\n * (normal clone) or a file (worktree / submodule), so we accept either.\n * Falls back to `startDir` when no `.git` is found anywhere up the tree.\n */\nfunction findRepoRoot(startDir: string): string {\n let dir = startDir;\n const fsRoot = parse(dir).root;\n while (true) {\n if (existsSync(join(dir, '.git'))) return dir;\n if (dir === fsRoot) return startDir;\n dir = dirname(dir);\n }\n}\n\ninterface HookPayload {\n tool_name?: string;\n tool_input?: {\n file_path?: string;\n };\n}\n\nfunction readSkipHooks(cwd: string): SkipHooksFile | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const skipPath = join(cwd, '.webpieces', 'skiphooks');\n if (!existsSync(skipPath)) return null;\n return JSON.parse(readFileSync(skipPath, 'utf8')) as SkipHooksFile;\n } catch (err: unknown) {\n // eslint-disable-next-line @webpieces/catch-error-pattern -- intentionally discard; malformed .skiphooks must not crash global hook\n return null;\n }\n}\n\nfunction run(rawInput: string): void {\n const cwd = findRepoRoot(process.cwd());\n\n let payload: HookPayload | null = null;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n if (rawInput.trim()) {\n payload = JSON.parse(rawInput) as HookPayload;\n }\n } catch (err: unknown) {\n //const error = toError(err); -- malformed stdin is intentionally ignored; must not crash global hook\n }\n\n // 1. Check .skiphooks — if valid and not expired, allow everything\n const skipHooks = readSkipHooks(cwd);\n if (skipHooks !== null) {\n const nowSeconds = Date.now() / 1000;\n if (skipHooks.expires === null || nowSeconds < skipHooks.expires) {\n process.exit(0);\n }\n }\n\n // 2. Always allow writing .webpieces/skiphooks (this is the escape hatch)\n const filePath = payload?.tool_input?.file_path ?? '';\n if (payload?.tool_name === 'Write' && filePath.endsWith('.webpieces/skiphooks')) {\n process.exit(0);\n }\n\n // 3. Delegate to per-repo hook if present\n const localHook = join(cwd, 'node_modules', '.bin', 'wp-ai-hook');\n if (existsSync(localHook)) {\n const result = spawnSync(localHook, [], {\n input: Buffer.from(rawInput),\n encoding: 'buffer',\n cwd,\n });\n if (result.stdout?.length) process.stdout.write(result.stdout);\n if (result.stderr?.length) process.stderr.write(result.stderr);\n process.exit(result.status ?? 0);\n }\n\n // 4. No local hook found — block and instruct AI\n process.stderr.write(\n 'STOP. You MUST ask the human to run one of 3 commands to continue.\\n' +\n '\\n' +\n 'This project is missing the webpieces AI hook (./node_modules/.bin/wp-ai-hook not found).\\n' +\n '\\n' +\n 'Present these THREE options to the human verbatim and wait for them to pick one.\\n' +\n 'Each option is a command the human can copy-paste into their own terminal:\\n' +\n '\\n' +\n ' A) PREFERRED — install the webpieces rules bundle (one package: hook + all rules + pr-gate).\\n' +\n ' If this is a pnpm workspace, run from the monorepo root:\\n' +\n ' pnpm add -Dw @webpieces/nx-webpieces-rules\\n' +\n ' Otherwise, run from the project root:\\n' +\n ' npm install --save-dev @webpieces/nx-webpieces-rules\\n' +\n '\\n' +\n ' B) Skip hooks temporarily (auto-expires). Ask the human how long, then have them run\\n' +\n ' ONE of these from the project root:\\n' +\n ' 1 hour: mkdir -p .webpieces && printf \\'{\"expires\": %s, \"reason\": \"temporary skip\"}\\' \"$(($(date +%s) + 3600))\" > .webpieces/skiphooks\\n' +\n ' 1 week: mkdir -p .webpieces && printf \\'{\"expires\": %s, \"reason\": \"temporary skip\"}\\' \"$(($(date +%s) + 604800))\" > .webpieces/skiphooks\\n' +\n '\\n' +\n ' C) Disable permanently (NEVER expires — hooks stay off until this file is deleted).\\n' +\n ' Only choose this if the human explicitly wants it. Have them run from the project root:\\n' +\n ' mkdir -p .webpieces && printf \\'{\"expires\": null, \"reason\": \"permanently disabled\"}\\' > .webpieces/skiphooks\\n' +\n ' To re-enable later: rm .webpieces/skiphooks\\n' +\n '\\n' +\n 'You are BLOCKED. Ask the human now and wait for their response.\\n',\n );\n process.exit(2);\n}\n\nlet stdinData = '';\nprocess.stdin.resume();\nprocess.stdin.setEncoding('utf8');\nprocess.stdin.on('data', (chunk: string) => {\n stdinData += chunk;\n});\nprocess.stdin.on('end', () => {\n run(stdinData);\n});\n"]}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { IsLineDisabled } from './types';
|
|
2
2
|
export declare class DirectiveIndex {
|
|
3
3
|
private readonly lineDisables;
|
|
4
|
-
|
|
5
|
-
constructor(lineDisables: Map<number, Set<string>>, fileDisables: Set<string>);
|
|
4
|
+
constructor(lineDisables: Map<number, Set<string>>);
|
|
6
5
|
isLineDisabled(lineNum: number, ruleName: string): boolean;
|
|
7
6
|
}
|
|
8
7
|
export declare function parseDirectives(source: string): DirectiveIndex;
|
|
@@ -3,35 +3,38 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DirectiveIndex = void 0;
|
|
4
4
|
exports.parseDirectives = parseDirectives;
|
|
5
5
|
exports.createIsLineDisabled = createIsLineDisabled;
|
|
6
|
-
const
|
|
6
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
7
|
+
// Exactly ONE disable form: `// webpieces-disable <rule>[, <rule2>] -- reason`.
|
|
8
|
+
// A rule (or comma-list of rules) is REQUIRED — there is no bare/`*` "disable everything",
|
|
9
|
+
// no `ai-hook-disable` alias, and no `-file`/`-next`/`-all` variants. A disable applies to
|
|
10
|
+
// the same line (when code precedes the `//`) or the next non-comment line.
|
|
11
|
+
const DIRECTIVE_RE = new RegExp(`//\\s*${rules_config_1.WEBPIECES_DISABLE}\\s+([\\w-]+(?:\\s*,\\s*[\\w-]+)*)(?:\\s*--\\s*.*)?\\s*$`);
|
|
12
|
+
const COMMENT_PREFIX_RE = new RegExp(`^//\\s*${rules_config_1.WEBPIECES_DISABLE}`);
|
|
7
13
|
class DirectiveIndex {
|
|
8
14
|
lineDisables;
|
|
9
|
-
|
|
10
|
-
constructor(lineDisables, fileDisables) {
|
|
15
|
+
constructor(lineDisables) {
|
|
11
16
|
this.lineDisables = lineDisables;
|
|
12
|
-
this.fileDisables = fileDisables;
|
|
13
17
|
}
|
|
14
18
|
isLineDisabled(lineNum, ruleName) {
|
|
15
|
-
if (this.fileDisables.has('*') || this.fileDisables.has(ruleName))
|
|
16
|
-
return true;
|
|
17
19
|
const set = this.lineDisables.get(lineNum);
|
|
18
20
|
if (!set)
|
|
19
21
|
return false;
|
|
20
|
-
return set.has(
|
|
22
|
+
return set.has(ruleName);
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
exports.DirectiveIndex = DirectiveIndex;
|
|
24
26
|
function parseRuleList(raw) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
return raw
|
|
28
|
+
.split(',')
|
|
29
|
+
.map((s) => s.trim())
|
|
30
|
+
.filter((s) => s.length > 0 && s !== '*');
|
|
28
31
|
}
|
|
29
32
|
function nextTargetLine(lines, lineIdx) {
|
|
30
33
|
for (let j = lineIdx + 1; j < lines.length; j += 1) {
|
|
31
34
|
const trimmed = lines[j].trim();
|
|
32
35
|
if (trimmed === '')
|
|
33
36
|
continue;
|
|
34
|
-
if (
|
|
37
|
+
if (COMMENT_PREFIX_RE.test(trimmed))
|
|
35
38
|
continue;
|
|
36
39
|
return j + 1;
|
|
37
40
|
}
|
|
@@ -58,34 +61,17 @@ function resolveTarget(line, lines, i, map, rules) {
|
|
|
58
61
|
function parseDirectives(source) {
|
|
59
62
|
const lines = source.split('\n');
|
|
60
63
|
const lineDisables = new Map();
|
|
61
|
-
const fileDisables = new Set();
|
|
62
64
|
for (let i = 0; i < lines.length; i += 1) {
|
|
63
65
|
const line = lines[i];
|
|
64
66
|
const match = line.match(DIRECTIVE_RE);
|
|
65
67
|
if (!match)
|
|
66
68
|
continue;
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
if (variant === 'file') {
|
|
70
|
-
if (i < 20) {
|
|
71
|
-
for (const r of rules)
|
|
72
|
-
fileDisables.add(r);
|
|
73
|
-
}
|
|
69
|
+
const rules = parseRuleList(match[1]);
|
|
70
|
+
if (rules.length === 0)
|
|
74
71
|
continue;
|
|
75
|
-
}
|
|
76
|
-
if (variant === 'next') {
|
|
77
|
-
const target = nextTargetLine(lines, i);
|
|
78
|
-
if (target !== null)
|
|
79
|
-
addDisable(lineDisables, target, rules);
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (variant === 'all') {
|
|
83
|
-
resolveTarget(line, lines, i, lineDisables, ['*']);
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
72
|
resolveTarget(line, lines, i, lineDisables, rules);
|
|
87
73
|
}
|
|
88
|
-
return new DirectiveIndex(lineDisables
|
|
74
|
+
return new DirectiveIndex(lineDisables);
|
|
89
75
|
}
|
|
90
76
|
function createIsLineDisabled(source) {
|
|
91
77
|
const index = parseDirectives(source);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"disable-directives.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/disable-directives.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"disable-directives.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/disable-directives.ts"],"names":[],"mappings":"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","sourcesContent":["import { WEBPIECES_DISABLE } from '@webpieces/rules-config';\nimport type { IsLineDisabled } from './types';\n\n// Exactly ONE disable form: `// webpieces-disable <rule>[, <rule2>] -- reason`.\n// A rule (or comma-list of rules) is REQUIRED — there is no bare/`*` \"disable everything\",\n// no `ai-hook-disable` alias, and no `-file`/`-next`/`-all` variants. A disable applies to\n// the same line (when code precedes the `//`) or the next non-comment line.\nconst DIRECTIVE_RE = new RegExp(\n `//\\\\s*${WEBPIECES_DISABLE}\\\\s+([\\\\w-]+(?:\\\\s*,\\\\s*[\\\\w-]+)*)(?:\\\\s*--\\\\s*.*)?\\\\s*$`,\n);\nconst COMMENT_PREFIX_RE = new RegExp(`^//\\\\s*${WEBPIECES_DISABLE}`);\n\nexport class DirectiveIndex {\n private readonly lineDisables: Map<number, Set<string>>;\n\n constructor(lineDisables: Map<number, Set<string>>) {\n this.lineDisables = lineDisables;\n }\n\n isLineDisabled(lineNum: number, ruleName: string): boolean {\n const set = this.lineDisables.get(lineNum);\n if (!set) return false;\n return set.has(ruleName);\n }\n}\n\nfunction parseRuleList(raw: string): readonly string[] {\n return raw\n .split(',')\n .map((s: string): string => s.trim())\n .filter((s: string): boolean => s.length > 0 && s !== '*');\n}\n\nfunction nextTargetLine(lines: readonly string[], lineIdx: number): number | null {\n for (let j = lineIdx + 1; j < lines.length; j += 1) {\n const trimmed = lines[j].trim();\n if (trimmed === '') continue;\n if (COMMENT_PREFIX_RE.test(trimmed)) continue;\n return j + 1;\n }\n return null;\n}\n\nfunction addDisable(map: Map<number, Set<string>>, lineNum: number, rules: readonly string[]): void {\n if (!map.has(lineNum)) map.set(lineNum, new Set<string>());\n const set = map.get(lineNum)!;\n for (const r of rules) set.add(r);\n}\n\nfunction resolveTarget(\n line: string, lines: readonly string[], i: number,\n map: Map<number, Set<string>>, rules: readonly string[],\n): void {\n const beforeComment = line.slice(0, line.indexOf('//')).trim();\n if (beforeComment !== '') {\n addDisable(map, i + 1, rules);\n } else {\n const target = nextTargetLine(lines, i);\n if (target !== null) addDisable(map, target, rules);\n }\n}\n\nexport function parseDirectives(source: string): DirectiveIndex {\n const lines = source.split('\\n');\n const lineDisables = new Map<number, Set<string>>();\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n const match = line.match(DIRECTIVE_RE);\n if (!match) continue;\n const rules = parseRuleList(match[1]);\n if (rules.length === 0) continue;\n resolveTarget(line, lines, i, lineDisables, rules);\n }\n return new DirectiveIndex(lineDisables);\n}\n\nexport function createIsLineDisabled(source: string): IsLineDisabled {\n const index = parseDirectives(source);\n return (lineNum: number, ruleName: string): boolean => index.isLineDisabled(lineNum, ruleName);\n}\n"]}
|
package/src/core/load-rules.js
CHANGED
|
@@ -21,6 +21,7 @@ const no_shell_substitution_1 = tslib_1.__importDefault(require("./rules/no-shel
|
|
|
21
21
|
const no_symbol_di_tokens_1 = tslib_1.__importDefault(require("./rules/no-symbol-di-tokens"));
|
|
22
22
|
const branch_creation_guard_1 = tslib_1.__importDefault(require("./rules/branch-creation-guard"));
|
|
23
23
|
const pr_creation_guard_1 = tslib_1.__importDefault(require("./rules/pr-creation-guard"));
|
|
24
|
+
const merge_in_progress_guard_1 = tslib_1.__importDefault(require("./rules/merge-in-progress-guard"));
|
|
24
25
|
const pr_merge_cleanup_1 = tslib_1.__importDefault(require("./rules/pr-merge-cleanup"));
|
|
25
26
|
const no_direct_main_update_1 = tslib_1.__importDefault(require("./rules/no-direct-main-update"));
|
|
26
27
|
const no_js_files_1 = tslib_1.__importDefault(require("./rules/no-js-files"));
|
|
@@ -41,6 +42,7 @@ const BUILT_IN_RULE_MAP = {
|
|
|
41
42
|
'no-symbol-di-tokens': no_symbol_di_tokens_1.default,
|
|
42
43
|
'branch-creation-guard': branch_creation_guard_1.default,
|
|
43
44
|
'pr-creation-guard': pr_creation_guard_1.default,
|
|
45
|
+
'merge-in-progress-guard': merge_in_progress_guard_1.default,
|
|
44
46
|
'pr-merge-cleanup': pr_merge_cleanup_1.default,
|
|
45
47
|
'no-direct-main-update': no_direct_main_update_1.default,
|
|
46
48
|
'no-js-files': no_js_files_1.default,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-rules.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/load-rules.ts"],"names":[],"mappings":";;AAgDA,8BAKC;AA4ED,kCAGC;;AApID,+CAAyB;AACzB,mDAA6B;AAG7B,mCAAwC;AACxC,yCAAqC;AACrC,yCAAiD;AACjD,oFAAkD;AAClD,sFAAoD;AACpD,oFAAkD;AAClD,4FAAyD;AACzD,oFAAmD;AACnD,8FAA4D;AAC5D,sGAAoE;AACpE,8FAA4D;AAC5D,gGAA8D;AAC9D,kGAAgE;AAChE,8FAA2D;AAC3D,kGAAgE;AAChE,0FAAwD;AACxD,wFAAsD;AACtD,kGAA+D;AAC/D,8EAA4C;AAC5C,sFAAmD;AAEnD,MAAM,eAAe,GAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9F,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvD,MAAM,iBAAiB,GAAyB;IAC5C,gBAAgB,EAAE,wBAAoB;IACtC,iBAAiB,EAAE,yBAAqB;IACxC,gBAAgB,EAAE,wBAAoB;IACtC,oBAAoB,EAAE,4BAAuB;IAC7C,gBAAgB,EAAE,wBAAqB;IACvC,qBAAqB,EAAE,6BAAyB;IAChD,yBAAyB,EAAE,iCAA6B;IACxD,qBAAqB,EAAE,6BAAyB;IAChD,sBAAsB,EAAE,8BAA0B;IAClD,uBAAuB,EAAE,+BAA2B;IACpD,qBAAqB,EAAE,6BAAwB;IAC/C,uBAAuB,EAAE,+BAA2B;IACpD,mBAAmB,EAAE,2BAAuB;IAC5C,kBAAkB,EAAE,0BAAsB;IAC1C,uBAAuB,EAAE,+BAA0B;IACnD,aAAa,EAAE,qBAAiB;IAChC,iBAAiB,EAAE,yBAAoB;CAC1C,CAAC;AAEF,SAAgB,SAAS,CAAC,MAAsB,EAAE,aAAqB;IACnE,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAW,CAAC,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC,CAAC;IAC7C,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,IAAU,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,gBAAgB;IACrB,MAAM,OAAO,GAAW,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,wBAAgB,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,EAAE,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,IAAI,IAAI,CAAC,CAAC;QACxE,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,SAAS,eAAe,CAAC,SAA4B,EAAE,aAAqB;IACxE,MAAM,OAAO,GAAW,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC1E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,MAAM,IAAI,CAAC,CAAC;YACnE,SAAS;QACb,CAAC;QACD,IAAI,OAAiB,CAAC;QACtB,8DAA8D;QAC9D,IAAI,CAAC;YACD,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,IAAI,qBAAa,CAAC,uCAAuC,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAChG,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACtC,8DAA8D;YAC9D,IAAI,CAAC;gBACD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;gBAC3B,MAAM,IAAI,qBAAa,CAAC,4BAA4B,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACnF,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,8FAA8F;AAC9F,SAAS,YAAY,CAAC,IAAa;IAC/B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACrE,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,gFAAgF;IAChF,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QAClC,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;YACzE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,IAAI,6BAA6B,KAAK,IAAI,CAAC,CAAC;YACrF,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAW,CAAC,EAAE,CAAC;QAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,MAAM,CAAC,wBAAwB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC;QACtG,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAClF,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,UAAU,EAAE,CAAC;QACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC;QACpF,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAgB,WAAW,CAAC,OAAe,EAAE,QAAgB;IACzD,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAChC,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACb,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACzB,EAAE,IAAI,IAAI,CAAC;gBACX,CAAC,IAAI,CAAC,CAAC;gBACP,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG;oBAAE,CAAC,IAAI,CAAC,CAAC;gBAC/B,SAAS;YACb,CAAC;YACD,EAAE,IAAI,OAAO,CAAC;YACd,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACb,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACb,EAAE,IAAI,MAAM,CAAC;YACb,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACb,CAAC;QACD,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/B,EAAE,IAAI,IAAI,GAAG,EAAE,CAAC;YAChB,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACb,CAAC;QACD,EAAE,IAAI,EAAE,CAAC;QACT,CAAC,IAAI,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;AACtC,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport type { Rule, ResolvedConfig } from './types';\nimport { InformAiError } from './types';\nimport { toError } from './to-error';\nimport { builtInRuleNames } from './rules/index';\nimport noAnyUnknown from './rules/no-any-unknown';\nimport noImplicitAny from './rules/no-implicit-any';\nimport maxFileLines from './rules/max-file-lines';\nimport validateTsInSrc from './rules/validate-ts-in-src';\nimport noDestructure from './rules/no-destructure';\nimport requireReturnType from './rules/require-return-type';\nimport noUnmanagedExceptions from './rules/no-unmanaged-exceptions';\nimport catchErrorPattern from './rules/catch-error-pattern';\nimport throwCauseRequired from './rules/throw-cause-required';\nimport noShellSubstitution from './rules/no-shell-substitution';\nimport noSymbolDiTokens from './rules/no-symbol-di-tokens';\nimport branchCreationGuard from './rules/branch-creation-guard';\nimport prCreationGuard from './rules/pr-creation-guard';\nimport prMergeCleanup from './rules/pr-merge-cleanup';\nimport noDirectMainUpdate from './rules/no-direct-main-update';\nimport noJsFiles from './rules/no-js-files';\nimport noEditOnMain from './rules/no-edit-on-main';\n\nconst REQUIRED_FIELDS: readonly string[] = ['name', 'description', 'scope', 'files', 'check'];\nconst VALID_SCOPES = new Set(['edit', 'file', 'bash']);\n\nconst BUILT_IN_RULE_MAP: Record<string, Rule> = {\n 'no-any-unknown': noAnyUnknown as Rule,\n 'no-implicit-any': noImplicitAny as Rule,\n 'max-file-lines': maxFileLines as Rule,\n 'validate-ts-in-src': validateTsInSrc as Rule,\n 'no-destructure': noDestructure as Rule,\n 'require-return-type': requireReturnType as Rule,\n 'no-unmanaged-exceptions': noUnmanagedExceptions as Rule,\n 'catch-error-pattern': catchErrorPattern as Rule,\n 'throw-cause-required': throwCauseRequired as Rule,\n 'no-shell-substitution': noShellSubstitution as Rule,\n 'no-symbol-di-tokens': noSymbolDiTokens as Rule,\n 'branch-creation-guard': branchCreationGuard as Rule,\n 'pr-creation-guard': prCreationGuard as Rule,\n 'pr-merge-cleanup': prMergeCleanup as Rule,\n 'no-direct-main-update': noDirectMainUpdate as Rule,\n 'no-js-files': noJsFiles as Rule,\n 'no-edit-on-main': noEditOnMain as Rule,\n};\n\nexport function loadRules(config: ResolvedConfig, workspaceRoot: string): readonly Rule[] {\n const builtIns = loadBuiltInRules();\n const custom = loadCustomRules(config.rulesDir, workspaceRoot);\n const all: Rule[] = [...builtIns, ...custom];\n return all.filter((rule: Rule) => validateRule(rule));\n}\n\nfunction loadBuiltInRules(): Rule[] {\n const modules: Rule[] = [];\n for (const name of builtInRuleNames) {\n const mod = BUILT_IN_RULE_MAP[name];\n if (mod) {\n modules.push(mod);\n } else {\n process.stderr.write(`[ai-hooks] unknown built-in rule: ${name}\\n`);\n }\n }\n return modules;\n}\n\nfunction loadCustomRules(rulesDirs: readonly string[], workspaceRoot: string): Rule[] {\n const modules: Rule[] = [];\n for (const dir of rulesDirs) {\n const absDir = path.isAbsolute(dir) ? dir : path.join(workspaceRoot, dir);\n if (!fs.existsSync(absDir)) {\n process.stderr.write(`[ai-hooks] rulesDir not found: ${absDir}\\n`);\n continue;\n }\n let entries: string[];\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n entries = fs.readdirSync(absDir).filter((e) => e.endsWith('.js'));\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(`Cannot read custom rules directory '${absDir}'`, { cause: error });\n }\n for (const entry of entries) {\n const full = path.join(absDir, entry);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mod = require(full);\n modules.push(mod.default || mod);\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(`Cannot load custom rule '${full}'`, { cause: error });\n }\n }\n }\n return modules;\n}\n\n// webpieces-disable no-any-unknown -- validates untrusted require() output at system boundary\nfunction validateRule(rule: unknown): rule is Rule {\n if (!rule || typeof rule !== 'object') {\n process.stderr.write('[ai-hooks] rule is not an object, skipping\\n');\n return false;\n }\n // webpieces-disable no-any-unknown -- narrowing from unknown at system boundary\n const obj = rule as Record<string, unknown>;\n for (const field of REQUIRED_FIELDS) {\n if (obj[field] === undefined) {\n const name = typeof obj['name'] === 'string' ? obj['name'] : '<unnamed>';\n process.stderr.write(`[ai-hooks] rule \"${name}\" missing required field: ${field}\\n`);\n return false;\n }\n }\n if (!VALID_SCOPES.has(obj['scope'] as string)) {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" has invalid scope: ${String(obj['scope'])}\\n`);\n return false;\n }\n if (!Array.isArray(obj['files'])) {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" files must be an array\\n`);\n return false;\n }\n if (typeof obj['check'] !== 'function') {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" check must be a function\\n`);\n return false;\n }\n return true;\n}\n\nexport function globMatches(pattern: string, filePath: string): boolean {\n const regex = globToRegex(pattern);\n return regex.test(filePath);\n}\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"]}
|
|
1
|
+
{"version":3,"file":"load-rules.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/load-rules.ts"],"names":[],"mappings":";;AAkDA,8BAKC;AA4ED,kCAGC;;AAtID,+CAAyB;AACzB,mDAA6B;AAG7B,mCAAwC;AACxC,yCAAqC;AACrC,yCAAiD;AACjD,oFAAkD;AAClD,sFAAoD;AACpD,oFAAkD;AAClD,4FAAyD;AACzD,oFAAmD;AACnD,8FAA4D;AAC5D,sGAAoE;AACpE,8FAA4D;AAC5D,gGAA8D;AAC9D,kGAAgE;AAChE,8FAA2D;AAC3D,kGAAgE;AAChE,0FAAwD;AACxD,sGAAmE;AACnE,wFAAsD;AACtD,kGAA+D;AAC/D,8EAA4C;AAC5C,sFAAmD;AAEnD,MAAM,eAAe,GAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9F,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvD,MAAM,iBAAiB,GAAyB;IAC5C,gBAAgB,EAAE,wBAAoB;IACtC,iBAAiB,EAAE,yBAAqB;IACxC,gBAAgB,EAAE,wBAAoB;IACtC,oBAAoB,EAAE,4BAAuB;IAC7C,gBAAgB,EAAE,wBAAqB;IACvC,qBAAqB,EAAE,6BAAyB;IAChD,yBAAyB,EAAE,iCAA6B;IACxD,qBAAqB,EAAE,6BAAyB;IAChD,sBAAsB,EAAE,8BAA0B;IAClD,uBAAuB,EAAE,+BAA2B;IACpD,qBAAqB,EAAE,6BAAwB;IAC/C,uBAAuB,EAAE,+BAA2B;IACpD,mBAAmB,EAAE,2BAAuB;IAC5C,yBAAyB,EAAE,iCAA4B;IACvD,kBAAkB,EAAE,0BAAsB;IAC1C,uBAAuB,EAAE,+BAA0B;IACnD,aAAa,EAAE,qBAAiB;IAChC,iBAAiB,EAAE,yBAAoB;CAC1C,CAAC;AAEF,SAAgB,SAAS,CAAC,MAAsB,EAAE,aAAqB;IACnE,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAW,CAAC,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC,CAAC;IAC7C,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,IAAU,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,gBAAgB;IACrB,MAAM,OAAO,GAAW,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,wBAAgB,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,EAAE,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,IAAI,IAAI,CAAC,CAAC;QACxE,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,SAAS,eAAe,CAAC,SAA4B,EAAE,aAAqB;IACxE,MAAM,OAAO,GAAW,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC1E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,MAAM,IAAI,CAAC,CAAC;YACnE,SAAS;QACb,CAAC;QACD,IAAI,OAAiB,CAAC;QACtB,8DAA8D;QAC9D,IAAI,CAAC;YACD,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,IAAI,qBAAa,CAAC,uCAAuC,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAChG,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACtC,8DAA8D;YAC9D,IAAI,CAAC;gBACD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;gBAC3B,MAAM,IAAI,qBAAa,CAAC,4BAA4B,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACnF,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,8FAA8F;AAC9F,SAAS,YAAY,CAAC,IAAa;IAC/B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACrE,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,gFAAgF;IAChF,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QAClC,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;YACzE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,IAAI,6BAA6B,KAAK,IAAI,CAAC,CAAC;YACrF,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAW,CAAC,EAAE,CAAC;QAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,MAAM,CAAC,wBAAwB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC;QACtG,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAClF,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,UAAU,EAAE,CAAC;QACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC;QACpF,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAgB,WAAW,CAAC,OAAe,EAAE,QAAgB;IACzD,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAChC,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACb,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACzB,EAAE,IAAI,IAAI,CAAC;gBACX,CAAC,IAAI,CAAC,CAAC;gBACP,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG;oBAAE,CAAC,IAAI,CAAC,CAAC;gBAC/B,SAAS;YACb,CAAC;YACD,EAAE,IAAI,OAAO,CAAC;YACd,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACb,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACb,EAAE,IAAI,MAAM,CAAC;YACb,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACb,CAAC;QACD,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/B,EAAE,IAAI,IAAI,GAAG,EAAE,CAAC;YAChB,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACb,CAAC;QACD,EAAE,IAAI,EAAE,CAAC;QACT,CAAC,IAAI,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;AACtC,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport type { Rule, ResolvedConfig } from './types';\nimport { InformAiError } from './types';\nimport { toError } from './to-error';\nimport { builtInRuleNames } from './rules/index';\nimport noAnyUnknown from './rules/no-any-unknown';\nimport noImplicitAny from './rules/no-implicit-any';\nimport maxFileLines from './rules/max-file-lines';\nimport validateTsInSrc from './rules/validate-ts-in-src';\nimport noDestructure from './rules/no-destructure';\nimport requireReturnType from './rules/require-return-type';\nimport noUnmanagedExceptions from './rules/no-unmanaged-exceptions';\nimport catchErrorPattern from './rules/catch-error-pattern';\nimport throwCauseRequired from './rules/throw-cause-required';\nimport noShellSubstitution from './rules/no-shell-substitution';\nimport noSymbolDiTokens from './rules/no-symbol-di-tokens';\nimport branchCreationGuard from './rules/branch-creation-guard';\nimport prCreationGuard from './rules/pr-creation-guard';\nimport mergeInProgressGuard from './rules/merge-in-progress-guard';\nimport prMergeCleanup from './rules/pr-merge-cleanup';\nimport noDirectMainUpdate from './rules/no-direct-main-update';\nimport noJsFiles from './rules/no-js-files';\nimport noEditOnMain from './rules/no-edit-on-main';\n\nconst REQUIRED_FIELDS: readonly string[] = ['name', 'description', 'scope', 'files', 'check'];\nconst VALID_SCOPES = new Set(['edit', 'file', 'bash']);\n\nconst BUILT_IN_RULE_MAP: Record<string, Rule> = {\n 'no-any-unknown': noAnyUnknown as Rule,\n 'no-implicit-any': noImplicitAny as Rule,\n 'max-file-lines': maxFileLines as Rule,\n 'validate-ts-in-src': validateTsInSrc as Rule,\n 'no-destructure': noDestructure as Rule,\n 'require-return-type': requireReturnType as Rule,\n 'no-unmanaged-exceptions': noUnmanagedExceptions as Rule,\n 'catch-error-pattern': catchErrorPattern as Rule,\n 'throw-cause-required': throwCauseRequired as Rule,\n 'no-shell-substitution': noShellSubstitution as Rule,\n 'no-symbol-di-tokens': noSymbolDiTokens as Rule,\n 'branch-creation-guard': branchCreationGuard as Rule,\n 'pr-creation-guard': prCreationGuard as Rule,\n 'merge-in-progress-guard': mergeInProgressGuard as Rule,\n 'pr-merge-cleanup': prMergeCleanup as Rule,\n 'no-direct-main-update': noDirectMainUpdate as Rule,\n 'no-js-files': noJsFiles as Rule,\n 'no-edit-on-main': noEditOnMain as Rule,\n};\n\nexport function loadRules(config: ResolvedConfig, workspaceRoot: string): readonly Rule[] {\n const builtIns = loadBuiltInRules();\n const custom = loadCustomRules(config.rulesDir, workspaceRoot);\n const all: Rule[] = [...builtIns, ...custom];\n return all.filter((rule: Rule) => validateRule(rule));\n}\n\nfunction loadBuiltInRules(): Rule[] {\n const modules: Rule[] = [];\n for (const name of builtInRuleNames) {\n const mod = BUILT_IN_RULE_MAP[name];\n if (mod) {\n modules.push(mod);\n } else {\n process.stderr.write(`[ai-hooks] unknown built-in rule: ${name}\\n`);\n }\n }\n return modules;\n}\n\nfunction loadCustomRules(rulesDirs: readonly string[], workspaceRoot: string): Rule[] {\n const modules: Rule[] = [];\n for (const dir of rulesDirs) {\n const absDir = path.isAbsolute(dir) ? dir : path.join(workspaceRoot, dir);\n if (!fs.existsSync(absDir)) {\n process.stderr.write(`[ai-hooks] rulesDir not found: ${absDir}\\n`);\n continue;\n }\n let entries: string[];\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n entries = fs.readdirSync(absDir).filter((e) => e.endsWith('.js'));\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(`Cannot read custom rules directory '${absDir}'`, { cause: error });\n }\n for (const entry of entries) {\n const full = path.join(absDir, entry);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mod = require(full);\n modules.push(mod.default || mod);\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(`Cannot load custom rule '${full}'`, { cause: error });\n }\n }\n }\n return modules;\n}\n\n// webpieces-disable no-any-unknown -- validates untrusted require() output at system boundary\nfunction validateRule(rule: unknown): rule is Rule {\n if (!rule || typeof rule !== 'object') {\n process.stderr.write('[ai-hooks] rule is not an object, skipping\\n');\n return false;\n }\n // webpieces-disable no-any-unknown -- narrowing from unknown at system boundary\n const obj = rule as Record<string, unknown>;\n for (const field of REQUIRED_FIELDS) {\n if (obj[field] === undefined) {\n const name = typeof obj['name'] === 'string' ? obj['name'] : '<unnamed>';\n process.stderr.write(`[ai-hooks] rule \"${name}\" missing required field: ${field}\\n`);\n return false;\n }\n }\n if (!VALID_SCOPES.has(obj['scope'] as string)) {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" has invalid scope: ${String(obj['scope'])}\\n`);\n return false;\n }\n if (!Array.isArray(obj['files'])) {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" files must be an array\\n`);\n return false;\n }\n if (typeof obj['check'] !== 'function') {\n process.stderr.write(`[ai-hooks] rule \"${obj['name']}\" check must be a function\\n`);\n return false;\n }\n return true;\n}\n\nexport function globMatches(pattern: string, filePath: string): boolean {\n const regex = globToRegex(pattern);\n return regex.test(filePath);\n}\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"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const types_1 = require("../types");
|
|
4
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
4
5
|
const instruct_ai_writer_1 = require("../instruct-ai-writer");
|
|
5
6
|
/**
|
|
6
7
|
* Matches a catch clause opening: } catch (paramName: typeAnnotation) {
|
|
@@ -33,7 +34,7 @@ const catchErrorPatternRule = {
|
|
|
33
34
|
if (!catchMatch)
|
|
34
35
|
continue;
|
|
35
36
|
const lineNum = i + 1;
|
|
36
|
-
if (ctx.isLineDisabled(lineNum,
|
|
37
|
+
if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.CATCH_ERROR_PATTERN))
|
|
37
38
|
continue;
|
|
38
39
|
const actualParam = catchMatch[1];
|
|
39
40
|
const typeAnnotation = catchMatch[2];
|
|
@@ -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":";;AACA,oCAA0C;AAC1C,8DAA+D;AAE/D;;;GAGG;AACH,MAAM,aAAa,GAAG,4CAA4C,CAAC;AAEnE;;;GAGG;AACH,MAAM,gBAAgB,GAAG,qEAAqE,CAAC;AAE/F,MAAM,qBAAqB,GAAa;IACpC,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,6EAA6E;IAC1F,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,4EAA4E;QAC5E,+DAA+D;QAC/D,gGAAgG;QAChG,6EAA6E;KAChF;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC;QAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChD,IAAI,CAAC,UAAU;gBAAE,SAAS;YAE1B,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,
|
|
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 type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { RULE_NAMES } from '@webpieces/rules-config';\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\nconst catchErrorPatternRule: EditRule = {\n name: 'catch-error-pattern',\n description: 'Catch blocks must use: catch (err: unknown) { const error = toError(err); }',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n 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); }',\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\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 default catchErrorPatternRule;\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"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
5
|
+
const path = tslib_1.__importStar(require("path"));
|
|
6
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
7
|
+
const types_1 = require("../types");
|
|
8
|
+
const FIX_HINT = [
|
|
9
|
+
'A 3-point merge is in progress and not yet validated.',
|
|
10
|
+
'Resolve the remaining conflicts in the working tree, then run:',
|
|
11
|
+
' pnpm wp-git-merge-complete',
|
|
12
|
+
'That scans for leftover conflict markers and runs the build; only when green does it',
|
|
13
|
+
'commit and unblock commit/push/PR. Then run: pnpm wp-upsert-pr',
|
|
14
|
+
];
|
|
15
|
+
// Returns the path of the first UNVALIDATED merge marker found, or null. We detect validation
|
|
16
|
+
// by a raw substring (no JSON.parse) so a malformed marker can never crash the guard.
|
|
17
|
+
function findUnvalidatedMerge(workspaceRoot) {
|
|
18
|
+
const tmpDir = path.join(workspaceRoot, rules_config_1.WEBPIECES_TMP_DIR);
|
|
19
|
+
if (!fs.existsSync(tmpDir))
|
|
20
|
+
return null;
|
|
21
|
+
for (const entry of fs.readdirSync(tmpDir)) {
|
|
22
|
+
if (!entry.startsWith(rules_config_1.MERGE_DIR_PREFIX))
|
|
23
|
+
continue;
|
|
24
|
+
const marker = path.join(tmpDir, entry, rules_config_1.MERGE_IN_PROGRESS_FILE);
|
|
25
|
+
if (!fs.existsSync(marker))
|
|
26
|
+
continue;
|
|
27
|
+
const raw = fs.readFileSync(marker, 'utf8');
|
|
28
|
+
if (!/"validated"\s*:\s*true/.test(raw))
|
|
29
|
+
return marker;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
// Operations that would let an agent route around the merge gate.
|
|
34
|
+
function isBlockedDuringMerge(cmd) {
|
|
35
|
+
return /\bgit\s+commit\b/.test(cmd)
|
|
36
|
+
|| /\bgit\s+push\b/.test(cmd)
|
|
37
|
+
|| /\bgit\s+merge\b/.test(cmd)
|
|
38
|
+
|| /\bgit\s+rebase\b/.test(cmd)
|
|
39
|
+
|| /\bgh\s+pr\s+(create|edit|merge)\b/.test(cmd);
|
|
40
|
+
}
|
|
41
|
+
const mergeInProgressGuard = {
|
|
42
|
+
name: 'merge-in-progress-guard',
|
|
43
|
+
description: 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-git-merge-complete.',
|
|
44
|
+
scope: 'bash',
|
|
45
|
+
files: [],
|
|
46
|
+
defaultOptions: {},
|
|
47
|
+
fixHint: FIX_HINT,
|
|
48
|
+
check(ctx) {
|
|
49
|
+
if (!isBlockedDuringMerge(ctx.command))
|
|
50
|
+
return [];
|
|
51
|
+
const marker = findUnvalidatedMerge(ctx.workspaceRoot);
|
|
52
|
+
if (!marker)
|
|
53
|
+
return [];
|
|
54
|
+
return [new types_1.Violation(1, truncate(ctx.command), [
|
|
55
|
+
'A merge is in progress and not yet validated — this command is blocked.',
|
|
56
|
+
`Marker: ${marker}`,
|
|
57
|
+
'Finish resolving conflicts, then run: pnpm wp-git-merge-complete',
|
|
58
|
+
].join('\n'))];
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
function truncate(s) {
|
|
62
|
+
const MAX = 120;
|
|
63
|
+
return s.length <= MAX ? s : s.slice(0, MAX) + '…';
|
|
64
|
+
}
|
|
65
|
+
exports.default = mergeInProgressGuard;
|
|
66
|
+
//# 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;AAC7B,0DAAsG;AAEtG,oCAA0C;AAE1C,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,MAAM,oBAAoB,GAAa;IACnC,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EAAE,6GAA6G;IAC1H,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,EAAE;IACT,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE,QAAQ;IAEjB,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,CAAC;AAEF,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,kBAAe,oBAAoB,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { WEBPIECES_TMP_DIR, MERGE_DIR_PREFIX, MERGE_IN_PROGRESS_FILE } from '@webpieces/rules-config';\nimport type { BashRule, BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\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\nconst mergeInProgressGuard: BashRule = {\n name: 'merge-in-progress-guard',\n description: 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-git-merge-complete.',\n scope: 'bash',\n files: [],\n defaultOptions: {},\n 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\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport default mergeInProgressGuard;\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const types_1 = require("../types");
|
|
4
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
4
5
|
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>)/;
|
|
5
6
|
const noAnyRule = {
|
|
6
7
|
name: 'no-any-unknown',
|
|
@@ -19,7 +20,7 @@ const noAnyRule = {
|
|
|
19
20
|
if (!ANY_PATTERN.test(stripped))
|
|
20
21
|
continue;
|
|
21
22
|
const lineNum = i + 1;
|
|
22
|
-
if (ctx.isLineDisabled(lineNum,
|
|
23
|
+
if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.NO_ANY_UNKNOWN))
|
|
23
24
|
continue;
|
|
24
25
|
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
26
|
}
|
|
@@ -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":";;AACA,oCAA0C;
|
|
1
|
+
{"version":3,"file":"no-any-unknown.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-any-unknown.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAC1C,0DAAqD;AAErD,MAAM,WAAW,GACb,4HAA4H,CAAC;AAEjI,MAAM,SAAS,GAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,+DAA+D;IAC5E,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,8DAA8D;QAC9D,0DAA0D;KAC7D;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,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,CAAC;AAEF,kBAAe,SAAS,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { RULE_NAMES } from '@webpieces/rules-config';\n\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>)/;\n\nconst noAnyRule: EditRule = {\n name: 'no-any-unknown',\n description: 'Disallow the `any` keyword. Use concrete types or interfaces.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n 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\nexport default noAnyRule;\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const types_1 = require("../types");
|
|
4
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
4
5
|
const VARIABLE_DESTRUCTURE = /\b(?:const|let|var)\s*\{/;
|
|
5
6
|
const noDestructureRule = {
|
|
6
7
|
name: 'no-destructure',
|
|
@@ -19,7 +20,7 @@ const noDestructureRule = {
|
|
|
19
20
|
if (!VARIABLE_DESTRUCTURE.test(stripped))
|
|
20
21
|
continue;
|
|
21
22
|
const lineNum = i + 1;
|
|
22
|
-
if (ctx.isLineDisabled(lineNum,
|
|
23
|
+
if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.NO_DESTRUCTURE))
|
|
23
24
|
continue;
|
|
24
25
|
violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), 'Destructuring pattern. Assign the whole result instead: const obj = methodCall(); then pass obj around or use obj.x'));
|
|
25
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-destructure.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-destructure.ts"],"names":[],"mappings":";;AACA,oCAA0C;
|
|
1
|
+
{"version":3,"file":"no-destructure.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-destructure.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAC1C,0DAAqD;AAErD,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAExD,MAAM,iBAAiB,GAAa;IAChC,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,8GAA8G;IAC3H,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE;IACvC,OAAO,EAAE;QACL,yHAAyH;QACzH,iDAAiD;KACpD;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACnD,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,qHAAqH,CACxH,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,kBAAe,iBAAiB,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { RULE_NAMES } from '@webpieces/rules-config';\n\nconst VARIABLE_DESTRUCTURE = /\\b(?:const|let|var)\\s*\\{/;\n\nconst noDestructureRule: EditRule = {\n name: 'no-destructure',\n description: 'Disallow destructuring patterns. Assign the whole result and pass it around or access properties explicitly.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: { allowTopLevel: true },\n fixHint: [\n 'Instead of: const { x, y } = methodCall(); prefer const obj = methodCall(); then pass obj to other methods or use obj.x',\n '// webpieces-disable no-destructure -- <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 (!VARIABLE_DESTRUCTURE.test(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.NO_DESTRUCTURE)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Destructuring pattern. Assign the whole result instead: const obj = methodCall(); then pass obj around or use obj.x',\n ));\n }\n return violations;\n },\n};\n\nexport default noDestructureRule;\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const types_1 = require("../types");
|
|
4
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
4
5
|
const ARROW_PARAMS_RE = /\(([^()]*)\)\s*=>/g;
|
|
5
6
|
const FN_DECL_PARAMS_RE = /\bfunction\s*[\w$]*\s*\(([^()]*)\)/g;
|
|
6
7
|
function firstUntypedParam(paramsStr) {
|
|
@@ -55,7 +56,7 @@ const noImplicitAnyRule = {
|
|
|
55
56
|
for (let i = 0; i < ctx.strippedLines.length; i += 1) {
|
|
56
57
|
const stripped = ctx.strippedLines[i];
|
|
57
58
|
const lineNum = i + 1;
|
|
58
|
-
if (ctx.isLineDisabled(lineNum,
|
|
59
|
+
if (ctx.isLineDisabled(lineNum, rules_config_1.RULE_NAMES.NO_IMPLICIT_ANY))
|
|
59
60
|
continue;
|
|
60
61
|
const offender = findOffender(stripped);
|
|
61
62
|
if (!offender)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-implicit-any.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-implicit-any.ts"],"names":[],"mappings":";;AACA,oCAA0C;
|
|
1
|
+
{"version":3,"file":"no-implicit-any.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-implicit-any.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { RULE_NAMES } from '@webpieces/rules-config';\n\nconst ARROW_PARAMS_RE = /\\(([^()]*)\\)\\s*=>/g;\nconst FN_DECL_PARAMS_RE = /\\bfunction\\s*[\\w$]*\\s*\\(([^()]*)\\)/g;\n\nfunction firstUntypedParam(paramsStr: string): string | null {\n if (paramsStr.includes('{') || paramsStr.includes('[')) return null;\n const parts = paramsStr.split(',').map((p) => p.trim()).filter((p) => p.length > 0);\n for (const part of parts) {\n if (part.startsWith('...')) continue;\n if (part.includes(':')) continue;\n if (part.includes('=')) continue;\n if (part === 'this') continue;\n if (/^[a-zA-Z_$][\\w$]*$/.test(part)) return part;\n }\n return null;\n}\n\nfunction findOffender(line: string): string | null {\n ARROW_PARAMS_RE.lastIndex = 0;\n let m: RegExpExecArray | null = ARROW_PARAMS_RE.exec(line);\n while (m !== null) {\n const bad = firstUntypedParam(m[1]);\n if (bad) return bad;\n m = ARROW_PARAMS_RE.exec(line);\n }\n FN_DECL_PARAMS_RE.lastIndex = 0;\n m = FN_DECL_PARAMS_RE.exec(line);\n while (m !== null) {\n const bad = firstUntypedParam(m[1]);\n if (bad) return bad;\n m = FN_DECL_PARAMS_RE.exec(line);\n }\n return null;\n}\n\nconst noImplicitAnyRule: EditRule = {\n name: 'no-implicit-any',\n description: 'Disallow function parameters without explicit type annotations (implicit-any).',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'Add explicit types: (x: string) => ... or function foo(x: number)',\n '// webpieces-disable no-implicit-any -- <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 const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, RULE_NAMES.NO_IMPLICIT_ANY)) continue;\n const offender = findOffender(stripped);\n if (!offender) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n `Parameter \"${offender}\" has no type annotation. Add an explicit type to avoid implicit-any.`,\n ));\n }\n return violations;\n },\n};\n\nexport default noImplicitAnyRule;\n"]}
|