@webpieces/ai-hook-rules 0.3.190 → 0.3.191

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.190",
3
+ "version": "0.3.191",
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.3.190"
35
+ "@webpieces/rules-config": "0.3.191"
36
36
  },
37
37
  "publishConfig": {
38
38
  "access": "public"
@@ -3,7 +3,7 @@ import type { BashContext, Violation } from '../types';
3
3
  import { BashRuleBase } from '../rule-base';
4
4
  export declare class MergeInProgressGuardRule extends BashRuleBase<MergeInProgressGuardConfig> {
5
5
  constructor(config: MergeInProgressGuardConfig);
6
- readonly description = "Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-git-merge-complete.";
6
+ readonly description = "Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-finish-upsert-pr.";
7
7
  readonly fixHint: readonly string[];
8
8
  check(ctx: BashContext): readonly Violation[];
9
9
  }
@@ -10,9 +10,9 @@ const rule_base_1 = require("../rule-base");
10
10
  const FIX_HINT = [
11
11
  'A 3-point merge is in progress and not yet validated.',
12
12
  'Resolve the remaining conflicts in the working tree, then run:',
13
- ' pnpm wp-git-merge-complete',
14
- 'That scans for leftover conflict markers and runs the build; only when green does it',
15
- 'commit and unblock commit/push/PR. Then run: pnpm wp-upsert-pr',
13
+ ' pnpm wp-finish-upsert-pr',
14
+ 'That scans for leftover conflict markers and runs the build; only when green does it commit,',
15
+ 'unblock commit/push/PR, render the dashboard, and create/update the PR.',
16
16
  ];
17
17
  // Returns the path of the first UNVALIDATED merge marker found, or null. We detect validation
18
18
  // by a raw substring (no JSON.parse) so a malformed marker can never crash the guard.
@@ -46,7 +46,7 @@ function truncate(s) {
46
46
  }
47
47
  class MergeInProgressGuardRule extends rule_base_1.BashRuleBase {
48
48
  constructor(config) { super(config, 'merge-in-progress-guard'); }
49
- description = 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-git-merge-complete.';
49
+ description = 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-finish-upsert-pr.';
50
50
  fixHint = FIX_HINT;
51
51
  check(ctx) {
52
52
  if (!isBlockedDuringMerge(ctx.command))
@@ -57,7 +57,7 @@ class MergeInProgressGuardRule extends rule_base_1.BashRuleBase {
57
57
  return [new types_1.Violation(1, truncate(ctx.command), [
58
58
  'A merge is in progress and not yet validated — this command is blocked.',
59
59
  `Marker: ${marker}`,
60
- 'Finish resolving conflicts, then run: pnpm wp-git-merge-complete',
60
+ 'Finish resolving conflicts, then run: pnpm wp-finish-upsert-pr',
61
61
  ].join('\n'))];
62
62
  }
63
63
  }
@@ -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,0DAAkI;AAGlI,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,QAAQ,GAAsB;IAChC,uDAAuD;IACvD,gEAAgE;IAChE,8BAA8B;IAC9B,sFAAsF;IACtF,gEAAgE;CACnE,CAAC;AAEF,8FAA8F;AAC9F,sFAAsF;AACtF,SAAS,oBAAoB,CAAC,aAAqB;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,gCAAiB,CAAC,CAAC;IAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,+BAAgB,CAAC;YAAE,SAAS;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,qCAAsB,CAAC,CAAC;QAChE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,SAAS;QACrC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,MAAM,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,kEAAkE;AAClE,SAAS,oBAAoB,CAAC,GAAW;IACrC,OAAO,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC5B,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC1B,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC3B,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC5B,mCAAmC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,MAAa,wBAAyB,SAAQ,wBAAwC;IAClF,YAAY,MAAkC,IAAI,KAAK,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC;IAEpF,WAAW,GAAG,6GAA6G,CAAC;IAC5H,OAAO,GAAG,QAAQ,CAAC;IAE5B,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,yEAAyE;gBACzE,WAAW,MAAM,EAAE;gBACnB,mEAAmE;aACtE,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ;AApBD,4DAoBC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { WEBPIECES_TMP_DIR, MERGE_DIR_PREFIX, MERGE_IN_PROGRESS_FILE, MergeInProgressGuardConfig } from '@webpieces/rules-config';\n\nimport type { BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { BashRuleBase } from '../rule-base';\n\nconst FIX_HINT: readonly string[] = [\n 'A 3-point merge is in progress and not yet validated.',\n 'Resolve the remaining conflicts in the working tree, then run:',\n ' pnpm wp-git-merge-complete',\n 'That scans for leftover conflict markers and runs the build; only when green does it',\n 'commit and unblock commit/push/PR. Then run: pnpm wp-upsert-pr',\n];\n\n// Returns the path of the first UNVALIDATED merge marker found, or null. We detect validation\n// by a raw substring (no JSON.parse) so a malformed marker can never crash the guard.\nfunction findUnvalidatedMerge(workspaceRoot: string): string | null {\n const tmpDir = path.join(workspaceRoot, WEBPIECES_TMP_DIR);\n if (!fs.existsSync(tmpDir)) return null;\n for (const entry of fs.readdirSync(tmpDir)) {\n if (!entry.startsWith(MERGE_DIR_PREFIX)) continue;\n const marker = path.join(tmpDir, entry, MERGE_IN_PROGRESS_FILE);\n if (!fs.existsSync(marker)) continue;\n const raw = fs.readFileSync(marker, 'utf8');\n if (!/\"validated\"\\s*:\\s*true/.test(raw)) return marker;\n }\n return null;\n}\n\n// Operations that would let an agent route around the merge gate.\nfunction isBlockedDuringMerge(cmd: string): boolean {\n return /\\bgit\\s+commit\\b/.test(cmd)\n || /\\bgit\\s+push\\b/.test(cmd)\n || /\\bgit\\s+merge\\b/.test(cmd)\n || /\\bgit\\s+rebase\\b/.test(cmd)\n || /\\bgh\\s+pr\\s+(create|edit|merge)\\b/.test(cmd);\n}\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport class MergeInProgressGuardRule extends BashRuleBase<MergeInProgressGuardConfig> {\n constructor(config: MergeInProgressGuardConfig) { super(config, 'merge-in-progress-guard'); }\n\n readonly description = 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-git-merge-complete.';\n readonly fixHint = FIX_HINT;\n\n check(ctx: BashContext): readonly Violation[] {\n if (!isBlockedDuringMerge(ctx.command)) return [];\n const marker = findUnvalidatedMerge(ctx.workspaceRoot);\n if (!marker) return [];\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'A merge is in progress and not yet validated — this command is blocked.',\n `Marker: ${marker}`,\n 'Finish resolving conflicts, then run: pnpm wp-git-merge-complete',\n ].join('\\n'),\n )];\n }\n}\n"]}
1
+ {"version":3,"file":"merge-in-progress-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/merge-in-progress-guard.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAE7B,0DAAkI;AAGlI,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,QAAQ,GAAsB;IAChC,uDAAuD;IACvD,gEAAgE;IAChE,4BAA4B;IAC5B,8FAA8F;IAC9F,yEAAyE;CAC5E,CAAC;AAEF,8FAA8F;AAC9F,sFAAsF;AACtF,SAAS,oBAAoB,CAAC,aAAqB;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,gCAAiB,CAAC,CAAC;IAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,+BAAgB,CAAC;YAAE,SAAS;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,qCAAsB,CAAC,CAAC;QAChE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,SAAS;QACrC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,MAAM,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,kEAAkE;AAClE,SAAS,oBAAoB,CAAC,GAAW;IACrC,OAAO,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC5B,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC1B,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC3B,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;WAC5B,mCAAmC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,MAAa,wBAAyB,SAAQ,wBAAwC;IAClF,YAAY,MAAkC,IAAI,KAAK,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC;IAEpF,WAAW,GAAG,2GAA2G,CAAC;IAC1H,OAAO,GAAG,QAAQ,CAAC;IAE5B,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,yEAAyE;gBACzE,WAAW,MAAM,EAAE;gBACnB,iEAAiE;aACpE,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ;AApBD,4DAoBC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { WEBPIECES_TMP_DIR, MERGE_DIR_PREFIX, MERGE_IN_PROGRESS_FILE, MergeInProgressGuardConfig } from '@webpieces/rules-config';\n\nimport type { BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { BashRuleBase } from '../rule-base';\n\nconst FIX_HINT: readonly string[] = [\n 'A 3-point merge is in progress and not yet validated.',\n 'Resolve the remaining conflicts in the working tree, then run:',\n ' pnpm wp-finish-upsert-pr',\n 'That scans for leftover conflict markers and runs the build; only when green does it commit,',\n 'unblock commit/push/PR, render the dashboard, and create/update the PR.',\n];\n\n// Returns the path of the first UNVALIDATED merge marker found, or null. We detect validation\n// by a raw substring (no JSON.parse) so a malformed marker can never crash the guard.\nfunction findUnvalidatedMerge(workspaceRoot: string): string | null {\n const tmpDir = path.join(workspaceRoot, WEBPIECES_TMP_DIR);\n if (!fs.existsSync(tmpDir)) return null;\n for (const entry of fs.readdirSync(tmpDir)) {\n if (!entry.startsWith(MERGE_DIR_PREFIX)) continue;\n const marker = path.join(tmpDir, entry, MERGE_IN_PROGRESS_FILE);\n if (!fs.existsSync(marker)) continue;\n const raw = fs.readFileSync(marker, 'utf8');\n if (!/\"validated\"\\s*:\\s*true/.test(raw)) return marker;\n }\n return null;\n}\n\n// Operations that would let an agent route around the merge gate.\nfunction isBlockedDuringMerge(cmd: string): boolean {\n return /\\bgit\\s+commit\\b/.test(cmd)\n || /\\bgit\\s+push\\b/.test(cmd)\n || /\\bgit\\s+merge\\b/.test(cmd)\n || /\\bgit\\s+rebase\\b/.test(cmd)\n || /\\bgh\\s+pr\\s+(create|edit|merge)\\b/.test(cmd);\n}\n\nfunction truncate(s: string): string {\n const MAX = 120;\n return s.length <= MAX ? s : s.slice(0, MAX) + '…';\n}\n\nexport class MergeInProgressGuardRule extends BashRuleBase<MergeInProgressGuardConfig> {\n constructor(config: MergeInProgressGuardConfig) { super(config, 'merge-in-progress-guard'); }\n\n readonly description = 'Block commit/push/merge/PR while a 3-point merge marker is unvalidated, forcing pnpm wp-finish-upsert-pr.';\n readonly fixHint = FIX_HINT;\n\n check(ctx: BashContext): readonly Violation[] {\n if (!isBlockedDuringMerge(ctx.command)) return [];\n const marker = findUnvalidatedMerge(ctx.workspaceRoot);\n if (!marker) return [];\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'A merge is in progress and not yet validated — this command is blocked.',\n `Marker: ${marker}`,\n 'Finish resolving conflicts, then run: pnpm wp-finish-upsert-pr',\n ].join('\\n'),\n )];\n }\n}\n"]}
@@ -3,19 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PrCreationGuardRule = void 0;
4
4
  const types_1 = require("../types");
5
5
  const rule_base_1 = require("../rule-base");
6
- const DEFAULT_UPSERT_PR_COMMAND = 'pnpm wp-upsert-pr';
6
+ const DEFAULT_UPSERT_PR_COMMAND = 'pnpm wp-start-upsert-pr';
7
7
  function fixHintFor(upsertPrCommand) {
8
8
  return [
9
- 'Direct PR creation is blocked. Create or update a PR ONLY via the gated command:',
9
+ 'Direct PR creation is blocked. Create or update a PR ONLY via the gated flow:',
10
10
  ` ${upsertPrCommand}`,
11
- 'It updates the branch from main (3-point merge), runs the real build (nx affected), and',
12
- 'assembles the PR dashboard then creates/updates the PR itself. A failing build = no PR.',
13
- 'There is nothing to paste or attest to; the command does the work.',
11
+ 'It updates the branch from main (3-point merge) and runs the real build (nx affected), then',
12
+ 'instructs you to write review.json and run `pnpm wp-finish-upsert-pr`, which assembles the',
13
+ 'dashboard and creates/updates the PR itself. A failing build = no PR.',
14
+ 'There is nothing to paste or attest to; the commands do the work.',
14
15
  ];
15
16
  }
16
- // Detect every way an agent could open/update a PR directly, so the ONLY path left is
17
- // `pnpm wp-upsert-pr` (whose internal `gh pr create` runs as a child process the hook
18
- // never sees). Read-only `gh pr list` / `gh api .../pulls` GET are intentionally allowed.
17
+ // Detect every way an agent could open/update a PR directly, so the ONLY path left is the gated
18
+ // flow (wp-start-upsert-pr → wp-finish-upsert-pr, whose internal `gh pr create` runs as a child
19
+ // process the hook never sees). Read-only `gh pr list` / `gh api .../pulls` GET are intentionally allowed.
19
20
  function isDirectPrCreation(cmd) {
20
21
  if (/\bgh\s+pr\s+(create|edit)\b/.test(cmd))
21
22
  return true;
@@ -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":";;;AAGA,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,yBAAyB,GAAG,mBAAmB,CAAC;AAEtD,SAAS,UAAU,CAAC,eAAuB;IACvC,OAAO;QACH,kFAAkF;QAClF,KAAK,eAAe,EAAE;QACtB,yFAAyF;QACzF,2FAA2F;QAC3F,oEAAoE;KACvE,CAAC;AACN,CAAC;AAED,sFAAsF;AACtF,sFAAsF;AACtF,0FAA0F;AAC1F,SAAS,kBAAkB,CAAC,GAAW;IACnC,IAAI,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzD,MAAM,UAAU,GAAG,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3D,IAAI,UAAU,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC9I,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,SAAS,GAAG,+CAA+C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5E,IAAI,SAAS,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC1G,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,MAAa,mBAAoB,SAAQ,wBAAmC;IACvD,eAAe,CAAS;IAEzC,YAAY,MAA6B;QACrC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;QACnC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI,yBAAyB,CAAC;IAC/E,CAAC;IAEQ,WAAW,GAAG,2GAA2G,CAAC;IACnI,IAAI,OAAO,KAAwB,OAAO,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAE7E,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAChD,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,uCAAuC;gBACvC,6EAA6E;gBAC7E,KAAK,IAAI,CAAC,eAAe,EAAE;aAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ;AAvBD,kDAuBC","sourcesContent":["import { PrCreationGuardConfig } from '@webpieces/rules-config';\n\nimport type { BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { BashRuleBase } from '../rule-base';\n\nconst DEFAULT_UPSERT_PR_COMMAND = 'pnpm wp-upsert-pr';\n\nfunction fixHintFor(upsertPrCommand: string): readonly string[] {\n return [\n 'Direct PR creation is blocked. Create or update a PR ONLY via the gated command:',\n ` ${upsertPrCommand}`,\n 'It updates the branch from main (3-point merge), runs the real build (nx affected), and',\n 'assembles the PR dashboard then creates/updates the PR itself. A failing build = no PR.',\n 'There is nothing to paste or attest to; the command does the work.',\n ];\n}\n\n// Detect every way an agent could open/update a PR directly, so the ONLY path left is\n// `pnpm wp-upsert-pr` (whose internal `gh pr create` runs as a child process the hook\n// never sees). Read-only `gh pr list` / `gh api .../pulls` GET are intentionally allowed.\nfunction isDirectPrCreation(cmd: string): boolean {\n if (/\\bgh\\s+pr\\s+(create|edit)\\b/.test(cmd)) return true;\n\n const ghApiPulls = /\\bgh\\s+api\\b[^\\n]*\\/pulls\\b/.test(cmd);\n if (ghApiPulls && (/--method\\s+POST/i.test(cmd) || /-X\\s+POST/i.test(cmd) || /\\s-f\\b/.test(cmd) || /\\s-F\\b/.test(cmd) || /--field\\b/.test(cmd))) {\n return true;\n }\n\n const curlPulls = /\\bcurl\\b[^\\n]*api\\.github\\.com[^\\n]*\\/pulls\\b/.test(cmd);\n if (curlPulls && (/-X\\s*POST/i.test(cmd) || /--request\\s+POST/i.test(cmd) || /(\\s-d\\b|--data\\b)/.test(cmd))) {\n return true;\n }\n return false;\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 PrCreationGuardRule extends BashRuleBase<PrCreationGuardConfig> {\n private readonly upsertPrCommand: string;\n\n constructor(config: PrCreationGuardConfig) {\n super(config, 'pr-creation-guard');\n this.upsertPrCommand = config.upsertPrCommand ?? DEFAULT_UPSERT_PR_COMMAND;\n }\n\n readonly description = 'Block direct PR creation/edit (gh pr / gh api / curl) so PRs go only through the gated upsert-pr command.';\n get fixHint(): readonly string[] { return fixHintFor(this.upsertPrCommand); }\n\n check(ctx: BashContext): readonly Violation[] {\n if (!isDirectPrCreation(ctx.command)) return [];\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'Direct PR creation/update is blocked.',\n 'Use the gated command instead — it runs the build and builds the dashboard:',\n ` ${this.upsertPrCommand}`,\n ].join('\\n'),\n )];\n }\n}\n"]}
1
+ {"version":3,"file":"pr-creation-guard.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/pr-creation-guard.ts"],"names":[],"mappings":";;;AAGA,oCAA0C;AAC1C,4CAA4C;AAE5C,MAAM,yBAAyB,GAAG,yBAAyB,CAAC;AAE5D,SAAS,UAAU,CAAC,eAAuB;IACvC,OAAO;QACH,+EAA+E;QAC/E,KAAK,eAAe,EAAE;QACtB,6FAA6F;QAC7F,4FAA4F;QAC5F,uEAAuE;QACvE,mEAAmE;KACtE,CAAC;AACN,CAAC;AAED,gGAAgG;AAChG,gGAAgG;AAChG,2GAA2G;AAC3G,SAAS,kBAAkB,CAAC,GAAW;IACnC,IAAI,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzD,MAAM,UAAU,GAAG,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3D,IAAI,UAAU,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC9I,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,SAAS,GAAG,+CAA+C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5E,IAAI,SAAS,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC1G,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC;AAED,MAAa,mBAAoB,SAAQ,wBAAmC;IACvD,eAAe,CAAS;IAEzC,YAAY,MAA6B;QACrC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;QACnC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI,yBAAyB,CAAC;IAC/E,CAAC;IAEQ,WAAW,GAAG,2GAA2G,CAAC;IACnI,IAAI,OAAO,KAAwB,OAAO,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAE7E,KAAK,CAAC,GAAgB;QAClB,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAChD,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB;gBACI,uCAAuC;gBACvC,6EAA6E;gBAC7E,KAAK,IAAI,CAAC,eAAe,EAAE;aAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC,CAAC;IACP,CAAC;CACJ;AAvBD,kDAuBC","sourcesContent":["import { PrCreationGuardConfig } from '@webpieces/rules-config';\n\nimport type { BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { BashRuleBase } from '../rule-base';\n\nconst DEFAULT_UPSERT_PR_COMMAND = 'pnpm wp-start-upsert-pr';\n\nfunction fixHintFor(upsertPrCommand: string): readonly string[] {\n return [\n 'Direct PR creation is blocked. Create or update a PR ONLY via the gated flow:',\n ` ${upsertPrCommand}`,\n 'It updates the branch from main (3-point merge) and runs the real build (nx affected), then',\n 'instructs you to write review.json and run `pnpm wp-finish-upsert-pr`, which assembles the',\n 'dashboard and creates/updates the PR itself. A failing build = no PR.',\n 'There is nothing to paste or attest to; the commands do the work.',\n ];\n}\n\n// Detect every way an agent could open/update a PR directly, so the ONLY path left is the gated\n// flow (wp-start-upsert-pr → wp-finish-upsert-pr, whose internal `gh pr create` runs as a child\n// process the hook never sees). Read-only `gh pr list` / `gh api .../pulls` GET are intentionally allowed.\nfunction isDirectPrCreation(cmd: string): boolean {\n if (/\\bgh\\s+pr\\s+(create|edit)\\b/.test(cmd)) return true;\n\n const ghApiPulls = /\\bgh\\s+api\\b[^\\n]*\\/pulls\\b/.test(cmd);\n if (ghApiPulls && (/--method\\s+POST/i.test(cmd) || /-X\\s+POST/i.test(cmd) || /\\s-f\\b/.test(cmd) || /\\s-F\\b/.test(cmd) || /--field\\b/.test(cmd))) {\n return true;\n }\n\n const curlPulls = /\\bcurl\\b[^\\n]*api\\.github\\.com[^\\n]*\\/pulls\\b/.test(cmd);\n if (curlPulls && (/-X\\s*POST/i.test(cmd) || /--request\\s+POST/i.test(cmd) || /(\\s-d\\b|--data\\b)/.test(cmd))) {\n return true;\n }\n return false;\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 PrCreationGuardRule extends BashRuleBase<PrCreationGuardConfig> {\n private readonly upsertPrCommand: string;\n\n constructor(config: PrCreationGuardConfig) {\n super(config, 'pr-creation-guard');\n this.upsertPrCommand = config.upsertPrCommand ?? DEFAULT_UPSERT_PR_COMMAND;\n }\n\n readonly description = 'Block direct PR creation/edit (gh pr / gh api / curl) so PRs go only through the gated upsert-pr command.';\n get fixHint(): readonly string[] { return fixHintFor(this.upsertPrCommand); }\n\n check(ctx: BashContext): readonly Violation[] {\n if (!isDirectPrCreation(ctx.command)) return [];\n return [new V(\n 1,\n truncate(ctx.command),\n [\n 'Direct PR creation/update is blocked.',\n 'Use the gated command instead — it runs the build and builds the dashboard:',\n ` ${this.upsertPrCommand}`,\n ].join('\\n'),\n )];\n }\n}\n"]}
@@ -12,5 +12,21 @@
12
12
  "no-unmanaged-exceptions": {},
13
13
  "nx-wiring": { "mode": "ON" }
14
14
  },
15
- "rulesDir": []
15
+ "rulesDir": [],
16
+ "pr-gate": {
17
+ "mode": "ON",
18
+ "buildCommandWhy": "--base is the FORK POINT via $(git merge-base origin/main HEAD), NOT origin/main: basing on origin/main marks projects from other people's already-merged PRs as 'affected' (your branch still has their pre-merge versions), wasting rebuilds. The fork point scopes affected to only your branch's work. The $(...) resolves because the gate runs with shell:true.",
19
+ "buildCommand": "pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)",
20
+ "gatesWhy": "color = the dashboard color shown WHEN a gate's patterns match a changed file (green is implicit when nothing matches; color is purely visual and never blocks the PR — only the build gate can). disabled:true gates are kept-in-file examples (JSON has no comments) you flip to false and tune for your project.",
21
+ "gates": [
22
+ { "name": "API Changed", "patterns": ["libraries/apis/**", "**/*Api.ts"], "color": "yellow" },
23
+ { "name": "Config Files Changed", "patterns": ["**/package.json", "**/tsconfig*.json", "nx.json", "**/*.config.*"], "color": "yellow" },
24
+ { "name": "Dependency Graph Changed", "patterns": ["architecture/dependencies.json"], "color": "yellow" },
25
+ { "name": "Claude / Rules Changed", "patterns": ["**/CLAUDE.md", "**/claude.*.md", ".claude/**", "webpieces.config.json"], "color": "yellow" },
26
+ { "name": "Package.json Changed", "patterns": ["**/package.json"], "color": "yellow", "disabled": true },
27
+ { "name": "Authentication Changed", "patterns": ["**/authentication.ts"], "color": "yellow", "disabled": true },
28
+ { "name": "DB Schema Changed", "patterns": ["**/schema.prisma"], "color": "red", "disabled": true },
29
+ { "name": "Migration Files", "patterns": ["**/migrations/**"], "color": "red", "disabled": true }
30
+ ]
31
+ }
16
32
  }