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.
- package/README.md +111 -196
- package/dist/ast/builtins.d.ts +28 -0
- package/dist/ast/builtins.d.ts.map +1 -0
- package/dist/ast/builtins.js +361 -0
- package/dist/ast/builtins.js.map +1 -0
- package/dist/ast/checker.d.ts +17 -0
- package/dist/ast/checker.d.ts.map +1 -0
- package/dist/ast/checker.js +97 -0
- package/dist/ast/checker.js.map +1 -0
- package/dist/ast/index.d.ts +5 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +7 -0
- package/dist/ast/index.js.map +1 -0
- package/dist/ast/parser.d.ts +55 -0
- package/dist/ast/parser.d.ts.map +1 -0
- package/dist/ast/parser.js +210 -0
- package/dist/ast/parser.js.map +1 -0
- package/dist/ast/query.d.ts +48 -0
- package/dist/ast/query.d.ts.map +1 -0
- package/dist/ast/query.js +102 -0
- package/dist/ast/query.js.map +1 -0
- package/dist/ast/validate-cli.d.ts +21 -0
- package/dist/ast/validate-cli.d.ts.map +1 -0
- package/dist/ast/validate-cli.js +73 -0
- package/dist/ast/validate-cli.js.map +1 -0
- package/dist/cli.js +105 -21
- package/dist/cli.js.map +1 -1
- package/dist/compiler/builtins.d.ts.map +1 -1
- package/dist/compiler/builtins.js +721 -4
- package/dist/compiler/builtins.js.map +1 -1
- package/dist/compiler/commands.d.ts +40 -0
- package/dist/compiler/commands.d.ts.map +1 -0
- package/dist/compiler/commands.js +311 -0
- package/dist/compiler/commands.js.map +1 -0
- package/dist/compiler/content.d.ts +160 -0
- package/dist/compiler/content.d.ts.map +1 -0
- package/dist/compiler/content.js +461 -0
- package/dist/compiler/content.js.map +1 -0
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/index.js +34 -7
- package/dist/compiler/index.js.map +1 -1
- package/dist/compiler/llm.d.ts.map +1 -1
- package/dist/compiler/llm.js +96 -9
- package/dist/compiler/llm.js.map +1 -1
- package/dist/compiler/prompt.d.ts +1 -1
- package/dist/compiler/prompt.d.ts.map +1 -1
- package/dist/compiler/prompt.js +247 -15
- package/dist/compiler/prompt.js.map +1 -1
- package/dist/config/leash-parser.d.ts +29 -0
- package/dist/config/leash-parser.d.ts.map +1 -0
- package/dist/config/leash-parser.js +70 -0
- package/dist/config/leash-parser.js.map +1 -0
- package/dist/config/loader.d.ts +2 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +18 -8
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +19 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/config/watcher.d.ts +18 -0
- package/dist/config/watcher.d.ts.map +1 -0
- package/dist/config/watcher.js +102 -0
- package/dist/config/watcher.js.map +1 -0
- package/dist/matcher.d.ts +18 -0
- package/dist/matcher.d.ts.map +1 -1
- package/dist/matcher.js +43 -0
- package/dist/matcher.js.map +1 -1
- package/dist/native/claude-code.d.ts.map +1 -1
- package/dist/native/claude-code.js +294 -50
- package/dist/native/claude-code.js.map +1 -1
- package/dist/native/cursor.d.ts +14 -1
- package/dist/native/cursor.d.ts.map +1 -1
- package/dist/native/cursor.js +340 -34
- package/dist/native/cursor.js.map +1 -1
- package/dist/native/index.d.ts +5 -0
- package/dist/native/index.d.ts.map +1 -1
- package/dist/native/index.js +56 -10
- package/dist/native/index.js.map +1 -1
- package/dist/native/opencode.d.ts.map +1 -1
- package/dist/native/opencode.js +15 -3
- package/dist/native/opencode.js.map +1 -1
- package/dist/native/validator.d.ts +15 -0
- package/dist/native/validator.d.ts.map +1 -0
- package/dist/native/validator.js +368 -0
- package/dist/native/validator.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/wrapper/daemon.d.ts.map +1 -1
- package/dist/wrapper/daemon.js +31 -2
- package/dist/wrapper/daemon.js.map +1 -1
- package/languages/tree-sitter-javascript.wasm +0 -0
- package/languages/tree-sitter-tsx.wasm +0 -0
- package/languages/tree-sitter-typescript.wasm +0 -0
- 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
|
-
//
|
|
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
|
-
|
|
124
|
-
|
|
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;
|