@webpieces/ai-hook-rules 0.4.495 → 0.4.496
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.4.
|
|
3
|
+
"version": "0.4.496",
|
|
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",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"directory": "packages/tooling/ai-hook-rules"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@webpieces/rules-config": "0.4.
|
|
35
|
+
"@webpieces/rules-config": "0.4.496"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public"
|
|
@@ -20,21 +20,40 @@ function fixHintFor(mergeCompleteCommand) {
|
|
|
20
20
|
}
|
|
21
21
|
// Returns the path of the first UNVALIDATED merge marker found, or null. We detect validation
|
|
22
22
|
// by a raw substring (no JSON.parse) so a malformed marker can never crash the guard.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
/**
|
|
24
|
+
* DEPTH-BOUNDED SCAN, not a fixed level. The marker's home has moved twice: it was
|
|
25
|
+
* `merge-info/<feature>/`, then `merge-info/<feature>/merge-<n>/` once each sync got its own run dir,
|
|
26
|
+
* and is now `merge-info/staged/<feature>/merge-<n>/` since merge-info split into staged/merged. This
|
|
27
|
+
* guard was still looking only at the FIRST layout's depth, so it silently found nothing and stopped
|
|
28
|
+
* blocking — a guard failing open without saying so. A bounded walk covers every layout, legacy
|
|
29
|
+
* included, and stops long before it could descend into anything large.
|
|
30
|
+
*/
|
|
31
|
+
const MARKER_SCAN_MAX_DEPTH = 3;
|
|
32
|
+
// webpieces-disable no-function-outside-class -- module-level guard helper, matches the rest of this file
|
|
33
|
+
function findMarkerUnder(dir, depth) {
|
|
34
|
+
if (depth < 0 || !fs.existsSync(dir))
|
|
27
35
|
return null;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (!fs.existsSync(marker))
|
|
31
|
-
continue;
|
|
36
|
+
const marker = path.join(dir, rules_config_1.MERGE_IN_PROGRESS_FILE);
|
|
37
|
+
if (fs.existsSync(marker)) {
|
|
32
38
|
const raw = fs.readFileSync(marker, 'utf8');
|
|
33
39
|
if (!/"validated"\s*:\s*true/.test(raw))
|
|
34
40
|
return marker;
|
|
35
41
|
}
|
|
42
|
+
if (depth === 0)
|
|
43
|
+
return null;
|
|
44
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
45
|
+
if (!entry.isDirectory())
|
|
46
|
+
continue;
|
|
47
|
+
const found = findMarkerUnder(path.join(dir, entry.name), depth - 1);
|
|
48
|
+
if (found !== null)
|
|
49
|
+
return found;
|
|
50
|
+
}
|
|
36
51
|
return null;
|
|
37
52
|
}
|
|
53
|
+
// webpieces-disable no-function-outside-class -- module-level guard helper, matches the rest of this file
|
|
54
|
+
function findUnvalidatedMerge(workspaceRoot) {
|
|
55
|
+
return findMarkerUnder(path.join(workspaceRoot, rules_config_1.WEBPIECES_TMP_DIR, rules_config_1.MERGE_INFO_DIR), MARKER_SCAN_MAX_DEPTH);
|
|
56
|
+
}
|
|
38
57
|
const BLOCKED_GIT_SUBCOMMANDS = ['commit', 'push', 'merge', 'rebase'];
|
|
39
58
|
const SCANNER = new command_scan_1.CommandScanner();
|
|
40
59
|
// Operations that would let an agent route around the merge gate.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge-in-progress-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/merge-in-progress-guard.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAE7B,0DAAgI;AAGhI,oCAA0C;AAC1C,4CAA4C;AAC5C,0CAAsC;AACtC,kDAAiD;AAEjD,MAAM,8BAA8B,GAAG,0BAA0B,CAAC;AAElE,SAAS,UAAU,CAAC,oBAA4B;IAC5C,OAAO,IAAI,kBAAO,CACd,yEAAyE,EACzE,2GAA2G;UACzG,kEAAkE;UAClE,KAAK,oBAAoB,IAAI;UAC7B,gGAAgG;UAChG,2EAA2E;UAC3E,kHAAkH,CACvH,CAAC;AACN,CAAC;AAED,8FAA8F;AAC9F,sFAAsF;AACtF,SAAS,
|
|
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":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { WEBPIECES_TMP_DIR, MERGE_INFO_DIR, MERGE_IN_PROGRESS_FILE, MergeInProgressGuardConfig } from '@webpieces/rules-config';\n\nimport type { BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { BashRuleBase } from '../rule-base';\nimport { FixHint } from '../fix-hint';\nimport { CommandScanner } from '../command-scan';\n\nconst DEFAULT_MERGE_COMPLETE_COMMAND = 'pnpm wp-finish-upsert-pr';\n\nfunction fixHintFor(mergeCompleteCommand: string): FixHint {\n return new FixHint(\n 'A merge is in progress and not yet validated — this command is blocked.',\n 'You started a merge but never called the finish-merge command, so a 3-point merge is still in progress.\\n'\n + 'Resolve the remaining conflicts in the working tree, then run:\\n'\n + ` ${mergeCompleteCommand}\\n`\n + 'That scans for leftover conflict markers and runs the build; only when green does it commit,\\n'\n + 'unblock commit/push/PR, render the dashboard, and create/update the PR.\\n'\n + 'Add to memory: while a merge is in progress, do not run other commands — finish it with the command above first.',\n );\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.\n/**\n * DEPTH-BOUNDED SCAN, not a fixed level. The marker's home has moved twice: it was\n * `merge-info/<feature>/`, then `merge-info/<feature>/merge-<n>/` once each sync got its own run dir,\n * and is now `merge-info/staged/<feature>/merge-<n>/` since merge-info split into staged/merged. This\n * guard was still looking only at the FIRST layout's depth, so it silently found nothing and stopped\n * blocking — a guard failing open without saying so. A bounded walk covers every layout, legacy\n * included, and stops long before it could descend into anything large.\n */\nconst MARKER_SCAN_MAX_DEPTH = 3;\n\n// webpieces-disable no-function-outside-class -- module-level guard helper, matches the rest of this file\nfunction findMarkerUnder(dir: string, depth: number): string | null {\n if (depth < 0 || !fs.existsSync(dir)) return null;\n const marker = path.join(dir, MERGE_IN_PROGRESS_FILE);\n if (fs.existsSync(marker)) {\n const raw = fs.readFileSync(marker, 'utf8');\n if (!/\"validated\"\\s*:\\s*true/.test(raw)) return marker;\n }\n if (depth === 0) return null;\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const found = findMarkerUnder(path.join(dir, entry.name), depth - 1);\n if (found !== null) return found;\n }\n return null;\n}\n\n// webpieces-disable no-function-outside-class -- module-level guard helper, matches the rest of this file\nfunction findUnvalidatedMerge(workspaceRoot: string): string | null {\n return findMarkerUnder(path.join(workspaceRoot, WEBPIECES_TMP_DIR, MERGE_INFO_DIR), MARKER_SCAN_MAX_DEPTH);\n}\n\nconst BLOCKED_GIT_SUBCOMMANDS: readonly string[] = ['commit', 'push', 'merge', 'rebase'];\nconst SCANNER = new CommandScanner();\n\n// Operations that would let an agent route around the merge gate.\n//\n// Routed through CommandScanner rather than `/\\bgit\\s+merge\\b/`: that pattern matches the read-only\n// `git merge-base origin/main HEAD` (`\\b` sits between `e` and `-`), which appears in this repo's own\n// documented build command — so an in-progress merge used to block a harmless diff-scope lookup.\nfunction isBlockedDuringMerge(cmd: string): boolean {\n return SCANNER.commandInvokesAnyGit(cmd, BLOCKED_GIT_SUBCOMMANDS)\n || /\\bgh\\s+pr\\s+(create|edit|merge)\\b/.test(cmd);\n}\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport class MergeInProgressGuardRule extends BashRuleBase<MergeInProgressGuardConfig> {\n private readonly mergeCompleteCommand: string;\n\n constructor(config: MergeInProgressGuardConfig) {\n super(config, 'merge-in-progress-guard');\n this.mergeCompleteCommand = config.mergeCompleteCommand ?? DEFAULT_MERGE_COMPLETE_COMMAND;\n }\n\n readonly description = 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing the merge-complete command.';\n get fixHint(): FixHint { return fixHintFor(this.mergeCompleteCommand); }\n\n check(ctx: BashContext): readonly Violation[] {\n if (!isBlockedDuringMerge(ctx.commandCode)) return [];\n const marker = findUnvalidatedMerge(ctx.workspaceRoot);\n if (!marker) return [];\n return [new V(\n 1,\n truncate(ctx.command),\n 'A merge is in progress and not yet validated — this command is blocked.\\n'\n + `Marker: ${marker}`,\n )];\n }\n}\n"]}
|