@nerviq/cli 1.0.1 → 1.2.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/bin/cli.js +170 -73
- package/package.json +1 -1
- package/src/activity.js +20 -0
- package/src/aider/domain-packs.js +27 -2
- package/src/aider/mcp-packs.js +231 -0
- package/src/aider/techniques.js +3211 -1397
- package/src/audit.js +257 -2
- package/src/catalog.js +18 -2
- package/src/codex/domain-packs.js +23 -1
- package/src/codex/mcp-packs.js +254 -0
- package/src/codex/techniques.js +4738 -3257
- package/src/copilot/domain-packs.js +23 -1
- package/src/copilot/mcp-packs.js +254 -0
- package/src/copilot/techniques.js +3433 -1936
- package/src/cursor/domain-packs.js +23 -1
- package/src/cursor/mcp-packs.js +257 -0
- package/src/cursor/techniques.js +3698 -1869
- package/src/deprecation.js +98 -0
- package/src/domain-pack-expansion.js +571 -0
- package/src/domain-packs.js +25 -2
- package/src/formatters/otel.js +151 -0
- package/src/gemini/domain-packs.js +23 -1
- package/src/gemini/mcp-packs.js +257 -0
- package/src/gemini/techniques.js +3734 -2238
- package/src/integrations.js +194 -0
- package/src/mcp-packs.js +233 -0
- package/src/opencode/domain-packs.js +23 -1
- package/src/opencode/mcp-packs.js +231 -0
- package/src/opencode/techniques.js +3501 -1687
- package/src/org.js +68 -0
- package/src/source-urls.js +410 -260
- package/src/stack-checks.js +565 -0
- package/src/supplemental-checks.js +817 -0
- package/src/techniques.js +2929 -1449
- package/src/telemetry.js +160 -0
- package/src/windsurf/domain-packs.js +23 -1
- package/src/windsurf/mcp-packs.js +257 -0
- package/src/windsurf/techniques.js +3648 -1834
- package/src/workspace.js +233 -0
package/src/aider/techniques.js
CHANGED
|
@@ -1,1397 +1,3211 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Aider Technique Database — 71 checks (AD-A01 through AD-P06)
|
|
3
|
-
*
|
|
4
|
-
* Aider is fundamentally different from IDE platforms:
|
|
5
|
-
* - Git-first CLI tool: git is the ONLY safety mechanism
|
|
6
|
-
* - No hooks, no MCP, no skills, no agents
|
|
7
|
-
* - Config: .aider.conf.yml (YAML), .aider.model.settings.yml, .env
|
|
8
|
-
* - 3 model roles: main (coding), editor (applying), weak (commit messages)
|
|
9
|
-
* - Architect mode (2-model workflow, ~1.73x cost vs standard)
|
|
10
|
-
* - Convention files must be EXPLICITLY passed AND referenced in prompts (no auto-discovery)
|
|
11
|
-
* - 4-level config precedence: env vars > CLI args > .aider.conf.yml > defaults
|
|
12
|
-
* - Key gotcha: default auto-commit bypasses pre-commit hooks (use --git-commit-verify)
|
|
13
|
-
* - Key gotcha: exit code 0 returned even on auth failure in headless mode
|
|
14
|
-
* - Key gotcha: Playwright auto-scrapes URLs in messages (unexpected side effect)
|
|
15
|
-
*
|
|
16
|
-
* Categories: Config(8), Git Safety(10), Model Config(8), Conventions(6),
|
|
17
|
-
* Architecture(4), Security(6), CI(4), Quality(6) + M/N/O/P expansion (19)
|
|
18
|
-
*
|
|
19
|
-
* Check ID prefix: AD-
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
const { containsEmbeddedSecret } = require('../secret-patterns');
|
|
23
|
-
const { attachSourceUrls } = require('../source-urls');
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/\bbe
|
|
29
|
-
/\
|
|
30
|
-
/\
|
|
31
|
-
/\
|
|
32
|
-
/\
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (typeof matcher === '
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// ============================================================================
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
// =========================================================================
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
// =========================================================================
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
// =========================================================================
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const
|
|
489
|
-
if (!
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
return
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
// =========================================================================
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
// =========================================================================
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
return
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if (
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
// =========================================================================
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
if (!
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
if (
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
// =========================================================================
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
if (!
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
// =========================================================================
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
return
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
return
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
// =========================================================================
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
// =========================================================================
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
// =========================================================================
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
// =========================================================================
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
if (
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Aider Technique Database — 71 checks (AD-A01 through AD-P06)
|
|
3
|
+
*
|
|
4
|
+
* Aider is fundamentally different from IDE platforms:
|
|
5
|
+
* - Git-first CLI tool: git is the ONLY safety mechanism
|
|
6
|
+
* - No hooks, no MCP, no skills, no agents
|
|
7
|
+
* - Config: .aider.conf.yml (YAML), .aider.model.settings.yml, .env
|
|
8
|
+
* - 3 model roles: main (coding), editor (applying), weak (commit messages)
|
|
9
|
+
* - Architect mode (2-model workflow, ~1.73x cost vs standard)
|
|
10
|
+
* - Convention files must be EXPLICITLY passed AND referenced in prompts (no auto-discovery)
|
|
11
|
+
* - 4-level config precedence: env vars > CLI args > .aider.conf.yml > defaults
|
|
12
|
+
* - Key gotcha: default auto-commit bypasses pre-commit hooks (use --git-commit-verify)
|
|
13
|
+
* - Key gotcha: exit code 0 returned even on auth failure in headless mode
|
|
14
|
+
* - Key gotcha: Playwright auto-scrapes URLs in messages (unexpected side effect)
|
|
15
|
+
*
|
|
16
|
+
* Categories: Config(8), Git Safety(10), Model Config(8), Conventions(6),
|
|
17
|
+
* Architecture(4), Security(6), CI(4), Quality(6) + M/N/O/P expansion (19)
|
|
18
|
+
*
|
|
19
|
+
* Check ID prefix: AD-
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const { containsEmbeddedSecret } = require('../secret-patterns');
|
|
23
|
+
const { attachSourceUrls } = require('../source-urls');
|
|
24
|
+
const { buildStackChecks } = require('../stack-checks');
|
|
25
|
+
const { isApiProject, isDatabaseProject, isAuthProject, isMonitoringRelevant } = require('../supplemental-checks');
|
|
26
|
+
|
|
27
|
+
const FILLER_PATTERNS = [
|
|
28
|
+
/\bbe helpful\b/i,
|
|
29
|
+
/\bbe accurate\b/i,
|
|
30
|
+
/\bbe concise\b/i,
|
|
31
|
+
/\balways do your best\b/i,
|
|
32
|
+
/\bmaintain high quality\b/i,
|
|
33
|
+
/\bwrite clean code\b/i,
|
|
34
|
+
/\bfollow best practices\b/i,
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function configContent(ctx) {
|
|
38
|
+
return ctx.configContent ? (ctx.configContent() || '') : (ctx.fileContent('.aider.conf.yml') || '');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function envContent(ctx) {
|
|
42
|
+
return ctx.envContent ? (ctx.envContent() || '') : (ctx.fileContent('.env') || '');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function modelSettingsContent(ctx) {
|
|
46
|
+
return ctx.modelSettingsContent ? (ctx.modelSettingsContent() || '') : (ctx.fileContent('.aider.model.settings.yml') || '');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function conventionFiles(ctx) {
|
|
50
|
+
return ctx.conventionFiles ? ctx.conventionFiles() : [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function conventionContent(ctx) {
|
|
54
|
+
const files = conventionFiles(ctx);
|
|
55
|
+
return files.map(f => ctx.fileContent(f) || '').join('\n');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function gitignoreContent(ctx) {
|
|
59
|
+
return ctx.gitignoreContent ? (ctx.gitignoreContent() || '') : (ctx.fileContent('.gitignore') || '');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function hasGitRepo(ctx) {
|
|
63
|
+
return ctx.hasGitRepo ? ctx.hasGitRepo() : ctx.files.includes('.git/');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function modelRoles(ctx) {
|
|
67
|
+
if (ctx.modelRoles) return ctx.modelRoles();
|
|
68
|
+
const config = ctx.parsedConfig ? ctx.parsedConfig() : { ok: false, data: null };
|
|
69
|
+
const data = config.ok ? config.data : {};
|
|
70
|
+
return {
|
|
71
|
+
main: data.model || data['main-model'] || null,
|
|
72
|
+
editor: data['editor-model'] || null,
|
|
73
|
+
weak: data['weak-model'] || null,
|
|
74
|
+
architect: data.architect || false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function firstLineMatching(text, matcher) {
|
|
79
|
+
const lines = text.split(/\r?\n/);
|
|
80
|
+
for (let index = 0; index < lines.length; index++) {
|
|
81
|
+
const line = lines[index];
|
|
82
|
+
if (typeof matcher === 'string' && line.includes(matcher)) return index + 1;
|
|
83
|
+
if (matcher instanceof RegExp && matcher.test(line)) { matcher.lastIndex = 0; return index + 1; }
|
|
84
|
+
if (typeof matcher === 'function' && matcher(line, index + 1)) return index + 1;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function findFillerLine(content) {
|
|
90
|
+
return firstLineMatching(content, (line) => FILLER_PATTERNS.some((pattern) => pattern.test(line)));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function repoLooksRegulated(ctx) {
|
|
94
|
+
const filenames = ctx.files.join('\n');
|
|
95
|
+
const packageJson = ctx.fileContent('package.json') || '';
|
|
96
|
+
const readme = ctx.fileContent('README.md') || '';
|
|
97
|
+
const combined = `${filenames}\n${packageJson}\n${readme}`;
|
|
98
|
+
return /\bhipaa\b|\bphi\b|\bpci\b|\bsoc2\b|\biso[- ]?27001\b|\bcompliance\b|\bhealth(?:care)?\b|\bmedical\b|\bbank(?:ing)?\b|\bpayments?\b|\bfintech\b/i.test(combined);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// 68 AIDER TECHNIQUES
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
const AIDER_TECHNIQUES = {
|
|
106
|
+
|
|
107
|
+
// =========================================================================
|
|
108
|
+
// A — Config (8 checks: AD-A01 .. AD-A08)
|
|
109
|
+
// =========================================================================
|
|
110
|
+
|
|
111
|
+
aiderConfYmlExists: {
|
|
112
|
+
id: 'AD-A01',
|
|
113
|
+
name: '.aider.conf.yml config file exists',
|
|
114
|
+
check: (ctx) => Boolean(ctx.fileContent('.aider.conf.yml')),
|
|
115
|
+
impact: 'critical',
|
|
116
|
+
rating: 5,
|
|
117
|
+
category: 'config',
|
|
118
|
+
fix: 'Create .aider.conf.yml with project-specific Aider settings.',
|
|
119
|
+
template: 'aider-conf-yml',
|
|
120
|
+
file: () => '.aider.conf.yml',
|
|
121
|
+
line: () => null,
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
aiderConfYmlValid: {
|
|
125
|
+
id: 'AD-A02',
|
|
126
|
+
name: '.aider.conf.yml is valid YAML',
|
|
127
|
+
check: (ctx) => {
|
|
128
|
+
const content = configContent(ctx);
|
|
129
|
+
if (!content) return null;
|
|
130
|
+
const parsed = ctx.parsedConfig ? ctx.parsedConfig() : require('./config-parser').tryParseYaml(content);
|
|
131
|
+
return parsed.ok;
|
|
132
|
+
},
|
|
133
|
+
impact: 'critical',
|
|
134
|
+
rating: 5,
|
|
135
|
+
category: 'config',
|
|
136
|
+
fix: 'Fix YAML syntax errors in .aider.conf.yml.',
|
|
137
|
+
template: 'aider-conf-yml',
|
|
138
|
+
file: () => '.aider.conf.yml',
|
|
139
|
+
line: () => null,
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
aiderModelSpecified: {
|
|
143
|
+
id: 'AD-A03',
|
|
144
|
+
name: 'Main model explicitly configured',
|
|
145
|
+
check: (ctx) => {
|
|
146
|
+
const config = configContent(ctx);
|
|
147
|
+
if (!config) return null;
|
|
148
|
+
return /\bmodel\s*:/i.test(config) || /\bmain-model\s*:/i.test(config);
|
|
149
|
+
},
|
|
150
|
+
impact: 'high',
|
|
151
|
+
rating: 4,
|
|
152
|
+
category: 'config',
|
|
153
|
+
fix: 'Set `model:` in .aider.conf.yml to pin the main coding model.',
|
|
154
|
+
template: 'aider-conf-yml',
|
|
155
|
+
file: () => '.aider.conf.yml',
|
|
156
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /\bmodel\s*:|main-model\s*:/i),
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
aiderAutoCommitsConfigured: {
|
|
160
|
+
id: 'AD-A04',
|
|
161
|
+
name: 'Auto-commits setting is explicit',
|
|
162
|
+
check: (ctx) => {
|
|
163
|
+
const config = configContent(ctx);
|
|
164
|
+
if (!config) return null;
|
|
165
|
+
return /\bauto-commits\s*:/i.test(config);
|
|
166
|
+
},
|
|
167
|
+
impact: 'high',
|
|
168
|
+
rating: 4,
|
|
169
|
+
category: 'config',
|
|
170
|
+
fix: 'Set `auto-commits: true` in .aider.conf.yml to ensure git safety.',
|
|
171
|
+
template: 'aider-conf-yml',
|
|
172
|
+
file: () => '.aider.conf.yml',
|
|
173
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /auto-commits\s*:/i),
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
aiderMapTokensConfigured: {
|
|
177
|
+
id: 'AD-A05',
|
|
178
|
+
name: 'Map tokens setting is configured',
|
|
179
|
+
check: (ctx) => {
|
|
180
|
+
const config = configContent(ctx);
|
|
181
|
+
if (!config) return null;
|
|
182
|
+
return /\bmap-tokens\s*:/i.test(config);
|
|
183
|
+
},
|
|
184
|
+
impact: 'medium',
|
|
185
|
+
rating: 3,
|
|
186
|
+
category: 'config',
|
|
187
|
+
fix: 'Set `map-tokens:` in .aider.conf.yml to control repo map size.',
|
|
188
|
+
template: 'aider-conf-yml',
|
|
189
|
+
file: () => '.aider.conf.yml',
|
|
190
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /map-tokens\s*:/i),
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
aiderLintCmdConfigured: {
|
|
194
|
+
id: 'AD-A06',
|
|
195
|
+
name: 'Lint command configured for auto-fix',
|
|
196
|
+
check: (ctx) => {
|
|
197
|
+
const config = configContent(ctx);
|
|
198
|
+
if (!config) return null;
|
|
199
|
+
return /\blint-cmd\s*:/i.test(config);
|
|
200
|
+
},
|
|
201
|
+
impact: 'high',
|
|
202
|
+
rating: 4,
|
|
203
|
+
category: 'config',
|
|
204
|
+
fix: 'Set `lint-cmd:` in .aider.conf.yml so Aider can auto-fix lint errors.',
|
|
205
|
+
template: 'aider-conf-yml',
|
|
206
|
+
file: () => '.aider.conf.yml',
|
|
207
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /lint-cmd\s*:/i),
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
aiderTestCmdConfigured: {
|
|
211
|
+
id: 'AD-A07',
|
|
212
|
+
name: 'Test command configured for auto-fix',
|
|
213
|
+
check: (ctx) => {
|
|
214
|
+
const config = configContent(ctx);
|
|
215
|
+
if (!config) return null;
|
|
216
|
+
return /\btest-cmd\s*:/i.test(config);
|
|
217
|
+
},
|
|
218
|
+
impact: 'high',
|
|
219
|
+
rating: 4,
|
|
220
|
+
category: 'config',
|
|
221
|
+
fix: 'Set `test-cmd:` in .aider.conf.yml so Aider can run and auto-fix tests.',
|
|
222
|
+
template: 'aider-conf-yml',
|
|
223
|
+
file: () => '.aider.conf.yml',
|
|
224
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /test-cmd\s*:/i),
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
aiderEditFormatConfigured: {
|
|
228
|
+
id: 'AD-A08',
|
|
229
|
+
name: 'Edit format explicitly set',
|
|
230
|
+
check: (ctx) => {
|
|
231
|
+
const config = configContent(ctx);
|
|
232
|
+
if (!config) return null;
|
|
233
|
+
return /\bedit-format\s*:/i.test(config);
|
|
234
|
+
},
|
|
235
|
+
impact: 'medium',
|
|
236
|
+
rating: 3,
|
|
237
|
+
category: 'config',
|
|
238
|
+
fix: 'Set `edit-format:` (diff, whole, udiff, diff-fenced) in .aider.conf.yml for predictable edits.',
|
|
239
|
+
template: 'aider-conf-yml',
|
|
240
|
+
file: () => '.aider.conf.yml',
|
|
241
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /edit-format\s*:/i),
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// =========================================================================
|
|
245
|
+
// B — Git Safety (8 checks: AD-B01 .. AD-B08)
|
|
246
|
+
// =========================================================================
|
|
247
|
+
|
|
248
|
+
aiderGitRepoExists: {
|
|
249
|
+
id: 'AD-B01',
|
|
250
|
+
name: 'Project is a git repository',
|
|
251
|
+
check: (ctx) => hasGitRepo(ctx),
|
|
252
|
+
impact: 'critical',
|
|
253
|
+
rating: 5,
|
|
254
|
+
category: 'git-safety',
|
|
255
|
+
fix: 'Initialize a git repo with `git init`. Aider requires git as its ONLY safety mechanism.',
|
|
256
|
+
template: null,
|
|
257
|
+
file: () => '.git',
|
|
258
|
+
line: () => null,
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
aiderAutoCommitsEnabled: {
|
|
262
|
+
id: 'AD-B02',
|
|
263
|
+
name: 'Auto-commits not disabled (git safety)',
|
|
264
|
+
check: (ctx) => {
|
|
265
|
+
const config = configContent(ctx);
|
|
266
|
+
if (!config) return null;
|
|
267
|
+
// Failing = auto-commits explicitly set to false
|
|
268
|
+
if (/\bauto-commits\s*:\s*false\b/i.test(config)) return false;
|
|
269
|
+
return true;
|
|
270
|
+
},
|
|
271
|
+
impact: 'critical',
|
|
272
|
+
rating: 5,
|
|
273
|
+
category: 'git-safety',
|
|
274
|
+
fix: 'Do not set `auto-commits: false` — git commits are Aider\'s primary safety mechanism.',
|
|
275
|
+
template: 'aider-conf-yml',
|
|
276
|
+
file: () => '.aider.conf.yml',
|
|
277
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /auto-commits\s*:\s*false/i),
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
aiderGitignoreCoversArtifacts: {
|
|
281
|
+
id: 'AD-B03',
|
|
282
|
+
name: '.gitignore includes .aider* artifacts',
|
|
283
|
+
check: (ctx) => {
|
|
284
|
+
const gi = gitignoreContent(ctx);
|
|
285
|
+
if (!gi) return false;
|
|
286
|
+
return /\.aider/i.test(gi);
|
|
287
|
+
},
|
|
288
|
+
impact: 'high',
|
|
289
|
+
rating: 4,
|
|
290
|
+
category: 'git-safety',
|
|
291
|
+
fix: 'Add `.aider*` to .gitignore to exclude chat history and cache files.',
|
|
292
|
+
template: 'gitignore',
|
|
293
|
+
file: () => '.gitignore',
|
|
294
|
+
line: (ctx) => firstLineMatching(gitignoreContent(ctx), /\.aider/i),
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
aiderDirtyTreeCheck: {
|
|
298
|
+
id: 'AD-B04',
|
|
299
|
+
name: 'No uncommitted changes when starting Aider (advisory)',
|
|
300
|
+
check: (ctx) => {
|
|
301
|
+
const status = ctx.gitStatus ? ctx.gitStatus() : null;
|
|
302
|
+
if (status === null) return null; // Can't check
|
|
303
|
+
return status === '';
|
|
304
|
+
},
|
|
305
|
+
impact: 'medium',
|
|
306
|
+
rating: 3,
|
|
307
|
+
category: 'git-safety',
|
|
308
|
+
fix: 'Commit or stash changes before running Aider so its auto-commits stay clean.',
|
|
309
|
+
template: null,
|
|
310
|
+
file: () => null,
|
|
311
|
+
line: () => null,
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
aiderDirtyCommitsNotDisabled: {
|
|
315
|
+
id: 'AD-B05',
|
|
316
|
+
name: 'Dirty-commits not disabled',
|
|
317
|
+
check: (ctx) => {
|
|
318
|
+
const config = configContent(ctx);
|
|
319
|
+
if (!config) return null;
|
|
320
|
+
if (/\bdirty-commits\s*:\s*false\b/i.test(config)) return false;
|
|
321
|
+
return true;
|
|
322
|
+
},
|
|
323
|
+
impact: 'medium',
|
|
324
|
+
rating: 3,
|
|
325
|
+
category: 'git-safety',
|
|
326
|
+
fix: 'Keep `dirty-commits` enabled (default) so Aider can commit even with dirty working tree.',
|
|
327
|
+
template: 'aider-conf-yml',
|
|
328
|
+
file: () => '.aider.conf.yml',
|
|
329
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /dirty-commits\s*:\s*false/i),
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
aiderAttributeAuthorConfigured: {
|
|
333
|
+
id: 'AD-B06',
|
|
334
|
+
name: 'Attribute author/committer set for traceability',
|
|
335
|
+
check: (ctx) => {
|
|
336
|
+
const config = configContent(ctx);
|
|
337
|
+
if (!config) return null;
|
|
338
|
+
return /\battribute-author\s*:/i.test(config) || /\battribute-committer\s*:/i.test(config);
|
|
339
|
+
},
|
|
340
|
+
impact: 'medium',
|
|
341
|
+
rating: 3,
|
|
342
|
+
category: 'git-safety',
|
|
343
|
+
fix: 'Set `attribute-author: true` or `attribute-committer: true` for AI-change traceability.',
|
|
344
|
+
template: 'aider-conf-yml',
|
|
345
|
+
file: () => '.aider.conf.yml',
|
|
346
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /attribute-(?:author|committer)\s*:/i),
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
aiderCommitPrefixConfigured: {
|
|
350
|
+
id: 'AD-B07',
|
|
351
|
+
name: 'Commit prefix set for AI-authored commits',
|
|
352
|
+
check: (ctx) => {
|
|
353
|
+
const config = configContent(ctx);
|
|
354
|
+
if (!config) return null;
|
|
355
|
+
return /\baider-commit-prefix\s*:/i.test(config) || /\bcommit-prefix\s*:/i.test(config);
|
|
356
|
+
},
|
|
357
|
+
impact: 'low',
|
|
358
|
+
rating: 2,
|
|
359
|
+
category: 'git-safety',
|
|
360
|
+
fix: 'Set `aider-commit-prefix:` to tag AI-authored commits (e.g., "aider: ").',
|
|
361
|
+
template: 'aider-conf-yml',
|
|
362
|
+
file: () => '.aider.conf.yml',
|
|
363
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /commit-prefix\s*:/i),
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
aiderUndoSafetyAware: {
|
|
367
|
+
id: 'AD-B08',
|
|
368
|
+
name: '/undo command awareness documented',
|
|
369
|
+
check: (ctx) => {
|
|
370
|
+
const conventions = conventionContent(ctx);
|
|
371
|
+
const config = configContent(ctx);
|
|
372
|
+
return /\bundo\b/i.test(conventions) || /\bundo\b/i.test(config);
|
|
373
|
+
},
|
|
374
|
+
impact: 'low',
|
|
375
|
+
rating: 2,
|
|
376
|
+
category: 'git-safety',
|
|
377
|
+
fix: 'Document the /undo command in conventions for reverting Aider changes.',
|
|
378
|
+
template: 'aider-conventions',
|
|
379
|
+
file: () => null,
|
|
380
|
+
line: () => null,
|
|
381
|
+
},
|
|
382
|
+
|
|
383
|
+
// =========================================================================
|
|
384
|
+
// C — Model Config (8 checks: AD-C01 .. AD-C08)
|
|
385
|
+
// =========================================================================
|
|
386
|
+
|
|
387
|
+
aiderEditorModelConfigured: {
|
|
388
|
+
id: 'AD-C01',
|
|
389
|
+
name: 'Editor model explicitly configured',
|
|
390
|
+
check: (ctx) => {
|
|
391
|
+
const roles = modelRoles(ctx);
|
|
392
|
+
return roles.editor !== null;
|
|
393
|
+
},
|
|
394
|
+
impact: 'high',
|
|
395
|
+
rating: 4,
|
|
396
|
+
category: 'model-config',
|
|
397
|
+
fix: 'Set `editor-model:` in .aider.conf.yml for the model that applies edits.',
|
|
398
|
+
template: 'aider-conf-yml',
|
|
399
|
+
file: () => '.aider.conf.yml',
|
|
400
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /editor-model\s*:/i),
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
aiderWeakModelConfigured: {
|
|
404
|
+
id: 'AD-C02',
|
|
405
|
+
name: 'Weak model configured for commit messages',
|
|
406
|
+
check: (ctx) => {
|
|
407
|
+
const roles = modelRoles(ctx);
|
|
408
|
+
return roles.weak !== null;
|
|
409
|
+
},
|
|
410
|
+
impact: 'medium',
|
|
411
|
+
rating: 3,
|
|
412
|
+
category: 'model-config',
|
|
413
|
+
fix: 'Set `weak-model:` in .aider.conf.yml for cheap commit message generation.',
|
|
414
|
+
template: 'aider-conf-yml',
|
|
415
|
+
file: () => '.aider.conf.yml',
|
|
416
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /weak-model\s*:/i),
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
aiderArchitectModeAvailable: {
|
|
420
|
+
id: 'AD-C03',
|
|
421
|
+
name: 'Architect mode configured (2-model workflow)',
|
|
422
|
+
check: (ctx) => {
|
|
423
|
+
const config = configContent(ctx);
|
|
424
|
+
if (!config) return null;
|
|
425
|
+
return /\barchitect\s*:\s*true\b/i.test(config);
|
|
426
|
+
},
|
|
427
|
+
impact: 'high',
|
|
428
|
+
rating: 4,
|
|
429
|
+
category: 'model-config',
|
|
430
|
+
fix: 'Set `architect: true` to use a 2-model workflow (architect plans, editor applies). NOTE: architect mode costs ~1.73x standard mode per edit ($0.00026 vs $0.00015 measured in live experiment). auto_accept_architect is on by default — no confirmation between steps.',
|
|
431
|
+
template: 'aider-conf-yml',
|
|
432
|
+
file: () => '.aider.conf.yml',
|
|
433
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /architect\s*:/i),
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
aiderModelSettingsFileExists: {
|
|
437
|
+
id: 'AD-C04',
|
|
438
|
+
name: '.aider.model.settings.yml exists for model customization',
|
|
439
|
+
check: (ctx) => Boolean(ctx.fileContent('.aider.model.settings.yml')),
|
|
440
|
+
impact: 'medium',
|
|
441
|
+
rating: 3,
|
|
442
|
+
category: 'model-config',
|
|
443
|
+
fix: 'Create .aider.model.settings.yml for custom model definitions and aliases.',
|
|
444
|
+
template: 'aider-model-settings',
|
|
445
|
+
file: () => '.aider.model.settings.yml',
|
|
446
|
+
line: () => null,
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
aiderApiKeyInEnvNotConfig: {
|
|
450
|
+
id: 'AD-C05',
|
|
451
|
+
name: 'API keys in .env, not in .aider.conf.yml',
|
|
452
|
+
check: (ctx) => {
|
|
453
|
+
const config = configContent(ctx);
|
|
454
|
+
if (!config) return null;
|
|
455
|
+
// Fail if API keys are in the YAML config instead of .env
|
|
456
|
+
return !/\b(?:api[_-]?key|openai[_-]?api[_-]?key|anthropic[_-]?api[_-]?key)\s*:/i.test(config);
|
|
457
|
+
},
|
|
458
|
+
impact: 'critical',
|
|
459
|
+
rating: 5,
|
|
460
|
+
category: 'model-config',
|
|
461
|
+
fix: 'Move API keys to .env file, not .aider.conf.yml which may be committed.',
|
|
462
|
+
template: 'aider-env',
|
|
463
|
+
file: () => '.aider.conf.yml',
|
|
464
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /api[_-]?key\s*:/i),
|
|
465
|
+
},
|
|
466
|
+
|
|
467
|
+
aiderCachePromptsEnabled: {
|
|
468
|
+
id: 'AD-C06',
|
|
469
|
+
name: 'Prompt caching enabled for cost savings',
|
|
470
|
+
check: (ctx) => {
|
|
471
|
+
const config = configContent(ctx);
|
|
472
|
+
if (!config) return null;
|
|
473
|
+
return /\bcache-prompts\s*:\s*true\b/i.test(config);
|
|
474
|
+
},
|
|
475
|
+
impact: 'medium',
|
|
476
|
+
rating: 3,
|
|
477
|
+
category: 'model-config',
|
|
478
|
+
fix: 'Set `cache-prompts: true` in .aider.conf.yml to reduce API costs.',
|
|
479
|
+
template: 'aider-conf-yml',
|
|
480
|
+
file: () => '.aider.conf.yml',
|
|
481
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /cache-prompts\s*:/i),
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
aiderMaxChatHistoryReasonable: {
|
|
485
|
+
id: 'AD-C07',
|
|
486
|
+
name: 'Max chat history tokens is bounded',
|
|
487
|
+
check: (ctx) => {
|
|
488
|
+
const config = configContent(ctx);
|
|
489
|
+
if (!config) return null;
|
|
490
|
+
const match = config.match(/\bmax-chat-history-tokens\s*:\s*(\d+)/i);
|
|
491
|
+
if (!match) return null; // Not set, using default
|
|
492
|
+
return Number.parseInt(match[1], 10) <= 32768;
|
|
493
|
+
},
|
|
494
|
+
impact: 'low',
|
|
495
|
+
rating: 2,
|
|
496
|
+
category: 'model-config',
|
|
497
|
+
fix: 'Set `max-chat-history-tokens` to a reasonable limit (e.g., 16384) to control costs.',
|
|
498
|
+
template: 'aider-conf-yml',
|
|
499
|
+
file: () => '.aider.conf.yml',
|
|
500
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /max-chat-history-tokens\s*:/i),
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
aiderStreamEnabled: {
|
|
504
|
+
id: 'AD-C08',
|
|
505
|
+
name: 'Streaming not disabled',
|
|
506
|
+
check: (ctx) => {
|
|
507
|
+
const config = configContent(ctx);
|
|
508
|
+
if (!config) return null;
|
|
509
|
+
if (/\bno-stream\s*:\s*true\b/i.test(config) || /\bstream\s*:\s*false\b/i.test(config)) return false;
|
|
510
|
+
return true;
|
|
511
|
+
},
|
|
512
|
+
impact: 'low',
|
|
513
|
+
rating: 2,
|
|
514
|
+
category: 'model-config',
|
|
515
|
+
fix: 'Keep streaming enabled for better developer experience.',
|
|
516
|
+
template: 'aider-conf-yml',
|
|
517
|
+
file: () => '.aider.conf.yml',
|
|
518
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /no-stream\s*:\s*true|stream\s*:\s*false/i),
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
// =========================================================================
|
|
522
|
+
// D — Conventions (6 checks: AD-D01 .. AD-D06)
|
|
523
|
+
// =========================================================================
|
|
524
|
+
|
|
525
|
+
aiderConventionFileExists: {
|
|
526
|
+
id: 'AD-D01',
|
|
527
|
+
name: 'Convention file exists for Aider context',
|
|
528
|
+
check: (ctx) => conventionFiles(ctx).length > 0,
|
|
529
|
+
impact: 'high',
|
|
530
|
+
rating: 4,
|
|
531
|
+
category: 'conventions',
|
|
532
|
+
fix: 'Create CONVENTIONS.md with project coding standards and pass via `read:` in .aider.conf.yml.',
|
|
533
|
+
template: 'aider-conventions',
|
|
534
|
+
file: () => 'CONVENTIONS.md',
|
|
535
|
+
line: () => null,
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
aiderConventionLinkedInConfig: {
|
|
539
|
+
id: 'AD-D02',
|
|
540
|
+
name: 'Convention file referenced in .aider.conf.yml read list',
|
|
541
|
+
check: (ctx) => {
|
|
542
|
+
const config = configContent(ctx);
|
|
543
|
+
if (!config) return null;
|
|
544
|
+
return /\bread\s*:/i.test(config);
|
|
545
|
+
},
|
|
546
|
+
impact: 'high',
|
|
547
|
+
rating: 4,
|
|
548
|
+
category: 'conventions',
|
|
549
|
+
fix: 'Add `read: [CONVENTIONS.md]` to .aider.conf.yml — Aider has NO auto-discovery. Additionally, convention files are only followed when EXPLICITLY referenced in the prompt itself (confirmed by live experiment with gpt-4o-mini). Just loading them via --read is not enough; your prompts must say "follow the conventions in CONVENTIONS.md".',
|
|
550
|
+
template: 'aider-conf-yml',
|
|
551
|
+
file: () => '.aider.conf.yml',
|
|
552
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /read\s*:/i),
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
aiderConventionHasArchitecture: {
|
|
556
|
+
id: 'AD-D03',
|
|
557
|
+
name: 'Convention file includes architecture/structure section',
|
|
558
|
+
check: (ctx) => {
|
|
559
|
+
const content = conventionContent(ctx);
|
|
560
|
+
if (!content) return null;
|
|
561
|
+
return /##\s+(?:Architecture|Structure|Project Map|Directory)/i.test(content) ||
|
|
562
|
+
/```mermaid/i.test(content);
|
|
563
|
+
},
|
|
564
|
+
impact: 'high',
|
|
565
|
+
rating: 4,
|
|
566
|
+
category: 'conventions',
|
|
567
|
+
fix: 'Add a ## Architecture section with project structure to your convention file.',
|
|
568
|
+
template: 'aider-conventions',
|
|
569
|
+
file: () => 'CONVENTIONS.md',
|
|
570
|
+
line: () => null,
|
|
571
|
+
},
|
|
572
|
+
|
|
573
|
+
aiderConventionHasVerification: {
|
|
574
|
+
id: 'AD-D04',
|
|
575
|
+
name: 'Convention file includes verification commands',
|
|
576
|
+
check: (ctx) => {
|
|
577
|
+
const content = conventionContent(ctx);
|
|
578
|
+
if (!content) return null;
|
|
579
|
+
return /\bnpm test\b|\bpnpm test\b|\byarn test\b|\bpytest\b|\bgo test\b|\bcargo test\b|\bmake test\b/i.test(content);
|
|
580
|
+
},
|
|
581
|
+
impact: 'high',
|
|
582
|
+
rating: 4,
|
|
583
|
+
category: 'conventions',
|
|
584
|
+
fix: 'Add test/lint/build commands to your convention file for Aider to use.',
|
|
585
|
+
template: 'aider-conventions',
|
|
586
|
+
file: () => 'CONVENTIONS.md',
|
|
587
|
+
line: () => null,
|
|
588
|
+
},
|
|
589
|
+
|
|
590
|
+
aiderConventionNoFiller: {
|
|
591
|
+
id: 'AD-D05',
|
|
592
|
+
name: 'Convention file has no filler/platitude lines',
|
|
593
|
+
check: (ctx) => {
|
|
594
|
+
const content = conventionContent(ctx);
|
|
595
|
+
if (!content) return null;
|
|
596
|
+
return !findFillerLine(content);
|
|
597
|
+
},
|
|
598
|
+
impact: 'medium',
|
|
599
|
+
rating: 3,
|
|
600
|
+
category: 'conventions',
|
|
601
|
+
fix: 'Remove generic filler like "be helpful" — use specific, actionable instructions.',
|
|
602
|
+
template: 'aider-conventions',
|
|
603
|
+
file: () => 'CONVENTIONS.md',
|
|
604
|
+
line: (ctx) => findFillerLine(conventionContent(ctx)),
|
|
605
|
+
},
|
|
606
|
+
|
|
607
|
+
aiderConventionReasonableSize: {
|
|
608
|
+
id: 'AD-D06',
|
|
609
|
+
name: 'Convention file not excessively large',
|
|
610
|
+
check: (ctx) => {
|
|
611
|
+
const content = conventionContent(ctx);
|
|
612
|
+
if (!content) return null;
|
|
613
|
+
return content.length < 32768;
|
|
614
|
+
},
|
|
615
|
+
impact: 'medium',
|
|
616
|
+
rating: 3,
|
|
617
|
+
category: 'conventions',
|
|
618
|
+
fix: 'Keep convention files under 32KB — large files consume context tokens.',
|
|
619
|
+
template: 'aider-conventions',
|
|
620
|
+
file: () => 'CONVENTIONS.md',
|
|
621
|
+
line: () => null,
|
|
622
|
+
},
|
|
623
|
+
|
|
624
|
+
// =========================================================================
|
|
625
|
+
// E — Architecture (4 checks: AD-E01 .. AD-E04)
|
|
626
|
+
// =========================================================================
|
|
627
|
+
|
|
628
|
+
aiderRepoMapEnabled: {
|
|
629
|
+
id: 'AD-E01',
|
|
630
|
+
name: 'Repo map not disabled',
|
|
631
|
+
check: (ctx) => {
|
|
632
|
+
const config = configContent(ctx);
|
|
633
|
+
if (!config) return null;
|
|
634
|
+
if (/\bmap-tokens\s*:\s*0\b/i.test(config) || /\bno-repo-map\s*:\s*true\b/i.test(config)) return false;
|
|
635
|
+
return true;
|
|
636
|
+
},
|
|
637
|
+
impact: 'high',
|
|
638
|
+
rating: 4,
|
|
639
|
+
category: 'architecture',
|
|
640
|
+
fix: 'Do not disable repo map — it gives Aider critical project structure awareness.',
|
|
641
|
+
template: 'aider-conf-yml',
|
|
642
|
+
file: () => '.aider.conf.yml',
|
|
643
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /map-tokens\s*:\s*0|no-repo-map\s*:\s*true/i),
|
|
644
|
+
},
|
|
645
|
+
|
|
646
|
+
aiderSubtreeUsedForLargeRepos: {
|
|
647
|
+
id: 'AD-E02',
|
|
648
|
+
name: 'Subtree-only or file filtering for large repos',
|
|
649
|
+
check: (ctx) => {
|
|
650
|
+
// Only relevant for large repos
|
|
651
|
+
if (ctx.files.length < 100) return null;
|
|
652
|
+
const config = configContent(ctx);
|
|
653
|
+
if (!config) return null;
|
|
654
|
+
return /\bsubtree-only\s*:\s*true\b/i.test(config) || /\bmap-tokens\s*:/i.test(config);
|
|
655
|
+
},
|
|
656
|
+
impact: 'medium',
|
|
657
|
+
rating: 3,
|
|
658
|
+
category: 'architecture',
|
|
659
|
+
fix: 'Use `subtree-only: true` or limit `map-tokens` for large repositories.',
|
|
660
|
+
template: 'aider-conf-yml',
|
|
661
|
+
file: () => '.aider.conf.yml',
|
|
662
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /subtree-only\s*:|map-tokens\s*:/i),
|
|
663
|
+
},
|
|
664
|
+
|
|
665
|
+
aiderAiderignoreExists: {
|
|
666
|
+
id: 'AD-E03',
|
|
667
|
+
name: '.aiderignore file exists for file filtering',
|
|
668
|
+
check: (ctx) => Boolean(ctx.fileContent('.aiderignore')),
|
|
669
|
+
impact: 'medium',
|
|
670
|
+
rating: 3,
|
|
671
|
+
category: 'architecture',
|
|
672
|
+
fix: 'Create .aiderignore to exclude files Aider should not edit (similar to .gitignore syntax).',
|
|
673
|
+
template: 'aiderignore',
|
|
674
|
+
file: () => '.aiderignore',
|
|
675
|
+
line: () => null,
|
|
676
|
+
},
|
|
677
|
+
|
|
678
|
+
aiderAutoTestEnabled: {
|
|
679
|
+
id: 'AD-E04',
|
|
680
|
+
name: 'Auto-test enabled for verification loop',
|
|
681
|
+
check: (ctx) => {
|
|
682
|
+
const config = configContent(ctx);
|
|
683
|
+
if (!config) return null;
|
|
684
|
+
return /\bauto-test\s*:\s*true\b/i.test(config);
|
|
685
|
+
},
|
|
686
|
+
impact: 'high',
|
|
687
|
+
rating: 4,
|
|
688
|
+
category: 'architecture',
|
|
689
|
+
fix: 'Set `auto-test: true` with `test-cmd` to enable automatic test verification.',
|
|
690
|
+
template: 'aider-conf-yml',
|
|
691
|
+
file: () => '.aider.conf.yml',
|
|
692
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /auto-test\s*:/i),
|
|
693
|
+
},
|
|
694
|
+
|
|
695
|
+
// =========================================================================
|
|
696
|
+
// F — Security (6 checks: AD-F01 .. AD-F06)
|
|
697
|
+
// =========================================================================
|
|
698
|
+
|
|
699
|
+
aiderEnvInGitignore: {
|
|
700
|
+
id: 'AD-F01',
|
|
701
|
+
name: '.env file excluded from git',
|
|
702
|
+
check: (ctx) => {
|
|
703
|
+
const gi = gitignoreContent(ctx);
|
|
704
|
+
if (!gi) return false;
|
|
705
|
+
return /^\.env$/m.test(gi) || /^\.env\b/m.test(gi);
|
|
706
|
+
},
|
|
707
|
+
impact: 'critical',
|
|
708
|
+
rating: 5,
|
|
709
|
+
category: 'security',
|
|
710
|
+
fix: 'Add `.env` to .gitignore to prevent API key leaks.',
|
|
711
|
+
template: 'gitignore',
|
|
712
|
+
file: () => '.gitignore',
|
|
713
|
+
line: (ctx) => firstLineMatching(gitignoreContent(ctx), /^\.env/m),
|
|
714
|
+
},
|
|
715
|
+
|
|
716
|
+
aiderNoSecretsInConfig: {
|
|
717
|
+
id: 'AD-F02',
|
|
718
|
+
name: 'No embedded secrets in .aider.conf.yml',
|
|
719
|
+
check: (ctx) => {
|
|
720
|
+
const config = configContent(ctx);
|
|
721
|
+
if (!config) return null;
|
|
722
|
+
return !containsEmbeddedSecret(config);
|
|
723
|
+
},
|
|
724
|
+
impact: 'critical',
|
|
725
|
+
rating: 5,
|
|
726
|
+
category: 'security',
|
|
727
|
+
fix: 'Remove secrets from .aider.conf.yml — use .env or environment variables instead.',
|
|
728
|
+
template: 'aider-conf-yml',
|
|
729
|
+
file: () => '.aider.conf.yml',
|
|
730
|
+
line: () => null,
|
|
731
|
+
},
|
|
732
|
+
|
|
733
|
+
aiderNoSecretsInConventions: {
|
|
734
|
+
id: 'AD-F03',
|
|
735
|
+
name: 'No embedded secrets in convention files',
|
|
736
|
+
check: (ctx) => {
|
|
737
|
+
const content = conventionContent(ctx);
|
|
738
|
+
if (!content) return null;
|
|
739
|
+
return !containsEmbeddedSecret(content);
|
|
740
|
+
},
|
|
741
|
+
impact: 'critical',
|
|
742
|
+
rating: 5,
|
|
743
|
+
category: 'security',
|
|
744
|
+
fix: 'Remove secrets from convention files.',
|
|
745
|
+
template: 'aider-conventions',
|
|
746
|
+
file: () => 'CONVENTIONS.md',
|
|
747
|
+
line: () => null,
|
|
748
|
+
},
|
|
749
|
+
|
|
750
|
+
aiderChatHistoryExcluded: {
|
|
751
|
+
id: 'AD-F04',
|
|
752
|
+
name: 'Chat history files excluded from git',
|
|
753
|
+
check: (ctx) => {
|
|
754
|
+
const gi = gitignoreContent(ctx);
|
|
755
|
+
if (!gi) return false;
|
|
756
|
+
return /\.aider\.chat\.history/i.test(gi) || /\.aider\*/i.test(gi) || /\.aider/i.test(gi);
|
|
757
|
+
},
|
|
758
|
+
impact: 'high',
|
|
759
|
+
rating: 4,
|
|
760
|
+
category: 'security',
|
|
761
|
+
fix: 'Ensure .aider.chat.history.md is gitignored — it may contain sensitive context.',
|
|
762
|
+
template: 'gitignore',
|
|
763
|
+
file: () => '.gitignore',
|
|
764
|
+
line: (ctx) => firstLineMatching(gitignoreContent(ctx), /\.aider/i),
|
|
765
|
+
},
|
|
766
|
+
|
|
767
|
+
aiderRegulatedRepoHasGuardrails: {
|
|
768
|
+
id: 'AD-F05',
|
|
769
|
+
name: 'Regulated repo has explicit guardrails in conventions',
|
|
770
|
+
check: (ctx) => {
|
|
771
|
+
if (!repoLooksRegulated(ctx)) return null;
|
|
772
|
+
const content = conventionContent(ctx);
|
|
773
|
+
if (!content) return false;
|
|
774
|
+
return /\bsecurity\b|\bcompliance\b|\breview\b|\bapproval\b/i.test(content);
|
|
775
|
+
},
|
|
776
|
+
impact: 'high',
|
|
777
|
+
rating: 4,
|
|
778
|
+
category: 'security',
|
|
779
|
+
fix: 'Add security and compliance guardrails to convention files for regulated repos.',
|
|
780
|
+
template: 'aider-conventions',
|
|
781
|
+
file: () => 'CONVENTIONS.md',
|
|
782
|
+
line: () => null,
|
|
783
|
+
},
|
|
784
|
+
|
|
785
|
+
aiderNoAutoRunInUntrusted: {
|
|
786
|
+
id: 'AD-F06',
|
|
787
|
+
name: 'Auto-run commands not enabled in untrusted context',
|
|
788
|
+
check: (ctx) => {
|
|
789
|
+
const config = configContent(ctx);
|
|
790
|
+
if (!config) return null;
|
|
791
|
+
// suggest-shell-commands with auto-run is risky
|
|
792
|
+
if (/\bauto-lint\s*:\s*true\b/i.test(config) && /\bauto-test\s*:\s*true\b/i.test(config)) {
|
|
793
|
+
// Both auto-lint and auto-test — check if commands are explicit
|
|
794
|
+
return /\blint-cmd\s*:/i.test(config) && /\btest-cmd\s*:/i.test(config);
|
|
795
|
+
}
|
|
796
|
+
return true;
|
|
797
|
+
},
|
|
798
|
+
impact: 'medium',
|
|
799
|
+
rating: 3,
|
|
800
|
+
category: 'security',
|
|
801
|
+
fix: 'When using auto-lint/auto-test, always specify explicit commands.',
|
|
802
|
+
template: 'aider-conf-yml',
|
|
803
|
+
file: () => '.aider.conf.yml',
|
|
804
|
+
line: () => null,
|
|
805
|
+
},
|
|
806
|
+
|
|
807
|
+
// =========================================================================
|
|
808
|
+
// G — CI (4 checks: AD-G01 .. AD-G04)
|
|
809
|
+
// =========================================================================
|
|
810
|
+
|
|
811
|
+
aiderCiWorkflowExists: {
|
|
812
|
+
id: 'AD-G01',
|
|
813
|
+
name: 'CI workflow exists',
|
|
814
|
+
check: (ctx) => {
|
|
815
|
+
const workflows = ctx.workflowFiles ? ctx.workflowFiles() : [];
|
|
816
|
+
return workflows.length > 0;
|
|
817
|
+
},
|
|
818
|
+
impact: 'high',
|
|
819
|
+
rating: 4,
|
|
820
|
+
category: 'ci',
|
|
821
|
+
fix: 'Add a CI workflow (.github/workflows/) to verify Aider-generated changes.',
|
|
822
|
+
template: 'aider-ci',
|
|
823
|
+
file: () => '.github/workflows/',
|
|
824
|
+
line: () => null,
|
|
825
|
+
},
|
|
826
|
+
|
|
827
|
+
aiderCiRunsTests: {
|
|
828
|
+
id: 'AD-G02',
|
|
829
|
+
name: 'CI runs tests on Aider PRs',
|
|
830
|
+
check: (ctx) => {
|
|
831
|
+
const workflows = ctx.workflowFiles ? ctx.workflowFiles() : [];
|
|
832
|
+
for (const wf of workflows) {
|
|
833
|
+
const content = ctx.fileContent(wf) || '';
|
|
834
|
+
if (/\btest\b/i.test(content) && /\bpull_request\b/i.test(content)) return true;
|
|
835
|
+
}
|
|
836
|
+
return workflows.length > 0 ? false : null;
|
|
837
|
+
},
|
|
838
|
+
impact: 'high',
|
|
839
|
+
rating: 4,
|
|
840
|
+
category: 'ci',
|
|
841
|
+
fix: 'Ensure CI runs tests on pull requests — Aider changes should be verified.',
|
|
842
|
+
template: 'aider-ci',
|
|
843
|
+
file: () => '.github/workflows/',
|
|
844
|
+
line: () => null,
|
|
845
|
+
},
|
|
846
|
+
|
|
847
|
+
aiderCiRunsLint: {
|
|
848
|
+
id: 'AD-G03',
|
|
849
|
+
name: 'CI runs linting',
|
|
850
|
+
check: (ctx) => {
|
|
851
|
+
const workflows = ctx.workflowFiles ? ctx.workflowFiles() : [];
|
|
852
|
+
for (const wf of workflows) {
|
|
853
|
+
const content = ctx.fileContent(wf) || '';
|
|
854
|
+
if (/\blint\b/i.test(content)) return true;
|
|
855
|
+
}
|
|
856
|
+
return workflows.length > 0 ? false : null;
|
|
857
|
+
},
|
|
858
|
+
impact: 'medium',
|
|
859
|
+
rating: 3,
|
|
860
|
+
category: 'ci',
|
|
861
|
+
fix: 'Add linting to CI to catch style issues in Aider-generated code.',
|
|
862
|
+
template: 'aider-ci',
|
|
863
|
+
file: () => '.github/workflows/',
|
|
864
|
+
line: () => null,
|
|
865
|
+
},
|
|
866
|
+
|
|
867
|
+
aiderGitHooksForPreCommit: {
|
|
868
|
+
id: 'AD-G04',
|
|
869
|
+
name: 'Git pre-commit hooks or CI gates for quality',
|
|
870
|
+
check: (ctx) => {
|
|
871
|
+
// Check for pre-commit config or husky
|
|
872
|
+
return Boolean(ctx.fileContent('.pre-commit-config.yaml')) ||
|
|
873
|
+
Boolean(ctx.fileContent('.husky/pre-commit')) ||
|
|
874
|
+
Boolean(ctx.fileContent('.lefthook.yml'));
|
|
875
|
+
},
|
|
876
|
+
impact: 'high',
|
|
877
|
+
rating: 4,
|
|
878
|
+
category: 'ci',
|
|
879
|
+
fix: 'Add pre-commit hooks (pre-commit, husky, lefthook). IMPORTANT: Aider default auto-commit BYPASSES pre-commit hooks (confirmed by live experiment). If hooks are critical, pass --git-commit-verify to Aider to restore hook enforcement.',
|
|
880
|
+
template: null,
|
|
881
|
+
file: () => null,
|
|
882
|
+
line: () => null,
|
|
883
|
+
},
|
|
884
|
+
|
|
885
|
+
aiderGitCommitVerify: {
|
|
886
|
+
id: 'AD-G05',
|
|
887
|
+
name: '--git-commit-verify recommended when pre-commit hooks exist',
|
|
888
|
+
check: (ctx) => {
|
|
889
|
+
// Only relevant if pre-commit hooks exist
|
|
890
|
+
const hasHooks = Boolean(ctx.fileContent('.pre-commit-config.yaml')) ||
|
|
891
|
+
Boolean(ctx.fileContent('.husky/pre-commit')) ||
|
|
892
|
+
Boolean(ctx.fileContent('.lefthook.yml'));
|
|
893
|
+
if (!hasHooks) return null;
|
|
894
|
+
const config = configContent(ctx);
|
|
895
|
+
if (!config) return false;
|
|
896
|
+
// Check if git-commit-verify is set in config or documented in conventions
|
|
897
|
+
return /git-commit-verify/i.test(config) ||
|
|
898
|
+
/git-commit-verify/i.test(conventionContent(ctx));
|
|
899
|
+
},
|
|
900
|
+
impact: 'high',
|
|
901
|
+
rating: 4,
|
|
902
|
+
category: 'ci',
|
|
903
|
+
fix: 'When pre-commit hooks exist, add --git-commit-verify to Aider invocations. Default Aider auto-commits SKIP pre-commit hooks entirely (experimentally confirmed). Without this flag, hooks that enforce security or quality checks are silently bypassed.',
|
|
904
|
+
template: 'aider-conf-yml',
|
|
905
|
+
file: () => '.aider.conf.yml',
|
|
906
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /git-commit-verify/i),
|
|
907
|
+
},
|
|
908
|
+
|
|
909
|
+
aiderCiExitCodeUnreliable: {
|
|
910
|
+
id: 'AD-G06',
|
|
911
|
+
name: 'CI scripts handle exit code 0 on auth failure (unreliable exit code)',
|
|
912
|
+
check: (ctx) => {
|
|
913
|
+
const workflows = ctx.workflowFiles ? ctx.workflowFiles() : [];
|
|
914
|
+
if (workflows.length === 0) return null;
|
|
915
|
+
// Check if any workflow mentions aider and has output checking
|
|
916
|
+
for (const wf of workflows) {
|
|
917
|
+
const content = ctx.fileContent(wf) || '';
|
|
918
|
+
if (/aider/i.test(content)) {
|
|
919
|
+
// Good if it checks output, not just exit code
|
|
920
|
+
return /grep|check.*output|--json|error.*detect/i.test(content);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return null;
|
|
924
|
+
},
|
|
925
|
+
impact: 'high',
|
|
926
|
+
rating: 4,
|
|
927
|
+
category: 'ci',
|
|
928
|
+
fix: 'Aider returns exit code 0 even on auth failure (experimentally confirmed). CI scripts that use Aider MUST NOT rely solely on exit codes to detect failure. Check Aider output for error strings or use output parsing to detect real failures.',
|
|
929
|
+
template: null,
|
|
930
|
+
file: () => '.github/workflows/',
|
|
931
|
+
line: () => null,
|
|
932
|
+
},
|
|
933
|
+
|
|
934
|
+
// =========================================================================
|
|
935
|
+
// H — Quality (6 checks: AD-H01 .. AD-H06)
|
|
936
|
+
// =========================================================================
|
|
937
|
+
|
|
938
|
+
aiderConventionHasCodingStandards: {
|
|
939
|
+
id: 'AD-H01',
|
|
940
|
+
name: 'Convention file has coding standards section',
|
|
941
|
+
check: (ctx) => {
|
|
942
|
+
const content = conventionContent(ctx);
|
|
943
|
+
if (!content) return null;
|
|
944
|
+
return /##\s+(?:Coding|Style|Standards|Formatting|Conventions)/i.test(content);
|
|
945
|
+
},
|
|
946
|
+
impact: 'high',
|
|
947
|
+
rating: 4,
|
|
948
|
+
category: 'quality',
|
|
949
|
+
fix: 'Add a ## Coding Standards section to your convention file.',
|
|
950
|
+
template: 'aider-conventions',
|
|
951
|
+
file: () => 'CONVENTIONS.md',
|
|
952
|
+
line: () => null,
|
|
953
|
+
},
|
|
954
|
+
|
|
955
|
+
aiderConventionHasErrorHandling: {
|
|
956
|
+
id: 'AD-H02',
|
|
957
|
+
name: 'Convention file covers error handling',
|
|
958
|
+
check: (ctx) => {
|
|
959
|
+
const content = conventionContent(ctx);
|
|
960
|
+
if (!content) return null;
|
|
961
|
+
return /\berror\s+handling\b|\bexception\b|\btry[- ]catch\b|\bResult\s*<\b/i.test(content);
|
|
962
|
+
},
|
|
963
|
+
impact: 'medium',
|
|
964
|
+
rating: 3,
|
|
965
|
+
category: 'quality',
|
|
966
|
+
fix: 'Document error handling patterns in your convention file.',
|
|
967
|
+
template: 'aider-conventions',
|
|
968
|
+
file: () => 'CONVENTIONS.md',
|
|
969
|
+
line: () => null,
|
|
970
|
+
},
|
|
971
|
+
|
|
972
|
+
aiderConventionHasTestingGuidelines: {
|
|
973
|
+
id: 'AD-H03',
|
|
974
|
+
name: 'Convention file covers testing guidelines',
|
|
975
|
+
check: (ctx) => {
|
|
976
|
+
const content = conventionContent(ctx);
|
|
977
|
+
if (!content) return null;
|
|
978
|
+
return /##\s+(?:Test|Testing)/i.test(content) || /\bunit test\b|\bintegration test\b|\btest coverage\b/i.test(content);
|
|
979
|
+
},
|
|
980
|
+
impact: 'high',
|
|
981
|
+
rating: 4,
|
|
982
|
+
category: 'quality',
|
|
983
|
+
fix: 'Add testing guidelines to your convention file.',
|
|
984
|
+
template: 'aider-conventions',
|
|
985
|
+
file: () => 'CONVENTIONS.md',
|
|
986
|
+
line: () => null,
|
|
987
|
+
},
|
|
988
|
+
|
|
989
|
+
aiderAutoLintEnabled: {
|
|
990
|
+
id: 'AD-H04',
|
|
991
|
+
name: 'Auto-lint enabled for code quality',
|
|
992
|
+
check: (ctx) => {
|
|
993
|
+
const config = configContent(ctx);
|
|
994
|
+
if (!config) return null;
|
|
995
|
+
return /\bauto-lint\s*:\s*true\b/i.test(config);
|
|
996
|
+
},
|
|
997
|
+
impact: 'high',
|
|
998
|
+
rating: 4,
|
|
999
|
+
category: 'quality',
|
|
1000
|
+
fix: 'Set `auto-lint: true` with `lint-cmd` to auto-fix lint errors after edits.',
|
|
1001
|
+
template: 'aider-conf-yml',
|
|
1002
|
+
file: () => '.aider.conf.yml',
|
|
1003
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /auto-lint\s*:/i),
|
|
1004
|
+
},
|
|
1005
|
+
|
|
1006
|
+
aiderShowDiffsEnabled: {
|
|
1007
|
+
id: 'AD-H05',
|
|
1008
|
+
name: 'Show-diffs enabled for review',
|
|
1009
|
+
check: (ctx) => {
|
|
1010
|
+
const config = configContent(ctx);
|
|
1011
|
+
if (!config) return null;
|
|
1012
|
+
if (/\bshow-diffs\s*:\s*false\b/i.test(config)) return false;
|
|
1013
|
+
return true; // Default is true
|
|
1014
|
+
},
|
|
1015
|
+
impact: 'medium',
|
|
1016
|
+
rating: 3,
|
|
1017
|
+
category: 'quality',
|
|
1018
|
+
fix: 'Keep `show-diffs` enabled so you can review changes before accepting.',
|
|
1019
|
+
template: 'aider-conf-yml',
|
|
1020
|
+
file: () => '.aider.conf.yml',
|
|
1021
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /show-diffs\s*:\s*false/i),
|
|
1022
|
+
},
|
|
1023
|
+
|
|
1024
|
+
aiderPrettyOutput: {
|
|
1025
|
+
id: 'AD-H06',
|
|
1026
|
+
name: 'Pretty output not disabled',
|
|
1027
|
+
check: (ctx) => {
|
|
1028
|
+
const config = configContent(ctx);
|
|
1029
|
+
if (!config) return null;
|
|
1030
|
+
if (/\bno-pretty\s*:\s*true\b/i.test(config) || /\bpretty\s*:\s*false\b/i.test(config)) return false;
|
|
1031
|
+
return true;
|
|
1032
|
+
},
|
|
1033
|
+
impact: 'low',
|
|
1034
|
+
rating: 2,
|
|
1035
|
+
category: 'quality',
|
|
1036
|
+
fix: 'Keep pretty output enabled for better readability.',
|
|
1037
|
+
template: 'aider-conf-yml',
|
|
1038
|
+
file: () => '.aider.conf.yml',
|
|
1039
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /no-pretty\s*:\s*true|pretty\s*:\s*false/i),
|
|
1040
|
+
},
|
|
1041
|
+
|
|
1042
|
+
// =========================================================================
|
|
1043
|
+
// M — Advanced Config (4 checks: AD-M01 .. AD-M04)
|
|
1044
|
+
// =========================================================================
|
|
1045
|
+
|
|
1046
|
+
aiderEnvFileExists: {
|
|
1047
|
+
id: 'AD-M01',
|
|
1048
|
+
name: '.env file exists with API configuration',
|
|
1049
|
+
check: (ctx) => Boolean(ctx.fileContent('.env')),
|
|
1050
|
+
impact: 'high',
|
|
1051
|
+
rating: 4,
|
|
1052
|
+
category: 'advanced-config',
|
|
1053
|
+
fix: 'Create .env with OPENAI_API_KEY or ANTHROPIC_API_KEY for Aider.',
|
|
1054
|
+
template: 'aider-env',
|
|
1055
|
+
file: () => '.env',
|
|
1056
|
+
line: () => null,
|
|
1057
|
+
},
|
|
1058
|
+
|
|
1059
|
+
aiderEnvHasApiKey: {
|
|
1060
|
+
id: 'AD-M02',
|
|
1061
|
+
name: '.env contains at least one API key',
|
|
1062
|
+
check: (ctx) => {
|
|
1063
|
+
const env = envContent(ctx);
|
|
1064
|
+
if (!env) return null;
|
|
1065
|
+
return /\b(?:OPENAI_API_KEY|ANTHROPIC_API_KEY|OPENROUTER_API_KEY|DEEPSEEK_API_KEY)\s*=/i.test(env);
|
|
1066
|
+
},
|
|
1067
|
+
impact: 'high',
|
|
1068
|
+
rating: 4,
|
|
1069
|
+
category: 'advanced-config',
|
|
1070
|
+
fix: 'Add an API key (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) to .env.',
|
|
1071
|
+
template: 'aider-env',
|
|
1072
|
+
file: () => '.env',
|
|
1073
|
+
line: () => null,
|
|
1074
|
+
},
|
|
1075
|
+
|
|
1076
|
+
aiderYesAlwaysNotSet: {
|
|
1077
|
+
id: 'AD-M03',
|
|
1078
|
+
name: '--yes-always not set as default (safety)',
|
|
1079
|
+
check: (ctx) => {
|
|
1080
|
+
const config = configContent(ctx);
|
|
1081
|
+
if (!config) return null;
|
|
1082
|
+
return !/\byes-always\s*:\s*true\b/i.test(config);
|
|
1083
|
+
},
|
|
1084
|
+
impact: 'high',
|
|
1085
|
+
rating: 4,
|
|
1086
|
+
category: 'advanced-config',
|
|
1087
|
+
fix: 'Do not set `yes-always: true` in config — it bypasses all confirmation prompts.',
|
|
1088
|
+
template: 'aider-conf-yml',
|
|
1089
|
+
file: () => '.aider.conf.yml',
|
|
1090
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /yes-always\s*:\s*true/i),
|
|
1091
|
+
},
|
|
1092
|
+
|
|
1093
|
+
aiderVerboseNotDefault: {
|
|
1094
|
+
id: 'AD-M04',
|
|
1095
|
+
name: 'Verbose mode not enabled by default',
|
|
1096
|
+
check: (ctx) => {
|
|
1097
|
+
const config = configContent(ctx);
|
|
1098
|
+
if (!config) return null;
|
|
1099
|
+
return !/\bverbose\s*:\s*true\b/i.test(config);
|
|
1100
|
+
},
|
|
1101
|
+
impact: 'low',
|
|
1102
|
+
rating: 2,
|
|
1103
|
+
category: 'advanced-config',
|
|
1104
|
+
fix: 'Do not default `verbose: true` in config — use --verbose as a CLI flag when needed.',
|
|
1105
|
+
template: 'aider-conf-yml',
|
|
1106
|
+
file: () => '.aider.conf.yml',
|
|
1107
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /verbose\s*:\s*true/i),
|
|
1108
|
+
},
|
|
1109
|
+
|
|
1110
|
+
// =========================================================================
|
|
1111
|
+
// N — Workflow Patterns (4 checks: AD-N01 .. AD-N04)
|
|
1112
|
+
// =========================================================================
|
|
1113
|
+
|
|
1114
|
+
aiderLintAndTestLoop: {
|
|
1115
|
+
id: 'AD-N01',
|
|
1116
|
+
name: 'Lint-and-test loop configured (lint-cmd + test-cmd + auto-lint + auto-test)',
|
|
1117
|
+
check: (ctx) => {
|
|
1118
|
+
const config = configContent(ctx);
|
|
1119
|
+
if (!config) return null;
|
|
1120
|
+
const hasLint = /\blint-cmd\s*:/i.test(config) && /\bauto-lint\s*:\s*true\b/i.test(config);
|
|
1121
|
+
const hasTest = /\btest-cmd\s*:/i.test(config) && /\bauto-test\s*:\s*true\b/i.test(config);
|
|
1122
|
+
return hasLint && hasTest;
|
|
1123
|
+
},
|
|
1124
|
+
impact: 'high',
|
|
1125
|
+
rating: 4,
|
|
1126
|
+
category: 'workflow-patterns',
|
|
1127
|
+
fix: 'Configure the full lint-and-test loop: lint-cmd + auto-lint + test-cmd + auto-test.',
|
|
1128
|
+
template: 'aider-conf-yml',
|
|
1129
|
+
file: () => '.aider.conf.yml',
|
|
1130
|
+
line: () => null,
|
|
1131
|
+
},
|
|
1132
|
+
|
|
1133
|
+
aiderBrowserModeForDocs: {
|
|
1134
|
+
id: 'AD-N02',
|
|
1135
|
+
name: 'Browser integration known (/web command)',
|
|
1136
|
+
check: (ctx) => {
|
|
1137
|
+
const content = conventionContent(ctx);
|
|
1138
|
+
return /\b\/web\b|\bbrowser\b/i.test(content);
|
|
1139
|
+
},
|
|
1140
|
+
impact: 'low',
|
|
1141
|
+
rating: 2,
|
|
1142
|
+
category: 'workflow-patterns',
|
|
1143
|
+
fix: 'Document the /web command in conventions for pulling in documentation.',
|
|
1144
|
+
template: 'aider-conventions',
|
|
1145
|
+
file: () => 'CONVENTIONS.md',
|
|
1146
|
+
line: () => null,
|
|
1147
|
+
},
|
|
1148
|
+
|
|
1149
|
+
aiderInChatCommandsDocumented: {
|
|
1150
|
+
id: 'AD-N03',
|
|
1151
|
+
name: 'Key in-chat commands documented in conventions',
|
|
1152
|
+
check: (ctx) => {
|
|
1153
|
+
const content = conventionContent(ctx);
|
|
1154
|
+
if (!content) return null;
|
|
1155
|
+
// Check for documentation of key commands
|
|
1156
|
+
const commands = ['/add', '/drop', '/run', '/test', '/undo'];
|
|
1157
|
+
const found = commands.filter(cmd => content.includes(cmd));
|
|
1158
|
+
return found.length >= 2;
|
|
1159
|
+
},
|
|
1160
|
+
impact: 'medium',
|
|
1161
|
+
rating: 3,
|
|
1162
|
+
category: 'workflow-patterns',
|
|
1163
|
+
fix: 'Document key Aider commands (/add, /drop, /run, /test, /undo) in conventions.',
|
|
1164
|
+
template: 'aider-conventions',
|
|
1165
|
+
file: () => 'CONVENTIONS.md',
|
|
1166
|
+
line: () => null,
|
|
1167
|
+
},
|
|
1168
|
+
|
|
1169
|
+
aiderVoiceModeAware: {
|
|
1170
|
+
id: 'AD-N04',
|
|
1171
|
+
name: 'Voice mode configuration known',
|
|
1172
|
+
check: (ctx) => {
|
|
1173
|
+
const config = configContent(ctx);
|
|
1174
|
+
if (!config) return null;
|
|
1175
|
+
return /\bvoice-language\s*:/i.test(config) || /\bvoice\b/i.test(conventionContent(ctx));
|
|
1176
|
+
},
|
|
1177
|
+
impact: 'low',
|
|
1178
|
+
rating: 2,
|
|
1179
|
+
category: 'workflow-patterns',
|
|
1180
|
+
fix: 'Optionally configure `voice-language:` for voice coding sessions.',
|
|
1181
|
+
template: 'aider-conf-yml',
|
|
1182
|
+
file: () => '.aider.conf.yml',
|
|
1183
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /voice-language\s*:/i),
|
|
1184
|
+
},
|
|
1185
|
+
|
|
1186
|
+
aiderPlaywrightUrlScraping: {
|
|
1187
|
+
id: 'AD-N05',
|
|
1188
|
+
name: 'Playwright URL auto-scraping side effect is expected',
|
|
1189
|
+
check: (ctx) => {
|
|
1190
|
+
const conventions = conventionContent(ctx);
|
|
1191
|
+
const config = configContent(ctx);
|
|
1192
|
+
// Check if team is aware of the Playwright auto-scraping behavior
|
|
1193
|
+
return /playwright|url.*scrap|scrape.*url|auto.*fetch|web.*fetch/i.test(conventions) ||
|
|
1194
|
+
/playwright|url.*scrap/i.test(config);
|
|
1195
|
+
},
|
|
1196
|
+
impact: 'medium',
|
|
1197
|
+
rating: 3,
|
|
1198
|
+
category: 'workflow-patterns',
|
|
1199
|
+
fix: 'Aider automatically scrapes URLs found in messages using Playwright (experimentally confirmed side effect). This causes unexpected network requests and delays. Document this in conventions, and avoid putting real URLs in messages unless scraping is intentional.',
|
|
1200
|
+
template: 'aider-conventions',
|
|
1201
|
+
file: () => 'CONVENTIONS.md',
|
|
1202
|
+
line: () => null,
|
|
1203
|
+
},
|
|
1204
|
+
|
|
1205
|
+
// =========================================================================
|
|
1206
|
+
// O — Editor Integration (4 checks: AD-O01 .. AD-O04)
|
|
1207
|
+
// =========================================================================
|
|
1208
|
+
|
|
1209
|
+
aiderEditorIntegrationDocumented: {
|
|
1210
|
+
id: 'AD-O01',
|
|
1211
|
+
name: 'Editor integration documented (VS Code, NeoVim, etc.)',
|
|
1212
|
+
check: (ctx) => {
|
|
1213
|
+
const content = conventionContent(ctx);
|
|
1214
|
+
if (!content) return null;
|
|
1215
|
+
return /\bvs\s*code\b|\bneovim\b|\bvim\b|\beditor\b/i.test(content);
|
|
1216
|
+
},
|
|
1217
|
+
impact: 'low',
|
|
1218
|
+
rating: 2,
|
|
1219
|
+
category: 'editor-integration',
|
|
1220
|
+
fix: 'Document editor integration (VS Code extension, NeoVim plugin) in conventions.',
|
|
1221
|
+
template: 'aider-conventions',
|
|
1222
|
+
file: () => 'CONVENTIONS.md',
|
|
1223
|
+
line: () => null,
|
|
1224
|
+
},
|
|
1225
|
+
|
|
1226
|
+
aiderWatchModeKnown: {
|
|
1227
|
+
id: 'AD-O02',
|
|
1228
|
+
name: 'Watch mode (--watch-files) documented or configured',
|
|
1229
|
+
check: (ctx) => {
|
|
1230
|
+
const config = configContent(ctx);
|
|
1231
|
+
if (!config) return null;
|
|
1232
|
+
return /\bwatch-files\s*:/i.test(config) || /\bwatch\b/i.test(conventionContent(ctx));
|
|
1233
|
+
},
|
|
1234
|
+
impact: 'medium',
|
|
1235
|
+
rating: 3,
|
|
1236
|
+
category: 'editor-integration',
|
|
1237
|
+
fix: 'Consider `watch-files: true` for automatic file change detection.',
|
|
1238
|
+
template: 'aider-conf-yml',
|
|
1239
|
+
file: () => '.aider.conf.yml',
|
|
1240
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /watch-files\s*:/i),
|
|
1241
|
+
},
|
|
1242
|
+
|
|
1243
|
+
aiderDarkModeConfigured: {
|
|
1244
|
+
id: 'AD-O03',
|
|
1245
|
+
name: 'Theme/dark mode configured for terminal',
|
|
1246
|
+
check: (ctx) => {
|
|
1247
|
+
const config = configContent(ctx);
|
|
1248
|
+
if (!config) return null;
|
|
1249
|
+
return /\bdark-mode\s*:/i.test(config) || /\blight-mode\s*:/i.test(config);
|
|
1250
|
+
},
|
|
1251
|
+
impact: 'low',
|
|
1252
|
+
rating: 1,
|
|
1253
|
+
category: 'editor-integration',
|
|
1254
|
+
fix: 'Set `dark-mode: true` or `light-mode: true` for terminal readability.',
|
|
1255
|
+
template: 'aider-conf-yml',
|
|
1256
|
+
file: () => '.aider.conf.yml',
|
|
1257
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /dark-mode\s*:|light-mode\s*:/i),
|
|
1258
|
+
},
|
|
1259
|
+
|
|
1260
|
+
aiderInputHistoryExcluded: {
|
|
1261
|
+
id: 'AD-O04',
|
|
1262
|
+
name: 'Input history file excluded from git',
|
|
1263
|
+
check: (ctx) => {
|
|
1264
|
+
const gi = gitignoreContent(ctx);
|
|
1265
|
+
if (!gi) return false;
|
|
1266
|
+
return /\.aider\.input\.history/i.test(gi) || /\.aider\*/i.test(gi) || /\.aider/i.test(gi);
|
|
1267
|
+
},
|
|
1268
|
+
impact: 'medium',
|
|
1269
|
+
rating: 3,
|
|
1270
|
+
category: 'editor-integration',
|
|
1271
|
+
fix: 'Ensure .aider.input.history is gitignored.',
|
|
1272
|
+
template: 'gitignore',
|
|
1273
|
+
file: () => '.gitignore',
|
|
1274
|
+
line: (ctx) => firstLineMatching(gitignoreContent(ctx), /\.aider/i),
|
|
1275
|
+
},
|
|
1276
|
+
|
|
1277
|
+
// =========================================================================
|
|
1278
|
+
// P — Release Readiness (3 checks: AD-P01 .. AD-P03)
|
|
1279
|
+
// =========================================================================
|
|
1280
|
+
|
|
1281
|
+
aiderVersionPinned: {
|
|
1282
|
+
id: 'AD-P01',
|
|
1283
|
+
name: 'Aider version pinned in requirements or package manager',
|
|
1284
|
+
check: (ctx) => {
|
|
1285
|
+
const req = ctx.fileContent('requirements.txt') || '';
|
|
1286
|
+
const pipfile = ctx.fileContent('Pipfile') || '';
|
|
1287
|
+
const pyproject = ctx.fileContent('pyproject.toml') || '';
|
|
1288
|
+
return /\baider-chat\b/i.test(req) || /\baider-chat\b/i.test(pipfile) || /\baider-chat\b/i.test(pyproject);
|
|
1289
|
+
},
|
|
1290
|
+
impact: 'medium',
|
|
1291
|
+
rating: 3,
|
|
1292
|
+
category: 'release-readiness',
|
|
1293
|
+
fix: 'Pin `aider-chat` version in requirements.txt or pyproject.toml.',
|
|
1294
|
+
template: null,
|
|
1295
|
+
file: () => null,
|
|
1296
|
+
line: () => null,
|
|
1297
|
+
},
|
|
1298
|
+
|
|
1299
|
+
aiderAllConfigSurfacesPresent: {
|
|
1300
|
+
id: 'AD-P02',
|
|
1301
|
+
name: 'All essential Aider config surfaces present',
|
|
1302
|
+
check: (ctx) => {
|
|
1303
|
+
const hasConf = Boolean(ctx.fileContent('.aider.conf.yml'));
|
|
1304
|
+
const hasEnv = Boolean(ctx.fileContent('.env'));
|
|
1305
|
+
const hasGitignore = Boolean(ctx.fileContent('.gitignore'));
|
|
1306
|
+
return hasConf && hasEnv && hasGitignore;
|
|
1307
|
+
},
|
|
1308
|
+
impact: 'high',
|
|
1309
|
+
rating: 4,
|
|
1310
|
+
category: 'release-readiness',
|
|
1311
|
+
fix: 'Ensure .aider.conf.yml, .env, and .gitignore all exist.',
|
|
1312
|
+
template: null,
|
|
1313
|
+
file: () => null,
|
|
1314
|
+
line: () => null,
|
|
1315
|
+
},
|
|
1316
|
+
|
|
1317
|
+
aiderDocumentedWorkflow: {
|
|
1318
|
+
id: 'AD-P03',
|
|
1319
|
+
name: 'Aider workflow documented in README or conventions',
|
|
1320
|
+
check: (ctx) => {
|
|
1321
|
+
const readme = ctx.fileContent('README.md') || '';
|
|
1322
|
+
const content = conventionContent(ctx);
|
|
1323
|
+
return /\baider\b/i.test(readme) || /\bworkflow\b/i.test(content);
|
|
1324
|
+
},
|
|
1325
|
+
impact: 'medium',
|
|
1326
|
+
rating: 3,
|
|
1327
|
+
category: 'release-readiness',
|
|
1328
|
+
fix: 'Document Aider workflow in README.md or convention files.',
|
|
1329
|
+
template: 'aider-conventions',
|
|
1330
|
+
file: () => 'README.md',
|
|
1331
|
+
line: () => null,
|
|
1332
|
+
},
|
|
1333
|
+
|
|
1334
|
+
aiderNoConflictingPlatformConfigs: {
|
|
1335
|
+
id: 'AD-P04',
|
|
1336
|
+
name: 'No conflicting platform configs (CLAUDE.md, AGENTS.md) without awareness',
|
|
1337
|
+
check: (ctx) => {
|
|
1338
|
+
const hasAider = Boolean(ctx.fileContent('.aider.conf.yml'));
|
|
1339
|
+
const hasClaude = Boolean(ctx.fileContent('CLAUDE.md')) || Boolean(ctx.fileContent('.claude/CLAUDE.md'));
|
|
1340
|
+
const hasCodex = Boolean(ctx.fileContent('AGENTS.md'));
|
|
1341
|
+
if (!hasAider) return null;
|
|
1342
|
+
// Multi-platform is fine — just check conventions mention it
|
|
1343
|
+
if (hasClaude || hasCodex) {
|
|
1344
|
+
const content = conventionContent(ctx);
|
|
1345
|
+
return /\bmulti[- ]?platform\b|\bclaude\b|\bcodex\b/i.test(content);
|
|
1346
|
+
}
|
|
1347
|
+
return true;
|
|
1348
|
+
},
|
|
1349
|
+
impact: 'medium',
|
|
1350
|
+
rating: 3,
|
|
1351
|
+
category: 'release-readiness',
|
|
1352
|
+
fix: 'If using multiple AI platforms, document the multi-platform strategy in conventions.',
|
|
1353
|
+
template: 'aider-conventions',
|
|
1354
|
+
file: () => 'CONVENTIONS.md',
|
|
1355
|
+
line: () => null,
|
|
1356
|
+
},
|
|
1357
|
+
|
|
1358
|
+
aiderModelCostAwareness: {
|
|
1359
|
+
id: 'AD-P05',
|
|
1360
|
+
name: 'Model cost awareness configured (cache-prompts or explicit model selection)',
|
|
1361
|
+
check: (ctx) => {
|
|
1362
|
+
const config = configContent(ctx);
|
|
1363
|
+
if (!config) return null;
|
|
1364
|
+
return /\bcache-prompts\s*:\s*true\b/i.test(config) ||
|
|
1365
|
+
/\bweak-model\s*:/i.test(config) ||
|
|
1366
|
+
/\beditor-model\s*:/i.test(config);
|
|
1367
|
+
},
|
|
1368
|
+
impact: 'medium',
|
|
1369
|
+
rating: 3,
|
|
1370
|
+
category: 'release-readiness',
|
|
1371
|
+
fix: 'Enable prompt caching or configure separate weak/editor models for cost optimization. Cost reference (measured): standard edit ~$0.00015, architect mode edit ~$0.00026 (~1.73x). Set cache-prompts: true for repeated context, and weak-model for commit messages to reduce costs.',
|
|
1372
|
+
template: 'aider-conf-yml',
|
|
1373
|
+
file: () => '.aider.conf.yml',
|
|
1374
|
+
line: (ctx) => firstLineMatching(configContent(ctx), /cache-prompts\s*:|weak-model\s*:|editor-model\s*:/i),
|
|
1375
|
+
},
|
|
1376
|
+
|
|
1377
|
+
aiderGitBranchStrategy: {
|
|
1378
|
+
id: 'AD-P06',
|
|
1379
|
+
name: 'Git branch strategy for Aider work',
|
|
1380
|
+
check: (ctx) => {
|
|
1381
|
+
const content = conventionContent(ctx);
|
|
1382
|
+
if (!content) return null;
|
|
1383
|
+
return /\bbranch\b/i.test(content) && /\baider\b/i.test(content);
|
|
1384
|
+
},
|
|
1385
|
+
impact: 'medium',
|
|
1386
|
+
rating: 3,
|
|
1387
|
+
category: 'release-readiness',
|
|
1388
|
+
fix: 'Document a branch strategy for Aider work (e.g., feature branches, PR workflow).',
|
|
1389
|
+
template: 'aider-conventions',
|
|
1390
|
+
file: () => 'CONVENTIONS.md',
|
|
1391
|
+
line: () => null,
|
|
1392
|
+
},
|
|
1393
|
+
|
|
1394
|
+
// =============================================
|
|
1395
|
+
// T. Cross-Cutting Engineering (48 checks) — AD-T01..AD-T48
|
|
1396
|
+
// =============================================
|
|
1397
|
+
|
|
1398
|
+
aiderTestFrameworkDetected: {
|
|
1399
|
+
id: 'AD-T01', name: 'Test framework detected in project',
|
|
1400
|
+
check: (ctx) => { const p = ctx.fileContent('package.json'); if (!p) return null; return /jest|vitest|mocha|jasmine|pytest/i.test(p) || ctx.files.some(f => /pytest|spec_helper|test_helper/i.test(f)); },
|
|
1401
|
+
impact: 'high', rating: 4, category: 'testing-strategy',
|
|
1402
|
+
fix: 'Add a test framework and document the test command in .aider.conf.yml `test-cmd:`.',
|
|
1403
|
+
template: null, file: () => 'package.json', line: () => null,
|
|
1404
|
+
},
|
|
1405
|
+
aiderCoverageConfigExists: {
|
|
1406
|
+
id: 'AD-T02', name: 'Coverage configuration exists',
|
|
1407
|
+
check: (ctx) => ctx.files.some(f => /\.nycrc|\.c8rc|jest\.config|vitest\.config|\.coveragerc/i.test(f)) || /coverage/i.test(ctx.fileContent('package.json') || ''),
|
|
1408
|
+
impact: 'medium', rating: 3, category: 'testing-strategy',
|
|
1409
|
+
fix: 'Add coverage configuration and set coverage thresholds in your test-cmd.',
|
|
1410
|
+
template: null, file: () => null, line: () => null,
|
|
1411
|
+
},
|
|
1412
|
+
aiderE2eSetupPresent: {
|
|
1413
|
+
id: 'AD-T03', name: 'E2E test setup present',
|
|
1414
|
+
check: (ctx) => ctx.files.some(f => /cypress\.config|playwright\.config|e2e\.(test|spec)\.(ts|js)/i.test(f)),
|
|
1415
|
+
impact: 'medium', rating: 3, category: 'testing-strategy',
|
|
1416
|
+
fix: 'Set up E2E tests (Playwright/Cypress) for integration-level coverage.',
|
|
1417
|
+
template: null, file: () => null, line: () => null,
|
|
1418
|
+
},
|
|
1419
|
+
aiderSnapshotTestsMentioned: {
|
|
1420
|
+
id: 'AD-T04', name: 'Snapshot testing strategy documented in conventions',
|
|
1421
|
+
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /snapshot/i.test(conv); },
|
|
1422
|
+
impact: 'low', rating: 2, category: 'testing-strategy',
|
|
1423
|
+
fix: 'Document snapshot testing strategy in CONVENTIONS.md for Aider.',
|
|
1424
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1425
|
+
},
|
|
1426
|
+
aiderTestCommandDocumented: {
|
|
1427
|
+
id: 'AD-T05', name: 'Test command configured in .aider.conf.yml',
|
|
1428
|
+
check: (ctx) => { const config = configContent(ctx); if (!config) return null; return /\btest-cmd\s*:/i.test(config); },
|
|
1429
|
+
impact: 'high', rating: 4, category: 'testing-strategy',
|
|
1430
|
+
fix: 'Set `test-cmd:` in .aider.conf.yml so Aider can auto-verify changes.',
|
|
1431
|
+
template: 'aider-conf-yml', file: () => '.aider.conf.yml', line: () => null,
|
|
1432
|
+
},
|
|
1433
|
+
aiderCiRunsTests: {
|
|
1434
|
+
id: 'AD-T06', name: 'CI workflow runs tests',
|
|
1435
|
+
check: (ctx) => { const wfs = ctx.files.filter(f => /\.github\/workflows\/.*\.ya?ml$/i.test(f)); if (!wfs.length) return null; return wfs.some(f => /\btest\b|\bpytest\b|\bvitest\b|\bjest\b/i.test(ctx.fileContent(f) || '')); },
|
|
1436
|
+
impact: 'high', rating: 5, category: 'testing-strategy',
|
|
1437
|
+
fix: 'Add a test step to CI to verify Aider-generated changes.',
|
|
1438
|
+
template: null, file: () => '.github/workflows/', line: () => null,
|
|
1439
|
+
},
|
|
1440
|
+
aiderLinterConfigured: {
|
|
1441
|
+
id: 'AD-T07', name: 'Linter configured',
|
|
1442
|
+
check: (ctx) => ctx.files.some(f => /\.eslintrc|eslint\.config|\.pylintrc|\.ruff\.toml|\.flake8/i.test(f)) || /eslint|ruff|pylint/i.test(ctx.fileContent('package.json') || '') || /\blint-cmd\s*:/i.test(configContent(ctx)),
|
|
1443
|
+
impact: 'high', rating: 4, category: 'code-quality',
|
|
1444
|
+
fix: 'Configure a linter and set `lint-cmd:` in .aider.conf.yml for auto-linting.',
|
|
1445
|
+
template: 'aider-conf-yml', file: () => '.aider.conf.yml', line: () => null,
|
|
1446
|
+
},
|
|
1447
|
+
aiderFormatterConfigured: {
|
|
1448
|
+
id: 'AD-T08', name: 'Code formatter configured',
|
|
1449
|
+
check: (ctx) => ctx.files.some(f => /\.prettierrc|biome\.json|\.editorconfig/i.test(f)) || /prettier|biome/i.test(ctx.fileContent('package.json') || ''),
|
|
1450
|
+
impact: 'medium', rating: 3, category: 'code-quality',
|
|
1451
|
+
fix: 'Configure Prettier/Biome and add formatting to your lint-cmd chain.',
|
|
1452
|
+
template: null, file: () => null, line: () => null,
|
|
1453
|
+
},
|
|
1454
|
+
aiderDeadCodeDetection: {
|
|
1455
|
+
id: 'AD-T09', name: 'Dead code detection documented in conventions',
|
|
1456
|
+
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /dead.?code|unused.?(import|var)|knip|ts-prune/i.test(conv); },
|
|
1457
|
+
impact: 'low', rating: 2, category: 'code-quality',
|
|
1458
|
+
fix: 'Document dead code detection in CONVENTIONS.md (knip, ts-prune).',
|
|
1459
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1460
|
+
},
|
|
1461
|
+
aiderComplexityAwareness: {
|
|
1462
|
+
id: 'AD-T10', name: 'Code complexity constraints in conventions',
|
|
1463
|
+
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /complex|cyclomatic|function.{0,20}length|line.{0,20}limit/i.test(conv); },
|
|
1464
|
+
impact: 'medium', rating: 3, category: 'code-quality',
|
|
1465
|
+
fix: 'Add complexity constraints to CONVENTIONS.md for Aider to follow.',
|
|
1466
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1467
|
+
},
|
|
1468
|
+
aiderConsistentNamingDocumented: {
|
|
1469
|
+
id: 'AD-T11', name: 'Naming conventions documented for Aider',
|
|
1470
|
+
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /camelCase|snake_case|PascalCase|naming.{0,30}convention/i.test(conv); },
|
|
1471
|
+
impact: 'medium', rating: 3, category: 'code-quality',
|
|
1472
|
+
fix: 'Add naming convention rules to CONVENTIONS.md.',
|
|
1473
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1474
|
+
},
|
|
1475
|
+
aiderCodeReviewProcessMentioned: {
|
|
1476
|
+
id: 'AD-T12', name: 'Code review process documented',
|
|
1477
|
+
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('CONTRIBUTING.md') || ''); if (!docs.trim()) return null; return /code.?review|PR|pull.?request/i.test(docs); },
|
|
1478
|
+
impact: 'medium', rating: 3, category: 'code-quality',
|
|
1479
|
+
fix: 'Document code review process in CONTRIBUTING.md.',
|
|
1480
|
+
template: null, file: () => 'CONTRIBUTING.md', line: () => null,
|
|
1481
|
+
},
|
|
1482
|
+
aiderEndpointDocumentation: {
|
|
1483
|
+
id: 'AD-T13', name: 'API endpoint documentation present',
|
|
1484
|
+
check: (ctx) => !isApiProject(ctx) ? null : ctx.files.some(f => /openapi|swagger|api\.ya?ml|api\.json/i.test(f)),
|
|
1485
|
+
impact: 'medium', rating: 3, category: 'api-design',
|
|
1486
|
+
fix: 'Add an OpenAPI/Swagger spec for Aider to understand the API surface.',
|
|
1487
|
+
template: null, file: () => null, line: () => null,
|
|
1488
|
+
},
|
|
1489
|
+
aiderApiVersioningMentioned: {
|
|
1490
|
+
id: 'AD-T14', name: 'API versioning strategy documented',
|
|
1491
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /api.{0,10}version|\/v\d|versioning/i.test(conv); },
|
|
1492
|
+
impact: 'medium', rating: 3, category: 'api-design',
|
|
1493
|
+
fix: 'Document API versioning in CONVENTIONS.md.',
|
|
1494
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1495
|
+
},
|
|
1496
|
+
aiderErrorHandlingPatterns: {
|
|
1497
|
+
id: 'AD-T15', name: 'Error handling patterns in conventions',
|
|
1498
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /error.{0,15}handl|exception|try.?catch|Result\s*</i.test(conv); },
|
|
1499
|
+
impact: 'high', rating: 4, category: 'api-design',
|
|
1500
|
+
fix: 'Add error handling patterns to CONVENTIONS.md for Aider.',
|
|
1501
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1502
|
+
},
|
|
1503
|
+
aiderRateLimitingAwareness: {
|
|
1504
|
+
id: 'AD-T16', name: 'Rate limiting awareness documented',
|
|
1505
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /rate.?limit|throttl|429/i.test(docs); },
|
|
1506
|
+
impact: 'medium', rating: 3, category: 'api-design',
|
|
1507
|
+
fix: 'Document rate limiting in CONVENTIONS.md.',
|
|
1508
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1509
|
+
},
|
|
1510
|
+
aiderRequestValidation: {
|
|
1511
|
+
id: 'AD-T17', name: 'Request validation strategy documented',
|
|
1512
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /validat|zod|yup|joi\b/i.test(conv); },
|
|
1513
|
+
impact: 'high', rating: 4, category: 'api-design',
|
|
1514
|
+
fix: 'Document request validation (Zod, Yup) in CONVENTIONS.md.',
|
|
1515
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1516
|
+
},
|
|
1517
|
+
aiderResponseFormatConsistent: {
|
|
1518
|
+
id: 'AD-T18', name: 'Response format consistency documented',
|
|
1519
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /response.{0,20}format|json.{0,10}response|envelope/i.test(conv); },
|
|
1520
|
+
impact: 'medium', rating: 3, category: 'api-design',
|
|
1521
|
+
fix: 'Document standard response format in CONVENTIONS.md.',
|
|
1522
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1523
|
+
},
|
|
1524
|
+
aiderMigrationStrategyDocumented: {
|
|
1525
|
+
id: 'AD-T19', name: 'Database migration strategy documented',
|
|
1526
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /migrations?\//i.test(f)) || /migration|alembic|flyway/i.test(conventionContent(ctx)),
|
|
1527
|
+
impact: 'high', rating: 4, category: 'database',
|
|
1528
|
+
fix: 'Document migration strategy in CONVENTIONS.md.',
|
|
1529
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1530
|
+
},
|
|
1531
|
+
aiderQueryOptimizationMentioned: {
|
|
1532
|
+
id: 'AD-T20', name: 'Query optimization guidance documented',
|
|
1533
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /n\+1|query.{0,15}optim|index|eager.{0,10}load/i.test(conv); },
|
|
1534
|
+
impact: 'medium', rating: 3, category: 'database',
|
|
1535
|
+
fix: 'Add N+1 prevention patterns to CONVENTIONS.md.',
|
|
1536
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1537
|
+
},
|
|
1538
|
+
aiderConnectionPoolingConfigured: {
|
|
1539
|
+
id: 'AD-T21', name: 'Connection pooling documented',
|
|
1540
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /connection.{0,15}pool|pool.{0,15}size/i.test(docs); },
|
|
1541
|
+
impact: 'medium', rating: 3, category: 'database',
|
|
1542
|
+
fix: 'Document connection pooling in CONVENTIONS.md.',
|
|
1543
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1544
|
+
},
|
|
1545
|
+
aiderBackupStrategyDocumented: {
|
|
1546
|
+
id: 'AD-T22', name: 'Database backup strategy documented',
|
|
1547
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /backup|restore|point.?in.?time/i.test(docs); },
|
|
1548
|
+
impact: 'medium', rating: 3, category: 'database',
|
|
1549
|
+
fix: 'Document database backup strategy in README.md.',
|
|
1550
|
+
template: null, file: () => 'README.md', line: () => null,
|
|
1551
|
+
},
|
|
1552
|
+
aiderSchemaDocumentation: {
|
|
1553
|
+
id: 'AD-T23', name: 'Database schema documentation present',
|
|
1554
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /schema\.(prisma|sql|graphql)|erd|dbml/i.test(f)),
|
|
1555
|
+
impact: 'medium', rating: 3, category: 'database',
|
|
1556
|
+
fix: 'Add schema documentation so Aider understands the data model.',
|
|
1557
|
+
template: null, file: () => null, line: () => null,
|
|
1558
|
+
},
|
|
1559
|
+
aiderSeedDataMentioned: {
|
|
1560
|
+
id: 'AD-T24', name: 'Seed data strategy documented',
|
|
1561
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /seed\.(ts|js|sql|py)|fixtures\//i.test(f)) || /seed.{0,10}data/i.test(conventionContent(ctx)),
|
|
1562
|
+
impact: 'low', rating: 2, category: 'database',
|
|
1563
|
+
fix: 'Add seed scripts and document local database setup.',
|
|
1564
|
+
template: null, file: () => null, line: () => null,
|
|
1565
|
+
},
|
|
1566
|
+
aiderAuthFlowDocumented: {
|
|
1567
|
+
id: 'AD-T25', name: 'Authentication flow documented in conventions',
|
|
1568
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /auth.{0,15}flow|login.{0,15}flow|authenticate/i.test(conv); },
|
|
1569
|
+
impact: 'high', rating: 4, category: 'authentication',
|
|
1570
|
+
fix: 'Document authentication flow in CONVENTIONS.md for Aider.',
|
|
1571
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1572
|
+
},
|
|
1573
|
+
aiderTokenHandlingGuidance: {
|
|
1574
|
+
id: 'AD-T26', name: 'Token handling guidance in conventions',
|
|
1575
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /jwt|token.{0,15}refresh|access.{0,10}token|bearer/i.test(conv); },
|
|
1576
|
+
impact: 'high', rating: 4, category: 'authentication',
|
|
1577
|
+
fix: 'Document JWT/token patterns in CONVENTIONS.md.',
|
|
1578
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1579
|
+
},
|
|
1580
|
+
aiderSessionManagementDocumented: {
|
|
1581
|
+
id: 'AD-T27', name: 'Session management documented',
|
|
1582
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /session.{0,15}manag|cookie|next.?auth/i.test(conv); },
|
|
1583
|
+
impact: 'medium', rating: 3, category: 'authentication',
|
|
1584
|
+
fix: 'Document session management approach in CONVENTIONS.md.',
|
|
1585
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1586
|
+
},
|
|
1587
|
+
aiderRbacPermissionsReferenced: {
|
|
1588
|
+
id: 'AD-T28', name: 'RBAC / permissions model referenced',
|
|
1589
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /rbac|role.?based|permission|authorization/i.test(conv); },
|
|
1590
|
+
impact: 'high', rating: 4, category: 'authentication',
|
|
1591
|
+
fix: 'Document RBAC/permissions model in CONVENTIONS.md.',
|
|
1592
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1593
|
+
},
|
|
1594
|
+
aiderOauthSsoMentioned: {
|
|
1595
|
+
id: 'AD-T29', name: 'OAuth/SSO configuration documented',
|
|
1596
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /oauth|sso|saml|oidc/i.test(docs); },
|
|
1597
|
+
impact: 'medium', rating: 3, category: 'authentication',
|
|
1598
|
+
fix: 'Document OAuth/SSO provider in README.md.',
|
|
1599
|
+
template: null, file: () => 'README.md', line: () => null,
|
|
1600
|
+
},
|
|
1601
|
+
aiderCredentialRotationDocumented: {
|
|
1602
|
+
id: 'AD-T30', name: 'Credential rotation policy documented',
|
|
1603
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /rotat.{0,10}secret|rotat.{0,10}key|credential.{0,10}rotat/i.test(conv); },
|
|
1604
|
+
impact: 'medium', rating: 3, category: 'authentication',
|
|
1605
|
+
fix: 'Document credential rotation in CONVENTIONS.md.',
|
|
1606
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1607
|
+
},
|
|
1608
|
+
aiderLoggingConfigured: {
|
|
1609
|
+
id: 'AD-T31', name: 'Logging framework configured',
|
|
1610
|
+
check: (ctx) => !isMonitoringRelevant(ctx) ? null : /winston|pino|bunyan|morgan|loguru/i.test(ctx.fileContent('package.json') || '') || ctx.files.some(f => /log(ger|ging)\.config/i.test(f)),
|
|
1611
|
+
impact: 'high', rating: 4, category: 'monitoring',
|
|
1612
|
+
fix: 'Add a logging framework and document log levels in CONVENTIONS.md.',
|
|
1613
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1614
|
+
},
|
|
1615
|
+
aiderErrorTrackingSetup: {
|
|
1616
|
+
id: 'AD-T32', name: 'Error tracking service configured',
|
|
1617
|
+
check: (ctx) => !isMonitoringRelevant(ctx) ? null : /sentry|bugsnag|rollbar/i.test(ctx.fileContent('package.json') || ''),
|
|
1618
|
+
impact: 'high', rating: 4, category: 'monitoring',
|
|
1619
|
+
fix: 'Set up error tracking (Sentry) to catch production errors.',
|
|
1620
|
+
template: null, file: () => null, line: () => null,
|
|
1621
|
+
},
|
|
1622
|
+
aiderApmMetricsMentioned: {
|
|
1623
|
+
id: 'AD-T33', name: 'APM / metrics platform documented',
|
|
1624
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /datadog|newrelic|prometheus|grafana|apm|opentelemetry/i.test(docs); },
|
|
1625
|
+
impact: 'medium', rating: 3, category: 'monitoring',
|
|
1626
|
+
fix: 'Document APM platform in README.md.',
|
|
1627
|
+
template: null, file: () => 'README.md', line: () => null,
|
|
1628
|
+
},
|
|
1629
|
+
aiderHealthCheckEndpoint: {
|
|
1630
|
+
id: 'AD-T34', name: 'Health check endpoint documented',
|
|
1631
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /health.?check|\/health|\/ping|\/status/i.test(docs); },
|
|
1632
|
+
impact: 'medium', rating: 3, category: 'monitoring',
|
|
1633
|
+
fix: 'Document health check endpoint in README.md.',
|
|
1634
|
+
template: null, file: () => 'README.md', line: () => null,
|
|
1635
|
+
},
|
|
1636
|
+
aiderAlertingReferenced: {
|
|
1637
|
+
id: 'AD-T35', name: 'Alerting strategy referenced',
|
|
1638
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /alert|pagerduty|opsgenie|oncall|incident/i.test(docs); },
|
|
1639
|
+
impact: 'medium', rating: 3, category: 'monitoring',
|
|
1640
|
+
fix: 'Document alerting strategy in README.md.',
|
|
1641
|
+
template: null, file: () => 'README.md', line: () => null,
|
|
1642
|
+
},
|
|
1643
|
+
aiderLogRotationMentioned: {
|
|
1644
|
+
id: 'AD-T36', name: 'Log rotation / retention documented',
|
|
1645
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /log.{0,15}rotat|log.{0,15}retent/i.test(docs); },
|
|
1646
|
+
impact: 'low', rating: 2, category: 'monitoring',
|
|
1647
|
+
fix: 'Document log rotation policy in README.md.',
|
|
1648
|
+
template: null, file: () => 'README.md', line: () => null,
|
|
1649
|
+
},
|
|
1650
|
+
aiderLockfilePresent: {
|
|
1651
|
+
id: 'AD-T37', name: 'Dependency lockfile present',
|
|
1652
|
+
check: (ctx) => ctx.files.some(f => /package-lock\.json|yarn\.lock|pnpm-lock\.yaml|poetry\.lock|cargo\.lock/i.test(f)),
|
|
1653
|
+
impact: 'critical', rating: 5, category: 'dependency-management',
|
|
1654
|
+
fix: 'Commit your lockfile for reproducible Aider environments.',
|
|
1655
|
+
template: null, file: () => null, line: () => null,
|
|
1656
|
+
},
|
|
1657
|
+
aiderOutdatedDepsAwareness: {
|
|
1658
|
+
id: 'AD-T38', name: 'Outdated dependency awareness configured',
|
|
1659
|
+
check: (ctx) => ctx.files.some(f => /\.github\/dependabot\.yml|renovate\.json/i.test(f)),
|
|
1660
|
+
impact: 'medium', rating: 3, category: 'dependency-management',
|
|
1661
|
+
fix: 'Enable Dependabot for automated dependency updates.',
|
|
1662
|
+
template: null, file: () => '.github/dependabot.yml', line: () => null,
|
|
1663
|
+
},
|
|
1664
|
+
aiderLicenseCompliance: {
|
|
1665
|
+
id: 'AD-T39', name: 'License compliance awareness configured',
|
|
1666
|
+
check: (ctx) => /license-checker|licensee|fossa/i.test(ctx.fileContent('package.json') || ''),
|
|
1667
|
+
impact: 'medium', rating: 3, category: 'dependency-management',
|
|
1668
|
+
fix: 'Add license compliance checking (license-checker).',
|
|
1669
|
+
template: null, file: () => null, line: () => null,
|
|
1670
|
+
},
|
|
1671
|
+
aiderNpmAuditConfigured: {
|
|
1672
|
+
id: 'AD-T40', name: 'Security audit in CI',
|
|
1673
|
+
check: (ctx) => { const wfs = ctx.files.filter(f => /\.github\/workflows\/.*\.ya?ml$/i.test(f)); return wfs.some(f => /npm audit|yarn audit|snyk/i.test(ctx.fileContent(f) || '')); },
|
|
1674
|
+
impact: 'high', rating: 4, category: 'dependency-management',
|
|
1675
|
+
fix: 'Add `npm audit` to CI to catch vulnerable dependencies.',
|
|
1676
|
+
template: null, file: () => '.github/workflows/', line: () => null,
|
|
1677
|
+
},
|
|
1678
|
+
aiderPinnedVersionsUsed: {
|
|
1679
|
+
id: 'AD-T41', name: 'Critical dependency versions pinned',
|
|
1680
|
+
check: (ctx) => { const p = ctx.fileContent('package.json'); if (!p) return null; try { const pkg = JSON.parse(p); const vals = Object.values({ ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }); if (!vals.length) return null; return vals.filter(v => /^\d/.test(String(v))).length / vals.length >= 0.1; } catch { return null; } },
|
|
1681
|
+
impact: 'medium', rating: 3, category: 'dependency-management',
|
|
1682
|
+
fix: 'Pin critical dependencies to exact versions.',
|
|
1683
|
+
template: null, file: () => 'package.json', line: () => null,
|
|
1684
|
+
},
|
|
1685
|
+
aiderAutoUpdatePolicy: {
|
|
1686
|
+
id: 'AD-T42', name: 'Dependency auto-update policy',
|
|
1687
|
+
check: (ctx) => ctx.files.some(f => /\.github\/dependabot\.yml|renovate\.json|\.renovaterc/i.test(f)),
|
|
1688
|
+
impact: 'medium', rating: 3, category: 'dependency-management',
|
|
1689
|
+
fix: 'Configure Dependabot for automated dependency updates.',
|
|
1690
|
+
template: null, file: () => '.github/dependabot.yml', line: () => null,
|
|
1691
|
+
},
|
|
1692
|
+
aiderTokenUsageAwareness: {
|
|
1693
|
+
id: 'AD-T43', name: 'Token usage awareness in conventions',
|
|
1694
|
+
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /token.{0,15}usage|token.{0,15}budget|context.{0,15}window/i.test(conv); },
|
|
1695
|
+
impact: 'medium', rating: 3, category: 'cost-optimization',
|
|
1696
|
+
fix: 'Document token budget awareness in CONVENTIONS.md for Aider.',
|
|
1697
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1698
|
+
},
|
|
1699
|
+
aiderModelSelectionGuidance: {
|
|
1700
|
+
id: 'AD-T44', name: 'Model selection guidance in config',
|
|
1701
|
+
check: (ctx) => { const config = configContent(ctx); if (!config) return null; return /\bmodel\s*:|gpt-4|claude-3|claude-4|opus|sonnet|flash/i.test(config); },
|
|
1702
|
+
impact: 'medium', rating: 3, category: 'cost-optimization',
|
|
1703
|
+
fix: 'Configure model selection in .aider.conf.yml for cost/quality balance.',
|
|
1704
|
+
template: 'aider-conf-yml', file: () => '.aider.conf.yml', line: () => null,
|
|
1705
|
+
},
|
|
1706
|
+
aiderCachingToReduceApiCalls: {
|
|
1707
|
+
id: 'AD-T45', name: 'Caching strategy documented',
|
|
1708
|
+
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /cach.{0,15}api|redis|memcache|cache-control/i.test(conv); },
|
|
1709
|
+
impact: 'medium', rating: 3, category: 'cost-optimization',
|
|
1710
|
+
fix: 'Document caching strategies in CONVENTIONS.md.',
|
|
1711
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1712
|
+
},
|
|
1713
|
+
aiderBatchProcessingMentioned: {
|
|
1714
|
+
id: 'AD-T46', name: 'Batch processing patterns documented',
|
|
1715
|
+
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /batch.{0,15}process|bulk.{0,15}operat|queue|background.{0,15}job/i.test(conv); },
|
|
1716
|
+
impact: 'medium', rating: 3, category: 'cost-optimization',
|
|
1717
|
+
fix: 'Document batch processing patterns in CONVENTIONS.md.',
|
|
1718
|
+
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1719
|
+
},
|
|
1720
|
+
aiderPromptCachingEnabled: {
|
|
1721
|
+
id: 'AD-T47', name: 'Prompt caching configured',
|
|
1722
|
+
check: (ctx) => { const config = configContent(ctx); if (!config) return null; return /\bcache-prompts\s*:\s*true\b/i.test(config); },
|
|
1723
|
+
impact: 'medium', rating: 3, category: 'cost-optimization',
|
|
1724
|
+
fix: 'Set `cache-prompts: true` in .aider.conf.yml to reduce API costs.',
|
|
1725
|
+
template: 'aider-conf-yml', file: () => '.aider.conf.yml', line: () => null,
|
|
1726
|
+
},
|
|
1727
|
+
aiderCostBudgetDefined: {
|
|
1728
|
+
id: 'AD-T48', name: 'AI cost budget or usage limits documented',
|
|
1729
|
+
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /cost.{0,15}budget|spending.{0,15}limit|usage.{0,15}limit/i.test(docs); },
|
|
1730
|
+
impact: 'low', rating: 2, category: 'cost-optimization',
|
|
1731
|
+
fix: 'Document AI cost budget in README.md.',
|
|
1732
|
+
template: null, file: () => 'README.md', line: () => null,
|
|
1733
|
+
},
|
|
1734
|
+
|
|
1735
|
+
// ============================================================
|
|
1736
|
+
// === PYTHON STACK CHECKS (category: 'python') ===============
|
|
1737
|
+
// ============================================================
|
|
1738
|
+
|
|
1739
|
+
aiderPythonProjectExists: {
|
|
1740
|
+
id: 'AD-PY01',
|
|
1741
|
+
name: 'Python project detected (pyproject.toml / setup.py / requirements.txt)',
|
|
1742
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; return true; },
|
|
1743
|
+
impact: 'high',
|
|
1744
|
+
category: 'python',
|
|
1745
|
+
fix: 'Ensure pyproject.toml, setup.py, or requirements.txt exists for Python projects.',
|
|
1746
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1747
|
+
confidence: 0.7,
|
|
1748
|
+
},
|
|
1749
|
+
|
|
1750
|
+
aiderPythonVersionSpecified: {
|
|
1751
|
+
id: 'AD-PY02',
|
|
1752
|
+
name: 'Python version specified (.python-version or requires-python)',
|
|
1753
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; return ctx.files.some(f => /\.python-version$/.test(f)) || /requires-python/i.test(ctx.fileContent('pyproject.toml') || ''); },
|
|
1754
|
+
impact: 'medium',
|
|
1755
|
+
category: 'python',
|
|
1756
|
+
fix: 'Create .python-version or add requires-python to pyproject.toml.',
|
|
1757
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1758
|
+
confidence: 0.7,
|
|
1759
|
+
},
|
|
1760
|
+
|
|
1761
|
+
aiderPythonVenvMentioned: {
|
|
1762
|
+
id: 'AD-PY03',
|
|
1763
|
+
name: 'Virtual environment mentioned in instructions',
|
|
1764
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /venv|virtualenv|conda|poetry shell|uv venv/i.test(docs); },
|
|
1765
|
+
impact: 'medium',
|
|
1766
|
+
category: 'python',
|
|
1767
|
+
fix: 'Document virtual environment setup in project instructions.',
|
|
1768
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1769
|
+
confidence: 0.7,
|
|
1770
|
+
},
|
|
1771
|
+
|
|
1772
|
+
aiderPythonLockfileExists: {
|
|
1773
|
+
id: 'AD-PY04',
|
|
1774
|
+
name: 'Python lockfile exists (poetry.lock / uv.lock / Pipfile.lock / pinned requirements)',
|
|
1775
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; return ctx.files.some(f => /poetry\.lock$|uv\.lock$|Pipfile\.lock$/.test(f)) || /==/m.test(ctx.fileContent('requirements.txt') || ''); },
|
|
1776
|
+
impact: 'high',
|
|
1777
|
+
category: 'python',
|
|
1778
|
+
fix: 'Add a lockfile (poetry.lock, uv.lock, Pipfile.lock) or pin versions with == in requirements.txt.',
|
|
1779
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1780
|
+
confidence: 0.7,
|
|
1781
|
+
},
|
|
1782
|
+
|
|
1783
|
+
aiderPythonPytestConfigured: {
|
|
1784
|
+
id: 'AD-PY05',
|
|
1785
|
+
name: 'pytest configured (pyproject.toml [tool.pytest] / pytest.ini / conftest.py)',
|
|
1786
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; return /\[tool\.pytest/i.test(ctx.fileContent('pyproject.toml') || '') || ctx.files.some(f => /pytest\.ini$|conftest\.py$/.test(f)); },
|
|
1787
|
+
impact: 'high',
|
|
1788
|
+
category: 'python',
|
|
1789
|
+
fix: 'Configure pytest in pyproject.toml [tool.pytest.ini_options] or create pytest.ini.',
|
|
1790
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1791
|
+
confidence: 0.7,
|
|
1792
|
+
},
|
|
1793
|
+
|
|
1794
|
+
aiderPythonLinterConfigured: {
|
|
1795
|
+
id: 'AD-PY06',
|
|
1796
|
+
name: 'Python linter configured (ruff / flake8 / pylint)',
|
|
1797
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const pp = ctx.fileContent('pyproject.toml') || ''; return /\[tool\.ruff|\[tool\.flake8|\[tool\.pylint/i.test(pp) || ctx.files.some(f => /\.flake8$|pylintrc$|\.pylintrc$|ruff\.toml$/.test(f)); },
|
|
1798
|
+
impact: 'medium',
|
|
1799
|
+
category: 'python',
|
|
1800
|
+
fix: 'Configure ruff, flake8, or pylint in pyproject.toml or dedicated config file.',
|
|
1801
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1802
|
+
confidence: 0.7,
|
|
1803
|
+
},
|
|
1804
|
+
|
|
1805
|
+
aiderPythonTypeCheckerConfigured: {
|
|
1806
|
+
id: 'AD-PY07',
|
|
1807
|
+
name: 'Type checker configured (mypy / pyright)',
|
|
1808
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const pp = ctx.fileContent('pyproject.toml') || ''; return /\[tool\.mypy|\[tool\.pyright/i.test(pp) || ctx.files.some(f => /mypy\.ini$|pyrightconfig\.json$/.test(f)); },
|
|
1809
|
+
impact: 'medium',
|
|
1810
|
+
category: 'python',
|
|
1811
|
+
fix: 'Configure mypy or pyright in pyproject.toml or dedicated config file.',
|
|
1812
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1813
|
+
confidence: 0.7,
|
|
1814
|
+
},
|
|
1815
|
+
|
|
1816
|
+
aiderPythonFormatterConfigured: {
|
|
1817
|
+
id: 'AD-PY08',
|
|
1818
|
+
name: 'Formatter configured (black / isort / ruff format)',
|
|
1819
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const pp = ctx.fileContent('pyproject.toml') || ''; return /\[tool\.black|\[tool\.isort|\[tool\.ruff\.format/i.test(pp); },
|
|
1820
|
+
impact: 'medium',
|
|
1821
|
+
category: 'python',
|
|
1822
|
+
fix: 'Configure black, isort, or ruff format in pyproject.toml.',
|
|
1823
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1824
|
+
confidence: 0.7,
|
|
1825
|
+
},
|
|
1826
|
+
|
|
1827
|
+
aiderPythonDjangoSettingsDocumented: {
|
|
1828
|
+
id: 'AD-PY09',
|
|
1829
|
+
name: 'Django settings documented if Django project',
|
|
1830
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; if (!ctx.files.some(f => /manage\.py$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /django|settings\.py|DJANGO_SETTINGS_MODULE/i.test(docs); },
|
|
1831
|
+
impact: 'high',
|
|
1832
|
+
category: 'python',
|
|
1833
|
+
fix: 'Document Django settings module and configuration in project instructions.',
|
|
1834
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1835
|
+
confidence: 0.7,
|
|
1836
|
+
},
|
|
1837
|
+
|
|
1838
|
+
aiderPythonFastapiEntryDocumented: {
|
|
1839
|
+
id: 'AD-PY10',
|
|
1840
|
+
name: 'FastAPI entry point documented if FastAPI project',
|
|
1841
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const deps = (ctx.fileContent('pyproject.toml') || '') + (ctx.fileContent('requirements.txt') || ''); if (!/fastapi/i.test(deps)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /fastapi|uvicorn|app\.py|main\.py/i.test(docs); },
|
|
1842
|
+
impact: 'high',
|
|
1843
|
+
category: 'python',
|
|
1844
|
+
fix: 'Document FastAPI entry point and how to run the development server.',
|
|
1845
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1846
|
+
confidence: 0.7,
|
|
1847
|
+
},
|
|
1848
|
+
|
|
1849
|
+
aiderPythonMigrationsDocumented: {
|
|
1850
|
+
id: 'AD-PY11',
|
|
1851
|
+
name: 'Database migrations mentioned (alembic / Django migrations)',
|
|
1852
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /alembic|migrate|makemigrations|django.{0,10}migration/i.test(docs) || ctx.files.some(f => /alembic[.]ini$|alembic[/]/.test(f)); },
|
|
1853
|
+
impact: 'medium',
|
|
1854
|
+
category: 'python',
|
|
1855
|
+
fix: 'Document database migration workflow (alembic or Django migrations).',
|
|
1856
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1857
|
+
confidence: 0.7,
|
|
1858
|
+
},
|
|
1859
|
+
|
|
1860
|
+
aiderPythonEnvHandlingDocumented: {
|
|
1861
|
+
id: 'AD-PY12',
|
|
1862
|
+
name: '.env handling documented (python-dotenv)',
|
|
1863
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const deps = (ctx.fileContent('pyproject.toml') || '') + (ctx.fileContent('requirements.txt') || ''); if (!/dotenv|python-dotenv|environs/i.test(deps)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /\.env|dotenv|environment.{0,10}variable/i.test(docs); },
|
|
1864
|
+
impact: 'medium',
|
|
1865
|
+
category: 'python',
|
|
1866
|
+
fix: 'Document .env file usage and python-dotenv configuration.',
|
|
1867
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1868
|
+
confidence: 0.7,
|
|
1869
|
+
},
|
|
1870
|
+
|
|
1871
|
+
aiderPythonPreCommitConfigured: {
|
|
1872
|
+
id: 'AD-PY13',
|
|
1873
|
+
name: 'pre-commit hooks configured (.pre-commit-config.yaml)',
|
|
1874
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; return ctx.files.some(f => /\.pre-commit-config\.yaml$/.test(f)); },
|
|
1875
|
+
impact: 'medium',
|
|
1876
|
+
category: 'python',
|
|
1877
|
+
fix: 'Add .pre-commit-config.yaml with Python-specific hooks (ruff, mypy, etc.).',
|
|
1878
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1879
|
+
confidence: 0.7,
|
|
1880
|
+
},
|
|
1881
|
+
|
|
1882
|
+
aiderPythonDockerBaseImage: {
|
|
1883
|
+
id: 'AD-PY14',
|
|
1884
|
+
name: 'Docker uses Python base image correctly',
|
|
1885
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const df = ctx.fileContent('Dockerfile') || ''; if (!df) return null; return /FROM.*python:/i.test(df); },
|
|
1886
|
+
impact: 'medium',
|
|
1887
|
+
category: 'python',
|
|
1888
|
+
fix: 'Use official Python base image in Dockerfile (e.g., FROM python:3.12-slim).',
|
|
1889
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1890
|
+
confidence: 0.7,
|
|
1891
|
+
},
|
|
1892
|
+
|
|
1893
|
+
aiderPythonTestMatrixConfigured: {
|
|
1894
|
+
id: 'AD-PY15',
|
|
1895
|
+
name: 'Test matrix configured (tox.ini / noxfile.py)',
|
|
1896
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; return ctx.files.some(f => /tox\.ini$|noxfile\.py$/.test(f)); },
|
|
1897
|
+
impact: 'low',
|
|
1898
|
+
category: 'python',
|
|
1899
|
+
fix: 'Configure tox or nox for multi-environment testing.',
|
|
1900
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1901
|
+
confidence: 0.7,
|
|
1902
|
+
},
|
|
1903
|
+
|
|
1904
|
+
aiderPythonValidationUsed: {
|
|
1905
|
+
id: 'AD-PY16',
|
|
1906
|
+
name: 'Pydantic or dataclass validation used',
|
|
1907
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const deps = (ctx.fileContent('pyproject.toml') || '') + (ctx.fileContent('requirements.txt') || ''); return /pydantic|dataclass/i.test(deps); },
|
|
1908
|
+
impact: 'medium',
|
|
1909
|
+
category: 'python',
|
|
1910
|
+
fix: 'Use pydantic or dataclasses for data validation and type safety.',
|
|
1911
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1912
|
+
confidence: 0.7,
|
|
1913
|
+
},
|
|
1914
|
+
|
|
1915
|
+
aiderPythonAsyncDocumented: {
|
|
1916
|
+
id: 'AD-PY17',
|
|
1917
|
+
name: 'Async patterns documented if async project',
|
|
1918
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const deps = (ctx.fileContent('pyproject.toml') || '') + (ctx.fileContent('requirements.txt') || ''); if (!/asyncio|aiohttp|fastapi|starlette|httpx/i.test(deps)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /async|await|asyncio|event.{0,5}loop/i.test(docs); },
|
|
1919
|
+
impact: 'medium',
|
|
1920
|
+
category: 'python',
|
|
1921
|
+
fix: 'Document async patterns and conventions used in the project.',
|
|
1922
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1923
|
+
confidence: 0.7,
|
|
1924
|
+
},
|
|
1925
|
+
|
|
1926
|
+
aiderPythonPinnedVersions: {
|
|
1927
|
+
id: 'AD-PY18',
|
|
1928
|
+
name: 'Requirements have pinned versions (== in requirements.txt)',
|
|
1929
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const req = ctx.fileContent('requirements.txt') || ''; if (!req.trim()) return null; const lines = req.split('\n').filter(l => l.trim() && !l.startsWith('#')); return lines.length > 0 && lines.every(l => /==/.test(l) || /^-/.test(l.trim())); },
|
|
1930
|
+
impact: 'high',
|
|
1931
|
+
category: 'python',
|
|
1932
|
+
fix: 'Pin all dependency versions with == in requirements.txt for reproducible builds.',
|
|
1933
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1934
|
+
confidence: 0.7,
|
|
1935
|
+
},
|
|
1936
|
+
|
|
1937
|
+
aiderPythonPackageStructure: {
|
|
1938
|
+
id: 'AD-PY19',
|
|
1939
|
+
name: 'Python package has proper structure (src/ layout or __init__.py)',
|
|
1940
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; return ctx.files.some(f => /src[/].*[/]__init__\.py$|^[^/]+[/]__init__\.py$/.test(f)); },
|
|
1941
|
+
impact: 'medium',
|
|
1942
|
+
category: 'python',
|
|
1943
|
+
fix: 'Use src/ layout or ensure packages have __init__.py files.',
|
|
1944
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1945
|
+
confidence: 0.7,
|
|
1946
|
+
},
|
|
1947
|
+
|
|
1948
|
+
aiderPythonDocsToolConfigured: {
|
|
1949
|
+
id: 'AD-PY20',
|
|
1950
|
+
name: 'Documentation tool configured (sphinx / mkdocs)',
|
|
1951
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; return ctx.files.some(f => /mkdocs\.yml$|conf\.py$|docs[/]/.test(f)) || /sphinx|mkdocs/i.test(ctx.fileContent('pyproject.toml') || ''); },
|
|
1952
|
+
impact: 'low',
|
|
1953
|
+
category: 'python',
|
|
1954
|
+
fix: 'Configure sphinx or mkdocs for project documentation.',
|
|
1955
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1956
|
+
confidence: 0.7,
|
|
1957
|
+
},
|
|
1958
|
+
|
|
1959
|
+
aiderPythonCoverageConfigured: {
|
|
1960
|
+
id: 'AD-PY21',
|
|
1961
|
+
name: 'Coverage configured (coverage / pytest-cov)',
|
|
1962
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const pp = ctx.fileContent('pyproject.toml') || ''; return /\[tool\.coverage|pytest-cov|coverage/i.test(pp) || ctx.files.some(f => /\.coveragerc$/.test(f)); },
|
|
1963
|
+
impact: 'medium',
|
|
1964
|
+
category: 'python',
|
|
1965
|
+
fix: 'Configure coverage reporting with pytest-cov or coverage.py.',
|
|
1966
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1967
|
+
confidence: 0.7,
|
|
1968
|
+
},
|
|
1969
|
+
|
|
1970
|
+
aiderPythonNoSecretsInSettings: {
|
|
1971
|
+
id: 'AD-PY22',
|
|
1972
|
+
name: 'No secrets in Django settings.py',
|
|
1973
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const settings = ctx.fileContent('settings.py') || ctx.files.filter(f => /settings\.py$/.test(f)).map(f => ctx.fileContent(f) || '').join(''); if (!settings) return null; return !/SECRET_KEY\s*=\s*['"][^'"]{10,}/i.test(settings); },
|
|
1974
|
+
impact: 'critical',
|
|
1975
|
+
category: 'python',
|
|
1976
|
+
fix: 'Move SECRET_KEY and other secrets to environment variables, not hardcoded in settings.py.',
|
|
1977
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1978
|
+
confidence: 0.7,
|
|
1979
|
+
},
|
|
1980
|
+
|
|
1981
|
+
aiderPythonWsgiAsgiDocumented: {
|
|
1982
|
+
id: 'AD-PY23',
|
|
1983
|
+
name: 'WSGI/ASGI server documented (gunicorn / uvicorn)',
|
|
1984
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const deps = (ctx.fileContent('pyproject.toml') || '') + (ctx.fileContent('requirements.txt') || ''); if (!/gunicorn|uvicorn|daphne|hypercorn/i.test(deps)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /gunicorn|uvicorn|daphne|hypercorn|wsgi|asgi/i.test(docs); },
|
|
1985
|
+
impact: 'medium',
|
|
1986
|
+
category: 'python',
|
|
1987
|
+
fix: 'Document WSGI/ASGI server configuration (gunicorn, uvicorn).',
|
|
1988
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
1989
|
+
confidence: 0.7,
|
|
1990
|
+
},
|
|
1991
|
+
|
|
1992
|
+
aiderPythonTaskQueueDocumented: {
|
|
1993
|
+
id: 'AD-PY24',
|
|
1994
|
+
name: 'Task queue documented if used (celery / rq)',
|
|
1995
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const deps = (ctx.fileContent('pyproject.toml') || '') + (ctx.fileContent('requirements.txt') || ''); if (!/celery|rq|dramatiq|huey/i.test(deps)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /celery|rq|dramatiq|huey|task.{0,10}queue|worker/i.test(docs); },
|
|
1996
|
+
impact: 'medium',
|
|
1997
|
+
category: 'python',
|
|
1998
|
+
fix: 'Document task queue configuration and worker setup.',
|
|
1999
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2000
|
+
confidence: 0.7,
|
|
2001
|
+
},
|
|
2002
|
+
|
|
2003
|
+
aiderPythonGitignore: {
|
|
2004
|
+
id: 'AD-PY25',
|
|
2005
|
+
name: 'Python-specific .gitignore (__pycache__, *.pyc, .venv)',
|
|
2006
|
+
check: (ctx) => { const hasPy = ctx.files.some(f => /pyproject\.toml$|requirements\.txt$|setup\.py$|manage\.py$/.test(f)); if (!hasPy) return null; const gi = ctx.fileContent('.gitignore') || ''; return /__pycache__|\*\.pyc|\.venv/i.test(gi); },
|
|
2007
|
+
impact: 'medium',
|
|
2008
|
+
category: 'python',
|
|
2009
|
+
fix: 'Add Python-specific entries to .gitignore (__pycache__, *.pyc, .venv, *.egg-info).',
|
|
2010
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2011
|
+
confidence: 0.7,
|
|
2012
|
+
},
|
|
2013
|
+
|
|
2014
|
+
// ============================================================
|
|
2015
|
+
// === GO STACK CHECKS (category: 'go') =======================
|
|
2016
|
+
// ============================================================
|
|
2017
|
+
|
|
2018
|
+
aiderGoModExists: {
|
|
2019
|
+
id: 'AD-GO01',
|
|
2020
|
+
name: 'go.mod exists',
|
|
2021
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; return true; },
|
|
2022
|
+
impact: 'high',
|
|
2023
|
+
category: 'go',
|
|
2024
|
+
fix: 'Initialize Go module with go mod init.',
|
|
2025
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2026
|
+
confidence: 0.7,
|
|
2027
|
+
},
|
|
2028
|
+
|
|
2029
|
+
aiderGoSumCommitted: {
|
|
2030
|
+
id: 'AD-GO02',
|
|
2031
|
+
name: 'go.sum committed',
|
|
2032
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; return ctx.files.some(f => /go\.sum$/.test(f)); },
|
|
2033
|
+
impact: 'high',
|
|
2034
|
+
category: 'go',
|
|
2035
|
+
fix: 'Commit go.sum to version control for reproducible builds.',
|
|
2036
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2037
|
+
confidence: 0.7,
|
|
2038
|
+
},
|
|
2039
|
+
|
|
2040
|
+
aiderGolangciLintConfigured: {
|
|
2041
|
+
id: 'AD-GO03',
|
|
2042
|
+
name: 'golangci-lint configured (.golangci.yml)',
|
|
2043
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; return ctx.files.some(f => /\.golangci\.ya?ml$|\.golangci\.toml$/.test(f)); },
|
|
2044
|
+
impact: 'medium',
|
|
2045
|
+
category: 'go',
|
|
2046
|
+
fix: 'Add .golangci.yml to configure linting rules.',
|
|
2047
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2048
|
+
confidence: 0.7,
|
|
2049
|
+
},
|
|
2050
|
+
|
|
2051
|
+
aiderGoTestDocumented: {
|
|
2052
|
+
id: 'AD-GO04',
|
|
2053
|
+
name: 'go test documented in instructions',
|
|
2054
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /go test/i.test(docs); },
|
|
2055
|
+
impact: 'high',
|
|
2056
|
+
category: 'go',
|
|
2057
|
+
fix: 'Document go test command in project instructions.',
|
|
2058
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2059
|
+
confidence: 0.7,
|
|
2060
|
+
},
|
|
2061
|
+
|
|
2062
|
+
aiderGoBuildDocumented: {
|
|
2063
|
+
id: 'AD-GO05',
|
|
2064
|
+
name: 'go build documented in instructions',
|
|
2065
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /go build|go install/i.test(docs); },
|
|
2066
|
+
impact: 'high',
|
|
2067
|
+
category: 'go',
|
|
2068
|
+
fix: 'Document go build command in project instructions.',
|
|
2069
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2070
|
+
confidence: 0.7,
|
|
2071
|
+
},
|
|
2072
|
+
|
|
2073
|
+
aiderGoStandardLayout: {
|
|
2074
|
+
id: 'AD-GO06',
|
|
2075
|
+
name: 'Standard Go layout (cmd/ / internal/ / pkg/)',
|
|
2076
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; return ctx.files.some(f => /^cmd[/]|^internal[/]|^pkg[/]/.test(f)); },
|
|
2077
|
+
impact: 'medium',
|
|
2078
|
+
category: 'go',
|
|
2079
|
+
fix: 'Use standard Go project layout with cmd/, internal/, and/or pkg/ directories.',
|
|
2080
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2081
|
+
confidence: 0.7,
|
|
2082
|
+
},
|
|
2083
|
+
|
|
2084
|
+
aiderGoErrorHandlingDocumented: {
|
|
2085
|
+
id: 'AD-GO07',
|
|
2086
|
+
name: 'Error handling patterns documented',
|
|
2087
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /error handling|errors?\.(?:New|Wrap|Is|As)|fmt\.Errorf/i.test(docs); },
|
|
2088
|
+
impact: 'medium',
|
|
2089
|
+
category: 'go',
|
|
2090
|
+
fix: 'Document error handling conventions (error wrapping, sentinel errors, etc.).',
|
|
2091
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2092
|
+
confidence: 0.7,
|
|
2093
|
+
},
|
|
2094
|
+
|
|
2095
|
+
aiderGoContextUsageDocumented: {
|
|
2096
|
+
id: 'AD-GO08',
|
|
2097
|
+
name: 'Context usage documented',
|
|
2098
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /context\.Context|ctx\.Done|context\.WithCancel|context\.WithTimeout/i.test(docs); },
|
|
2099
|
+
impact: 'medium',
|
|
2100
|
+
category: 'go',
|
|
2101
|
+
fix: 'Document context.Context usage patterns for cancellation and timeouts.',
|
|
2102
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2103
|
+
confidence: 0.7,
|
|
2104
|
+
},
|
|
2105
|
+
|
|
2106
|
+
aiderGoroutineSafetyDocumented: {
|
|
2107
|
+
id: 'AD-GO09',
|
|
2108
|
+
name: 'Goroutine safety documented',
|
|
2109
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /goroutine|sync\.Mutex|sync\.WaitGroup|channel|concurren/i.test(docs); },
|
|
2110
|
+
impact: 'medium',
|
|
2111
|
+
category: 'go',
|
|
2112
|
+
fix: 'Document goroutine safety patterns, mutex usage, and channel conventions.',
|
|
2113
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2114
|
+
confidence: 0.7,
|
|
2115
|
+
},
|
|
2116
|
+
|
|
2117
|
+
aiderGoModTidyMentioned: {
|
|
2118
|
+
id: 'AD-GO10',
|
|
2119
|
+
name: 'go mod tidy mentioned in instructions',
|
|
2120
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /go mod tidy/i.test(docs); },
|
|
2121
|
+
impact: 'low',
|
|
2122
|
+
category: 'go',
|
|
2123
|
+
fix: 'Document go mod tidy in project workflow instructions.',
|
|
2124
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2125
|
+
confidence: 0.7,
|
|
2126
|
+
},
|
|
2127
|
+
|
|
2128
|
+
aiderGoVetConfigured: {
|
|
2129
|
+
id: 'AD-GO11',
|
|
2130
|
+
name: 'go vet or staticcheck configured',
|
|
2131
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; const ci = ctx.fileContent('.github/workflows/ci.yml') || ctx.fileContent('.github/workflows/go.yml') || ''; return /go vet|staticcheck/i.test(docs + ci); },
|
|
2132
|
+
impact: 'medium',
|
|
2133
|
+
category: 'go',
|
|
2134
|
+
fix: 'Configure go vet and/or staticcheck in CI or project instructions.',
|
|
2135
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2136
|
+
confidence: 0.7,
|
|
2137
|
+
},
|
|
2138
|
+
|
|
2139
|
+
aiderGoMakefileExists: {
|
|
2140
|
+
id: 'AD-GO12',
|
|
2141
|
+
name: 'Makefile or Taskfile exists for Go project',
|
|
2142
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; return ctx.files.some(f => /^Makefile$|^Taskfile\.ya?ml$/.test(f)); },
|
|
2143
|
+
impact: 'medium',
|
|
2144
|
+
category: 'go',
|
|
2145
|
+
fix: 'Add Makefile or Taskfile.yml with common Go targets (build, test, lint).',
|
|
2146
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2147
|
+
confidence: 0.7,
|
|
2148
|
+
},
|
|
2149
|
+
|
|
2150
|
+
aiderGoDockerMultiStage: {
|
|
2151
|
+
id: 'AD-GO13',
|
|
2152
|
+
name: 'Docker multi-stage build for Go',
|
|
2153
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const df = ctx.fileContent('Dockerfile') || ''; if (!df) return null; return /FROM.*golang.*AS/i.test(df) && /FROM.*(?:alpine|scratch|distroless|gcr\.io)/i.test(df); },
|
|
2154
|
+
impact: 'medium',
|
|
2155
|
+
category: 'go',
|
|
2156
|
+
fix: 'Use multi-stage Docker build: build in golang image, run in minimal image (alpine/scratch/distroless).',
|
|
2157
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2158
|
+
confidence: 0.7,
|
|
2159
|
+
},
|
|
2160
|
+
|
|
2161
|
+
aiderGoCgoDocumented: {
|
|
2162
|
+
id: 'AD-GO14',
|
|
2163
|
+
name: 'CGO documented if used',
|
|
2164
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const goMod = ctx.fileContent('go.mod') || ''; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; if (!/CGO_ENABLED|import "C"/i.test(goMod + docs)) return null; return /CGO|cgo/i.test(docs); },
|
|
2165
|
+
impact: 'low',
|
|
2166
|
+
category: 'go',
|
|
2167
|
+
fix: 'Document CGO usage, dependencies, and build requirements.',
|
|
2168
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2169
|
+
confidence: 0.7,
|
|
2170
|
+
},
|
|
2171
|
+
|
|
2172
|
+
aiderGoWorkForMonorepo: {
|
|
2173
|
+
id: 'AD-GO15',
|
|
2174
|
+
name: 'go.work for monorepo',
|
|
2175
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const multiMod = ctx.files.filter(f => /go\.mod$/.test(f)).length > 1; if (!multiMod) return null; return ctx.files.some(f => /go\.work$/.test(f)); },
|
|
2176
|
+
impact: 'medium',
|
|
2177
|
+
category: 'go',
|
|
2178
|
+
fix: 'Use go.work for Go workspace in monorepo with multiple modules.',
|
|
2179
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2180
|
+
confidence: 0.7,
|
|
2181
|
+
},
|
|
2182
|
+
|
|
2183
|
+
aiderGoBenchmarkTests: {
|
|
2184
|
+
id: 'AD-GO16',
|
|
2185
|
+
name: 'Benchmark tests mentioned',
|
|
2186
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /go test.*-bench|Benchmark/i.test(docs); },
|
|
2187
|
+
impact: 'low',
|
|
2188
|
+
category: 'go',
|
|
2189
|
+
fix: 'Document benchmark testing with go test -bench.',
|
|
2190
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2191
|
+
confidence: 0.7,
|
|
2192
|
+
},
|
|
2193
|
+
|
|
2194
|
+
aiderGoRaceDetector: {
|
|
2195
|
+
id: 'AD-GO17',
|
|
2196
|
+
name: 'Race detector (-race) documented',
|
|
2197
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; const ci = ctx.fileContent('.github/workflows/ci.yml') || ctx.fileContent('.github/workflows/go.yml') || ''; return /-race/i.test(docs + ci); },
|
|
2198
|
+
impact: 'medium',
|
|
2199
|
+
category: 'go',
|
|
2200
|
+
fix: 'Document and enable race detector with go test -race.',
|
|
2201
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2202
|
+
confidence: 0.7,
|
|
2203
|
+
},
|
|
2204
|
+
|
|
2205
|
+
aiderGoGenerateDocumented: {
|
|
2206
|
+
id: 'AD-GO18',
|
|
2207
|
+
name: 'go generate documented',
|
|
2208
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /go generate/i.test(docs) || ctx.files.some(f => /generate\.go$/.test(f)); },
|
|
2209
|
+
impact: 'low',
|
|
2210
|
+
category: 'go',
|
|
2211
|
+
fix: 'Document go generate usage and generated files.',
|
|
2212
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2213
|
+
confidence: 0.7,
|
|
2214
|
+
},
|
|
2215
|
+
|
|
2216
|
+
aiderGoInterfaceDesignDocumented: {
|
|
2217
|
+
id: 'AD-GO19',
|
|
2218
|
+
name: 'Interface-based design documented',
|
|
2219
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /interface|mock|stub|dependency injection/i.test(docs); },
|
|
2220
|
+
impact: 'low',
|
|
2221
|
+
category: 'go',
|
|
2222
|
+
fix: 'Document interface-based design patterns for testability and dependency injection.',
|
|
2223
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2224
|
+
confidence: 0.7,
|
|
2225
|
+
},
|
|
2226
|
+
|
|
2227
|
+
aiderGoGitignore: {
|
|
2228
|
+
id: 'AD-GO20',
|
|
2229
|
+
name: 'Go-specific .gitignore entries',
|
|
2230
|
+
check: (ctx) => { if (!ctx.files.some(f => /go\.mod$/.test(f))) return null; const gi = ctx.fileContent('.gitignore') || ''; return /vendor[/]|\*\.exe|\*\.test|\*\.out|[/]bin[/]/i.test(gi); },
|
|
2231
|
+
impact: 'low',
|
|
2232
|
+
category: 'go',
|
|
2233
|
+
fix: 'Add Go-specific entries to .gitignore (vendor/, *.exe, *.test, /bin/).',
|
|
2234
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2235
|
+
confidence: 0.7,
|
|
2236
|
+
},
|
|
2237
|
+
// ============================================================
|
|
2238
|
+
// === RUST STACK CHECKS (category: 'rust') ===================
|
|
2239
|
+
// ============================================================
|
|
2240
|
+
|
|
2241
|
+
aiderRustCargoTomlExists: {
|
|
2242
|
+
id: 'AD-RS01',
|
|
2243
|
+
name: 'Cargo.toml exists with edition field',
|
|
2244
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const cargo = ctx.fileContent('Cargo.toml') || ''; return /edition\s*=/.test(cargo); },
|
|
2245
|
+
impact: 'high',
|
|
2246
|
+
category: 'rust',
|
|
2247
|
+
fix: 'Ensure Cargo.toml exists and specifies the edition field (e.g., edition = "2021").',
|
|
2248
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2249
|
+
confidence: 0.7,
|
|
2250
|
+
},
|
|
2251
|
+
|
|
2252
|
+
aiderRustCargoLockCommitted: {
|
|
2253
|
+
id: 'AD-RS02',
|
|
2254
|
+
name: 'Cargo.lock committed (for binary crates)',
|
|
2255
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; return ctx.files.some(f => /Cargo\.lock$/.test(f)); },
|
|
2256
|
+
impact: 'high',
|
|
2257
|
+
category: 'rust',
|
|
2258
|
+
fix: 'Commit Cargo.lock for binary crates to ensure reproducible builds.',
|
|
2259
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2260
|
+
confidence: 0.7,
|
|
2261
|
+
},
|
|
2262
|
+
|
|
2263
|
+
aiderRustClippyConfigured: {
|
|
2264
|
+
id: 'AD-RS03',
|
|
2265
|
+
name: 'Clippy configured (CI or .cargo/config.toml)',
|
|
2266
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const ci = ctx.fileContent('.github/workflows/ci.yml') || ctx.fileContent('.github/workflows/rust.yml') || ''; const cargoConfig = ctx.fileContent('.cargo/config.toml') || ''; return /clippy/i.test(ci + cargoConfig); },
|
|
2267
|
+
impact: 'medium',
|
|
2268
|
+
category: 'rust',
|
|
2269
|
+
fix: 'Configure clippy in CI or .cargo/config.toml for lint enforcement.',
|
|
2270
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2271
|
+
confidence: 0.7,
|
|
2272
|
+
},
|
|
2273
|
+
|
|
2274
|
+
aiderRustFmtConfigured: {
|
|
2275
|
+
id: 'AD-RS04',
|
|
2276
|
+
name: 'rustfmt configured (rustfmt.toml or .rustfmt.toml)',
|
|
2277
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; return ctx.files.some(f => /rustfmt\.toml$|\.rustfmt\.toml$/.test(f)); },
|
|
2278
|
+
impact: 'medium',
|
|
2279
|
+
category: 'rust',
|
|
2280
|
+
fix: 'Create rustfmt.toml or .rustfmt.toml to configure code formatting.',
|
|
2281
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2282
|
+
confidence: 0.7,
|
|
2283
|
+
},
|
|
2284
|
+
|
|
2285
|
+
aiderRustCargoTestDocumented: {
|
|
2286
|
+
id: 'AD-RS05',
|
|
2287
|
+
name: 'cargo test documented in instructions',
|
|
2288
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /cargo test/i.test(docs); },
|
|
2289
|
+
impact: 'high',
|
|
2290
|
+
category: 'rust',
|
|
2291
|
+
fix: 'Document cargo test command in project instructions.',
|
|
2292
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2293
|
+
confidence: 0.7,
|
|
2294
|
+
},
|
|
2295
|
+
|
|
2296
|
+
aiderRustCargoBuildDocumented: {
|
|
2297
|
+
id: 'AD-RS06',
|
|
2298
|
+
name: 'cargo build/check documented in instructions',
|
|
2299
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /cargo (?:build|check)/i.test(docs); },
|
|
2300
|
+
impact: 'high',
|
|
2301
|
+
category: 'rust',
|
|
2302
|
+
fix: 'Document cargo build or cargo check command in project instructions.',
|
|
2303
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2304
|
+
confidence: 0.7,
|
|
2305
|
+
},
|
|
2306
|
+
|
|
2307
|
+
aiderRustUnsafePolicyDocumented: {
|
|
2308
|
+
id: 'AD-RS07',
|
|
2309
|
+
name: 'Unsafe code policy documented',
|
|
2310
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /unsafe|#!?\[forbid\(unsafe|#!?\[deny\(unsafe/i.test(docs); },
|
|
2311
|
+
impact: 'high',
|
|
2312
|
+
category: 'rust',
|
|
2313
|
+
fix: 'Document unsafe code policy (forbidden, minimized, or where allowed).',
|
|
2314
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2315
|
+
confidence: 0.7,
|
|
2316
|
+
},
|
|
2317
|
+
|
|
2318
|
+
aiderRustErrorHandlingStrategy: {
|
|
2319
|
+
id: 'AD-RS08',
|
|
2320
|
+
name: 'Error handling strategy (anyhow/thiserror in deps)',
|
|
2321
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const cargo = ctx.fileContent('Cargo.toml') || ''; return /anyhow|thiserror|eyre|color-eyre/i.test(cargo); },
|
|
2322
|
+
impact: 'medium',
|
|
2323
|
+
category: 'rust',
|
|
2324
|
+
fix: 'Use anyhow (applications) or thiserror (libraries) for structured error handling.',
|
|
2325
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2326
|
+
confidence: 0.7,
|
|
2327
|
+
},
|
|
2328
|
+
|
|
2329
|
+
aiderRustFeatureFlagsDocumented: {
|
|
2330
|
+
id: 'AD-RS09',
|
|
2331
|
+
name: 'Feature flags documented (Cargo.toml [features])',
|
|
2332
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const cargo = ctx.fileContent('Cargo.toml') || ''; if (!/\[features\]/i.test(cargo)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /feature|--features|--all-features/i.test(docs); },
|
|
2333
|
+
impact: 'medium',
|
|
2334
|
+
category: 'rust',
|
|
2335
|
+
fix: 'Document feature flags and their purpose in project instructions.',
|
|
2336
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2337
|
+
confidence: 0.7,
|
|
2338
|
+
},
|
|
2339
|
+
|
|
2340
|
+
aiderRustWorkspaceConfig: {
|
|
2341
|
+
id: 'AD-RS10',
|
|
2342
|
+
name: 'Workspace config if multi-crate (Cargo.toml [workspace])',
|
|
2343
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const cargo = ctx.fileContent('Cargo.toml') || ''; if (ctx.files.filter(f => /Cargo\.toml$/.test(f)).length <= 1) return null; return /\[workspace\]/i.test(cargo); },
|
|
2344
|
+
impact: 'medium',
|
|
2345
|
+
category: 'rust',
|
|
2346
|
+
fix: 'Configure [workspace] in root Cargo.toml for multi-crate projects.',
|
|
2347
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2348
|
+
confidence: 0.7,
|
|
2349
|
+
},
|
|
2350
|
+
|
|
2351
|
+
aiderRustMsrvSpecified: {
|
|
2352
|
+
id: 'AD-RS11',
|
|
2353
|
+
name: 'MSRV specified (rust-version field)',
|
|
2354
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const cargo = ctx.fileContent('Cargo.toml') || ''; return /rust-version\s*=/.test(cargo); },
|
|
2355
|
+
impact: 'medium',
|
|
2356
|
+
category: 'rust',
|
|
2357
|
+
fix: 'Specify rust-version (MSRV) in Cargo.toml for compatibility guarantees.',
|
|
2358
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2359
|
+
confidence: 0.7,
|
|
2360
|
+
},
|
|
2361
|
+
|
|
2362
|
+
aiderRustDocCommentsEncouraged: {
|
|
2363
|
+
id: 'AD-RS12',
|
|
2364
|
+
name: 'Doc comments (///) encouraged in instructions',
|
|
2365
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /doc comment|\/{3}|rustdoc|cargo doc/i.test(docs); },
|
|
2366
|
+
impact: 'low',
|
|
2367
|
+
category: 'rust',
|
|
2368
|
+
fix: 'Encourage /// doc comments and cargo doc in project instructions.',
|
|
2369
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2370
|
+
confidence: 0.7,
|
|
2371
|
+
},
|
|
2372
|
+
|
|
2373
|
+
aiderRustBenchmarksConfigured: {
|
|
2374
|
+
id: 'AD-RS13',
|
|
2375
|
+
name: 'Criterion benchmarks mentioned (benches/ dir)',
|
|
2376
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; return ctx.files.some(f => /benches[/]/.test(f)) || /criterion/i.test(ctx.fileContent('Cargo.toml') || ''); },
|
|
2377
|
+
impact: 'low',
|
|
2378
|
+
category: 'rust',
|
|
2379
|
+
fix: 'Set up criterion benchmarks in benches/ directory.',
|
|
2380
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2381
|
+
confidence: 0.7,
|
|
2382
|
+
},
|
|
2383
|
+
|
|
2384
|
+
aiderRustCrossCompilationDocumented: {
|
|
2385
|
+
id: 'AD-RS14',
|
|
2386
|
+
name: 'Cross-compilation documented',
|
|
2387
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /cross.?compil|--target|rustup target|cargo build.*--target/i.test(docs); },
|
|
2388
|
+
impact: 'low',
|
|
2389
|
+
category: 'rust',
|
|
2390
|
+
fix: 'Document cross-compilation targets and setup instructions.',
|
|
2391
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2392
|
+
confidence: 0.7,
|
|
2393
|
+
},
|
|
2394
|
+
|
|
2395
|
+
aiderRustMemorySafetyDocumented: {
|
|
2396
|
+
id: 'AD-RS15',
|
|
2397
|
+
name: 'Memory safety patterns documented',
|
|
2398
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /ownership|borrow|lifetime|memory.?safe|Arc|Rc|RefCell/i.test(docs); },
|
|
2399
|
+
impact: 'medium',
|
|
2400
|
+
category: 'rust',
|
|
2401
|
+
fix: 'Document memory safety patterns (ownership, borrowing, lifetime conventions).',
|
|
2402
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2403
|
+
confidence: 0.7,
|
|
2404
|
+
},
|
|
2405
|
+
|
|
2406
|
+
aiderRustAsyncRuntimeDocumented: {
|
|
2407
|
+
id: 'AD-RS16',
|
|
2408
|
+
name: 'Async runtime documented (tokio/async-std in deps)',
|
|
2409
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const cargo = ctx.fileContent('Cargo.toml') || ''; if (!/tokio|async-std|smol/i.test(cargo)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /tokio|async-std|async|await|runtime/i.test(docs); },
|
|
2410
|
+
impact: 'medium',
|
|
2411
|
+
category: 'rust',
|
|
2412
|
+
fix: 'Document async runtime choice and patterns (tokio, async-std).',
|
|
2413
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2414
|
+
confidence: 0.7,
|
|
2415
|
+
},
|
|
2416
|
+
|
|
2417
|
+
aiderRustSerdeDocumented: {
|
|
2418
|
+
id: 'AD-RS17',
|
|
2419
|
+
name: 'Serde patterns documented',
|
|
2420
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const cargo = ctx.fileContent('Cargo.toml') || ''; if (!/serde/i.test(cargo)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /serde|Serialize|Deserialize|serde_json|serde_yaml/i.test(docs); },
|
|
2421
|
+
impact: 'medium',
|
|
2422
|
+
category: 'rust',
|
|
2423
|
+
fix: 'Document serde serialization/deserialization patterns and conventions.',
|
|
2424
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2425
|
+
confidence: 0.7,
|
|
2426
|
+
},
|
|
2427
|
+
|
|
2428
|
+
aiderRustCargoAuditConfigured: {
|
|
2429
|
+
id: 'AD-RS18',
|
|
2430
|
+
name: 'cargo-audit configured in CI',
|
|
2431
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const ci = ctx.fileContent('.github/workflows/ci.yml') || ctx.fileContent('.github/workflows/rust.yml') || ctx.fileContent('.github/workflows/audit.yml') || ''; return /cargo.?audit|advisory/i.test(ci); },
|
|
2432
|
+
impact: 'medium',
|
|
2433
|
+
category: 'rust',
|
|
2434
|
+
fix: 'Configure cargo-audit in CI for vulnerability scanning.',
|
|
2435
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2436
|
+
confidence: 0.7,
|
|
2437
|
+
},
|
|
2438
|
+
|
|
2439
|
+
aiderRustWasmTargetDocumented: {
|
|
2440
|
+
id: 'AD-RS19',
|
|
2441
|
+
name: 'WASM target documented if applicable',
|
|
2442
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const cargo = ctx.fileContent('Cargo.toml') || ''; if (!/wasm|wasm-bindgen|wasm-pack/i.test(cargo)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /wasm|WebAssembly|wasm-pack|wasm-bindgen/i.test(docs); },
|
|
2443
|
+
impact: 'low',
|
|
2444
|
+
category: 'rust',
|
|
2445
|
+
fix: 'Document WASM target configuration and build process.',
|
|
2446
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2447
|
+
confidence: 0.7,
|
|
2448
|
+
},
|
|
2449
|
+
|
|
2450
|
+
aiderRustGitignore: {
|
|
2451
|
+
id: 'AD-RS20',
|
|
2452
|
+
name: 'Rust .gitignore includes target/',
|
|
2453
|
+
check: (ctx) => { if (!ctx.files.some(f => /Cargo\.toml$/.test(f))) return null; const gi = ctx.fileContent('.gitignore') || ''; return /target[/]|[/]target/i.test(gi); },
|
|
2454
|
+
impact: 'medium',
|
|
2455
|
+
category: 'rust',
|
|
2456
|
+
fix: 'Add target/ to .gitignore for Rust build artifacts.',
|
|
2457
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2458
|
+
confidence: 0.7,
|
|
2459
|
+
},
|
|
2460
|
+
|
|
2461
|
+
// ============================================================
|
|
2462
|
+
// === JAVA/SPRING STACK CHECKS (category: 'java') ============
|
|
2463
|
+
// ============================================================
|
|
2464
|
+
|
|
2465
|
+
aiderJavaBuildFileExists: {
|
|
2466
|
+
id: 'AD-JV01',
|
|
2467
|
+
name: 'pom.xml or build.gradle exists',
|
|
2468
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; return true; },
|
|
2469
|
+
impact: 'high',
|
|
2470
|
+
category: 'java',
|
|
2471
|
+
fix: 'Ensure pom.xml or build.gradle exists for Java projects.',
|
|
2472
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2473
|
+
confidence: 0.7,
|
|
2474
|
+
},
|
|
2475
|
+
|
|
2476
|
+
aiderJavaVersionSpecified: {
|
|
2477
|
+
id: 'AD-JV02',
|
|
2478
|
+
name: 'Java version specified',
|
|
2479
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const pom = ctx.fileContent('pom.xml') || ''; const gradle = ctx.fileContent('build.gradle') || ctx.fileContent('build.gradle.kts') || ''; return /java\.version|maven\.compiler\.source|sourceCompatibility|JavaVersion/i.test(pom + gradle) || ctx.files.some(f => /\.java-version$/.test(f)); },
|
|
2480
|
+
impact: 'high',
|
|
2481
|
+
category: 'java',
|
|
2482
|
+
fix: 'Specify Java version in pom.xml properties, build.gradle, or .java-version file.',
|
|
2483
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2484
|
+
confidence: 0.7,
|
|
2485
|
+
},
|
|
2486
|
+
|
|
2487
|
+
aiderJavaWrapperCommitted: {
|
|
2488
|
+
id: 'AD-JV03',
|
|
2489
|
+
name: 'Maven/Gradle wrapper committed (mvnw or gradlew)',
|
|
2490
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; return ctx.files.some(f => /mvnw$|gradlew$/.test(f)); },
|
|
2491
|
+
impact: 'high',
|
|
2492
|
+
category: 'java',
|
|
2493
|
+
fix: 'Commit mvnw (Maven) or gradlew (Gradle) wrapper for reproducible builds.',
|
|
2494
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2495
|
+
confidence: 0.7,
|
|
2496
|
+
},
|
|
2497
|
+
|
|
2498
|
+
aiderJavaSpringBootVersion: {
|
|
2499
|
+
id: 'AD-JV04',
|
|
2500
|
+
name: 'Spring Boot version documented if Spring project',
|
|
2501
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const pom = ctx.fileContent('pom.xml') || ''; const gradle = ctx.fileContent('build.gradle') || ctx.fileContent('build.gradle.kts') || ''; if (!/spring-boot/i.test(pom + gradle)) return null; return /spring-boot.*\d+\.\d+/i.test(pom + gradle); },
|
|
2502
|
+
impact: 'high',
|
|
2503
|
+
category: 'java',
|
|
2504
|
+
fix: 'Document Spring Boot version in build configuration.',
|
|
2505
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2506
|
+
confidence: 0.7,
|
|
2507
|
+
},
|
|
2508
|
+
|
|
2509
|
+
aiderJavaApplicationConfig: {
|
|
2510
|
+
id: 'AD-JV05',
|
|
2511
|
+
name: 'application.yml or application.properties exists',
|
|
2512
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; return ctx.files.some(f => /application\.ya?ml$|application\.properties$/.test(f)); },
|
|
2513
|
+
impact: 'medium',
|
|
2514
|
+
category: 'java',
|
|
2515
|
+
fix: 'Create application.yml or application.properties for Spring configuration.',
|
|
2516
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2517
|
+
confidence: 0.7,
|
|
2518
|
+
},
|
|
2519
|
+
|
|
2520
|
+
aiderJavaTestFramework: {
|
|
2521
|
+
id: 'AD-JV06',
|
|
2522
|
+
name: 'Test framework configured (JUnit/TestNG in deps)',
|
|
2523
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const pom = ctx.fileContent('pom.xml') || ''; const gradle = ctx.fileContent('build.gradle') || ctx.fileContent('build.gradle.kts') || ''; return /junit|testng|spring-boot-starter-test/i.test(pom + gradle); },
|
|
2524
|
+
impact: 'high',
|
|
2525
|
+
category: 'java',
|
|
2526
|
+
fix: 'Configure JUnit or TestNG test framework in project dependencies.',
|
|
2527
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2528
|
+
confidence: 0.7,
|
|
2529
|
+
},
|
|
2530
|
+
|
|
2531
|
+
aiderJavaCodeStyleConfigured: {
|
|
2532
|
+
id: 'AD-JV07',
|
|
2533
|
+
name: 'Code style configured (checkstyle.xml, spotbugs)',
|
|
2534
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; return ctx.files.some(f => /checkstyle\.xml$|spotbugs.*\.xml$/.test(f)) || /checkstyle|spotbugs|google-java-format/i.test((ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || '')); },
|
|
2535
|
+
impact: 'medium',
|
|
2536
|
+
category: 'java',
|
|
2537
|
+
fix: 'Configure checkstyle or spotbugs for code quality enforcement.',
|
|
2538
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2539
|
+
confidence: 0.7,
|
|
2540
|
+
},
|
|
2541
|
+
|
|
2542
|
+
aiderJavaSpringProfilesDocumented: {
|
|
2543
|
+
id: 'AD-JV08',
|
|
2544
|
+
name: 'Spring profiles documented',
|
|
2545
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const deps = (ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || ''); if (!/spring/i.test(deps)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /spring[.]profiles|@Profile|SPRING_PROFILES_ACTIVE/i.test(docs); },
|
|
2546
|
+
impact: 'medium',
|
|
2547
|
+
category: 'java',
|
|
2548
|
+
fix: 'Document Spring profiles and their configuration in project instructions.',
|
|
2549
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2550
|
+
confidence: 0.7,
|
|
2551
|
+
},
|
|
2552
|
+
|
|
2553
|
+
aiderJavaDatabaseMigration: {
|
|
2554
|
+
id: 'AD-JV09',
|
|
2555
|
+
name: 'Database migration configured (flyway/liquibase)',
|
|
2556
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const deps = (ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || ''); return /flyway|liquibase/i.test(deps) || ctx.files.some(f => /db[/]migration|flyway|liquibase/i.test(f)); },
|
|
2557
|
+
impact: 'medium',
|
|
2558
|
+
category: 'java',
|
|
2559
|
+
fix: 'Configure database migration tool (Flyway or Liquibase) for schema management.',
|
|
2560
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2561
|
+
confidence: 0.7,
|
|
2562
|
+
},
|
|
2563
|
+
|
|
2564
|
+
aiderJavaLombokDocumented: {
|
|
2565
|
+
id: 'AD-JV10',
|
|
2566
|
+
name: 'Lombok/MapStruct documented if used',
|
|
2567
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const deps = (ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || ''); if (!/lombok|mapstruct/i.test(deps)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /lombok|mapstruct/i.test(docs); },
|
|
2568
|
+
impact: 'low',
|
|
2569
|
+
category: 'java',
|
|
2570
|
+
fix: 'Document Lombok/MapStruct usage and IDE setup requirements.',
|
|
2571
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2572
|
+
confidence: 0.7,
|
|
2573
|
+
},
|
|
2574
|
+
|
|
2575
|
+
aiderJavaApiDocsConfigured: {
|
|
2576
|
+
id: 'AD-JV11',
|
|
2577
|
+
name: 'API docs configured (springdoc/swagger deps)',
|
|
2578
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const deps = (ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || ''); return /springdoc|swagger|openapi/i.test(deps); },
|
|
2579
|
+
impact: 'medium',
|
|
2580
|
+
category: 'java',
|
|
2581
|
+
fix: 'Configure API documentation with springdoc-openapi or Swagger.',
|
|
2582
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2583
|
+
confidence: 0.7,
|
|
2584
|
+
},
|
|
2585
|
+
|
|
2586
|
+
aiderJavaSecurityConfigured: {
|
|
2587
|
+
id: 'AD-JV12',
|
|
2588
|
+
name: 'Security configuration documented',
|
|
2589
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const deps = (ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || ''); if (!/spring-security|spring-boot-starter-security/i.test(deps)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /security|authentication|authorization|SecurityConfig|@EnableWebSecurity/i.test(docs); },
|
|
2590
|
+
impact: 'high',
|
|
2591
|
+
category: 'java',
|
|
2592
|
+
fix: 'Document Spring Security configuration and authentication setup.',
|
|
2593
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2594
|
+
confidence: 0.7,
|
|
2595
|
+
},
|
|
2596
|
+
|
|
2597
|
+
aiderJavaActuatorConfigured: {
|
|
2598
|
+
id: 'AD-JV13',
|
|
2599
|
+
name: 'Actuator/health checks configured',
|
|
2600
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const deps = (ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || ''); return /actuator|spring-boot-starter-actuator/i.test(deps); },
|
|
2601
|
+
impact: 'medium',
|
|
2602
|
+
category: 'java',
|
|
2603
|
+
fix: 'Configure Spring Boot Actuator for health checks and monitoring.',
|
|
2604
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2605
|
+
confidence: 0.7,
|
|
2606
|
+
},
|
|
2607
|
+
|
|
2608
|
+
aiderJavaLoggingConfigured: {
|
|
2609
|
+
id: 'AD-JV14',
|
|
2610
|
+
name: 'Logging configured (logback.xml or log4j2.xml)',
|
|
2611
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; return ctx.files.some(f => /logback.*\.xml$|log4j2?.*\.xml$|logging\.properties$/.test(f)); },
|
|
2612
|
+
impact: 'medium',
|
|
2613
|
+
category: 'java',
|
|
2614
|
+
fix: 'Configure logging with logback.xml or log4j2.xml.',
|
|
2615
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2616
|
+
confidence: 0.7,
|
|
2617
|
+
},
|
|
2618
|
+
|
|
2619
|
+
aiderJavaMultiModuleProject: {
|
|
2620
|
+
id: 'AD-JV15',
|
|
2621
|
+
name: 'Multi-module project configured if applicable',
|
|
2622
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const buildFiles = ctx.files.filter(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f)); if (buildFiles.length <= 1) return null; const rootPom = ctx.fileContent('pom.xml') || ''; const rootGradle = ctx.fileContent('settings.gradle') || ctx.fileContent('settings.gradle.kts') || ''; return /<modules>|include\s/i.test(rootPom + rootGradle); },
|
|
2623
|
+
impact: 'medium',
|
|
2624
|
+
category: 'java',
|
|
2625
|
+
fix: 'Configure multi-module project structure in root build file.',
|
|
2626
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2627
|
+
confidence: 0.7,
|
|
2628
|
+
},
|
|
2629
|
+
|
|
2630
|
+
aiderJavaDockerConfigured: {
|
|
2631
|
+
id: 'AD-JV16',
|
|
2632
|
+
name: 'Docker build configured (Dockerfile or Jib plugin)',
|
|
2633
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const df = ctx.fileContent('Dockerfile') || ''; const deps = (ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || ''); return /FROM.*(?:openjdk|eclipse-temurin|amazoncorretto)/i.test(df) || /jib/i.test(deps); },
|
|
2634
|
+
impact: 'medium',
|
|
2635
|
+
category: 'java',
|
|
2636
|
+
fix: 'Configure Docker build with Dockerfile or Jib plugin.',
|
|
2637
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2638
|
+
confidence: 0.7,
|
|
2639
|
+
},
|
|
2640
|
+
|
|
2641
|
+
aiderJavaEnvConfigsSeparated: {
|
|
2642
|
+
id: 'AD-JV17',
|
|
2643
|
+
name: 'Environment-specific configs separated',
|
|
2644
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; return ctx.files.some(f => /application-(?:dev|prod|staging|test|local)\.(?:ya?ml|properties)$/.test(f)); },
|
|
2645
|
+
impact: 'medium',
|
|
2646
|
+
category: 'java',
|
|
2647
|
+
fix: 'Separate environment configs (application-dev.yml, application-prod.yml, etc.).',
|
|
2648
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2649
|
+
confidence: 0.7,
|
|
2650
|
+
},
|
|
2651
|
+
|
|
2652
|
+
aiderJavaNoSecretsInConfig: {
|
|
2653
|
+
id: 'AD-JV18',
|
|
2654
|
+
name: 'No secrets in application.yml/properties',
|
|
2655
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const appYml = ctx.files.filter(f => /application.*\.ya?ml$|application.*\.properties$/.test(f)).map(f => ctx.fileContent(f) || '').join(''); if (!appYml) return null; return !/password\s*[:=]\s*[^$\{\s][^\s]{8,}|secret\s*[:=]\s*[^$\{\s][^\s]{8,}/i.test(appYml); },
|
|
2656
|
+
impact: 'critical',
|
|
2657
|
+
category: 'java',
|
|
2658
|
+
fix: 'Move secrets to environment variables or external secret management, not application config files.',
|
|
2659
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2660
|
+
confidence: 0.7,
|
|
2661
|
+
},
|
|
2662
|
+
|
|
2663
|
+
aiderJavaIntegrationTestsSeparate: {
|
|
2664
|
+
id: 'AD-JV19',
|
|
2665
|
+
name: 'Integration tests separate from unit tests',
|
|
2666
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; return ctx.files.some(f => /src[/](?:integration-?test|it)[/]|IT\.java$|Integration(?:Test)?\.java$/.test(f)) || /failsafe|integration-test/i.test((ctx.fileContent('pom.xml') || '') + (ctx.fileContent('build.gradle') || '') + (ctx.fileContent('build.gradle.kts') || '')); },
|
|
2667
|
+
impact: 'medium',
|
|
2668
|
+
category: 'java',
|
|
2669
|
+
fix: 'Separate integration tests from unit tests using Maven Failsafe or dedicated source set.',
|
|
2670
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2671
|
+
confidence: 0.7,
|
|
2672
|
+
},
|
|
2673
|
+
|
|
2674
|
+
aiderJavaBuildCommandDocumented: {
|
|
2675
|
+
id: 'AD-JV20',
|
|
2676
|
+
name: 'Build command documented in instructions',
|
|
2677
|
+
check: (ctx) => { if (!ctx.files.some(f => /pom\.xml$|build\.gradle$|build\.gradle\.kts$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /mvn|gradle|mvnw|gradlew|maven|./i.test(docs) && /build|compile|package|install/i.test(docs); },
|
|
2678
|
+
impact: 'high',
|
|
2679
|
+
category: 'java',
|
|
2680
|
+
fix: 'Document build command (mvnw package, gradlew build) in project instructions.',
|
|
2681
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2682
|
+
confidence: 0.7,
|
|
2683
|
+
},
|
|
2684
|
+
|
|
2685
|
+
// ============================================================
|
|
2686
|
+
// === RUBY/RAILS STACK CHECKS (category: 'ruby') =============
|
|
2687
|
+
// ============================================================
|
|
2688
|
+
|
|
2689
|
+
aiderrubyGemfileExists: {
|
|
2690
|
+
id: 'AD-RB01',
|
|
2691
|
+
name: 'Gemfile exists',
|
|
2692
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; return true; },
|
|
2693
|
+
impact: 'high',
|
|
2694
|
+
category: 'ruby',
|
|
2695
|
+
fix: 'Create a Gemfile to manage Ruby dependencies.',
|
|
2696
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2697
|
+
confidence: 0.7,
|
|
2698
|
+
},
|
|
2699
|
+
|
|
2700
|
+
aiderrubyGemfileLockCommitted: {
|
|
2701
|
+
id: 'AD-RB02',
|
|
2702
|
+
name: 'Gemfile.lock committed',
|
|
2703
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; return ctx.files.some(f => /Gemfile\.lock$/.test(f)); },
|
|
2704
|
+
impact: 'high',
|
|
2705
|
+
category: 'ruby',
|
|
2706
|
+
fix: 'Commit Gemfile.lock to version control for reproducible builds.',
|
|
2707
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2708
|
+
confidence: 0.7,
|
|
2709
|
+
},
|
|
2710
|
+
|
|
2711
|
+
aiderrubyVersionSpecified: {
|
|
2712
|
+
id: 'AD-RB03',
|
|
2713
|
+
name: 'Ruby version specified (.ruby-version)',
|
|
2714
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; return ctx.files.some(f => /\.ruby-version$/.test(f)) || /ruby ['"]~?\d/i.test(ctx.fileContent('Gemfile') || ''); },
|
|
2715
|
+
impact: 'medium',
|
|
2716
|
+
category: 'ruby',
|
|
2717
|
+
fix: 'Create .ruby-version or specify ruby version in Gemfile.',
|
|
2718
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2719
|
+
confidence: 0.7,
|
|
2720
|
+
},
|
|
2721
|
+
|
|
2722
|
+
aiderrubyRubocopConfigured: {
|
|
2723
|
+
id: 'AD-RB04',
|
|
2724
|
+
name: 'RuboCop configured (.rubocop.yml)',
|
|
2725
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; return ctx.files.some(f => /\.rubocop\.ya?ml$/.test(f)); },
|
|
2726
|
+
impact: 'medium',
|
|
2727
|
+
category: 'ruby',
|
|
2728
|
+
fix: 'Add .rubocop.yml to configure Ruby style checking.',
|
|
2729
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2730
|
+
confidence: 0.7,
|
|
2731
|
+
},
|
|
2732
|
+
|
|
2733
|
+
aiderrubyTestFrameworkConfigured: {
|
|
2734
|
+
id: 'AD-RB05',
|
|
2735
|
+
name: 'RSpec or Minitest configured (spec/ or test/)',
|
|
2736
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; return ctx.files.some(f => /^spec\/|^test\/|spec_helper\.rb$|test_helper\.rb$/.test(f)); },
|
|
2737
|
+
impact: 'high',
|
|
2738
|
+
category: 'ruby',
|
|
2739
|
+
fix: 'Configure RSpec (spec/) or Minitest (test/) for testing.',
|
|
2740
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2741
|
+
confidence: 0.7,
|
|
2742
|
+
},
|
|
2743
|
+
|
|
2744
|
+
aiderrubyRailsCredentialsDocumented: {
|
|
2745
|
+
id: 'AD-RB06',
|
|
2746
|
+
name: 'Rails credentials documented in instructions',
|
|
2747
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; if (!ctx.files.some(f => /config\/credentials/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /credentials|encrypted|master\.key|secret_key_base/i.test(docs); },
|
|
2748
|
+
impact: 'high',
|
|
2749
|
+
category: 'ruby',
|
|
2750
|
+
fix: 'Document Rails credentials management (rails credentials:edit) in project instructions.',
|
|
2751
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2752
|
+
confidence: 0.7,
|
|
2753
|
+
},
|
|
2754
|
+
|
|
2755
|
+
aiderrubyMigrationsDocumented: {
|
|
2756
|
+
id: 'AD-RB07',
|
|
2757
|
+
name: 'Database migrations documented (db/migrate/)',
|
|
2758
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; if (!ctx.files.some(f => /db\/migrate\//.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /migration|migrate|db:migrate|rails db/i.test(docs); },
|
|
2759
|
+
impact: 'medium',
|
|
2760
|
+
category: 'ruby',
|
|
2761
|
+
fix: 'Document database migration workflow (rails db:migrate) in project instructions.',
|
|
2762
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2763
|
+
confidence: 0.7,
|
|
2764
|
+
},
|
|
2765
|
+
|
|
2766
|
+
aiderrubyBundlerAuditConfigured: {
|
|
2767
|
+
id: 'AD-RB08',
|
|
2768
|
+
name: 'Bundler audit configured',
|
|
2769
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; const gf = ctx.fileContent('Gemfile') || ''; return /bundler-audit|bundle.audit/i.test(gf) || ctx.files.some(f => /\.bundler-audit/i.test(f)); },
|
|
2770
|
+
impact: 'medium',
|
|
2771
|
+
category: 'ruby',
|
|
2772
|
+
fix: 'Add bundler-audit gem for dependency vulnerability scanning.',
|
|
2773
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2774
|
+
confidence: 0.7,
|
|
2775
|
+
},
|
|
2776
|
+
|
|
2777
|
+
aiderrubyTypeCheckingConfigured: {
|
|
2778
|
+
id: 'AD-RB09',
|
|
2779
|
+
name: 'Sorbet/RBS type checking configured (sorbet/ or sig/)',
|
|
2780
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; return ctx.files.some(f => /sorbet\/|sig\/|\.rbs$/.test(f)) || /sorbet|tapioca/i.test(ctx.fileContent('Gemfile') || ''); },
|
|
2781
|
+
impact: 'low',
|
|
2782
|
+
category: 'ruby',
|
|
2783
|
+
fix: 'Configure Sorbet or RBS for type checking.',
|
|
2784
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2785
|
+
confidence: 0.7,
|
|
2786
|
+
},
|
|
2787
|
+
|
|
2788
|
+
aiderrubyRailsRoutesDocumented: {
|
|
2789
|
+
id: 'AD-RB10',
|
|
2790
|
+
name: 'Rails routes documented',
|
|
2791
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; if (!ctx.files.some(f => /config\/routes\.rb$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /routes|endpoints|api.*path|REST/i.test(docs); },
|
|
2792
|
+
impact: 'medium',
|
|
2793
|
+
category: 'ruby',
|
|
2794
|
+
fix: 'Document key routes and API endpoints in project instructions.',
|
|
2795
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2796
|
+
confidence: 0.7,
|
|
2797
|
+
},
|
|
2798
|
+
|
|
2799
|
+
aiderrubyBackgroundJobsDocumented: {
|
|
2800
|
+
id: 'AD-RB11',
|
|
2801
|
+
name: 'Background jobs documented (Sidekiq/GoodJob)',
|
|
2802
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; const gf = ctx.fileContent('Gemfile') || ''; if (!/sidekiq|good_job|delayed_job|resque/i.test(gf)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /sidekiq|good_job|delayed_job|resque|background.*job|worker|queue/i.test(docs); },
|
|
2803
|
+
impact: 'medium',
|
|
2804
|
+
category: 'ruby',
|
|
2805
|
+
fix: 'Document background job framework and worker configuration.',
|
|
2806
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2807
|
+
confidence: 0.7,
|
|
2808
|
+
},
|
|
2809
|
+
|
|
2810
|
+
aiderrubyRailsEnvConfigsSeparated: {
|
|
2811
|
+
id: 'AD-RB12',
|
|
2812
|
+
name: 'Rails environment configs separated (config/environments/)',
|
|
2813
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; return ctx.files.some(f => /config\/environments\//.test(f)); },
|
|
2814
|
+
impact: 'medium',
|
|
2815
|
+
category: 'ruby',
|
|
2816
|
+
fix: 'Ensure config/environments/ has separate files for development, test, and production.',
|
|
2817
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2818
|
+
confidence: 0.7,
|
|
2819
|
+
},
|
|
2820
|
+
|
|
2821
|
+
aiderrubyAssetPipelineDocumented: {
|
|
2822
|
+
id: 'AD-RB13',
|
|
2823
|
+
name: 'Asset pipeline documented',
|
|
2824
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; const gf = ctx.fileContent('Gemfile') || ''; if (!/sprockets|propshaft|webpacker|jsbundling|cssbundling/i.test(gf)) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /asset|sprockets|propshaft|webpacker|jsbundling|cssbundling|esbuild|vite/i.test(docs); },
|
|
2825
|
+
impact: 'low',
|
|
2826
|
+
category: 'ruby',
|
|
2827
|
+
fix: 'Document asset pipeline configuration (Sprockets, Propshaft, or JS/CSS bundling).',
|
|
2828
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2829
|
+
confidence: 0.7,
|
|
2830
|
+
},
|
|
2831
|
+
|
|
2832
|
+
aiderrubyMasterKeyInGitignore: {
|
|
2833
|
+
id: 'AD-RB14',
|
|
2834
|
+
name: 'Rails master.key in .gitignore',
|
|
2835
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; if (!ctx.files.some(f => /config\/credentials/.test(f))) return null; const gi = ctx.fileContent('.gitignore') || ''; return /master\.key/i.test(gi); },
|
|
2836
|
+
impact: 'critical',
|
|
2837
|
+
category: 'ruby',
|
|
2838
|
+
fix: 'Add config/master.key to .gitignore to prevent secret leakage.',
|
|
2839
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2840
|
+
confidence: 0.7,
|
|
2841
|
+
},
|
|
2842
|
+
|
|
2843
|
+
aiderrubyTestDataFactories: {
|
|
2844
|
+
id: 'AD-RB15',
|
|
2845
|
+
name: 'Factory Bot/fixtures for test data (spec/factories/)',
|
|
2846
|
+
check: (ctx) => { if (!ctx.files.some(f => /Gemfile$/.test(f))) return null; return ctx.files.some(f => /spec\/factories\/|test\/fixtures\//.test(f)) || /factory_bot|fabrication/i.test(ctx.fileContent('Gemfile') || ''); },
|
|
2847
|
+
impact: 'medium',
|
|
2848
|
+
category: 'ruby',
|
|
2849
|
+
fix: 'Configure Factory Bot (spec/factories/) or fixtures (test/fixtures/) for test data.',
|
|
2850
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2851
|
+
confidence: 0.7,
|
|
2852
|
+
},
|
|
2853
|
+
|
|
2854
|
+
// ============================================================
|
|
2855
|
+
// === .NET/C# STACK CHECKS (category: 'dotnet') ==============
|
|
2856
|
+
// ============================================================
|
|
2857
|
+
|
|
2858
|
+
aiderdotnetProjectExists: {
|
|
2859
|
+
id: 'AD-DN01',
|
|
2860
|
+
name: '.csproj or .sln exists',
|
|
2861
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return true; },
|
|
2862
|
+
impact: 'high',
|
|
2863
|
+
category: 'dotnet',
|
|
2864
|
+
fix: 'Ensure .csproj or .sln file exists for .NET projects.',
|
|
2865
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2866
|
+
confidence: 0.7,
|
|
2867
|
+
},
|
|
2868
|
+
|
|
2869
|
+
aiderdotnetVersionSpecified: {
|
|
2870
|
+
id: 'AD-DN02',
|
|
2871
|
+
name: '.NET version specified (global.json or TargetFramework)',
|
|
2872
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => /global\.json$/.test(f)) || ctx.files.some(f => { if (!/\.csproj$/.test(f)) return false; const c = ctx.fileContent(f) || ''; return /TargetFramework/i.test(c); }); },
|
|
2873
|
+
impact: 'medium',
|
|
2874
|
+
category: 'dotnet',
|
|
2875
|
+
fix: 'Create global.json or ensure TargetFramework is set in .csproj.',
|
|
2876
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2877
|
+
confidence: 0.7,
|
|
2878
|
+
},
|
|
2879
|
+
|
|
2880
|
+
aiderdotnetPackagesLock: {
|
|
2881
|
+
id: 'AD-DN03',
|
|
2882
|
+
name: 'NuGet packages lock (packages.lock.json)',
|
|
2883
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => /packages\.lock\.json$/.test(f)); },
|
|
2884
|
+
impact: 'medium',
|
|
2885
|
+
category: 'dotnet',
|
|
2886
|
+
fix: 'Enable NuGet lock file (packages.lock.json) for reproducible restores.',
|
|
2887
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2888
|
+
confidence: 0.7,
|
|
2889
|
+
},
|
|
2890
|
+
|
|
2891
|
+
aiderdotnetTestDocumented: {
|
|
2892
|
+
id: 'AD-DN04',
|
|
2893
|
+
name: 'dotnet test documented',
|
|
2894
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /dotnet test|xunit|nunit|mstest/i.test(docs); },
|
|
2895
|
+
impact: 'high',
|
|
2896
|
+
category: 'dotnet',
|
|
2897
|
+
fix: 'Document how to run tests with dotnet test in project instructions.',
|
|
2898
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2899
|
+
confidence: 0.7,
|
|
2900
|
+
},
|
|
2901
|
+
|
|
2902
|
+
aiderdotnetEditorConfigExists: {
|
|
2903
|
+
id: 'AD-DN05',
|
|
2904
|
+
name: 'EditorConfig configured (.editorconfig)',
|
|
2905
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => /\.editorconfig$/.test(f)); },
|
|
2906
|
+
impact: 'medium',
|
|
2907
|
+
category: 'dotnet',
|
|
2908
|
+
fix: 'Add .editorconfig for consistent code style across the team.',
|
|
2909
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2910
|
+
confidence: 0.7,
|
|
2911
|
+
},
|
|
2912
|
+
|
|
2913
|
+
aiderdotnetRoslynAnalyzers: {
|
|
2914
|
+
id: 'AD-DN06',
|
|
2915
|
+
name: 'Roslyn analyzers configured',
|
|
2916
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => { if (!/\.csproj$/.test(f)) return false; const c = ctx.fileContent(f) || ''; return /Analyzer|StyleCop|SonarAnalyzer|Microsoft\.CodeAnalysis/i.test(c); }); },
|
|
2917
|
+
impact: 'medium',
|
|
2918
|
+
category: 'dotnet',
|
|
2919
|
+
fix: 'Add Roslyn analyzers (StyleCop.Analyzers, Microsoft.CodeAnalysis) to the project.',
|
|
2920
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2921
|
+
confidence: 0.7,
|
|
2922
|
+
},
|
|
2923
|
+
|
|
2924
|
+
aiderdotnetAppsettingsExists: {
|
|
2925
|
+
id: 'AD-DN07',
|
|
2926
|
+
name: 'appsettings.json exists',
|
|
2927
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => /appsettings\.json$/.test(f)); },
|
|
2928
|
+
impact: 'medium',
|
|
2929
|
+
category: 'dotnet',
|
|
2930
|
+
fix: 'Create appsettings.json for application configuration.',
|
|
2931
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2932
|
+
confidence: 0.7,
|
|
2933
|
+
},
|
|
2934
|
+
|
|
2935
|
+
aiderdotnetUserSecretsDocumented: {
|
|
2936
|
+
id: 'AD-DN08',
|
|
2937
|
+
name: 'User secrets configured in instructions',
|
|
2938
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /user.?secrets|dotnet secrets|Secret Manager/i.test(docs); },
|
|
2939
|
+
impact: 'high',
|
|
2940
|
+
category: 'dotnet',
|
|
2941
|
+
fix: 'Document user secrets management (dotnet user-secrets) in project instructions.',
|
|
2942
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2943
|
+
confidence: 0.7,
|
|
2944
|
+
},
|
|
2945
|
+
|
|
2946
|
+
aiderdotnetEfMigrations: {
|
|
2947
|
+
id: 'AD-DN09',
|
|
2948
|
+
name: 'Entity Framework migrations (Migrations/ directory)',
|
|
2949
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => /Migrations\//.test(f)); },
|
|
2950
|
+
impact: 'medium',
|
|
2951
|
+
category: 'dotnet',
|
|
2952
|
+
fix: 'Document Entity Framework migration workflow (dotnet ef migrations).',
|
|
2953
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2954
|
+
confidence: 0.7,
|
|
2955
|
+
},
|
|
2956
|
+
|
|
2957
|
+
aiderdotnetHealthChecks: {
|
|
2958
|
+
id: 'AD-DN10',
|
|
2959
|
+
name: 'Health checks configured',
|
|
2960
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => { if (!/\.cs$/.test(f)) return false; const c = ctx.fileContent(f) || ''; return /AddHealthChecks|MapHealthChecks|IHealthCheck/i.test(c); }); },
|
|
2961
|
+
impact: 'medium',
|
|
2962
|
+
category: 'dotnet',
|
|
2963
|
+
fix: 'Configure health checks with AddHealthChecks() and MapHealthChecks().',
|
|
2964
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2965
|
+
confidence: 0.7,
|
|
2966
|
+
},
|
|
2967
|
+
|
|
2968
|
+
aiderdotnetSwaggerConfigured: {
|
|
2969
|
+
id: 'AD-DN11',
|
|
2970
|
+
name: 'Swagger/OpenAPI configured',
|
|
2971
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => { if (!/\.cs$|.csproj$/.test(f)) return false; const c = ctx.fileContent(f) || ''; return /Swashbuckle|AddSwaggerGen|UseSwagger|NSwag|AddOpenApi/i.test(c); }); },
|
|
2972
|
+
impact: 'medium',
|
|
2973
|
+
category: 'dotnet',
|
|
2974
|
+
fix: 'Configure Swagger/OpenAPI with Swashbuckle or NSwag.',
|
|
2975
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2976
|
+
confidence: 0.7,
|
|
2977
|
+
},
|
|
2978
|
+
|
|
2979
|
+
aiderdotnetNoConnectionStringsInConfig: {
|
|
2980
|
+
id: 'AD-DN12',
|
|
2981
|
+
name: 'No connection strings in appsettings.json',
|
|
2982
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; const settings = ctx.fileContent('appsettings.json') || ''; if (!settings) return null; return !/Server=.*Password=|Data Source=.*Password=/i.test(settings); },
|
|
2983
|
+
impact: 'critical',
|
|
2984
|
+
category: 'dotnet',
|
|
2985
|
+
fix: 'Move connection strings with passwords to user secrets or environment variables.',
|
|
2986
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2987
|
+
confidence: 0.7,
|
|
2988
|
+
},
|
|
2989
|
+
|
|
2990
|
+
aiderdotnetDockerSupport: {
|
|
2991
|
+
id: 'AD-DN13',
|
|
2992
|
+
name: 'Docker support configured',
|
|
2993
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; const df = ctx.fileContent('Dockerfile') || ''; return /dotnet|aspnet|sdk/i.test(df); },
|
|
2994
|
+
impact: 'medium',
|
|
2995
|
+
category: 'dotnet',
|
|
2996
|
+
fix: 'Add Dockerfile with official .NET SDK/ASP.NET base images.',
|
|
2997
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
2998
|
+
confidence: 0.7,
|
|
2999
|
+
},
|
|
3000
|
+
|
|
3001
|
+
aiderdotnetTestProjectSeparate: {
|
|
3002
|
+
id: 'AD-DN14',
|
|
3003
|
+
name: 'Unit test project separate (.Tests.csproj)',
|
|
3004
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => /\.Tests?\.csproj$|Tests?\/.*\.csproj$/.test(f)); },
|
|
3005
|
+
impact: 'high',
|
|
3006
|
+
category: 'dotnet',
|
|
3007
|
+
fix: 'Create separate test project (e.g., MyApp.Tests.csproj) for unit tests.',
|
|
3008
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3009
|
+
confidence: 0.7,
|
|
3010
|
+
},
|
|
3011
|
+
|
|
3012
|
+
aiderdotnetGlobalUsingsDocumented: {
|
|
3013
|
+
id: 'AD-DN15',
|
|
3014
|
+
name: 'GlobalUsings documented',
|
|
3015
|
+
check: (ctx) => { if (!ctx.files.some(f => /\.csproj$|\.sln$/.test(f))) return null; return ctx.files.some(f => /GlobalUsings\.cs$|Usings\.cs$/.test(f)) || ctx.files.some(f => { if (!/\.csproj$/.test(f)) return false; const c = ctx.fileContent(f) || ''; return /ImplicitUsings/i.test(c); }); },
|
|
3016
|
+
impact: 'low',
|
|
3017
|
+
category: 'dotnet',
|
|
3018
|
+
fix: 'Document global using directives in GlobalUsings.cs or enable ImplicitUsings in .csproj.',
|
|
3019
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3020
|
+
confidence: 0.7,
|
|
3021
|
+
},
|
|
3022
|
+
|
|
3023
|
+
// ============================================================
|
|
3024
|
+
// === PHP/LARAVEL STACK CHECKS (category: 'php') ==============
|
|
3025
|
+
// ============================================================
|
|
3026
|
+
|
|
3027
|
+
aiderphpComposerJsonExists: {
|
|
3028
|
+
id: 'AD-PHP01',
|
|
3029
|
+
name: 'composer.json exists',
|
|
3030
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; return true; },
|
|
3031
|
+
impact: 'high',
|
|
3032
|
+
category: 'php',
|
|
3033
|
+
fix: 'Create composer.json to manage PHP dependencies.',
|
|
3034
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3035
|
+
confidence: 0.7,
|
|
3036
|
+
},
|
|
3037
|
+
|
|
3038
|
+
aiderphpComposerLockCommitted: {
|
|
3039
|
+
id: 'AD-PHP02',
|
|
3040
|
+
name: 'composer.lock committed',
|
|
3041
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; return ctx.files.some(f => /composer\.lock$/.test(f)); },
|
|
3042
|
+
impact: 'high',
|
|
3043
|
+
category: 'php',
|
|
3044
|
+
fix: 'Commit composer.lock to version control for reproducible installs.',
|
|
3045
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3046
|
+
confidence: 0.7,
|
|
3047
|
+
},
|
|
3048
|
+
|
|
3049
|
+
aiderphpVersionSpecified: {
|
|
3050
|
+
id: 'AD-PHP03',
|
|
3051
|
+
name: 'PHP version specified (composer.json require.php)',
|
|
3052
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; const cj = ctx.fileContent('composer.json') || ''; return /"php"s*:/i.test(cj); },
|
|
3053
|
+
impact: 'medium',
|
|
3054
|
+
category: 'php',
|
|
3055
|
+
fix: 'Specify PHP version requirement in composer.json require section.',
|
|
3056
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3057
|
+
confidence: 0.7,
|
|
3058
|
+
},
|
|
3059
|
+
|
|
3060
|
+
aiderphpStaticAnalysisConfigured: {
|
|
3061
|
+
id: 'AD-PHP04',
|
|
3062
|
+
name: 'PHPStan/Psalm configured (phpstan.neon)',
|
|
3063
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; return ctx.files.some(f => /phpstan\.neon$|phpstan\.neon\.dist$|psalm\.xml$/.test(f)); },
|
|
3064
|
+
impact: 'medium',
|
|
3065
|
+
category: 'php',
|
|
3066
|
+
fix: 'Configure PHPStan (phpstan.neon) or Psalm (psalm.xml) for static analysis.',
|
|
3067
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3068
|
+
confidence: 0.7,
|
|
3069
|
+
},
|
|
3070
|
+
|
|
3071
|
+
aiderphpCsFixerConfigured: {
|
|
3072
|
+
id: 'AD-PHP05',
|
|
3073
|
+
name: 'PHP CS Fixer configured (.php-cs-fixer.php)',
|
|
3074
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; return ctx.files.some(f => /\.php-cs-fixer\.php$|\.php-cs-fixer\.dist\.php$/.test(f)); },
|
|
3075
|
+
impact: 'medium',
|
|
3076
|
+
category: 'php',
|
|
3077
|
+
fix: 'Add .php-cs-fixer.php for consistent code formatting.',
|
|
3078
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3079
|
+
confidence: 0.7,
|
|
3080
|
+
},
|
|
3081
|
+
|
|
3082
|
+
aiderphpUnitConfigured: {
|
|
3083
|
+
id: 'AD-PHP06',
|
|
3084
|
+
name: 'PHPUnit configured (phpunit.xml)',
|
|
3085
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; return ctx.files.some(f => /phpunit\.xml$|phpunit\.xml\.dist$/.test(f)); },
|
|
3086
|
+
impact: 'high',
|
|
3087
|
+
category: 'php',
|
|
3088
|
+
fix: 'Configure PHPUnit with phpunit.xml for testing.',
|
|
3089
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3090
|
+
confidence: 0.7,
|
|
3091
|
+
},
|
|
3092
|
+
|
|
3093
|
+
aiderphpLaravelEnvExample: {
|
|
3094
|
+
id: 'AD-PHP07',
|
|
3095
|
+
name: 'Laravel .env.example exists',
|
|
3096
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; if (!ctx.files.some(f => /artisan$/.test(f))) return null; return ctx.files.some(f => /\.env\.example$/.test(f)); },
|
|
3097
|
+
impact: 'high',
|
|
3098
|
+
category: 'php',
|
|
3099
|
+
fix: 'Create .env.example with all required environment variables documented.',
|
|
3100
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3101
|
+
confidence: 0.7,
|
|
3102
|
+
},
|
|
3103
|
+
|
|
3104
|
+
aiderphpLaravelAppKeyNotCommitted: {
|
|
3105
|
+
id: 'AD-PHP08',
|
|
3106
|
+
name: 'Laravel APP_KEY not committed',
|
|
3107
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; if (!ctx.files.some(f => /artisan$/.test(f))) return null; const env = ctx.fileContent('.env') || ''; if (!env) return null; return !/APP_KEY=base64:[A-Za-z0-9+/=]{30,}/i.test(env); },
|
|
3108
|
+
impact: 'critical',
|
|
3109
|
+
category: 'php',
|
|
3110
|
+
fix: 'Ensure .env with APP_KEY is in .gitignore — never commit application keys.',
|
|
3111
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3112
|
+
confidence: 0.7,
|
|
3113
|
+
},
|
|
3114
|
+
|
|
3115
|
+
aiderphpLaravelMigrationsExist: {
|
|
3116
|
+
id: 'AD-PHP09',
|
|
3117
|
+
name: 'Laravel migrations exist (database/migrations/)',
|
|
3118
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; if (!ctx.files.some(f => /artisan$/.test(f))) return null; return ctx.files.some(f => /database\/migrations\//.test(f)); },
|
|
3119
|
+
impact: 'medium',
|
|
3120
|
+
category: 'php',
|
|
3121
|
+
fix: 'Create database migrations in database/migrations/ directory.',
|
|
3122
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3123
|
+
confidence: 0.7,
|
|
3124
|
+
},
|
|
3125
|
+
|
|
3126
|
+
aiderphpArtisanCommandsDocumented: {
|
|
3127
|
+
id: 'AD-PHP10',
|
|
3128
|
+
name: 'Artisan commands documented',
|
|
3129
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; if (!ctx.files.some(f => /artisan$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /artisan|php artisan|make:model|make:controller|migrate/i.test(docs); },
|
|
3130
|
+
impact: 'medium',
|
|
3131
|
+
category: 'php',
|
|
3132
|
+
fix: 'Document key Artisan commands (migrate, seed, make:*) in project instructions.',
|
|
3133
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3134
|
+
confidence: 0.7,
|
|
3135
|
+
},
|
|
3136
|
+
|
|
3137
|
+
aiderphpQueueWorkerDocumented: {
|
|
3138
|
+
id: 'AD-PHP11',
|
|
3139
|
+
name: 'Queue worker documented',
|
|
3140
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; const cj = ctx.fileContent('composer.json') || ''; if (!/horizon|queue/i.test(cj) && !ctx.files.some(f => /artisan$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /queue|horizon|worker|job|dispatch/i.test(docs); },
|
|
3141
|
+
impact: 'medium',
|
|
3142
|
+
category: 'php',
|
|
3143
|
+
fix: 'Document queue worker setup (php artisan queue:work, Horizon).',
|
|
3144
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3145
|
+
confidence: 0.7,
|
|
3146
|
+
},
|
|
3147
|
+
|
|
3148
|
+
aiderphpLaravelPintConfigured: {
|
|
3149
|
+
id: 'AD-PHP12',
|
|
3150
|
+
name: 'Laravel Pint configured (pint.json)',
|
|
3151
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; return ctx.files.some(f => /pint\.json$/.test(f)) || /laravel\/pint/i.test(ctx.fileContent('composer.json') || ''); },
|
|
3152
|
+
impact: 'low',
|
|
3153
|
+
category: 'php',
|
|
3154
|
+
fix: 'Configure Laravel Pint (pint.json) for code style enforcement.',
|
|
3155
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3156
|
+
confidence: 0.7,
|
|
3157
|
+
},
|
|
3158
|
+
|
|
3159
|
+
aiderphpAssetBundlingDocumented: {
|
|
3160
|
+
id: 'AD-PHP13',
|
|
3161
|
+
name: 'Vite/Mix asset bundling documented',
|
|
3162
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; if (!ctx.files.some(f => /vite\.config\.|webpack\.mix\.js$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /vite|mix|asset|npm run dev|npm run build/i.test(docs); },
|
|
3163
|
+
impact: 'low',
|
|
3164
|
+
category: 'php',
|
|
3165
|
+
fix: 'Document asset bundling setup (Vite or Mix) in project instructions.',
|
|
3166
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3167
|
+
confidence: 0.7,
|
|
3168
|
+
},
|
|
3169
|
+
|
|
3170
|
+
aiderphpConfigCachingDocumented: {
|
|
3171
|
+
id: 'AD-PHP14',
|
|
3172
|
+
name: 'Laravel config caching documented',
|
|
3173
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; if (!ctx.files.some(f => /artisan$/.test(f))) return null; const docs = (ctx.claudeMdContent ? ctx.claudeMdContent() : ctx.fileContent('CLAUDE.md')) || ctx.fileContent('README.md') || ''; return /config:cache|config:clear|route:cache|optimize/i.test(docs); },
|
|
3174
|
+
impact: 'low',
|
|
3175
|
+
category: 'php',
|
|
3176
|
+
fix: 'Document config/route caching strategy (php artisan config:cache) for production.',
|
|
3177
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3178
|
+
confidence: 0.7,
|
|
3179
|
+
},
|
|
3180
|
+
|
|
3181
|
+
aiderphpComposerScriptsDefined: {
|
|
3182
|
+
id: 'AD-PHP15',
|
|
3183
|
+
name: 'Composer scripts defined',
|
|
3184
|
+
check: (ctx) => { if (!ctx.files.some(f => /composer\.json$/.test(f))) return null; const cj = ctx.fileContent('composer.json') || ''; return /"scripts"s*:/i.test(cj); },
|
|
3185
|
+
impact: 'medium',
|
|
3186
|
+
category: 'php',
|
|
3187
|
+
fix: 'Define composer scripts for common tasks (test, lint, analyze) in composer.json.',
|
|
3188
|
+
// sourceUrl assigned by attachSourceUrls via category mapping
|
|
3189
|
+
confidence: 0.7,
|
|
3190
|
+
},
|
|
3191
|
+
|
|
3192
|
+
|
|
3193
|
+
};
|
|
3194
|
+
|
|
3195
|
+
Object.assign(AIDER_TECHNIQUES, buildStackChecks({
|
|
3196
|
+
platform: 'aider',
|
|
3197
|
+
objectPrefix: 'aider',
|
|
3198
|
+
idPrefix: 'AD',
|
|
3199
|
+
docs: (ctx) => [
|
|
3200
|
+
ctx.fileContent('README.md') || '',
|
|
3201
|
+
ctx.fileContent('CONTRIBUTING.md') || '',
|
|
3202
|
+
ctx.fileContent('AGENTS.md') || '',
|
|
3203
|
+
ctx.fileContent('CLAUDE.md') || '',
|
|
3204
|
+
].filter(Boolean).join('\n'),
|
|
3205
|
+
}));
|
|
3206
|
+
|
|
3207
|
+
attachSourceUrls('aider', AIDER_TECHNIQUES);
|
|
3208
|
+
|
|
3209
|
+
module.exports = {
|
|
3210
|
+
AIDER_TECHNIQUES,
|
|
3211
|
+
};
|