@webpieces/ai-hook-rules 0.3.167 → 0.3.168
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/ai-hook-rules",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.168",
|
|
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",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"directory": "packages/tooling/ai-hook-rules"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@webpieces/rules-config": "0.3.
|
|
39
|
+
"@webpieces/rules-config": "0.3.168"
|
|
40
40
|
},
|
|
41
41
|
"publishConfig": {
|
|
42
42
|
"access": "public"
|
|
@@ -55,13 +55,17 @@ function run(rawInput) {
|
|
|
55
55
|
process.exit(result.status ?? 0);
|
|
56
56
|
}
|
|
57
57
|
// 4. No local hook found — block and instruct AI
|
|
58
|
-
process.stderr.write('
|
|
59
|
-
'
|
|
60
|
-
'
|
|
61
|
-
'
|
|
62
|
-
'
|
|
63
|
-
'
|
|
64
|
-
'
|
|
58
|
+
process.stderr.write('STOP. DO NOT TAKE ANY ACTION. You MUST ask the human and wait for their answer.\n' +
|
|
59
|
+
'\n' +
|
|
60
|
+
'This project is missing the webpieces AI hook (./node_modules/.bin/wp-ai-hook not found).\n' +
|
|
61
|
+
'\n' +
|
|
62
|
+
'Present these options to the human and wait for their choice:\n' +
|
|
63
|
+
' A) PREFERRED: Ask the human to run `npm install @webpieces/ai-hook-rules` (or pnpm install)\n' +
|
|
64
|
+
' B) Skip hooks temporarily: ask human how long (1 hour / 1 day / forever), then write\n' +
|
|
65
|
+
' .webpieces/skiphooks with {"expires": <unix epoch seconds>, "reason": "..."}\n' +
|
|
66
|
+
'\n' +
|
|
67
|
+
'WARNING: DO NOT disable permanently (expires: null) without explicit human permission.\n' +
|
|
68
|
+
'You are BLOCKED. Ask the human now and wait for their response.\n');
|
|
65
69
|
process.exit(2);
|
|
66
70
|
}
|
|
67
71
|
let stdinData = '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-hook.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/adapters/global-hook.ts"],"names":[],"mappings":";;AAAA,2BAA8C;AAC9C,+BAA4B;AAC5B,iDAA0C;AAc1C,SAAS,aAAa,CAAC,GAAW;IAC9B,8DAA8D;IAC9D,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;QACtD,IAAI,CAAC,IAAA,eAAU,EAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAkB,CAAC;IACvE,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,oIAAoI;QACpI,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC;AAED,SAAS,GAAG,CAAC,QAAgB;IACzB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE1B,IAAI,OAAO,GAAuB,IAAI,CAAC;IACvC,8DAA8D;IAC9D,IAAI,CAAC;QACD,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;YAClB,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAgB,CAAC;QAClD,CAAC;IACL,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,qGAAqG;IACzG,CAAC;IAED,mEAAmE;IACnE,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QACrC,IAAI,SAAS,CAAC,OAAO,KAAK,IAAI,IAAI,UAAU,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;YAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC;IAED,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,OAAO,EAAE,UAAU,EAAE,SAAS,IAAI,EAAE,CAAC;IACtD,IAAI,OAAO,EAAE,SAAS,KAAK,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,0CAA0C;IAC1C,MAAM,SAAS,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IAClE,IAAI,IAAA,eAAU,EAAC,SAAS,CAAC,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,SAAS,EAAE,EAAE,EAAE;YACpC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC5B,QAAQ,EAAE,QAAQ;SACrB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/D,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,iDAAiD;IACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,6FAA6F;
|
|
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 } from 'path';\nimport { spawnSync } from 'child_process';\n\ninterface SkipHooksFile {\n expires: number | null;\n reason?: string;\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 = 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 });\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. DO NOT TAKE ANY ACTION. You MUST ask the human and wait for their answer.\\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 options to the human and wait for their choice:\\n' +\n ' A) PREFERRED: Ask the human to run `npm install @webpieces/ai-hook-rules` (or pnpm install)\\n' +\n ' B) Skip hooks temporarily: ask human how long (1 hour / 1 day / forever), then write\\n' +\n ' .webpieces/skiphooks with {\"expires\": <unix epoch seconds>, \"reason\": \"...\"}\\n' +\n '\\n' +\n 'WARNING: DO NOT disable permanently (expires: null) without explicit human permission.\\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"]}
|
package/src/core/runner.js
CHANGED
|
@@ -21,6 +21,10 @@ function runInternal(toolKind, input, cwd) {
|
|
|
21
21
|
'Do not proceed until the human has done this.');
|
|
22
22
|
}
|
|
23
23
|
const workspaceRoot = path.dirname(config.configPath);
|
|
24
|
+
// Always allow edits to webpieces.config.json — it's the fix target when out of sync
|
|
25
|
+
if (path.resolve(input.filePath) === path.resolve(config.configPath)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
24
28
|
const rules = (0, load_rules_1.loadRules)(config, workspaceRoot);
|
|
25
29
|
if (rules.length === 0)
|
|
26
30
|
return null;
|
package/src/core/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\n\nimport { buildContexts, buildBashContext } from './build-context';\nimport { loadConfig } from './load-config';\nimport { loadRules, globMatches } from './load-rules';\nimport { toError } from './to-error';\nimport { formatReport } from './report';\nimport {\n ToolKind, NormalizedToolInput, BlockedResult,\n Rule, EditRule, FileRule, BashRule, Violation, RuleGroup,\n EditContext, FileContext, BashContext,\n ResolvedConfig, ResolvedRuleConfig, RuleOptions,\n} from './types';\n\nexport function run(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n cwd: string,\n): BlockedResult | null {\n return runInternal(toolKind, input, cwd);\n}\n\nfunction runInternal(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n cwd: string,\n): BlockedResult | null {\n const config = loadConfig(cwd);\n if (!config.configPath) {\n return new BlockedResult(\n 'webpieces.config.json not found.\\n' +\n 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\\n' +\n 'Do not proceed until the human has done this.',\n );\n }\n\n const workspaceRoot = path.dirname(config.configPath);\n const rules = loadRules(config, workspaceRoot);\n if (rules.length === 0) return null;\n\n const outOfSync = checkConfigSync(rules, config);\n if (outOfSync) return outOfSync;\n\n const contexts = buildContexts(toolKind, input, workspaceRoot);\n const relativePath = path.relative(workspaceRoot, input.filePath);\n\n const editGroups = runEditRules(rules, contexts.editContexts, config);\n const fileGroups = runFileRules(rules, contexts.fileContext, config);\n const allGroups = [...editGroups, ...fileGroups];\n\n if (allGroups.length === 0) return null;\n\n const report = formatReport(relativePath, allGroups);\n return new BlockedResult(report);\n}\n\nexport function runBash(command: string, cwd: string): BlockedResult | null {\n return runBashInternal(command, cwd);\n}\n\nfunction runBashInternal(command: string, cwd: string): BlockedResult | null {\n const config = loadConfig(cwd);\n if (!config.configPath) {\n return new BlockedResult(\n 'webpieces.config.json not found.\\n' +\n 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\\n' +\n 'Do not proceed until the human has done this.',\n );\n }\n\n const workspaceRoot = path.dirname(config.configPath);\n const rules = loadRules(config, workspaceRoot);\n if (rules.length === 0) return null;\n\n const outOfSync = checkConfigSync(rules, config);\n if (outOfSync) return outOfSync;\n\n const ctx = buildBashContext(command, workspaceRoot);\n const groups = runBashRules(rules, ctx, config);\n if (groups.length === 0) return null;\n\n const report = formatReport('<bash>', groups);\n return new BlockedResult(report);\n}\n\nfunction checkConfigSync(rules: readonly Rule[], config: ResolvedConfig): BlockedResult | null {\n const unconfiguredRules = rules.filter((r: Rule) => !config.userConfiguredRuleNames.has(r.name));\n if (unconfiguredRules.length === 0) return null;\n\n const lines = [\n 'webpieces.config.json is out of sync — new built-in rules are present that have no entry in webpieces.config.json.',\n '',\n 'Tell the human: the following rules need to be configured. Ask for each one:',\n ' - Should this rule be ON, OFF, MODIFIED_CODE, or MODIFIED_FILES?',\n ' - What values do you want for the options listed below?',\n 'Then update webpieces.config.json and retry.',\n '',\n 'Do NOT proceed until webpieces.config.json has an entry for every rule below.',\n '',\n ];\n\n for (const rule of unconfiguredRules) {\n lines.push(`--- ${rule.name} ---`);\n lines.push(`Description: ${rule.description}`);\n const opts = rule.defaultOptions;\n const optKeys = Object.keys(opts);\n if (optKeys.length > 0) {\n lines.push(`Available options (suggested defaults shown):`);\n for (const key of optKeys) {\n lines.push(` ${key}: ${JSON.stringify(opts[key])}`);\n }\n } else {\n lines.push('Available options: none beyond mode');\n }\n lines.push(`Example entry for webpieces.config.json:`);\n lines.push(` \"${rule.name}\": { \"mode\": \"ON\" }`);\n lines.push('');\n }\n\n return new BlockedResult(lines.join('\\n'));\n}\n\n// N-legs pattern: each rule runs independently; crash → visible violation so AI sees it, not silent []\nfunction runRuleCheck(rule: Rule, ctx: EditContext | FileContext | BashContext): readonly Violation[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return (rule as EditRule | FileRule | BashRule).check(ctx as never);\n } catch (err: unknown) {\n const error = toError(err);\n return [new Violation(0, '', `Rule '${rule.name}' crashed: ${error.message}`)];\n }\n}\n\nfunction runBashRules(\n rules: readonly Rule[],\n bashContext: BashContext,\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'bash') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n bashContext.options = mergeOptions(rule.defaultOptions, ruleConfig);\n const vs = runRuleCheck(rule, bashContext);\n if (vs.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, [...rule.fixHint], [...vs],\n ));\n }\n }\n return groups;\n}\n\nfunction ruleMatchesFile(rule: Rule, relativePath: string): boolean {\n for (const pattern of rule.files) {\n if (globMatches(pattern, relativePath)) return true;\n }\n return false;\n}\n\nfunction mergeOptions(defaultOptions: RuleOptions, ruleConfig: ResolvedRuleConfig): RuleOptions {\n // webpieces-disable no-any-unknown -- building an options bag from opaque RuleOptions\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(defaultOptions)) out[key] = defaultOptions[key];\n for (const key of Object.keys(ruleConfig.options)) {\n // 'mode' is the framework-level on/off switch, not a rule option.\n if (key === 'mode') continue;\n out[key] = ruleConfig.options[key];\n }\n return out;\n}\n\nfunction runEditRules(\n rules: readonly Rule[],\n editContexts: readonly EditContext[],\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'edit') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n const allViolations: Violation[] = [];\n for (const ctx of editContexts) {\n if (!ruleMatchesFile(rule, ctx.relativePath)) continue;\n ctx.options = mergeOptions(rule.defaultOptions, ruleConfig);\n const vs = runRuleCheck(rule, ctx);\n for (const v of vs) {\n const copy = new Violation(v.line, v.snippet, v.message);\n copy.editIndex = ctx.editIndex;\n copy.editCount = ctx.editCount;\n allViolations.push(copy);\n }\n }\n if (allViolations.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, [...rule.fixHint], allViolations,\n ));\n }\n }\n return groups;\n}\n\nfunction runFileRules(\n rules: readonly Rule[],\n fileContext: FileContext,\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'file') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n if (!ruleMatchesFile(rule, fileContext.relativePath)) continue;\n fileContext.options = mergeOptions(rule.defaultOptions, ruleConfig);\n const vs = runRuleCheck(rule, fileContext);\n if (vs.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, [...rule.fixHint], [...vs],\n ));\n }\n }\n return groups;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../../../../packages/tooling/ai-hook-rules/src/core/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\n\nimport { buildContexts, buildBashContext } from './build-context';\nimport { loadConfig } from './load-config';\nimport { loadRules, globMatches } from './load-rules';\nimport { toError } from './to-error';\nimport { formatReport } from './report';\nimport {\n ToolKind, NormalizedToolInput, BlockedResult,\n Rule, EditRule, FileRule, BashRule, Violation, RuleGroup,\n EditContext, FileContext, BashContext,\n ResolvedConfig, ResolvedRuleConfig, RuleOptions,\n} from './types';\n\nexport function run(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n cwd: string,\n): BlockedResult | null {\n return runInternal(toolKind, input, cwd);\n}\n\nfunction runInternal(\n toolKind: ToolKind,\n input: NormalizedToolInput,\n cwd: string,\n): BlockedResult | null {\n const config = loadConfig(cwd);\n if (!config.configPath) {\n return new BlockedResult(\n 'webpieces.config.json not found.\\n' +\n 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\\n' +\n 'Do not proceed until the human has done this.',\n );\n }\n\n const workspaceRoot = path.dirname(config.configPath);\n\n // Always allow edits to webpieces.config.json — it's the fix target when out of sync\n if (path.resolve(input.filePath) === path.resolve(config.configPath)) {\n return null;\n }\n\n const rules = loadRules(config, workspaceRoot);\n if (rules.length === 0) return null;\n\n const outOfSync = checkConfigSync(rules, config);\n if (outOfSync) return outOfSync;\n\n const contexts = buildContexts(toolKind, input, workspaceRoot);\n const relativePath = path.relative(workspaceRoot, input.filePath);\n\n const editGroups = runEditRules(rules, contexts.editContexts, config);\n const fileGroups = runFileRules(rules, contexts.fileContext, config);\n const allGroups = [...editGroups, ...fileGroups];\n\n if (allGroups.length === 0) return null;\n\n const report = formatReport(relativePath, allGroups);\n return new BlockedResult(report);\n}\n\nexport function runBash(command: string, cwd: string): BlockedResult | null {\n return runBashInternal(command, cwd);\n}\n\nfunction runBashInternal(command: string, cwd: string): BlockedResult | null {\n const config = loadConfig(cwd);\n if (!config.configPath) {\n return new BlockedResult(\n 'webpieces.config.json not found.\\n' +\n 'Tell the human: run `./node_modules/.bin/wp-setup-ai-hooks` to initialize the project configuration.\\n' +\n 'Do not proceed until the human has done this.',\n );\n }\n\n const workspaceRoot = path.dirname(config.configPath);\n const rules = loadRules(config, workspaceRoot);\n if (rules.length === 0) return null;\n\n const outOfSync = checkConfigSync(rules, config);\n if (outOfSync) return outOfSync;\n\n const ctx = buildBashContext(command, workspaceRoot);\n const groups = runBashRules(rules, ctx, config);\n if (groups.length === 0) return null;\n\n const report = formatReport('<bash>', groups);\n return new BlockedResult(report);\n}\n\nfunction checkConfigSync(rules: readonly Rule[], config: ResolvedConfig): BlockedResult | null {\n const unconfiguredRules = rules.filter((r: Rule) => !config.userConfiguredRuleNames.has(r.name));\n if (unconfiguredRules.length === 0) return null;\n\n const lines = [\n 'webpieces.config.json is out of sync — new built-in rules are present that have no entry in webpieces.config.json.',\n '',\n 'Tell the human: the following rules need to be configured. Ask for each one:',\n ' - Should this rule be ON, OFF, MODIFIED_CODE, or MODIFIED_FILES?',\n ' - What values do you want for the options listed below?',\n 'Then update webpieces.config.json and retry.',\n '',\n 'Do NOT proceed until webpieces.config.json has an entry for every rule below.',\n '',\n ];\n\n for (const rule of unconfiguredRules) {\n lines.push(`--- ${rule.name} ---`);\n lines.push(`Description: ${rule.description}`);\n const opts = rule.defaultOptions;\n const optKeys = Object.keys(opts);\n if (optKeys.length > 0) {\n lines.push(`Available options (suggested defaults shown):`);\n for (const key of optKeys) {\n lines.push(` ${key}: ${JSON.stringify(opts[key])}`);\n }\n } else {\n lines.push('Available options: none beyond mode');\n }\n lines.push(`Example entry for webpieces.config.json:`);\n lines.push(` \"${rule.name}\": { \"mode\": \"ON\" }`);\n lines.push('');\n }\n\n return new BlockedResult(lines.join('\\n'));\n}\n\n// N-legs pattern: each rule runs independently; crash → visible violation so AI sees it, not silent []\nfunction runRuleCheck(rule: Rule, ctx: EditContext | FileContext | BashContext): readonly Violation[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return (rule as EditRule | FileRule | BashRule).check(ctx as never);\n } catch (err: unknown) {\n const error = toError(err);\n return [new Violation(0, '', `Rule '${rule.name}' crashed: ${error.message}`)];\n }\n}\n\nfunction runBashRules(\n rules: readonly Rule[],\n bashContext: BashContext,\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'bash') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n bashContext.options = mergeOptions(rule.defaultOptions, ruleConfig);\n const vs = runRuleCheck(rule, bashContext);\n if (vs.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, [...rule.fixHint], [...vs],\n ));\n }\n }\n return groups;\n}\n\nfunction ruleMatchesFile(rule: Rule, relativePath: string): boolean {\n for (const pattern of rule.files) {\n if (globMatches(pattern, relativePath)) return true;\n }\n return false;\n}\n\nfunction mergeOptions(defaultOptions: RuleOptions, ruleConfig: ResolvedRuleConfig): RuleOptions {\n // webpieces-disable no-any-unknown -- building an options bag from opaque RuleOptions\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(defaultOptions)) out[key] = defaultOptions[key];\n for (const key of Object.keys(ruleConfig.options)) {\n // 'mode' is the framework-level on/off switch, not a rule option.\n if (key === 'mode') continue;\n out[key] = ruleConfig.options[key];\n }\n return out;\n}\n\nfunction runEditRules(\n rules: readonly Rule[],\n editContexts: readonly EditContext[],\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'edit') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n const allViolations: Violation[] = [];\n for (const ctx of editContexts) {\n if (!ruleMatchesFile(rule, ctx.relativePath)) continue;\n ctx.options = mergeOptions(rule.defaultOptions, ruleConfig);\n const vs = runRuleCheck(rule, ctx);\n for (const v of vs) {\n const copy = new Violation(v.line, v.snippet, v.message);\n copy.editIndex = ctx.editIndex;\n copy.editCount = ctx.editCount;\n allViolations.push(copy);\n }\n }\n if (allViolations.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, [...rule.fixHint], allViolations,\n ));\n }\n }\n return groups;\n}\n\nfunction runFileRules(\n rules: readonly Rule[],\n fileContext: FileContext,\n config: ResolvedConfig,\n): readonly RuleGroup[] {\n const groups: RuleGroup[] = [];\n for (const rule of rules) {\n if (rule.scope !== 'file') continue;\n const ruleConfig = config.rules.get(rule.name);\n if (!ruleConfig || ruleConfig.isOff) continue;\n if (!ruleMatchesFile(rule, fileContext.relativePath)) continue;\n fileContext.options = mergeOptions(rule.defaultOptions, ruleConfig);\n const vs = runRuleCheck(rule, fileContext);\n if (vs.length > 0) {\n groups.push(new RuleGroup(\n rule.name, rule.description, [...rule.fixHint], [...vs],\n ));\n }\n }\n return groups;\n}\n"]}
|