veto-leash 0.1.3 → 1.0.1

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 (96) hide show
  1. package/README.md +111 -196
  2. package/dist/ast/builtins.d.ts +28 -0
  3. package/dist/ast/builtins.d.ts.map +1 -0
  4. package/dist/ast/builtins.js +361 -0
  5. package/dist/ast/builtins.js.map +1 -0
  6. package/dist/ast/checker.d.ts +17 -0
  7. package/dist/ast/checker.d.ts.map +1 -0
  8. package/dist/ast/checker.js +97 -0
  9. package/dist/ast/checker.js.map +1 -0
  10. package/dist/ast/index.d.ts +5 -0
  11. package/dist/ast/index.d.ts.map +1 -0
  12. package/dist/ast/index.js +7 -0
  13. package/dist/ast/index.js.map +1 -0
  14. package/dist/ast/parser.d.ts +55 -0
  15. package/dist/ast/parser.d.ts.map +1 -0
  16. package/dist/ast/parser.js +210 -0
  17. package/dist/ast/parser.js.map +1 -0
  18. package/dist/ast/query.d.ts +48 -0
  19. package/dist/ast/query.d.ts.map +1 -0
  20. package/dist/ast/query.js +102 -0
  21. package/dist/ast/query.js.map +1 -0
  22. package/dist/ast/validate-cli.d.ts +21 -0
  23. package/dist/ast/validate-cli.d.ts.map +1 -0
  24. package/dist/ast/validate-cli.js +73 -0
  25. package/dist/ast/validate-cli.js.map +1 -0
  26. package/dist/cli.js +105 -21
  27. package/dist/cli.js.map +1 -1
  28. package/dist/compiler/builtins.d.ts.map +1 -1
  29. package/dist/compiler/builtins.js +721 -4
  30. package/dist/compiler/builtins.js.map +1 -1
  31. package/dist/compiler/commands.d.ts +40 -0
  32. package/dist/compiler/commands.d.ts.map +1 -0
  33. package/dist/compiler/commands.js +311 -0
  34. package/dist/compiler/commands.js.map +1 -0
  35. package/dist/compiler/content.d.ts +160 -0
  36. package/dist/compiler/content.d.ts.map +1 -0
  37. package/dist/compiler/content.js +461 -0
  38. package/dist/compiler/content.js.map +1 -0
  39. package/dist/compiler/index.d.ts.map +1 -1
  40. package/dist/compiler/index.js +34 -7
  41. package/dist/compiler/index.js.map +1 -1
  42. package/dist/compiler/llm.d.ts.map +1 -1
  43. package/dist/compiler/llm.js +96 -9
  44. package/dist/compiler/llm.js.map +1 -1
  45. package/dist/compiler/prompt.d.ts +1 -1
  46. package/dist/compiler/prompt.d.ts.map +1 -1
  47. package/dist/compiler/prompt.js +247 -15
  48. package/dist/compiler/prompt.js.map +1 -1
  49. package/dist/config/leash-parser.d.ts +29 -0
  50. package/dist/config/leash-parser.d.ts.map +1 -0
  51. package/dist/config/leash-parser.js +70 -0
  52. package/dist/config/leash-parser.js.map +1 -0
  53. package/dist/config/loader.d.ts +2 -1
  54. package/dist/config/loader.d.ts.map +1 -1
  55. package/dist/config/loader.js +18 -8
  56. package/dist/config/loader.js.map +1 -1
  57. package/dist/config/schema.d.ts +8 -0
  58. package/dist/config/schema.d.ts.map +1 -1
  59. package/dist/config/schema.js +19 -0
  60. package/dist/config/schema.js.map +1 -1
  61. package/dist/config/watcher.d.ts +18 -0
  62. package/dist/config/watcher.d.ts.map +1 -0
  63. package/dist/config/watcher.js +102 -0
  64. package/dist/config/watcher.js.map +1 -0
  65. package/dist/matcher.d.ts +18 -0
  66. package/dist/matcher.d.ts.map +1 -1
  67. package/dist/matcher.js +43 -0
  68. package/dist/matcher.js.map +1 -1
  69. package/dist/native/claude-code.d.ts.map +1 -1
  70. package/dist/native/claude-code.js +294 -50
  71. package/dist/native/claude-code.js.map +1 -1
  72. package/dist/native/cursor.d.ts +14 -1
  73. package/dist/native/cursor.d.ts.map +1 -1
  74. package/dist/native/cursor.js +340 -34
  75. package/dist/native/cursor.js.map +1 -1
  76. package/dist/native/index.d.ts +5 -0
  77. package/dist/native/index.d.ts.map +1 -1
  78. package/dist/native/index.js +56 -10
  79. package/dist/native/index.js.map +1 -1
  80. package/dist/native/opencode.d.ts.map +1 -1
  81. package/dist/native/opencode.js +15 -3
  82. package/dist/native/opencode.js.map +1 -1
  83. package/dist/native/validator.d.ts +15 -0
  84. package/dist/native/validator.d.ts.map +1 -0
  85. package/dist/native/validator.js +368 -0
  86. package/dist/native/validator.js.map +1 -0
  87. package/dist/types.d.ts +114 -0
  88. package/dist/types.d.ts.map +1 -1
  89. package/dist/types.js.map +1 -1
  90. package/dist/wrapper/daemon.d.ts.map +1 -1
  91. package/dist/wrapper/daemon.js +31 -2
  92. package/dist/wrapper/daemon.js.map +1 -1
  93. package/languages/tree-sitter-javascript.wasm +0 -0
  94. package/languages/tree-sitter-tsx.wasm +0 -0
  95. package/languages/tree-sitter-typescript.wasm +0 -0
  96. package/package.json +5 -2
@@ -1,4 +1,5 @@
1
1
  // src/compiler/builtins.ts
2
+ import { LODASH_PATTERNS, ANY_TYPE_PATTERNS, CONSOLE_PATTERNS, CLASS_COMPONENT_PATTERNS, EVAL_PATTERNS, MOMENT_PATTERNS, COMMON_PATTERNS, EXCEPTION_PATTERNS, } from './content.js';
2
3
  export const BUILTINS = {
3
4
  'test files': {
4
5
  include: [
@@ -111,17 +112,733 @@ export const BUILTINS = {
111
112
  exclude: ['src/core/**/*.log', 'src/core/**/*.tmp'],
112
113
  description: 'Core source directory',
113
114
  },
115
+ // ═══════════════════════════════════════════════════════════════════════════
116
+ // COMMAND-AWARE BUILTINS (Phase 1)
117
+ // These enforce preferences at the command level, not just file level
118
+ // ═══════════════════════════════════════════════════════════════════════════
119
+ // Package manager preferences
120
+ 'prefer pnpm': {
121
+ include: [],
122
+ exclude: [],
123
+ description: 'Use pnpm instead of npm/yarn',
124
+ commandRules: [
125
+ {
126
+ block: ['npm install*', 'npm i *', 'npm i', 'npm ci', 'npm add*'],
127
+ suggest: 'pnpm install',
128
+ reason: 'Project uses pnpm',
129
+ },
130
+ {
131
+ block: ['yarn', 'yarn install', 'yarn add*'],
132
+ suggest: 'pnpm add',
133
+ reason: 'Project uses pnpm',
134
+ },
135
+ ],
136
+ },
137
+ 'use pnpm': {
138
+ include: [],
139
+ exclude: [],
140
+ description: 'Use pnpm instead of npm/yarn',
141
+ commandRules: [
142
+ {
143
+ block: ['npm install*', 'npm i *', 'npm i', 'npm ci', 'npm add*'],
144
+ suggest: 'pnpm install',
145
+ reason: 'Project uses pnpm',
146
+ },
147
+ {
148
+ block: ['yarn', 'yarn install', 'yarn add*'],
149
+ suggest: 'pnpm add',
150
+ reason: 'Project uses pnpm',
151
+ },
152
+ ],
153
+ },
154
+ 'pnpm not npm': {
155
+ include: [],
156
+ exclude: [],
157
+ description: 'Use pnpm instead of npm',
158
+ commandRules: [
159
+ {
160
+ block: ['npm install*', 'npm i *', 'npm i', 'npm ci', 'npm add*', 'npm run*'],
161
+ suggest: 'pnpm',
162
+ reason: 'Project uses pnpm',
163
+ },
164
+ ],
165
+ },
166
+ 'prefer bun': {
167
+ include: [],
168
+ exclude: [],
169
+ description: 'Use bun instead of npm/pnpm/yarn',
170
+ commandRules: [
171
+ {
172
+ block: ['npm install*', 'npm i *', 'npm i', 'npm ci', 'npm add*', 'npm run*'],
173
+ suggest: 'bun',
174
+ reason: 'Project uses bun',
175
+ },
176
+ {
177
+ block: ['pnpm install*', 'pnpm i *', 'pnpm add*', 'pnpm run*'],
178
+ suggest: 'bun',
179
+ reason: 'Project uses bun',
180
+ },
181
+ {
182
+ block: ['yarn', 'yarn install', 'yarn add*', 'yarn run*'],
183
+ suggest: 'bun',
184
+ reason: 'Project uses bun',
185
+ },
186
+ ],
187
+ },
188
+ 'use bun': {
189
+ include: [],
190
+ exclude: [],
191
+ description: 'Use bun instead of npm/pnpm/yarn',
192
+ commandRules: [
193
+ {
194
+ block: ['npm install*', 'npm i *', 'npm i', 'npm ci', 'npm add*', 'npm run*'],
195
+ suggest: 'bun',
196
+ reason: 'Project uses bun',
197
+ },
198
+ {
199
+ block: ['pnpm install*', 'pnpm i *', 'pnpm add*', 'pnpm run*'],
200
+ suggest: 'bun',
201
+ reason: 'Project uses bun',
202
+ },
203
+ {
204
+ block: ['yarn', 'yarn install', 'yarn add*', 'yarn run*'],
205
+ suggest: 'bun',
206
+ reason: 'Project uses bun',
207
+ },
208
+ ],
209
+ },
210
+ 'bun not npm': {
211
+ include: [],
212
+ exclude: [],
213
+ description: 'Use bun instead of npm',
214
+ commandRules: [
215
+ {
216
+ block: ['npm install*', 'npm i *', 'npm i', 'npm ci', 'npm add*', 'npm run*'],
217
+ suggest: 'bun',
218
+ reason: 'Project uses bun',
219
+ },
220
+ ],
221
+ },
222
+ 'prefer yarn': {
223
+ include: [],
224
+ exclude: [],
225
+ description: 'Use yarn instead of npm',
226
+ commandRules: [
227
+ {
228
+ block: ['npm install*', 'npm i *', 'npm i', 'npm ci', 'npm add*'],
229
+ suggest: 'yarn add',
230
+ reason: 'Project uses yarn',
231
+ },
232
+ ],
233
+ },
234
+ // Dangerous command prevention
235
+ 'no sudo': {
236
+ include: [],
237
+ exclude: [],
238
+ description: 'Prevent sudo commands',
239
+ commandRules: [
240
+ {
241
+ block: ['sudo *'],
242
+ reason: 'sudo not allowed in this project',
243
+ },
244
+ ],
245
+ },
246
+ 'no force push': {
247
+ include: [],
248
+ exclude: [],
249
+ description: 'Prevent git force push',
250
+ commandRules: [
251
+ {
252
+ block: ['git push --force*', 'git push -f*', 'git push * --force*', 'git push * -f*'],
253
+ reason: 'Force push not allowed - could overwrite team changes',
254
+ },
255
+ ],
256
+ },
257
+ 'no hard reset': {
258
+ include: [],
259
+ exclude: [],
260
+ description: 'Prevent git hard reset',
261
+ commandRules: [
262
+ {
263
+ block: ['git reset --hard*'],
264
+ suggest: 'git reset --soft or git stash',
265
+ reason: 'Hard reset can lose uncommitted work',
266
+ },
267
+ ],
268
+ },
269
+ // Testing framework preferences
270
+ 'use vitest': {
271
+ include: [],
272
+ exclude: [],
273
+ description: 'Use vitest instead of jest',
274
+ commandRules: [
275
+ {
276
+ block: ['jest*', 'npx jest*', 'npm run jest*', 'pnpm jest*'],
277
+ suggest: 'vitest',
278
+ reason: 'Project uses vitest',
279
+ },
280
+ ],
281
+ },
282
+ 'vitest not jest': {
283
+ include: [],
284
+ exclude: [],
285
+ description: 'Use vitest instead of jest',
286
+ commandRules: [
287
+ {
288
+ block: ['jest*', 'npx jest*', 'npm run jest*', 'pnpm jest*'],
289
+ suggest: 'vitest or pnpm test',
290
+ reason: 'Project uses vitest',
291
+ },
292
+ ],
293
+ },
294
+ 'use pytest': {
295
+ include: [],
296
+ exclude: [],
297
+ description: 'Use pytest instead of unittest',
298
+ commandRules: [
299
+ {
300
+ block: ['python -m unittest*', 'python3 -m unittest*'],
301
+ suggest: 'pytest',
302
+ reason: 'Project uses pytest',
303
+ },
304
+ ],
305
+ },
306
+ // External network restrictions
307
+ 'no curl external': {
308
+ include: [],
309
+ exclude: [],
310
+ description: 'Prevent curl to external URLs',
311
+ commandRules: [
312
+ {
313
+ block: ['curl http://*', 'curl https://*', 'wget http://*', 'wget https://*'],
314
+ reason: 'External network requests not allowed',
315
+ },
316
+ ],
317
+ },
318
+ 'no curl pipe bash': {
319
+ include: [],
320
+ exclude: [],
321
+ description: 'Prevent piping curl output to bash',
322
+ commandRules: [
323
+ {
324
+ block: ['curl * | bash*', 'curl * | sh*', 'wget * | bash*', 'wget * | sh*'],
325
+ reason: 'Piping remote scripts to shell is dangerous',
326
+ },
327
+ ],
328
+ },
329
+ // Production safety
330
+ 'no production db': {
331
+ include: [],
332
+ exclude: [],
333
+ description: 'Prevent commands targeting production database',
334
+ commandRules: [
335
+ {
336
+ block: ['*--production*migrate*', '*prod*migrate*', '*DATABASE_URL=*prod*'],
337
+ reason: 'Production database commands blocked',
338
+ },
339
+ ],
340
+ },
341
+ // Docker preferences
342
+ 'use docker compose': {
343
+ include: [],
344
+ exclude: [],
345
+ description: 'Use docker compose (v2) instead of docker-compose',
346
+ commandRules: [
347
+ {
348
+ block: ['docker-compose *'],
349
+ suggest: 'docker compose',
350
+ reason: 'Use docker compose v2 syntax',
351
+ },
352
+ ],
353
+ },
354
+ // ═══════════════════════════════════════════════════════════════════════════
355
+ // CONTENT-AWARE BUILTINS (Phase 2)
356
+ // These check file contents for banned patterns/imports
357
+ //
358
+ // DESIGN: Each builtin uses COMPREHENSIVE pattern sets with:
359
+ // - 'strict' mode: strips comments/strings before matching (fewer false positives)
360
+ // - exceptions: patterns that indicate false positives
361
+ // - multiple patterns: catches all variants (ES imports, CommonJS, dynamic, etc.)
362
+ // ═══════════════════════════════════════════════════════════════════════════
363
+ // Import restrictions
364
+ 'no lodash': {
365
+ include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx', '**/*.mts', '**/*.mjs'],
366
+ exclude: [],
367
+ description: 'Prefer native methods over lodash',
368
+ commandRules: [
369
+ {
370
+ block: [
371
+ 'npm install lodash*', 'npm i lodash*',
372
+ 'pnpm add lodash*', 'pnpm i lodash*',
373
+ 'bun add lodash*', 'bun i lodash*',
374
+ 'yarn add lodash*',
375
+ ],
376
+ reason: 'Use native array/object methods instead of lodash',
377
+ },
378
+ ],
379
+ contentRules: [
380
+ // ES6 imports (default, named, namespace)
381
+ {
382
+ pattern: LODASH_PATTERNS.any,
383
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx', '*.mts', '*.mjs'],
384
+ reason: 'Use native array/object methods instead of lodash',
385
+ suggest: 'Use Array.map(), Array.filter(), Object.keys(), etc.',
386
+ mode: 'strict',
387
+ exceptions: [EXCEPTION_PATTERNS.lodashMigration],
388
+ },
389
+ ],
390
+ },
391
+ 'dont use lodash': {
392
+ include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx'],
393
+ exclude: [],
394
+ description: 'Prefer native methods over lodash',
395
+ commandRules: [
396
+ {
397
+ block: ['npm install lodash*', 'pnpm add lodash*', 'bun add lodash*'],
398
+ reason: 'Use native methods instead of lodash',
399
+ },
400
+ ],
401
+ contentRules: [
402
+ {
403
+ pattern: LODASH_PATTERNS.any,
404
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
405
+ reason: 'Use native methods instead of lodash',
406
+ suggest: 'Use native Array/Object methods',
407
+ mode: 'strict',
408
+ },
409
+ ],
410
+ },
411
+ 'no moment': {
412
+ include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx'],
413
+ exclude: [],
414
+ description: 'Use date-fns or native Date instead of moment.js',
415
+ commandRules: [
416
+ {
417
+ block: [
418
+ 'npm install moment*', 'npm i moment',
419
+ 'pnpm add moment*',
420
+ 'bun add moment*',
421
+ 'yarn add moment*',
422
+ ],
423
+ suggest: 'date-fns or native Date',
424
+ reason: 'moment.js is deprecated and heavy',
425
+ },
426
+ ],
427
+ contentRules: [
428
+ {
429
+ pattern: MOMENT_PATTERNS.any,
430
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
431
+ reason: 'moment.js is deprecated; use date-fns or native Date',
432
+ suggest: 'import { format } from "date-fns"',
433
+ mode: 'strict',
434
+ },
435
+ ],
436
+ },
437
+ 'no jquery': {
438
+ include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx'],
439
+ exclude: [],
440
+ description: 'Use modern DOM APIs instead of jQuery',
441
+ commandRules: [
442
+ {
443
+ block: ['npm install jquery*', 'pnpm add jquery*', 'bun add jquery*', 'yarn add jquery*'],
444
+ reason: 'Use native DOM APIs instead of jQuery',
445
+ },
446
+ ],
447
+ contentRules: [
448
+ {
449
+ pattern: COMMON_PATTERNS.jqueryImport,
450
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
451
+ reason: 'Use native DOM APIs instead of jQuery',
452
+ suggest: 'document.querySelector(), fetch(), etc.',
453
+ mode: 'strict',
454
+ },
455
+ ],
456
+ },
457
+ // Console/Debug restrictions
458
+ 'no console.log': {
459
+ include: ['src/**/*.ts', 'src/**/*.js', 'src/**/*.tsx', 'src/**/*.jsx'],
460
+ exclude: ['**/*.test.*', '**/*.spec.*', '**/test/**', '**/tests/**', '**/__tests__/**'],
461
+ description: 'No console.log in production code',
462
+ contentRules: [
463
+ {
464
+ pattern: CONSOLE_PATTERNS.log,
465
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
466
+ reason: 'Use proper logging instead of console.log',
467
+ suggest: 'Use a logging library like pino or winston',
468
+ mode: 'strict',
469
+ exceptions: [EXCEPTION_PATTERNS.consoleInTest],
470
+ },
471
+ // Also catch destructured and aliased console
472
+ {
473
+ pattern: CONSOLE_PATTERNS.destructure,
474
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
475
+ reason: 'Destructured console.log detected',
476
+ mode: 'strict',
477
+ },
478
+ ],
479
+ },
480
+ 'no console': {
481
+ include: ['src/**/*.ts', 'src/**/*.js', 'src/**/*.tsx', 'src/**/*.jsx'],
482
+ exclude: ['**/*.test.*', '**/*.spec.*', '**/test/**', '**/tests/**'],
483
+ description: 'No console statements in production code',
484
+ contentRules: [
485
+ {
486
+ pattern: CONSOLE_PATTERNS.anyMethod,
487
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
488
+ reason: 'Use proper logging instead of console statements',
489
+ suggest: 'Use a logging library',
490
+ mode: 'strict',
491
+ exceptions: [EXCEPTION_PATTERNS.consoleInTest],
492
+ },
493
+ {
494
+ pattern: CONSOLE_PATTERNS.bracket,
495
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
496
+ reason: 'Console accessed via bracket notation',
497
+ mode: 'strict',
498
+ },
499
+ {
500
+ pattern: CONSOLE_PATTERNS.destructure,
501
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
502
+ reason: 'Destructured console methods detected',
503
+ mode: 'strict',
504
+ },
505
+ ],
506
+ },
507
+ 'no debugger': {
508
+ include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx'],
509
+ exclude: [],
510
+ description: 'No debugger statements',
511
+ contentRules: [
512
+ {
513
+ pattern: COMMON_PATTERNS.debugger,
514
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
515
+ reason: 'Remove debugger statements before committing',
516
+ mode: 'strict', // Won't match 'debugger' in strings/comments
517
+ },
518
+ ],
519
+ },
520
+ // React patterns
521
+ 'no class components': {
522
+ include: ['**/*.tsx', '**/*.jsx'],
523
+ exclude: [],
524
+ description: 'Use functional React components with hooks',
525
+ contentRules: [
526
+ {
527
+ pattern: CLASS_COMPONENT_PATTERNS.any,
528
+ fileTypes: ['*.tsx', '*.jsx'],
529
+ reason: 'Use functional components with hooks instead of class components',
530
+ suggest: 'Convert to: const Component = () => { ... }',
531
+ mode: 'strict',
532
+ },
533
+ ],
534
+ },
535
+ 'functional components only': {
536
+ include: ['**/*.tsx', '**/*.jsx'],
537
+ exclude: [],
538
+ description: 'Use functional React components',
539
+ contentRules: [
540
+ {
541
+ pattern: CLASS_COMPONENT_PATTERNS.any,
542
+ fileTypes: ['*.tsx', '*.jsx'],
543
+ reason: 'Use functional components with hooks',
544
+ suggest: 'const Component: FC = () => { ... }',
545
+ mode: 'strict',
546
+ },
547
+ ],
548
+ },
549
+ // TypeScript strictness - COMPREHENSIVE any detection
550
+ 'no any': {
551
+ include: ['**/*.ts', '**/*.tsx'],
552
+ exclude: ['**/*.d.ts', '**/types/**', '**/@types/**'],
553
+ description: 'Avoid any type in TypeScript',
554
+ contentRules: [
555
+ // Type annotations: : any
556
+ {
557
+ pattern: ANY_TYPE_PATTERNS.annotation,
558
+ fileTypes: ['*.ts', '*.tsx'],
559
+ reason: 'Use proper TypeScript types instead of any',
560
+ suggest: 'Use unknown, specific types, or generics',
561
+ mode: 'strict',
562
+ exceptions: [EXCEPTION_PATTERNS.anyInVariableName],
563
+ },
564
+ // Generic parameters: <any>
565
+ {
566
+ pattern: ANY_TYPE_PATTERNS.genericParam,
567
+ fileTypes: ['*.ts', '*.tsx'],
568
+ reason: 'Avoid any in generic parameters',
569
+ suggest: 'Use specific type or unknown',
570
+ mode: 'strict',
571
+ },
572
+ // Common generics with any
573
+ {
574
+ pattern: ANY_TYPE_PATTERNS.genericInArray,
575
+ fileTypes: ['*.ts', '*.tsx'],
576
+ reason: 'Avoid Array<any>',
577
+ suggest: 'Use Array<unknown> or specific type[]',
578
+ mode: 'strict',
579
+ },
580
+ // Type assertions: as any
581
+ {
582
+ pattern: ANY_TYPE_PATTERNS.asAny,
583
+ fileTypes: ['*.ts', '*.tsx'],
584
+ reason: 'Avoid casting to any',
585
+ suggest: 'Use proper type narrowing or as unknown',
586
+ mode: 'strict',
587
+ },
588
+ ],
589
+ },
590
+ 'no any types': {
591
+ include: ['**/*.ts', '**/*.tsx'],
592
+ exclude: ['**/*.d.ts'],
593
+ description: 'Comprehensive any type detection',
594
+ contentRules: [
595
+ // Use the combined common pattern for basic cases
596
+ {
597
+ pattern: ANY_TYPE_PATTERNS.common,
598
+ fileTypes: ['*.ts', '*.tsx'],
599
+ reason: 'Use proper TypeScript types instead of any',
600
+ suggest: 'Use unknown or specific types',
601
+ mode: 'strict',
602
+ exceptions: [EXCEPTION_PATTERNS.anyInVariableName],
603
+ },
604
+ // Type aliases
605
+ {
606
+ pattern: ANY_TYPE_PATTERNS.typeAlias,
607
+ fileTypes: ['*.ts', '*.tsx'],
608
+ reason: 'Avoid type aliases to any',
609
+ mode: 'strict',
610
+ },
611
+ // Generic defaults
612
+ {
613
+ pattern: ANY_TYPE_PATTERNS.genericDefault,
614
+ fileTypes: ['*.ts', '*.tsx'],
615
+ reason: 'Avoid any as generic default',
616
+ suggest: 'Use unknown as default',
617
+ mode: 'strict',
618
+ },
619
+ // Record<string, any>
620
+ {
621
+ pattern: ANY_TYPE_PATTERNS.genericInRecord,
622
+ fileTypes: ['*.ts', '*.tsx'],
623
+ reason: 'Avoid Record<string, any>',
624
+ suggest: 'Use Record<string, unknown>',
625
+ mode: 'strict',
626
+ },
627
+ ],
628
+ },
629
+ 'strict types': {
630
+ include: ['**/*.ts', '**/*.tsx'],
631
+ exclude: ['**/*.d.ts'],
632
+ description: 'Enforce strict TypeScript typing - catches ALL any variants',
633
+ contentRules: [
634
+ // Basic any
635
+ {
636
+ pattern: ANY_TYPE_PATTERNS.common,
637
+ fileTypes: ['*.ts', '*.tsx'],
638
+ reason: 'Strict typing: avoid any',
639
+ mode: 'strict',
640
+ exceptions: [EXCEPTION_PATTERNS.anyInVariableName],
641
+ },
642
+ // Union/intersection with any
643
+ {
644
+ pattern: ANY_TYPE_PATTERNS.unionAny,
645
+ fileTypes: ['*.ts', '*.tsx'],
646
+ reason: 'Strict typing: avoid | any unions',
647
+ mode: 'strict',
648
+ },
649
+ {
650
+ pattern: ANY_TYPE_PATTERNS.intersectionAny,
651
+ fileTypes: ['*.ts', '*.tsx'],
652
+ reason: 'Strict typing: avoid & any intersections',
653
+ mode: 'strict',
654
+ },
655
+ // Double assertion
656
+ {
657
+ pattern: ANY_TYPE_PATTERNS.asUnknownAsAny,
658
+ fileTypes: ['*.ts', '*.tsx'],
659
+ reason: 'Strict typing: avoid as unknown as any',
660
+ mode: 'strict',
661
+ },
662
+ ],
663
+ },
664
+ // Security patterns
665
+ 'no eval': {
666
+ include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx'],
667
+ exclude: [],
668
+ description: 'Prevent use of eval() and similar unsafe constructs',
669
+ contentRules: [
670
+ {
671
+ pattern: EVAL_PATTERNS.direct,
672
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
673
+ reason: 'eval() is a security risk',
674
+ suggest: 'Use JSON.parse() or safer alternatives',
675
+ mode: 'strict',
676
+ },
677
+ {
678
+ pattern: EVAL_PATTERNS.functionConstructor,
679
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
680
+ reason: 'new Function() is equivalent to eval()',
681
+ suggest: 'Use a safer approach',
682
+ mode: 'strict',
683
+ },
684
+ {
685
+ pattern: EVAL_PATTERNS.setTimeoutString,
686
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
687
+ reason: 'setTimeout with string is eval-like',
688
+ suggest: 'Pass a function instead of string',
689
+ mode: 'strict',
690
+ },
691
+ ],
692
+ },
693
+ 'no innerHTML': {
694
+ include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx'],
695
+ exclude: [],
696
+ description: 'Prevent direct innerHTML assignment (XSS risk)',
697
+ contentRules: [
698
+ {
699
+ pattern: COMMON_PATTERNS.innerHtml,
700
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
701
+ reason: 'innerHTML is an XSS risk',
702
+ suggest: 'Use textContent or DOM methods',
703
+ mode: 'strict',
704
+ },
705
+ {
706
+ pattern: COMMON_PATTERNS.dangerouslySetInnerHTML,
707
+ fileTypes: ['*.tsx', '*.jsx'],
708
+ reason: 'dangerouslySetInnerHTML should be avoided',
709
+ suggest: 'Use proper React rendering',
710
+ mode: 'strict',
711
+ },
712
+ ],
713
+ },
714
+ // Code quality
715
+ 'no todos': {
716
+ include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx'],
717
+ exclude: [],
718
+ description: 'No TODO comments in committed code',
719
+ contentRules: [
720
+ {
721
+ pattern: COMMON_PATTERNS.todoComment,
722
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
723
+ reason: 'Resolve TODO comments before committing',
724
+ // Note: mode 'fast' intentionally - we WANT to catch comments
725
+ },
726
+ {
727
+ pattern: COMMON_PATTERNS.fixmeComment,
728
+ fileTypes: ['*.ts', '*.js', '*.tsx', '*.jsx'],
729
+ reason: 'Resolve FIXME comments before committing',
730
+ },
731
+ ],
732
+ },
733
+ };
734
+ /**
735
+ * Phrase variations that map to builtins
736
+ */
737
+ const PHRASE_ALIASES = {
738
+ // Package managers
739
+ 'pnpm over npm': 'prefer pnpm',
740
+ 'pnpm instead of npm': 'prefer pnpm',
741
+ 'use pnpm not npm': 'pnpm not npm',
742
+ 'bun over npm': 'prefer bun',
743
+ 'bun instead of npm': 'prefer bun',
744
+ 'use bun not npm': 'bun not npm',
745
+ 'bun over pnpm': 'prefer bun',
746
+ 'yarn over npm': 'prefer yarn',
747
+ // Testing
748
+ 'vitest over jest': 'vitest not jest',
749
+ 'vitest instead of jest': 'vitest not jest',
750
+ 'pytest over unittest': 'use pytest',
751
+ // Git safety
752
+ 'no force pushing': 'no force push',
753
+ 'prevent force push': 'no force push',
754
+ 'block force push': 'no force push',
755
+ 'no git force push': 'no force push',
756
+ // Docker
757
+ 'docker compose v2': 'use docker compose',
758
+ // Content rules - lodash
759
+ 'avoid lodash': 'no lodash',
760
+ 'ban lodash': 'no lodash',
761
+ 'native methods': 'no lodash',
762
+ 'no underscore': 'no lodash',
763
+ // Content rules - moment
764
+ 'avoid moment': 'no moment',
765
+ 'ban moment': 'no moment',
766
+ 'no moment.js': 'no moment',
767
+ // Content rules - jQuery
768
+ 'avoid jquery': 'no jquery',
769
+ 'ban jquery': 'no jquery',
770
+ 'no jquery please': 'no jquery',
771
+ // Content rules - console
772
+ 'no console statements': 'no console',
773
+ 'no logging': 'no console',
774
+ 'remove console.log': 'no console.log',
775
+ 'clean console': 'no console.log',
776
+ // Content rules - React
777
+ 'use functional components': 'no class components',
778
+ 'hooks only': 'no class components',
779
+ 'no react classes': 'no class components',
780
+ 'modern react': 'no class components',
781
+ // Content rules - TypeScript
782
+ 'avoid any': 'no any',
783
+ 'ban any type': 'no any',
784
+ 'strict typescript': 'strict types',
785
+ 'no any keyword': 'no any',
786
+ // Content rules - security
787
+ 'no eval usage': 'no eval',
788
+ 'ban eval': 'no eval',
789
+ 'no xss': 'no innerHTML',
790
+ 'safe dom': 'no innerHTML',
791
+ // Content rules - code quality
792
+ 'no todo comments': 'no todos',
793
+ 'clean todos': 'no todos',
794
+ 'resolve todos': 'no todos',
795
+ 'no fixme': 'no todos',
114
796
  };
115
797
  export function findBuiltin(phrase) {
116
- const normalized = phrase.toLowerCase().trim();
798
+ const normalized = phrase.toLowerCase().trim()
799
+ // Normalize common patterns
800
+ .replace(/don'?t\s+(use\s+)?/g, 'no ')
801
+ .replace(/^prefer\s+to\s+use\s+/g, 'prefer ')
802
+ .replace(/\s+over\s+/g, ' not ')
803
+ .replace(/\s+instead\s+of\s+/g, ' not ')
804
+ .trim();
805
+ // Check phrase aliases first
806
+ if (PHRASE_ALIASES[normalized]) {
807
+ return BUILTINS[PHRASE_ALIASES[normalized]];
808
+ }
117
809
  // Direct match
118
810
  if (BUILTINS[normalized]) {
119
811
  return BUILTINS[normalized];
120
812
  }
121
- // Partial match
813
+ // Check with common prefix/suffix variations
814
+ const variations = [
815
+ normalized,
816
+ normalized.replace(/^use\s+/, ''),
817
+ normalized.replace(/^prefer\s+/, ''),
818
+ normalized.replace(/^no\s+/, ''),
819
+ `use ${normalized}`,
820
+ `prefer ${normalized}`,
821
+ `no ${normalized}`,
822
+ ];
823
+ for (const variation of variations) {
824
+ if (BUILTINS[variation]) {
825
+ return BUILTINS[variation];
826
+ }
827
+ }
828
+ // Partial match - be more careful to avoid false positives
122
829
  for (const [key, value] of Object.entries(BUILTINS)) {
123
- if (normalized.includes(key) || key.includes(normalized)) {
124
- return value;
830
+ // Skip command-only builtins for partial file matching
831
+ if (value.include.length === 0 && value.commandRules) {
832
+ // For command builtins, require closer match
833
+ if (normalized.includes(key) || key.includes(normalized)) {
834
+ return value;
835
+ }
836
+ }
837
+ else {
838
+ // For file builtins, allow partial match
839
+ if (normalized.includes(key) || key.includes(normalized)) {
840
+ return value;
841
+ }
125
842
  }
126
843
  }
127
844
  return null;