@webpieces/nx-webpieces-rules 0.4.842 → 0.4.844

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/README.md CHANGED
@@ -4,6 +4,8 @@ Nx inference plugin that auto-creates webpieces validation targets (architecture
4
4
  graph checks, code-size/style rules, and a per-project circular-import gate)
5
5
  without any manual `project.json` wiring.
6
6
 
7
+ Install `@webpieces/webpieces-tooling` for the complete toolchain. This package implements the Nx plugin and executors; it does not aggregate hook or PR workflow packages.
8
+
7
9
  Add it to `nx.json`:
8
10
 
9
11
  ```jsonc
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@webpieces/nx-webpieces-rules",
3
- "version": "0.4.842",
4
- "description": "Nx-specific webpieces validation rules and graph tooling. Bundles all @webpieces rule packages with Nx graph validators and an inference plugin.",
3
+ "version": "0.4.844",
4
+ "description": "Nx-specific webpieces validation rules, graph tooling, executors, and inference plugin.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
7
7
  "executors": "./executors.json",
@@ -10,7 +10,7 @@
10
10
  "./package.json": "./package.json",
11
11
  "./plugin": "./src/plugin.js",
12
12
  "./executors.json": "./executors.json",
13
- "./openclaw-plugin": "./src/adapters/openclaw-plugin.js"
13
+ "./rule-pack": "./src/rule-pack.js"
14
14
  },
15
15
  "files": [
16
16
  "src/**/*",
@@ -19,18 +19,13 @@
19
19
  "README.md"
20
20
  ],
21
21
  "dependencies": {
22
- "@webpieces/ai-hook-rules": "0.4.842",
23
- "@webpieces/hook-runtime": "0.4.842",
24
- "@webpieces/api-doc-model": "0.4.842",
25
- "@webpieces/code-rules": "0.4.842",
26
- "@webpieces/core-util": "0.4.842",
27
- "@webpieces/eslint-rules": "0.4.842",
28
- "@webpieces/pr-gate": "0.4.842",
29
- "@webpieces/rules-config": "0.4.842",
22
+ "@webpieces/api-doc-model": "0.4.844",
23
+ "@webpieces/code-rules": "0.4.844",
24
+ "@webpieces/core-util": "0.4.844",
25
+ "@webpieces/eslint-rules": "0.4.844",
26
+ "@webpieces/rules-config": "0.4.844",
30
27
  "madge": "8.0.0",
31
- "@webpieces/tooling-common": "0.4.842",
32
- "@webpieces/repo-workflow-core": "0.4.842",
33
- "@webpieces/agent-workflow-rules": "0.4.842"
28
+ "@webpieces/rules-sdk": "0.4.844"
34
29
  },
35
30
  "peerDependencies": {
36
31
  "@nx/devkit": ">=18.0.0"
@@ -45,6 +40,9 @@
45
40
  "publishConfig": {
46
41
  "access": "public"
47
42
  },
43
+ "devDependencies": {
44
+ "@webpieces/tooling-testkit": "0.4.844"
45
+ },
48
46
  "types": "./src/index.d.ts",
49
47
  "bin": {
50
48
  "wp-design-visualize": "./src/scripts/wp-design-visualize.js"
@@ -0,0 +1,2 @@
1
+ import { RulePackManifest } from '@webpieces/rules-sdk';
2
+ export declare const rulePackManifest: RulePackManifest;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rulePackManifest = void 0;
4
+ const rules_sdk_1 = require("@webpieces/rules-sdk");
5
+ const rules_config_1 = require("@webpieces/rules-config");
6
+ const package_json_1 = require("../package.json");
7
+ /** Compatibility manifest; executable registries remain authoritative until ownership migration. */
8
+ const ownedRuleIds = [
9
+ "no-file-import-cycles",
10
+ "runtime-architecture",
11
+ "nx-wiring",
12
+ "di-graph",
13
+ "missing-design-annotation",
14
+ "validate-architecture-unchanged",
15
+ "validate-no-architecture-cycles",
16
+ "validate-packagejson",
17
+ "validate-versions-locked",
18
+ "validate-eslint-sync",
19
+ "no-root-union-api-type",
20
+ "api-rules-for-openapi",
21
+ "api-rules-for-mcp",
22
+ "api-lib-dependencies",
23
+ "api-lib-path",
24
+ "framework-folder"
25
+ ];
26
+ exports.rulePackManifest = new rules_sdk_1.RulePackManifest('@webpieces/nx-webpieces-rules', package_json_1.version, rules_sdk_1.RULE_PACK_API_VERSION, ownedRuleIds.map(id => new rules_sdk_1.OwnedRuleDefinition(id, rules_config_1.RULE_SCHEMAS[id], rules_sdk_1.RULE_SCHEMA_API_VERSION)), [
27
+ ...ownedRuleIds.map(id => new rules_sdk_1.RuleContribution(id, '@webpieces/nx-webpieces-rules', 'build')),
28
+ new rules_sdk_1.RuleContribution('validate-ts-in-src', '@webpieces/ai-hook-rules', 'build'),
29
+ ]);
30
+ //# sourceMappingURL=rule-pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-pack.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/rule-pack.ts"],"names":[],"mappings":";;;AAAA,oDAA+I;AAC/I,0DAAuD;AACvD,kDAA0C;AAE1C,oGAAoG;AACpG,MAAM,YAAY,GAAG;IACjB,uBAAuB;IACvB,sBAAsB;IACtB,WAAW;IACX,UAAU;IACV,2BAA2B;IAC3B,iCAAiC;IACjC,iCAAiC;IACjC,sBAAsB;IACtB,0BAA0B;IAC1B,sBAAsB;IACtB,wBAAwB;IACxB,uBAAuB;IACvB,mBAAmB;IACnB,sBAAsB;IACtB,cAAc;IACd,kBAAkB;CACZ,CAAC;AAEE,QAAA,gBAAgB,GAAG,IAAI,4BAAgB,CAChD,+BAA+B,EAC/B,sBAAO,EACP,iCAAqB,EACrB,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,+BAAmB,CAAC,EAAE,EAAE,2BAAY,CAAC,EAAE,CAAC,EAAE,mCAAuB,CAAC,CAAC,EAC9F;IACI,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,4BAAgB,CAAC,EAAE,EAAE,+BAA+B,EAAE,OAAO,CAAC,CAAC;IAC7F,IAAI,4BAAgB,CAAC,oBAAoB,EAAE,0BAA0B,EAAE,OAAO,CAAC;CAClF,CACJ,CAAC","sourcesContent":["import { OwnedRuleDefinition, RuleContribution, RulePackManifest, RULE_PACK_API_VERSION, RULE_SCHEMA_API_VERSION } from '@webpieces/rules-sdk';\nimport { RULE_SCHEMAS } from '@webpieces/rules-config';\nimport { version } from '../package.json';\n\n/** Compatibility manifest; executable registries remain authoritative until ownership migration. */\nconst ownedRuleIds = [\n \"no-file-import-cycles\",\n \"runtime-architecture\",\n \"nx-wiring\",\n \"di-graph\",\n \"missing-design-annotation\",\n \"validate-architecture-unchanged\",\n \"validate-no-architecture-cycles\",\n \"validate-packagejson\",\n \"validate-versions-locked\",\n \"validate-eslint-sync\",\n \"no-root-union-api-type\",\n \"api-rules-for-openapi\",\n \"api-rules-for-mcp\",\n \"api-lib-dependencies\",\n \"api-lib-path\",\n \"framework-folder\"\n] as const;\n\nexport const rulePackManifest = new RulePackManifest(\n '@webpieces/nx-webpieces-rules',\n version,\n RULE_PACK_API_VERSION,\n ownedRuleIds.map(id => new OwnedRuleDefinition(id, RULE_SCHEMAS[id], RULE_SCHEMA_API_VERSION)),\n [\n ...ownedRuleIds.map(id => new RuleContribution(id, '@webpieces/nx-webpieces-rules', 'build')),\n new RuleContribution('validate-ts-in-src', '@webpieces/ai-hook-rules', 'build'),\n ],\n);\n"]}
@@ -1,14 +0,0 @@
1
- interface ToolCallEvent {
2
- toolName: string;
3
- arguments: Record<string, unknown>;
4
- }
5
- interface HookContext {
6
- [key: string]: unknown;
7
- }
8
- declare class OpenclawHandlerResult {
9
- readonly status: 'approved' | 'rejected';
10
- readonly reason: string | undefined;
11
- constructor(status: 'approved' | 'rejected', reason?: string);
12
- }
13
- export default function handler(event: ToolCallEvent, _context: HookContext): Promise<OpenclawHandlerResult | undefined>;
14
- export {};
@@ -1,99 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = handler;
4
- const tslib_1 = require("tslib");
5
- const path = tslib_1.__importStar(require("path"));
6
- const fs = tslib_1.__importStar(require("fs"));
7
- const rules_config_1 = require("@webpieces/rules-config");
8
- const ai_hook_rules_1 = require("@webpieces/ai-hook-rules");
9
- const agent_workflow_rules_1 = require("@webpieces/agent-workflow-rules");
10
- const hook_runtime_1 = require("@webpieces/hook-runtime");
11
- const rules_config_2 = require("@webpieces/rules-config");
12
- const hook_runtime_2 = require("@webpieces/hook-runtime");
13
- const tooling_common_1 = require("@webpieces/tooling-common");
14
- const rules_config_3 = require("@webpieces/rules-config");
15
- const to_error_1 = require("@webpieces/tooling-common/to-error");
16
- class OpenclawHandlerResult {
17
- status;
18
- reason;
19
- constructor(status, reason) {
20
- this.status = status;
21
- this.reason = reason;
22
- }
23
- }
24
- const TOOL_MAP = {
25
- 'write': 'Write',
26
- 'edit': 'Edit',
27
- };
28
- // webpieces-disable no-function-outside-class -- existing OpenClaw callback and stateless adapter helpers moved intact; the SDK invokes this module function
29
- function mapToolName(openclawName) {
30
- return TOOL_MAP[openclawName] || null;
31
- }
32
- // webpieces-disable no-any-unknown -- openclaw SDK passes opaque tool arguments
33
- // webpieces-disable no-function-outside-class -- existing OpenClaw callback and stateless adapter helpers moved intact; the SDK invokes this module function
34
- function mapToolInput(toolName, args) {
35
- const filePath = typeof args['path'] === 'string' ? args['path'] : null;
36
- if (!filePath)
37
- return null;
38
- if (toolName === 'write') {
39
- const content = typeof args['content'] === 'string' ? args['content'] : '';
40
- return new hook_runtime_2.NormalizedToolInput(filePath, [new hook_runtime_2.NormalizedEdit('', content)]);
41
- }
42
- if (toolName === 'edit') {
43
- const oldStr = typeof args['old_string'] === 'string' ? args['old_string'] : '';
44
- const newStr = typeof args['new_string'] === 'string' ? args['new_string'] : '';
45
- return new hook_runtime_2.NormalizedToolInput(filePath, [new hook_runtime_2.NormalizedEdit(oldStr, newStr)]);
46
- }
47
- return null;
48
- }
49
- // webpieces-disable no-function-outside-class -- existing OpenClaw callback and stateless adapter helpers moved intact; the SDK invokes this module function
50
- function findWorkspaceRoot(filePath) {
51
- let dir = path.dirname(filePath);
52
- while (true) {
53
- if (fs.existsSync(path.join(dir, rules_config_1.CONFIG_FILENAME)))
54
- return dir;
55
- const parent = path.dirname(dir);
56
- if (parent === dir)
57
- return null;
58
- dir = parent;
59
- }
60
- }
61
- // webpieces-disable no-function-outside-class -- existing OpenClaw callback and stateless adapter helpers moved intact; the SDK invokes this module function
62
- async function handler(event, _context) {
63
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
64
- try {
65
- const toolKind = mapToolName(event.toolName);
66
- if (!toolKind)
67
- return undefined;
68
- const input = mapToolInput(event.toolName, event.arguments);
69
- if (!input)
70
- return undefined;
71
- const wsRoot = findWorkspaceRoot(input.filePath);
72
- if (!wsRoot)
73
- return undefined;
74
- const loaded = (0, rules_config_2.loadAndValidate)(wsRoot);
75
- const source = new ai_hook_rules_1.SourceHookRules().load(loaded);
76
- const guards = new agent_workflow_rules_1.WorkflowHookRules().load(loaded);
77
- const result = new hook_runtime_1.HookFileEvaluation().evaluate(toolKind, input, loaded, new hook_runtime_1.HookRuleSet([...source.builtInRules, ...guards.rules, ...source.extensionRules], [...source.configuredRules, ...guards.configuredRules]));
78
- if (!result)
79
- return new OpenclawHandlerResult('approved');
80
- return new OpenclawHandlerResult('rejected', result.report);
81
- }
82
- catch (err) {
83
- const error = (0, to_error_1.toError)(err);
84
- // An escaped RuleFailError or InformAiError carries an AI-readable message; anything else is
85
- // an unexpected bug. All reject (fail closed).
86
- let msg;
87
- if (error instanceof rules_config_3.RuleFailError) {
88
- msg = (0, rules_config_1.renderRuleFailForAi)(error);
89
- }
90
- else if (error instanceof tooling_common_1.InformAiError) {
91
- msg = error.message;
92
- }
93
- else {
94
- msg = `[ai-hooks] openclaw adapter crashed — failing closed: ${error.message}`;
95
- }
96
- return new OpenclawHandlerResult('rejected', msg);
97
- }
98
- }
99
- //# sourceMappingURL=openclaw-plugin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"openclaw-plugin.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/adapters/openclaw-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport * as fs from 'fs';\n\nimport { CONFIG_FILENAME, renderRuleFailForAi } from '@webpieces/rules-config';\nimport { SourceHookRules } from '@webpieces/ai-hook-rules';\nimport { WorkflowHookRules } from '@webpieces/agent-workflow-rules';\nimport { HookRuleSet, HookFileEvaluation } from '@webpieces/hook-runtime';\nimport { loadAndValidate } from '@webpieces/rules-config';\nimport { NormalizedToolInput, NormalizedEdit, ToolKind } from '@webpieces/hook-runtime';\nimport { InformAiError } from '@webpieces/tooling-common';\nimport { RuleFailError } from '@webpieces/rules-config';\nimport { toError } from '@webpieces/tooling-common/to-error';\n\ninterface ToolCallEvent {\n toolName: string;\n // webpieces-disable no-any-unknown -- openclaw SDK passes opaque tool arguments\n arguments: Record<string, unknown>;\n}\n\ninterface HookContext {\n // webpieces-disable no-any-unknown -- openclaw SDK context shape is opaque\n [key: string]: unknown;\n}\n\nclass OpenclawHandlerResult {\n readonly status: 'approved' | 'rejected';\n readonly reason: string | undefined;\n\n constructor(status: 'approved' | 'rejected', reason?: string) {\n this.status = status;\n this.reason = reason;\n }\n}\n\nconst TOOL_MAP: Record<string, ToolKind> = {\n 'write': 'Write',\n 'edit': 'Edit',\n};\n\n// webpieces-disable no-function-outside-class -- existing OpenClaw callback and stateless adapter helpers moved intact; the SDK invokes this module function\nfunction mapToolName(openclawName: string): ToolKind | null {\n return TOOL_MAP[openclawName] || null;\n}\n\n// webpieces-disable no-any-unknown -- openclaw SDK passes opaque tool arguments\n// webpieces-disable no-function-outside-class -- existing OpenClaw callback and stateless adapter helpers moved intact; the SDK invokes this module function\nfunction mapToolInput(toolName: string, args: Record<string, unknown>): NormalizedToolInput | null {\n const filePath = typeof args['path'] === 'string' ? args['path'] as string : null;\n if (!filePath) return null;\n\n if (toolName === 'write') {\n const content = typeof args['content'] === 'string' ? args['content'] as string : '';\n return new NormalizedToolInput(filePath, [new NormalizedEdit('', content)]);\n }\n if (toolName === 'edit') {\n const oldStr = typeof args['old_string'] === 'string' ? args['old_string'] as string : '';\n const newStr = typeof args['new_string'] === 'string' ? args['new_string'] as string : '';\n return new NormalizedToolInput(filePath, [new NormalizedEdit(oldStr, newStr)]);\n }\n return null;\n}\n\n// webpieces-disable no-function-outside-class -- existing OpenClaw callback and stateless adapter helpers moved intact; the SDK invokes this module function\nfunction findWorkspaceRoot(filePath: string): string | null {\n let dir = path.dirname(filePath);\n while (true) {\n if (fs.existsSync(path.join(dir, CONFIG_FILENAME))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n// webpieces-disable no-function-outside-class -- existing OpenClaw callback and stateless adapter helpers moved intact; the SDK invokes this module function\nexport default async function handler(\n event: ToolCallEvent,\n _context: HookContext,\n): Promise<OpenclawHandlerResult | undefined> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const toolKind = mapToolName(event.toolName);\n if (!toolKind) return undefined;\n\n const input = mapToolInput(event.toolName, event.arguments);\n if (!input) return undefined;\n\n const wsRoot = findWorkspaceRoot(input.filePath);\n if (!wsRoot) return undefined;\n\n const loaded = loadAndValidate(wsRoot);\n const source = new SourceHookRules().load(loaded);\n const guards = new WorkflowHookRules().load(loaded);\n const result = new HookFileEvaluation().evaluate(toolKind, input, loaded,\n new HookRuleSet([...source.builtInRules, ...guards.rules, ...source.extensionRules], [...source.configuredRules, ...guards.configuredRules]));\n if (!result) return new OpenclawHandlerResult('approved');\n return new OpenclawHandlerResult('rejected', result.report);\n } catch (err: unknown) {\n const error = toError(err);\n // An escaped RuleFailError or InformAiError carries an AI-readable message; anything else is\n // an unexpected bug. All reject (fail closed).\n let msg: string;\n if (error instanceof RuleFailError) {\n msg = renderRuleFailForAi(error);\n } else if (error instanceof InformAiError) {\n msg = error.message;\n } else {\n msg = `[ai-hooks] openclaw adapter crashed — failing closed: ${error.message}`;\n }\n return new OpenclawHandlerResult('rejected', msg);\n }\n}\n"]}