@webpieces/ai-hook-rules 0.0.1 → 0.2.113

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.
Files changed (99) hide show
  1. package/package.json +4 -3
  2. package/src/adapters/claude-code-hook.d.ts +1 -0
  3. package/src/adapters/claude-code-hook.js +112 -0
  4. package/src/adapters/claude-code-hook.js.map +1 -0
  5. package/src/adapters/openclaw-plugin.d.ts +14 -0
  6. package/src/adapters/openclaw-plugin.js +73 -0
  7. package/src/adapters/openclaw-plugin.js.map +1 -0
  8. package/src/core/build-context.d.ts +8 -0
  9. package/src/core/build-context.js +62 -0
  10. package/src/core/build-context.js.map +1 -0
  11. package/src/core/configs/default.d.ts +2 -0
  12. package/src/core/configs/{default.ts → default.js} +6 -3
  13. package/src/core/configs/default.js.map +1 -0
  14. package/src/core/disable-directives.d.ts +9 -0
  15. package/src/core/disable-directives.js +92 -0
  16. package/src/core/disable-directives.js.map +1 -0
  17. package/src/core/instruct-ai-writer.d.ts +1 -0
  18. package/src/core/instruct-ai-writer.js +18 -0
  19. package/src/core/instruct-ai-writer.js.map +1 -0
  20. package/src/core/load-config.d.ts +1 -0
  21. package/src/core/load-config.js +10 -0
  22. package/src/core/load-config.js.map +1 -0
  23. package/src/core/load-rules.d.ts +3 -0
  24. package/src/core/{load-rules.ts → load-rules.js} +33 -32
  25. package/src/core/load-rules.js.map +1 -0
  26. package/src/core/rejection-log.d.ts +2 -0
  27. package/src/core/{rejection-log.ts → rejection-log.js} +34 -51
  28. package/src/core/rejection-log.js.map +1 -0
  29. package/src/core/report.d.ts +2 -0
  30. package/src/core/{report.ts → report.js} +7 -8
  31. package/src/core/report.js.map +1 -0
  32. package/src/core/rules/catch-error-pattern.d.ts +3 -0
  33. package/src/core/rules/{catch-error-pattern.ts → catch-error-pattern.js} +25 -54
  34. package/src/core/rules/catch-error-pattern.js.map +1 -0
  35. package/src/core/rules/file-location.d.ts +3 -0
  36. package/src/core/rules/{file-location.ts → file-location.js} +29 -43
  37. package/src/core/rules/file-location.js.map +1 -0
  38. package/src/core/rules/index.d.ts +1 -0
  39. package/src/core/rules/{index.ts → index.js} +5 -1
  40. package/src/core/rules/index.js.map +1 -0
  41. package/src/core/rules/max-file-lines.d.ts +3 -0
  42. package/src/core/rules/{max-file-lines.ts → max-file-lines.js} +17 -23
  43. package/src/core/rules/max-file-lines.js.map +1 -0
  44. package/src/core/rules/no-any-unknown.d.ts +3 -0
  45. package/src/core/rules/no-any-unknown.js +30 -0
  46. package/src/core/rules/no-any-unknown.js.map +1 -0
  47. package/src/core/rules/no-destructure.d.ts +3 -0
  48. package/src/core/rules/{no-destructure.ts → no-destructure.js} +13 -17
  49. package/src/core/rules/no-destructure.js.map +1 -0
  50. package/src/core/rules/no-implicit-any.d.ts +3 -0
  51. package/src/core/rules/{no-implicit-any.ts → no-implicit-any.js} +32 -30
  52. package/src/core/rules/no-implicit-any.js.map +1 -0
  53. package/src/core/rules/no-shell-substitution.d.ts +3 -0
  54. package/src/core/rules/no-shell-substitution.js +54 -0
  55. package/src/core/rules/no-shell-substitution.js.map +1 -0
  56. package/src/core/rules/no-unmanaged-exceptions.d.ts +3 -0
  57. package/src/core/rules/{no-unmanaged-exceptions.ts → no-unmanaged-exceptions.js} +21 -24
  58. package/src/core/rules/no-unmanaged-exceptions.js.map +1 -0
  59. package/src/core/rules/require-return-type.d.ts +3 -0
  60. package/src/core/rules/{require-return-type.ts → require-return-type.js} +21 -28
  61. package/src/core/rules/require-return-type.js.map +1 -0
  62. package/src/core/runner.d.ts +3 -0
  63. package/src/core/runner.js +181 -0
  64. package/src/core/runner.js.map +1 -0
  65. package/src/core/strip-ts-noise.d.ts +1 -0
  66. package/src/core/strip-ts-noise.js +178 -0
  67. package/src/core/strip-ts-noise.js.map +1 -0
  68. package/src/core/to-error.d.ts +5 -0
  69. package/src/core/{to-error.ts → to-error.js} +7 -4
  70. package/src/core/to-error.js.map +1 -0
  71. package/src/core/types.d.ts +93 -0
  72. package/src/core/types.js +93 -0
  73. package/src/core/types.js.map +1 -0
  74. package/src/index.d.ts +5 -0
  75. package/src/index.js +25 -0
  76. package/src/index.js.map +1 -0
  77. package/LICENSE +0 -373
  78. package/src/adapters/claude-code-hook.ts +0 -117
  79. package/src/adapters/openclaw-plugin.ts +0 -88
  80. package/src/core/__tests__/disable-directives.test.ts +0 -114
  81. package/src/core/__tests__/rules/file-location.test.ts +0 -90
  82. package/src/core/__tests__/rules/max-file-lines.test.ts +0 -53
  83. package/src/core/__tests__/rules/no-any.test.ts +0 -68
  84. package/src/core/__tests__/rules/no-destructure.test.ts +0 -50
  85. package/src/core/__tests__/rules/no-shell-substitution.test.ts +0 -118
  86. package/src/core/__tests__/rules/no-unmanaged-exceptions.test.ts +0 -54
  87. package/src/core/__tests__/rules/require-return-type.test.ts +0 -79
  88. package/src/core/__tests__/runner.test.ts +0 -288
  89. package/src/core/__tests__/strip-ts-noise.test.ts +0 -109
  90. package/src/core/build-context.ts +0 -96
  91. package/src/core/disable-directives.ts +0 -90
  92. package/src/core/instruct-ai-writer.ts +0 -15
  93. package/src/core/load-config.ts +0 -3
  94. package/src/core/rules/no-any-unknown.ts +0 -35
  95. package/src/core/rules/no-shell-substitution.ts +0 -71
  96. package/src/core/runner.ts +0 -205
  97. package/src/core/strip-ts-noise.ts +0 -103
  98. package/src/core/types.ts +0 -196
  99. package/src/index.ts +0 -14
@@ -1,34 +1,32 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
-
4
- import type { FileRule, FileContext, Violation } from '../types';
5
- import { Violation as V } from '../types';
6
-
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const fs = tslib_1.__importStar(require("fs"));
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const types_1 = require("../types");
7
7
  const DEFAULT_EXCLUDE_PATHS = [
8
8
  'node_modules', 'dist', '.nx', '.git',
9
9
  'architecture', 'tmp', 'scripts',
10
10
  ];
11
11
  const DEFAULT_ALLOWED_ROOT_FILES = ['jest.setup.ts'];
12
-
13
- function isNodeModulesDir(name: string): boolean {
12
+ function isNodeModulesDir(name) {
14
13
  return name === 'node_modules' || name.startsWith('node_modules_');
15
14
  }
16
-
17
- function shouldSkipDir(name: string, excludePaths: readonly string[]): boolean {
18
- if (isNodeModulesDir(name)) return true;
15
+ function shouldSkipDir(name, excludePaths) {
16
+ if (isNodeModulesDir(name))
17
+ return true;
19
18
  return excludePaths.indexOf(name) >= 0;
20
19
  }
21
-
22
- function findProjectRoot(filePath: string, workspaceRoot: string): string | null {
20
+ function findProjectRoot(filePath, workspaceRoot) {
23
21
  let dir = path.dirname(filePath);
24
22
  while (dir !== workspaceRoot && dir.startsWith(workspaceRoot)) {
25
- if (fs.existsSync(path.join(dir, 'project.json'))) return dir;
23
+ if (fs.existsSync(path.join(dir, 'project.json')))
24
+ return dir;
26
25
  dir = path.dirname(dir);
27
26
  }
28
27
  return null;
29
28
  }
30
-
31
- const fileLocationRule: FileRule = {
29
+ const fileLocationRule = {
32
30
  name: 'file-location',
33
31
  description: 'Every .ts file must belong to a project\'s src/ directory.',
34
32
  scope: 'file',
@@ -41,47 +39,35 @@ const fileLocationRule: FileRule = {
41
39
  'Move the file into an existing project\'s src/ directory, or create a new project with project.json that owns the directory.',
42
40
  'Add the dir to file-location.excludePaths in webpieces.ai-hooks.json',
43
41
  ],
44
-
45
- check(ctx: FileContext): readonly Violation[] {
46
- if (ctx.tool !== 'Write') return [];
47
-
42
+ check(ctx) {
43
+ if (ctx.tool !== 'Write')
44
+ return [];
48
45
  const excludePaths = Array.isArray(ctx.options['excludePaths'])
49
- ? ctx.options['excludePaths'] as string[]
46
+ ? ctx.options['excludePaths']
50
47
  : DEFAULT_EXCLUDE_PATHS;
51
48
  const allowedRootFiles = Array.isArray(ctx.options['allowedRootFiles'])
52
- ? ctx.options['allowedRootFiles'] as string[]
49
+ ? ctx.options['allowedRootFiles']
53
50
  : DEFAULT_ALLOWED_ROOT_FILES;
54
-
55
51
  const relParts = ctx.relativePath.split(path.sep);
56
52
  const topDir = relParts[0];
57
-
58
- if (topDir && shouldSkipDir(topDir, excludePaths)) return [];
59
- if (relParts.length === 1 && allowedRootFiles.indexOf(relParts[0]) >= 0) return [];
60
-
53
+ if (topDir && shouldSkipDir(topDir, excludePaths))
54
+ return [];
55
+ if (relParts.length === 1 && allowedRootFiles.indexOf(relParts[0]) >= 0)
56
+ return [];
61
57
  const projectRoot = findProjectRoot(ctx.filePath, ctx.workspaceRoot);
62
-
63
58
  if (!projectRoot) {
64
- return [new V(
65
- 1,
66
- ctx.relativePath,
67
- 'File is not inside any Nx project. Move it into a project\'s src/ directory.',
68
- )];
59
+ return [new types_1.Violation(1, ctx.relativePath, 'File is not inside any Nx project. Move it into a project\'s src/ directory.')];
69
60
  }
70
-
71
61
  const relToProject = path.relative(projectRoot, ctx.filePath);
72
62
  const fileName = path.basename(ctx.filePath);
73
- if (fileName === 'jest.config.ts') return [];
63
+ if (fileName === 'jest.config.ts')
64
+ return [];
74
65
  if (!relToProject.startsWith('src' + path.sep) && relToProject !== 'src') {
75
66
  const projectName = path.relative(ctx.workspaceRoot, projectRoot);
76
- return [new V(
77
- 1,
78
- ctx.relativePath,
79
- `File is inside project \`${projectName}\` but outside its src/ directory. Move it into src/.`,
80
- )];
67
+ return [new types_1.Violation(1, ctx.relativePath, `File is inside project \`${projectName}\` but outside its src/ directory. Move it into src/.`)];
81
68
  }
82
-
83
69
  return [];
84
70
  },
85
71
  };
86
-
87
- export default fileLocationRule;
72
+ exports.default = fileLocationRule;
73
+ //# sourceMappingURL=file-location.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-location.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/file-location.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport type { FileRule, FileContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst DEFAULT_EXCLUDE_PATHS = [\n 'node_modules', 'dist', '.nx', '.git',\n 'architecture', 'tmp', 'scripts',\n];\nconst DEFAULT_ALLOWED_ROOT_FILES = ['jest.setup.ts'];\n\nfunction isNodeModulesDir(name: string): boolean {\n return name === 'node_modules' || name.startsWith('node_modules_');\n}\n\nfunction shouldSkipDir(name: string, excludePaths: readonly string[]): boolean {\n if (isNodeModulesDir(name)) return true;\n return excludePaths.indexOf(name) >= 0;\n}\n\nfunction findProjectRoot(filePath: string, workspaceRoot: string): string | null {\n let dir = path.dirname(filePath);\n while (dir !== workspaceRoot && dir.startsWith(workspaceRoot)) {\n if (fs.existsSync(path.join(dir, 'project.json'))) return dir;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nconst fileLocationRule: FileRule = {\n name: 'file-location',\n description: 'Every .ts file must belong to a project\\'s src/ directory.',\n scope: 'file',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {\n excludePaths: DEFAULT_EXCLUDE_PATHS,\n allowedRootFiles: DEFAULT_ALLOWED_ROOT_FILES,\n },\n fixHint: [\n 'Move the file into an existing project\\'s src/ directory, or create a new project with project.json that owns the directory.',\n 'Add the dir to file-location.excludePaths in webpieces.ai-hooks.json',\n ],\n\n check(ctx: FileContext): readonly Violation[] {\n if (ctx.tool !== 'Write') return [];\n\n const excludePaths = Array.isArray(ctx.options['excludePaths'])\n ? ctx.options['excludePaths'] as string[]\n : DEFAULT_EXCLUDE_PATHS;\n const allowedRootFiles = Array.isArray(ctx.options['allowedRootFiles'])\n ? ctx.options['allowedRootFiles'] as string[]\n : DEFAULT_ALLOWED_ROOT_FILES;\n\n const relParts = ctx.relativePath.split(path.sep);\n const topDir = relParts[0];\n\n if (topDir && shouldSkipDir(topDir, excludePaths)) return [];\n if (relParts.length === 1 && allowedRootFiles.indexOf(relParts[0]) >= 0) return [];\n\n const projectRoot = findProjectRoot(ctx.filePath, ctx.workspaceRoot);\n\n if (!projectRoot) {\n return [new V(\n 1,\n ctx.relativePath,\n 'File is not inside any Nx project. Move it into a project\\'s src/ directory.',\n )];\n }\n\n const relToProject = path.relative(projectRoot, ctx.filePath);\n const fileName = path.basename(ctx.filePath);\n if (fileName === 'jest.config.ts') return [];\n if (!relToProject.startsWith('src' + path.sep) && relToProject !== 'src') {\n const projectName = path.relative(ctx.workspaceRoot, projectRoot);\n return [new V(\n 1,\n ctx.relativePath,\n `File is inside project \\`${projectName}\\` but outside its src/ directory. Move it into src/.`,\n )];\n }\n\n return [];\n },\n};\n\nexport default fileLocationRule;\n"]}
@@ -0,0 +1 @@
1
+ export declare const builtInRuleNames: readonly string[];
@@ -1,4 +1,7 @@
1
- export const builtInRuleNames: readonly string[] = [
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.builtInRuleNames = void 0;
4
+ exports.builtInRuleNames = [
2
5
  'no-any-unknown',
3
6
  'no-implicit-any',
4
7
  'max-file-lines',
@@ -9,3 +12,4 @@ export const builtInRuleNames: readonly string[] = [
9
12
  'catch-error-pattern',
10
13
  'no-shell-substitution',
11
14
  ];
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/index.ts"],"names":[],"mappings":";;;AAAa,QAAA,gBAAgB,GAAsB;IAC/C,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,eAAe;IACf,gBAAgB;IAChB,qBAAqB;IACrB,yBAAyB;IACzB,qBAAqB;IACrB,uBAAuB;CAC1B,CAAC","sourcesContent":["export const builtInRuleNames: readonly string[] = [\n 'no-any-unknown',\n 'no-implicit-any',\n 'max-file-lines',\n 'file-location',\n 'no-destructure',\n 'require-return-type',\n 'no-unmanaged-exceptions',\n 'catch-error-pattern',\n 'no-shell-substitution',\n];\n"]}
@@ -0,0 +1,3 @@
1
+ import type { FileRule } from '../types';
2
+ declare const maxFileLinesRule: FileRule;
3
+ export default maxFileLinesRule;
@@ -1,14 +1,13 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
-
4
- import type { FileRule, FileContext, Violation } from '../types';
5
- import { Violation as V } from '../types';
6
-
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const fs = tslib_1.__importStar(require("fs"));
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const types_1 = require("../types");
7
7
  const DEFAULT_LIMIT = 900;
8
8
  const INSTRUCT_DIR = '.webpieces/instruct-ai';
9
9
  const INSTRUCT_FILE = 'webpieces.filesize.md';
10
-
11
- const maxFileLinesRule: FileRule = {
10
+ const maxFileLinesRule = {
12
11
  name: 'max-file-lines',
13
12
  description: 'Cap file length at a configured line limit.',
14
13
  scope: 'file',
@@ -18,29 +17,24 @@ const maxFileLinesRule: FileRule = {
18
17
  'READ .webpieces/instruct-ai/webpieces.filesize.md for step-by-step refactoring guidance.',
19
18
  '// eslint-disable-next-line @webpieces/max-file-lines (also suppresses the eslint rule)',
20
19
  ],
21
-
22
- check(ctx: FileContext): readonly Violation[] {
20
+ check(ctx) {
23
21
  const limit = typeof ctx.options['limit'] === 'number'
24
- ? ctx.options['limit'] as number
22
+ ? ctx.options['limit']
25
23
  : DEFAULT_LIMIT;
26
- if (ctx.projectedFileLines <= limit) return [];
24
+ if (ctx.projectedFileLines <= limit)
25
+ return [];
27
26
  writeInstructionFile(ctx.workspaceRoot);
28
- return [new V(
29
- 1,
30
- `(projected ${String(ctx.projectedFileLines)} lines)`,
31
- `File will be ${String(ctx.projectedFileLines)} lines, exceeding the ${String(limit)}-line limit. See .webpieces/instruct-ai/webpieces.filesize.md for detailed refactoring instructions.`,
32
- )];
27
+ return [new types_1.Violation(1, `(projected ${String(ctx.projectedFileLines)} lines)`, `File will be ${String(ctx.projectedFileLines)} lines, exceeding the ${String(limit)}-line limit. See .webpieces/instruct-ai/webpieces.filesize.md for detailed refactoring instructions.`)];
33
28
  },
34
29
  };
35
-
36
- function writeInstructionFile(workspaceRoot: string): void {
30
+ function writeInstructionFile(workspaceRoot) {
37
31
  const dir = path.join(workspaceRoot, INSTRUCT_DIR);
38
32
  const filePath = path.join(dir, INSTRUCT_FILE);
39
- if (fs.existsSync(filePath)) return;
33
+ if (fs.existsSync(filePath))
34
+ return;
40
35
  fs.mkdirSync(dir, { recursive: true });
41
36
  fs.writeFileSync(filePath, FILESIZE_DOC_CONTENT);
42
37
  }
43
-
44
38
  // eslint-disable-next-line @webpieces/max-file-lines
45
39
  const FILESIZE_DOC_CONTENT = `# AI Agent Instructions: File Too Long
46
40
 
@@ -133,5 +127,5 @@ If refactoring is genuinely not feasible, add a disable comment:
133
127
 
134
128
  Remember: Find the "child code" and pull it down into a new class. Once moved, the code's purpose becomes clear, making it easy to rename to a logical name.
135
129
  `;
136
-
137
- export default maxFileLinesRule;
130
+ exports.default = maxFileLinesRule;
131
+ //# sourceMappingURL=max-file-lines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"max-file-lines.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/max-file-lines.ts"],"names":[],"mappings":";;;AAAA,+CAAyB;AACzB,mDAA6B;AAG7B,oCAA0C;AAE1C,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,YAAY,GAAG,wBAAwB,CAAC;AAC9C,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAE9C,MAAM,gBAAgB,GAAa;IAC/B,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,6CAA6C;IAC1D,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IACxC,OAAO,EAAE;QACL,0FAA0F;QAC1F,0FAA0F;KAC7F;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,QAAQ;YAClD,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAW;YAChC,CAAC,CAAC,aAAa,CAAC;QACpB,IAAI,GAAG,CAAC,kBAAkB,IAAI,KAAK;YAAE,OAAO,EAAE,CAAC;QAC/C,oBAAoB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,iBAAC,CACT,CAAC,EACD,cAAc,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,SAAS,EACrD,gBAAgB,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,sGAAsG,CAC7L,CAAC,CAAC;IACP,CAAC;CACJ,CAAC;AAEF,SAAS,oBAAoB,CAAC,aAAqB;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC/C,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO;IACpC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;AACrD,CAAC;AAED,qDAAqD;AACrD,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0F5B,CAAC;AAEF,kBAAe,gBAAgB,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport type { FileRule, FileContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst DEFAULT_LIMIT = 900;\nconst INSTRUCT_DIR = '.webpieces/instruct-ai';\nconst INSTRUCT_FILE = 'webpieces.filesize.md';\n\nconst maxFileLinesRule: FileRule = {\n name: 'max-file-lines',\n description: 'Cap file length at a configured line limit.',\n scope: 'file',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: { limit: DEFAULT_LIMIT },\n fixHint: [\n 'READ .webpieces/instruct-ai/webpieces.filesize.md for step-by-step refactoring guidance.',\n '// eslint-disable-next-line @webpieces/max-file-lines (also suppresses the eslint rule)',\n ],\n\n check(ctx: FileContext): readonly Violation[] {\n const limit = typeof ctx.options['limit'] === 'number'\n ? ctx.options['limit'] as number\n : DEFAULT_LIMIT;\n if (ctx.projectedFileLines <= limit) return [];\n writeInstructionFile(ctx.workspaceRoot);\n return [new V(\n 1,\n `(projected ${String(ctx.projectedFileLines)} lines)`,\n `File will be ${String(ctx.projectedFileLines)} lines, exceeding the ${String(limit)}-line limit. See .webpieces/instruct-ai/webpieces.filesize.md for detailed refactoring instructions.`,\n )];\n },\n};\n\nfunction writeInstructionFile(workspaceRoot: string): void {\n const dir = path.join(workspaceRoot, INSTRUCT_DIR);\n const filePath = path.join(dir, INSTRUCT_FILE);\n if (fs.existsSync(filePath)) return;\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(filePath, FILESIZE_DOC_CONTENT);\n}\n\n// eslint-disable-next-line @webpieces/max-file-lines\nconst FILESIZE_DOC_CONTENT = `# AI Agent Instructions: File Too Long\n\n**READ THIS FILE to fix files that are too long**\n\n## Core Principle\n\nWith **stateless systems + dependency injection, refactor is trivial**.\nPick a method or a few and move to new class XXXXX, then inject XXXXX\ninto all users of those methods via the constructor.\nDelete those methods from original class.\n\n**99% of files can be less than the configured max lines of code.**\n\nFiles should contain a SINGLE COHESIVE UNIT.\n- One class per file (Java convention)\n- If class is too large, extract child responsibilities\n- Use dependency injection to compose functionality\n\n## Command: Reduce File Size\n\n### Step 1: Check for Multiple Classes\nIf the file contains multiple classes, **SEPARATE each class into its own file**.\n\n### Step 2: Extract Child Responsibilities (if single class is too large)\n\n#### Pattern: Create New Service Class with Dependency Injection\n\n\\`\\`\\`typescript\n// BAD: UserController.ts (800 lines, single class)\n@provideSingleton()\n@Controller()\nexport class UserController {\n // 200 lines: CRUD operations\n // 300 lines: validation logic\n // 200 lines: notification logic\n}\n\n// GOOD: Extract validation service\n// 1. Create UserValidationService.ts\n@provideSingleton()\nexport class UserValidationService {\n validateUserData(data: UserData): ValidationResult { /* ... */ }\n validateEmail(email: string): boolean { /* ... */ }\n}\n\n// 2. Inject into UserController.ts\n@provideSingleton()\n@Controller()\nexport class UserController {\n constructor(\n @inject(TYPES.UserValidationService)\n private validator: UserValidationService\n ) {}\n}\n\\`\\`\\`\n\n## AI Agent Action Steps\n\n1. **ANALYZE** the file structure:\n - Count classes (if >1, separate immediately)\n - Identify logical responsibilities within single class\n\n2. **IDENTIFY** \"child code\" to extract:\n - Validation logic -> ValidationService\n - Notification logic -> NotificationService\n - Data transformation -> TransformerService\n - External API calls -> ApiService\n - Business rules -> RulesEngine\n\n3. **CREATE** new service file(s):\n - Add \\`@provideSingleton()\\` decorator\n - Move child methods to new class\n\n4. **UPDATE** dependency injection:\n - Inject new service into original class constructor\n - Replace direct method calls with \\`this.serviceName.method()\\`\n\n5. **VERIFY** file sizes:\n - Original file should now be under the limit\n - Each extracted file should be under the limit\n\n## Escape Hatch\n\nIf refactoring is genuinely not feasible, add a disable comment:\n\n\\`\\`\\`typescript\n// eslint-disable-next-line @webpieces/max-file-lines\n\\`\\`\\`\n\nRemember: Find the \"child code\" and pull it down into a new class. Once moved, the code's purpose becomes clear, making it easy to rename to a logical name.\n`;\n\nexport default maxFileLinesRule;\n"]}
@@ -0,0 +1,3 @@
1
+ import type { EditRule } from '../types';
2
+ declare const noAnyRule: EditRule;
3
+ export default noAnyRule;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("../types");
4
+ const ANY_PATTERN = /(?::\s*any\b|\bas\s+any\b|<any>|any\[\]|Array<any>|Promise<any>|Map<[^,<>]+,\s*any\s*>|Record<[^,<>]+,\s*any\s*>|Set<any>)/;
5
+ const noAnyRule = {
6
+ name: 'no-any-unknown',
7
+ description: 'Disallow the `any` keyword. Use concrete types or interfaces.',
8
+ scope: 'edit',
9
+ files: ['**/*.ts', '**/*.tsx'],
10
+ defaultOptions: {},
11
+ fixHint: [
12
+ 'Prefer: interface MyData { ... } or class MyData { ... }',
13
+ '// webpieces-disable no-any-unknown -- <one-line reason>',
14
+ ],
15
+ check(ctx) {
16
+ const violations = [];
17
+ for (let i = 0; i < ctx.strippedLines.length; i += 1) {
18
+ const stripped = ctx.strippedLines[i];
19
+ if (!ANY_PATTERN.test(stripped))
20
+ continue;
21
+ const lineNum = i + 1;
22
+ if (ctx.isLineDisabled(lineNum, 'no-any-unknown'))
23
+ continue;
24
+ violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), '`any` erases type information. Use a concrete type, an interface, or `unknown` with type guards.'));
25
+ }
26
+ return violations;
27
+ },
28
+ };
29
+ exports.default = noAnyRule;
30
+ //# sourceMappingURL=no-any-unknown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-any-unknown.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-any-unknown.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAE1C,MAAM,WAAW,GACb,4HAA4H,CAAC;AAEjI,MAAM,SAAS,GAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,+DAA+D;IAC5E,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,8DAA8D;QAC9D,0DAA0D;KAC7D;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,gBAAgB,CAAC;gBAAE,SAAS;YAC5D,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,kGAAkG,CACrG,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,kBAAe,SAAS,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst ANY_PATTERN =\n /(?::\\s*any\\b|\\bas\\s+any\\b|<any>|any\\[\\]|Array<any>|Promise<any>|Map<[^,<>]+,\\s*any\\s*>|Record<[^,<>]+,\\s*any\\s*>|Set<any>)/;\n\nconst noAnyRule: EditRule = {\n name: 'no-any-unknown',\n description: 'Disallow the `any` keyword. Use concrete types or interfaces.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'Prefer: interface MyData { ... } or class MyData { ... }',\n '// webpieces-disable no-any-unknown -- <one-line reason>',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!ANY_PATTERN.test(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, 'no-any-unknown')) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n '`any` erases type information. Use a concrete type, an interface, or `unknown` with type guards.',\n ));\n }\n return violations;\n },\n};\n\nexport default noAnyRule;\n"]}
@@ -0,0 +1,3 @@
1
+ import type { EditRule } from '../types';
2
+ declare const noDestructureRule: EditRule;
3
+ export default noDestructureRule;
@@ -1,9 +1,8 @@
1
- import type { EditRule, EditContext, Violation } from '../types';
2
- import { Violation as V } from '../types';
3
-
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("../types");
4
4
  const VARIABLE_DESTRUCTURE = /\b(?:const|let|var)\s*\{/;
5
-
6
- const noDestructureRule: EditRule = {
5
+ const noDestructureRule = {
7
6
  name: 'no-destructure',
8
7
  description: 'Disallow destructuring patterns. Assign the whole result and pass it around or access properties explicitly.',
9
8
  scope: 'edit',
@@ -13,22 +12,19 @@ const noDestructureRule: EditRule = {
13
12
  'Instead of: const { x, y } = methodCall(); prefer const obj = methodCall(); then pass obj to other methods or use obj.x',
14
13
  '// webpieces-disable no-destructure -- <reason>',
15
14
  ],
16
-
17
- check(ctx: EditContext): readonly Violation[] {
18
- const violations: V[] = [];
15
+ check(ctx) {
16
+ const violations = [];
19
17
  for (let i = 0; i < ctx.strippedLines.length; i += 1) {
20
18
  const stripped = ctx.strippedLines[i];
21
- if (!VARIABLE_DESTRUCTURE.test(stripped)) continue;
19
+ if (!VARIABLE_DESTRUCTURE.test(stripped))
20
+ continue;
22
21
  const lineNum = i + 1;
23
- if (ctx.isLineDisabled(lineNum, 'no-destructure')) continue;
24
- violations.push(new V(
25
- lineNum,
26
- ctx.lines[i].trim(),
27
- 'Destructuring pattern. Assign the whole result instead: const obj = methodCall(); then pass obj around or use obj.x',
28
- ));
22
+ if (ctx.isLineDisabled(lineNum, 'no-destructure'))
23
+ continue;
24
+ violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), 'Destructuring pattern. Assign the whole result instead: const obj = methodCall(); then pass obj around or use obj.x'));
29
25
  }
30
26
  return violations;
31
27
  },
32
28
  };
33
-
34
- export default noDestructureRule;
29
+ exports.default = noDestructureRule;
30
+ //# sourceMappingURL=no-destructure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-destructure.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-destructure.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAE1C,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAExD,MAAM,iBAAiB,GAAa;IAChC,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,8GAA8G;IAC3H,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE;IACvC,OAAO,EAAE;QACL,yHAAyH;QACzH,iDAAiD;KACpD;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACnD,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,gBAAgB,CAAC;gBAAE,SAAS;YAC5D,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,qHAAqH,CACxH,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,kBAAe,iBAAiB,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst VARIABLE_DESTRUCTURE = /\\b(?:const|let|var)\\s*\\{/;\n\nconst noDestructureRule: EditRule = {\n name: 'no-destructure',\n description: 'Disallow destructuring patterns. Assign the whole result and pass it around or access properties explicitly.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: { allowTopLevel: true },\n fixHint: [\n 'Instead of: const { x, y } = methodCall(); prefer const obj = methodCall(); then pass obj to other methods or use obj.x',\n '// webpieces-disable no-destructure -- <reason>',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!VARIABLE_DESTRUCTURE.test(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, 'no-destructure')) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'Destructuring pattern. Assign the whole result instead: const obj = methodCall(); then pass obj around or use obj.x',\n ));\n }\n return violations;\n },\n};\n\nexport default noDestructureRule;\n"]}
@@ -0,0 +1,3 @@
1
+ import type { EditRule } from '../types';
2
+ declare const noImplicitAnyRule: EditRule;
3
+ export default noImplicitAnyRule;
@@ -1,41 +1,46 @@
1
- import type { EditRule, EditContext, Violation } from '../types';
2
- import { Violation as V } from '../types';
3
-
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("../types");
4
4
  const ARROW_PARAMS_RE = /\(([^()]*)\)\s*=>/g;
5
5
  const FN_DECL_PARAMS_RE = /\bfunction\s*[\w$]*\s*\(([^()]*)\)/g;
6
-
7
- function firstUntypedParam(paramsStr: string): string | null {
8
- if (paramsStr.includes('{') || paramsStr.includes('[')) return null;
6
+ function firstUntypedParam(paramsStr) {
7
+ if (paramsStr.includes('{') || paramsStr.includes('['))
8
+ return null;
9
9
  const parts = paramsStr.split(',').map((p) => p.trim()).filter((p) => p.length > 0);
10
10
  for (const part of parts) {
11
- if (part.startsWith('...')) continue;
12
- if (part.includes(':')) continue;
13
- if (part.includes('=')) continue;
14
- if (part === 'this') continue;
15
- if (/^[a-zA-Z_$][\w$]*$/.test(part)) return part;
11
+ if (part.startsWith('...'))
12
+ continue;
13
+ if (part.includes(':'))
14
+ continue;
15
+ if (part.includes('='))
16
+ continue;
17
+ if (part === 'this')
18
+ continue;
19
+ if (/^[a-zA-Z_$][\w$]*$/.test(part))
20
+ return part;
16
21
  }
17
22
  return null;
18
23
  }
19
-
20
- function findOffender(line: string): string | null {
24
+ function findOffender(line) {
21
25
  ARROW_PARAMS_RE.lastIndex = 0;
22
- let m: RegExpExecArray | null = ARROW_PARAMS_RE.exec(line);
26
+ let m = ARROW_PARAMS_RE.exec(line);
23
27
  while (m !== null) {
24
28
  const bad = firstUntypedParam(m[1]);
25
- if (bad) return bad;
29
+ if (bad)
30
+ return bad;
26
31
  m = ARROW_PARAMS_RE.exec(line);
27
32
  }
28
33
  FN_DECL_PARAMS_RE.lastIndex = 0;
29
34
  m = FN_DECL_PARAMS_RE.exec(line);
30
35
  while (m !== null) {
31
36
  const bad = firstUntypedParam(m[1]);
32
- if (bad) return bad;
37
+ if (bad)
38
+ return bad;
33
39
  m = FN_DECL_PARAMS_RE.exec(line);
34
40
  }
35
41
  return null;
36
42
  }
37
-
38
- const noImplicitAnyRule: EditRule = {
43
+ const noImplicitAnyRule = {
39
44
  name: 'no-implicit-any',
40
45
  description: 'Disallow function parameters without explicit type annotations (implicit-any).',
41
46
  scope: 'edit',
@@ -45,23 +50,20 @@ const noImplicitAnyRule: EditRule = {
45
50
  'Add explicit types: (x: string) => ... or function foo(x: number)',
46
51
  '// webpieces-disable no-implicit-any -- <one-line reason>',
47
52
  ],
48
-
49
- check(ctx: EditContext): readonly Violation[] {
50
- const violations: V[] = [];
53
+ check(ctx) {
54
+ const violations = [];
51
55
  for (let i = 0; i < ctx.strippedLines.length; i += 1) {
52
56
  const stripped = ctx.strippedLines[i];
53
57
  const lineNum = i + 1;
54
- if (ctx.isLineDisabled(lineNum, 'no-implicit-any')) continue;
58
+ if (ctx.isLineDisabled(lineNum, 'no-implicit-any'))
59
+ continue;
55
60
  const offender = findOffender(stripped);
56
- if (!offender) continue;
57
- violations.push(new V(
58
- lineNum,
59
- ctx.lines[i].trim(),
60
- `Parameter "${offender}" has no type annotation. Add an explicit type to avoid implicit-any.`,
61
- ));
61
+ if (!offender)
62
+ continue;
63
+ violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), `Parameter "${offender}" has no type annotation. Add an explicit type to avoid implicit-any.`));
62
64
  }
63
65
  return violations;
64
66
  },
65
67
  };
66
-
67
- export default noImplicitAnyRule;
68
+ exports.default = noImplicitAnyRule;
69
+ //# sourceMappingURL=no-implicit-any.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-implicit-any.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-implicit-any.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst ARROW_PARAMS_RE = /\\(([^()]*)\\)\\s*=>/g;\nconst FN_DECL_PARAMS_RE = /\\bfunction\\s*[\\w$]*\\s*\\(([^()]*)\\)/g;\n\nfunction firstUntypedParam(paramsStr: string): string | null {\n if (paramsStr.includes('{') || paramsStr.includes('[')) return null;\n const parts = paramsStr.split(',').map((p) => p.trim()).filter((p) => p.length > 0);\n for (const part of parts) {\n if (part.startsWith('...')) continue;\n if (part.includes(':')) continue;\n if (part.includes('=')) continue;\n if (part === 'this') continue;\n if (/^[a-zA-Z_$][\\w$]*$/.test(part)) return part;\n }\n return null;\n}\n\nfunction findOffender(line: string): string | null {\n ARROW_PARAMS_RE.lastIndex = 0;\n let m: RegExpExecArray | null = ARROW_PARAMS_RE.exec(line);\n while (m !== null) {\n const bad = firstUntypedParam(m[1]);\n if (bad) return bad;\n m = ARROW_PARAMS_RE.exec(line);\n }\n FN_DECL_PARAMS_RE.lastIndex = 0;\n m = FN_DECL_PARAMS_RE.exec(line);\n while (m !== null) {\n const bad = firstUntypedParam(m[1]);\n if (bad) return bad;\n m = FN_DECL_PARAMS_RE.exec(line);\n }\n return null;\n}\n\nconst noImplicitAnyRule: EditRule = {\n name: 'no-implicit-any',\n description: 'Disallow function parameters without explicit type annotations (implicit-any).',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'Add explicit types: (x: string) => ... or function foo(x: number)',\n '// webpieces-disable no-implicit-any -- <one-line reason>',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, 'no-implicit-any')) continue;\n const offender = findOffender(stripped);\n if (!offender) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n `Parameter \"${offender}\" has no type annotation. Add an explicit type to avoid implicit-any.`,\n ));\n }\n return violations;\n },\n};\n\nexport default noImplicitAnyRule;\n"]}
@@ -0,0 +1,3 @@
1
+ import type { BashRule } from '../types';
2
+ declare const noShellSubstitutionRule: BashRule;
3
+ export default noShellSubstitutionRule;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("../types");
4
+ const FIX_HINT = [
5
+ 'Shell substitutions trigger Claude Code "simple_expansion" permission prompts that interrupt the user.',
6
+ 'Instead:',
7
+ ' • Build payload files with Write, then: node script.js < /path/to/payload',
8
+ ' • Use Read, Grep, or Glob instead of piping shell output through $(...)',
9
+ ' • Write a small script file with Write and execute it: bash /path/to/script.sh',
10
+ ];
11
+ const noShellSubstitutionRule = {
12
+ name: 'no-shell-substitution',
13
+ description: 'Reject Bash commands containing shell substitutions ($(...), backticks, $VAR).',
14
+ scope: 'bash',
15
+ files: [],
16
+ defaultOptions: {},
17
+ fixHint: FIX_HINT,
18
+ check(ctx) {
19
+ const scanned = stripSingleQuoted(ctx.command);
20
+ const violations = [];
21
+ if (/\$\(/.test(scanned)) {
22
+ violations.push(new types_1.Violation(1, truncate(ctx.command), 'Command contains `$(...)` command substitution.'));
23
+ }
24
+ if (hasUnescapedBacktick(scanned)) {
25
+ violations.push(new types_1.Violation(1, truncate(ctx.command), 'Command contains backtick command substitution.'));
26
+ }
27
+ if (/\$\{[A-Za-z_][A-Za-z0-9_]*\}/.test(scanned) || hasBareVarExpansion(scanned)) {
28
+ violations.push(new types_1.Violation(1, truncate(ctx.command), 'Command contains `$VAR` or `${VAR}` variable expansion.'));
29
+ }
30
+ return violations;
31
+ },
32
+ };
33
+ function stripSingleQuoted(cmd) {
34
+ return cmd.replace(/'[^']*'/g, "''");
35
+ }
36
+ function hasUnescapedBacktick(cmd) {
37
+ for (let i = 0; i < cmd.length; i += 1) {
38
+ if (cmd[i] === '`' && (i === 0 || cmd[i - 1] !== '\\'))
39
+ return true;
40
+ }
41
+ return false;
42
+ }
43
+ function hasBareVarExpansion(cmd) {
44
+ const re = /(^|[^\\])\$([A-Za-z_][A-Za-z0-9_]*)/g;
45
+ return re.test(cmd);
46
+ }
47
+ function truncate(s) {
48
+ const MAX = 120;
49
+ if (s.length <= MAX)
50
+ return s;
51
+ return s.slice(0, MAX) + '…';
52
+ }
53
+ exports.default = noShellSubstitutionRule;
54
+ //# sourceMappingURL=no-shell-substitution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-shell-substitution.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-shell-substitution.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAE1C,MAAM,QAAQ,GAAsB;IAChC,wGAAwG;IACxG,UAAU;IACV,6EAA6E;IAC7E,2EAA2E;IAC3E,kFAAkF;CACrF,CAAC;AAEF,MAAM,uBAAuB,GAAa;IACtC,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EAAE,gFAAgF;IAC7F,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,EAAE;IACT,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE,QAAQ;IAEjB,KAAK,CAAC,GAAgB;QAClB,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAgB,EAAE,CAAC;QAEnC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACvB,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB,iDAAiD,CACpD,CAAC,CAAC;QACP,CAAC;QACD,IAAI,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB,iDAAiD,CACpD,CAAC,CAAC;QACP,CAAC;QACD,IAAI,8BAA8B,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/E,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,CAAC,EACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EACrB,yDAAyD,CAC5D,CAAC,CAAC;QACP,CAAC;QACD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,SAAS,iBAAiB,CAAC,GAAW;IAClC,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,oBAAoB,CAAC,GAAW;IACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACxE,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAW;IACpC,MAAM,EAAE,GAAG,sCAAsC,CAAC;IAClD,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS;IACvB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;AACjC,CAAC;AAED,kBAAe,uBAAuB,CAAC","sourcesContent":["import type { BashRule, BashContext, Violation } from '../types';\nimport { Violation as V } from '../types';\n\nconst FIX_HINT: readonly string[] = [\n 'Shell substitutions trigger Claude Code \"simple_expansion\" permission prompts that interrupt the user.',\n 'Instead:',\n ' • Build payload files with Write, then: node script.js < /path/to/payload',\n ' • Use Read, Grep, or Glob instead of piping shell output through $(...)',\n ' • Write a small script file with Write and execute it: bash /path/to/script.sh',\n];\n\nconst noShellSubstitutionRule: BashRule = {\n name: 'no-shell-substitution',\n description: 'Reject Bash commands containing shell substitutions ($(...), backticks, $VAR).',\n scope: 'bash',\n files: [],\n defaultOptions: {},\n fixHint: FIX_HINT,\n\n check(ctx: BashContext): readonly Violation[] {\n const scanned = stripSingleQuoted(ctx.command);\n const violations: Violation[] = [];\n\n if (/\\$\\(/.test(scanned)) {\n violations.push(new V(\n 1,\n truncate(ctx.command),\n 'Command contains `$(...)` command substitution.',\n ));\n }\n if (hasUnescapedBacktick(scanned)) {\n violations.push(new V(\n 1,\n truncate(ctx.command),\n 'Command contains backtick command substitution.',\n ));\n }\n if (/\\$\\{[A-Za-z_][A-Za-z0-9_]*\\}/.test(scanned) || hasBareVarExpansion(scanned)) {\n violations.push(new V(\n 1,\n truncate(ctx.command),\n 'Command contains `$VAR` or `${VAR}` variable expansion.',\n ));\n }\n return violations;\n },\n};\n\nfunction stripSingleQuoted(cmd: string): string {\n return cmd.replace(/'[^']*'/g, \"''\");\n}\n\nfunction hasUnescapedBacktick(cmd: string): boolean {\n for (let i = 0; i < cmd.length; i += 1) {\n if (cmd[i] === '`' && (i === 0 || cmd[i - 1] !== '\\\\')) return true;\n }\n return false;\n}\n\nfunction hasBareVarExpansion(cmd: string): boolean {\n const re = /(^|[^\\\\])\\$([A-Za-z_][A-Za-z0-9_]*)/g;\n return re.test(cmd);\n}\n\nfunction truncate(s: string): string {\n const MAX = 120;\n if (s.length <= MAX) return s;\n return s.slice(0, MAX) + '…';\n}\n\nexport default noShellSubstitutionRule;\n"]}
@@ -0,0 +1,3 @@
1
+ import type { EditRule } from '../types';
2
+ declare const noUnmanagedExceptionsRule: EditRule;
3
+ export default noUnmanagedExceptionsRule;
@@ -1,13 +1,11 @@
1
- import type { EditRule, EditContext, Violation } from '../types';
2
- import { Violation as V } from '../types';
3
- import { writeTemplateIfMissing } from '../instruct-ai-writer';
4
-
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("../types");
4
+ const instruct_ai_writer_1 = require("../instruct-ai-writer");
5
5
  const TRY_PATTERN = /\btry\s*\{/;
6
-
7
6
  // Both webpieces-disable and the existing ESLint directive suppress this rule
8
7
  const DISABLE_PATTERN = /@webpieces\/no-unmanaged-exceptions|webpieces-disable\s+(?:[\w-]+,\s*)*no-unmanaged-exceptions/;
9
-
10
- const noUnmanagedExceptionsRule: EditRule = {
8
+ const noUnmanagedExceptionsRule = {
11
9
  name: 'no-unmanaged-exceptions',
12
10
  description: 'try/catch is generally not allowed. Only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.',
13
11
  scope: 'edit',
@@ -19,30 +17,29 @@ const noUnmanagedExceptionsRule: EditRule = {
19
17
  '// webpieces-disable no-unmanaged-exceptions -- <reason>',
20
18
  'When try/catch IS used (after disabling), the catch block MUST use: catch (err: unknown) { const error = toError(err); ... } or //const error = toError(err); to explicitly ignore.',
21
19
  ],
22
-
23
- check(ctx: EditContext): readonly Violation[] {
24
- const violations: V[] = [];
20
+ check(ctx) {
21
+ const violations = [];
25
22
  for (let i = 0; i < ctx.strippedLines.length; i += 1) {
26
23
  const stripped = ctx.strippedLines[i];
27
- if (!TRY_PATTERN.test(stripped)) continue;
24
+ if (!TRY_PATTERN.test(stripped))
25
+ continue;
28
26
  const lineNum = i + 1;
29
- if (ctx.isLineDisabled(lineNum, 'no-unmanaged-exceptions')) continue;
30
- if (hasPrecedingDisable(ctx.lines, i)) continue;
31
- violations.push(new V(
32
- lineNum,
33
- ctx.lines[i].trim(),
34
- 'try/catch is generally not allowed. It is only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.',
35
- ));
27
+ if (ctx.isLineDisabled(lineNum, 'no-unmanaged-exceptions'))
28
+ continue;
29
+ if (hasPrecedingDisable(ctx.lines, i))
30
+ continue;
31
+ violations.push(new types_1.Violation(lineNum, ctx.lines[i].trim(), 'try/catch is generally not allowed. It is only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.'));
36
32
  }
37
- if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');
33
+ if (violations.length > 0)
34
+ (0, instruct_ai_writer_1.writeTemplateIfMissing)(ctx.workspaceRoot, 'webpieces.exceptions.md');
38
35
  return violations;
39
36
  },
40
37
  };
41
-
42
- function hasPrecedingDisable(lines: readonly string[], idx: number): boolean {
43
- if (idx === 0) return false;
38
+ function hasPrecedingDisable(lines, idx) {
39
+ if (idx === 0)
40
+ return false;
44
41
  const prevLine = lines[idx - 1];
45
42
  return DISABLE_PATTERN.test(prevLine);
46
43
  }
47
-
48
- export default noUnmanagedExceptionsRule;
44
+ exports.default = noUnmanagedExceptionsRule;
45
+ //# sourceMappingURL=no-unmanaged-exceptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unmanaged-exceptions.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/ai-hook-rules/src/core/rules/no-unmanaged-exceptions.ts"],"names":[],"mappings":";;AACA,oCAA0C;AAC1C,8DAA+D;AAE/D,MAAM,WAAW,GAAG,YAAY,CAAC;AAEjC,8EAA8E;AAC9E,MAAM,eAAe,GAAG,gGAAgG,CAAC;AAEzH,MAAM,yBAAyB,GAAa;IACxC,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EAAE,uHAAuH;IACpI,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE;QACL,4GAA4G;QAC5G,qIAAqI;QACrI,0DAA0D;QAC1D,qLAAqL;KACxL;IAED,KAAK,CAAC,GAAgB;QAClB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,yBAAyB,CAAC;gBAAE,SAAS;YACrE,IAAI,mBAAmB,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;gBAAE,SAAS;YAChD,UAAU,CAAC,IAAI,CAAC,IAAI,iBAAC,CACjB,OAAO,EACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACnB,6HAA6H,CAChI,CAAC,CAAC;QACP,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,IAAA,2CAAsB,EAAC,GAAG,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC;QAChG,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ,CAAC;AAEF,SAAS,mBAAmB,CAAC,KAAwB,EAAE,GAAW;IAC9D,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAChC,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,kBAAe,yBAAyB,CAAC","sourcesContent":["import type { EditRule, EditContext, Violation } from '../types';\nimport { Violation as V } from '../types';\nimport { writeTemplateIfMissing } from '../instruct-ai-writer';\n\nconst TRY_PATTERN = /\\btry\\s*\\{/;\n\n// Both webpieces-disable and the existing ESLint directive suppress this rule\nconst DISABLE_PATTERN = /@webpieces\\/no-unmanaged-exceptions|webpieces-disable\\s+(?:[\\w-]+,\\s*)*no-unmanaged-exceptions/;\n\nconst noUnmanagedExceptionsRule: EditRule = {\n name: 'no-unmanaged-exceptions',\n description: 'try/catch is generally not allowed. Only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.',\n scope: 'edit',\n files: ['**/*.ts', '**/*.tsx'],\n defaultOptions: {},\n fixHint: [\n 'VERY IMPORTANT: READ .webpieces/instruct-ai/webpieces.exceptions.md to understand why and how to fix this!',\n 'Exceptions should bubble to a chokepoint (filter in node.js, globalErrorHandler in Angular). Most code should NOT catch exceptions.',\n '// webpieces-disable no-unmanaged-exceptions -- <reason>',\n 'When try/catch IS used (after disabling), the catch block MUST use: catch (err: unknown) { const error = toError(err); ... } or //const error = toError(err); to explicitly ignore.',\n ],\n\n check(ctx: EditContext): readonly Violation[] {\n const violations: V[] = [];\n for (let i = 0; i < ctx.strippedLines.length; i += 1) {\n const stripped = ctx.strippedLines[i];\n if (!TRY_PATTERN.test(stripped)) continue;\n const lineNum = i + 1;\n if (ctx.isLineDisabled(lineNum, 'no-unmanaged-exceptions')) continue;\n if (hasPrecedingDisable(ctx.lines, i)) continue;\n violations.push(new V(\n lineNum,\n ctx.lines[i].trim(),\n 'try/catch is generally not allowed. It is only allowed in chokepoints (filter, globalErrorHandler) or other rare locations.',\n ));\n }\n if (violations.length > 0) writeTemplateIfMissing(ctx.workspaceRoot, 'webpieces.exceptions.md');\n return violations;\n },\n};\n\nfunction hasPrecedingDisable(lines: readonly string[], idx: number): boolean {\n if (idx === 0) return false;\n const prevLine = lines[idx - 1];\n return DISABLE_PATTERN.test(prevLine);\n}\n\nexport default noUnmanagedExceptionsRule;\n"]}
@@ -0,0 +1,3 @@
1
+ import type { EditRule } from '../types';
2
+ declare const requireReturnTypeRule: EditRule;
3
+ export default requireReturnTypeRule;