@webpieces/ai-hook-rules 0.3.162 → 0.3.163
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.163",
|
|
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.163"
|
|
40
40
|
},
|
|
41
41
|
"publishConfig": {
|
|
42
42
|
"access": "public"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const child_process_1 = require("child_process");
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
4
6
|
const types_1 = require("../types");
|
|
5
7
|
const FIX_HINT = [
|
|
6
8
|
'Branch is not up-to-date with origin/main.',
|
|
@@ -16,7 +18,7 @@ const prCreationGuard = {
|
|
|
16
18
|
description: 'Block PR creation when the branch has not been updated from origin/main via the squash-merge process.',
|
|
17
19
|
scope: 'bash',
|
|
18
20
|
files: [],
|
|
19
|
-
defaultOptions: {},
|
|
21
|
+
defaultOptions: { buildCommand: '', requireTextInPr: '' },
|
|
20
22
|
fixHint: FIX_HINT,
|
|
21
23
|
check(ctx) {
|
|
22
24
|
if (!/gh\s+pr\s+create/.test(ctx.command))
|
|
@@ -24,18 +26,49 @@ const prCreationGuard = {
|
|
|
24
26
|
(0, child_process_1.execSync)('git fetch origin main --quiet', { cwd: ctx.workspaceRoot, encoding: 'utf8' });
|
|
25
27
|
// spawnSync is used here because exit code 1 means "not an ancestor" (not an error)
|
|
26
28
|
const result = (0, child_process_1.spawnSync)('git', ['merge-base', '--is-ancestor', 'origin/main', 'HEAD'], { cwd: ctx.workspaceRoot });
|
|
27
|
-
if (result.status
|
|
28
|
-
return [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'
|
|
36
|
-
|
|
29
|
+
if (result.status !== 0) {
|
|
30
|
+
return [new types_1.Violation(1, truncate(ctx.command), [
|
|
31
|
+
'Branch is not up-to-date with origin/main.',
|
|
32
|
+
'Run the squash-update process first:',
|
|
33
|
+
' ./scripts/git-updateFromMain.sh',
|
|
34
|
+
'Do NOT use "git merge origin/main" or "git rebase" — these break the 3-point fork-point system.',
|
|
35
|
+
'See docs/git-workflow.md for details.',
|
|
36
|
+
'Then retry: gh pr create',
|
|
37
|
+
].join('\n'))];
|
|
38
|
+
}
|
|
39
|
+
const buildCmd = ctx.options['buildCommand'];
|
|
40
|
+
const requireText = ctx.options['requireTextInPr'];
|
|
41
|
+
if (buildCmd && requireText) {
|
|
42
|
+
const body = extractPrBody(ctx.command, ctx.workspaceRoot);
|
|
43
|
+
if (body !== null && !body.includes(requireText)) {
|
|
44
|
+
return [new types_1.Violation(1, truncate(ctx.command), [
|
|
45
|
+
'PR body is missing the required CI confirmation.',
|
|
46
|
+
'Before creating a PR you must run:',
|
|
47
|
+
` ${buildCmd}`,
|
|
48
|
+
'After it passes, add this exact phrase to your PR description:',
|
|
49
|
+
` "${requireText}"`,
|
|
50
|
+
].join('\n'))];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [];
|
|
37
54
|
},
|
|
38
55
|
};
|
|
56
|
+
function extractPrBody(command, workspaceRoot) {
|
|
57
|
+
const fileMatch = /--body-file\s+(\S+)/.exec(command);
|
|
58
|
+
if (fileMatch) {
|
|
59
|
+
const resolved = (0, path_1.isAbsolute)(fileMatch[1]) ? fileMatch[1] : (0, path_1.join)(workspaceRoot, fileMatch[1]);
|
|
60
|
+
if (!(0, fs_1.existsSync)(resolved))
|
|
61
|
+
return null;
|
|
62
|
+
return (0, fs_1.readFileSync)(resolved, 'utf8');
|
|
63
|
+
}
|
|
64
|
+
const doubleQuoteMatch = /--body\s+"((?:[^"\\]|\\.)*)"/.exec(command);
|
|
65
|
+
if (doubleQuoteMatch)
|
|
66
|
+
return doubleQuoteMatch[1];
|
|
67
|
+
const singleQuoteMatch = /--body\s+'((?:[^'\\]|\\.)*)'/.exec(command);
|
|
68
|
+
if (singleQuoteMatch)
|
|
69
|
+
return singleQuoteMatch[1];
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
39
72
|
function truncate(s) {
|
|
40
73
|
const MAX = 120;
|
|
41
74
|
return s.length <= MAX ? s : s.slice(0, MAX) + '…';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-creation-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-creation-guard.ts"],"names":[],"mappings":";;AAAA,iDAAoD;
|
|
1
|
+
{"version":3,"file":"pr-creation-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-creation-guard.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync, spawnSync } from 'child_process';\nimport { readFileSync, existsSync } from 'fs';\nimport { isAbsolute, join } from 'path';\nimport type { BashRule, BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst FIX_HINT: readonly string[] = [\n 'Branch is not up-to-date with origin/main.',\n 'Run the squash-update process to sync with main first:',\n ' ./scripts/git-updateFromMain.sh',\n 'Then retry: gh pr create ...',\n '',\n 'Do NOT use \"git merge origin/main\" or \"git rebase\" — these break the 3-point fork-point system.',\n 'See docs/git-workflow.md for the full squash-merge update process.',\n];\n\nconst prCreationGuard: BashRule = {\n name: 'pr-creation-guard',\n description: 'Block PR creation when the branch has not been updated from origin/main via the squash-merge process.',\n scope: 'bash',\n files: [],\n defaultOptions: { buildCommand: '', requireTextInPr: '' },\n fixHint: FIX_HINT,\n\n check(ctx: BashContext): readonly Violation[] {\n if (!/gh\\s+pr\\s+create/.test(ctx.command)) return [];\n\n execSync('git fetch origin main --quiet', { cwd: ctx.workspaceRoot, encoding: 'utf8' });\n\n // spawnSync is used here because exit code 1 means \"not an ancestor\" (not an error)\n const result = spawnSync(\n 'git', ['merge-base', '--is-ancestor', 'origin/main', 'HEAD'],\n { cwd: ctx.workspaceRoot },\n );\n\n if (result.status !== 0) {\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'Branch is not up-to-date with origin/main.',\n 'Run the squash-update process first:',\n ' ./scripts/git-updateFromMain.sh',\n 'Do NOT use \"git merge origin/main\" or \"git rebase\" — these break the 3-point fork-point system.',\n 'See docs/git-workflow.md for details.',\n 'Then retry: gh pr create',\n ].join('\\n'),\n )];\n }\n\n const buildCmd = ctx.options['buildCommand'] as string;\n const requireText = ctx.options['requireTextInPr'] as string;\n if (buildCmd && requireText) {\n const body = extractPrBody(ctx.command, ctx.workspaceRoot);\n if (body !== null && !body.includes(requireText)) {\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'PR body is missing the required CI confirmation.',\n 'Before creating a PR you must run:',\n ` ${buildCmd}`,\n 'After it passes, add this exact phrase to your PR description:',\n ` \"${requireText}\"`,\n ].join('\\n'),\n )];\n }\n }\n return [];\n },\n};\n\nfunction extractPrBody(command: string, workspaceRoot: string): string | null {\n const fileMatch = /--body-file\\s+(\\S+)/.exec(command);\n if (fileMatch) {\n const resolved = isAbsolute(fileMatch[1]) ? fileMatch[1] : join(workspaceRoot, fileMatch[1]);\n if (!existsSync(resolved)) return null;\n return readFileSync(resolved, 'utf8');\n }\n const doubleQuoteMatch = /--body\\s+\"((?:[^\"\\\\]|\\\\.)*)\"/.exec(command);\n if (doubleQuoteMatch) return doubleQuoteMatch[1];\n const singleQuoteMatch = /--body\\s+'((?:[^'\\\\]|\\\\.)*)'/.exec(command);\n if (singleQuoteMatch) return singleQuoteMatch[1];\n return null;\n}\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport default prCreationGuard;\n"]}
|