coverme-scanner 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +227 -0
  2. package/commands/scan.md +317 -0
  3. package/dist/cli/index.d.ts +3 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +39 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/cli/init.d.ts +6 -0
  8. package/dist/cli/init.d.ts.map +1 -0
  9. package/dist/cli/init.js +636 -0
  10. package/dist/cli/init.js.map +1 -0
  11. package/dist/cli/scan.d.ts +11 -0
  12. package/dist/cli/scan.d.ts.map +1 -0
  13. package/dist/cli/scan.js +498 -0
  14. package/dist/cli/scan.js.map +1 -0
  15. package/dist/report/generator.d.ts +48 -0
  16. package/dist/report/generator.d.ts.map +1 -0
  17. package/dist/report/generator.js +368 -0
  18. package/dist/report/generator.js.map +1 -0
  19. package/dist/report/index.d.ts +35 -0
  20. package/dist/report/index.d.ts.map +1 -0
  21. package/dist/report/index.js +463 -0
  22. package/dist/report/index.js.map +1 -0
  23. package/dist/templates/report.html +796 -0
  24. package/dist/types.d.ts +94 -0
  25. package/dist/types.d.ts.map +1 -0
  26. package/dist/types.js +3 -0
  27. package/dist/types.js.map +1 -0
  28. package/package.json +48 -0
  29. package/src/cli/index.ts +43 -0
  30. package/src/cli/init.ts +611 -0
  31. package/src/cli/scan.ts +483 -0
  32. package/src/prompts/architecture-reviewer.md +171 -0
  33. package/src/prompts/consensus-builder.md +247 -0
  34. package/src/prompts/context-discovery.md +174 -0
  35. package/src/prompts/cross-validator.md +224 -0
  36. package/src/prompts/deep-dive-expert.md +224 -0
  37. package/src/prompts/dependency-auditor.md +190 -0
  38. package/src/prompts/performance-hunter.md +200 -0
  39. package/src/prompts/quality-analyzer.md +150 -0
  40. package/src/prompts/report-generator.md +285 -0
  41. package/src/prompts/security-scanner.md +180 -0
  42. package/src/report/generator.ts +382 -0
  43. package/src/report/index.ts +483 -0
  44. package/src/templates/report.html +796 -0
  45. package/src/types.ts +107 -0
  46. package/tsconfig.json +20 -0
@@ -0,0 +1,796 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{projectName}} - Security Report</title>
7
+ <style>
8
+ :root {
9
+ --bg: #fafafa;
10
+ --card: #ffffff;
11
+ --text: #111111;
12
+ --text-muted: #666666;
13
+ --text-light: #999999;
14
+ --border: #eaeaea;
15
+ --accent: #000000;
16
+
17
+ --critical: #e11d48;
18
+ --high: #f97316;
19
+ --medium: #eab308;
20
+ --low: #3b82f6;
21
+ --success: #10b981;
22
+
23
+ --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
24
+ --mono: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
25
+ }
26
+
27
+ * { margin: 0; padding: 0; box-sizing: border-box; }
28
+
29
+ body {
30
+ font-family: var(--font);
31
+ font-size: 15px;
32
+ line-height: 1.7;
33
+ color: var(--text);
34
+ background: var(--bg);
35
+ -webkit-font-smoothing: antialiased;
36
+ }
37
+
38
+ .container {
39
+ max-width: 800px;
40
+ margin: 0 auto;
41
+ padding: 60px 24px;
42
+ }
43
+
44
+ /* Header */
45
+ header {
46
+ text-align: center;
47
+ margin-bottom: 60px;
48
+ }
49
+
50
+ .logo {
51
+ font-size: 11px;
52
+ font-weight: 600;
53
+ letter-spacing: 2px;
54
+ text-transform: uppercase;
55
+ color: var(--text-light);
56
+ margin-bottom: 20px;
57
+ }
58
+
59
+ h1 {
60
+ font-size: 42px;
61
+ font-weight: 700;
62
+ letter-spacing: -1px;
63
+ margin-bottom: 8px;
64
+ }
65
+
66
+ .date {
67
+ color: var(--text-muted);
68
+ font-size: 14px;
69
+ }
70
+
71
+ /* Score */
72
+ .score-section {
73
+ display: flex;
74
+ justify-content: center;
75
+ gap: 40px;
76
+ margin: 50px 0;
77
+ padding: 40px;
78
+ background: var(--card);
79
+ border-radius: 16px;
80
+ box-shadow: 0 1px 3px rgba(0,0,0,0.04);
81
+ }
82
+
83
+ .score-circle {
84
+ width: 100px;
85
+ height: 100px;
86
+ border-radius: 50%;
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ font-size: 36px;
91
+ font-weight: 700;
92
+ color: white;
93
+ text-transform: uppercase;
94
+ }
95
+
96
+ .grade-a { background: var(--success); }
97
+ .grade-b { background: #22c55e; }
98
+ .grade-c { background: var(--medium); }
99
+ .grade-d { background: var(--high); }
100
+ .grade-f { background: var(--critical); }
101
+
102
+ .stats {
103
+ display: flex;
104
+ align-items: center;
105
+ gap: 32px;
106
+ }
107
+
108
+ .stat {
109
+ text-align: center;
110
+ }
111
+
112
+ .stat-num {
113
+ font-size: 28px;
114
+ font-weight: 700;
115
+ line-height: 1;
116
+ }
117
+
118
+ .stat-label {
119
+ font-size: 11px;
120
+ text-transform: uppercase;
121
+ letter-spacing: 1px;
122
+ color: var(--text-light);
123
+ margin-top: 6px;
124
+ }
125
+
126
+ .stat-critical .stat-num { color: var(--critical); }
127
+ .stat-high .stat-num { color: var(--high); }
128
+ .stat-medium .stat-num { color: var(--medium); }
129
+ .stat-low .stat-num { color: var(--low); }
130
+
131
+ /* Copy All Button */
132
+ .copy-all {
133
+ text-align: center;
134
+ margin: 40px 0;
135
+ }
136
+
137
+ .copy-all-btn {
138
+ display: inline-flex;
139
+ align-items: center;
140
+ gap: 10px;
141
+ padding: 16px 32px;
142
+ background: var(--accent);
143
+ color: white;
144
+ border: none;
145
+ border-radius: 100px;
146
+ font-size: 14px;
147
+ font-weight: 600;
148
+ cursor: pointer;
149
+ transition: transform 0.2s, opacity 0.2s;
150
+ }
151
+
152
+ .copy-all-btn:hover {
153
+ transform: translateY(-2px);
154
+ opacity: 0.9;
155
+ }
156
+
157
+ .copy-all-btn.copied {
158
+ background: var(--success);
159
+ }
160
+
161
+ .copy-hint {
162
+ margin-top: 10px;
163
+ font-size: 13px;
164
+ color: var(--text-light);
165
+ }
166
+
167
+ /* Section */
168
+ section {
169
+ margin-bottom: 50px;
170
+ }
171
+
172
+ .section-title {
173
+ font-size: 13px;
174
+ font-weight: 600;
175
+ text-transform: uppercase;
176
+ letter-spacing: 1.5px;
177
+ color: var(--text-light);
178
+ margin-bottom: 20px;
179
+ padding-bottom: 12px;
180
+ border-bottom: 1px solid var(--border);
181
+ }
182
+
183
+ /* Finding Card */
184
+ .finding {
185
+ background: var(--card);
186
+ border-radius: 12px;
187
+ margin-bottom: 16px;
188
+ overflow: hidden;
189
+ box-shadow: 0 1px 3px rgba(0,0,0,0.04);
190
+ border: 1px solid var(--border);
191
+ }
192
+
193
+ .finding-header {
194
+ display: flex;
195
+ align-items: center;
196
+ gap: 16px;
197
+ padding: 20px 24px;
198
+ cursor: pointer;
199
+ transition: background 0.15s;
200
+ }
201
+
202
+ .finding-header:hover {
203
+ background: var(--bg);
204
+ }
205
+
206
+ .severity-badge {
207
+ padding: 4px 10px;
208
+ border-radius: 100px;
209
+ font-size: 10px;
210
+ font-weight: 700;
211
+ text-transform: uppercase;
212
+ letter-spacing: 0.5px;
213
+ color: white;
214
+ }
215
+
216
+ .sev-critical { background: var(--critical); }
217
+ .sev-high { background: var(--high); }
218
+ .sev-medium { background: var(--medium); color: #000; }
219
+ .sev-low { background: var(--low); }
220
+
221
+ .finding-title {
222
+ flex: 1;
223
+ font-weight: 600;
224
+ font-size: 15px;
225
+ }
226
+
227
+ .finding-file {
228
+ font-family: var(--mono);
229
+ font-size: 12px;
230
+ color: var(--text-light);
231
+ }
232
+
233
+ .expand-arrow {
234
+ color: var(--text-light);
235
+ transition: transform 0.2s;
236
+ }
237
+
238
+ .finding.open .expand-arrow {
239
+ transform: rotate(180deg);
240
+ }
241
+
242
+ /* Finding Body */
243
+ .finding-body {
244
+ display: none;
245
+ padding: 0 24px 24px;
246
+ }
247
+
248
+ .finding.open .finding-body {
249
+ display: block;
250
+ }
251
+
252
+ .location {
253
+ display: inline-flex;
254
+ align-items: center;
255
+ gap: 8px;
256
+ padding: 8px 14px;
257
+ background: #f4f4f5;
258
+ border-radius: 8px;
259
+ font-family: var(--mono);
260
+ font-size: 13px;
261
+ margin-bottom: 20px;
262
+ }
263
+
264
+ .location-file { color: var(--text); }
265
+ .location-line { color: var(--text-muted); }
266
+
267
+ .copy-btn {
268
+ padding: 4px 10px;
269
+ background: white;
270
+ border: 1px solid var(--border);
271
+ border-radius: 6px;
272
+ font-size: 11px;
273
+ cursor: pointer;
274
+ margin-left: 8px;
275
+ }
276
+
277
+ .copy-btn:hover { background: var(--bg); }
278
+
279
+ /* Info Boxes */
280
+ .info-grid {
281
+ display: flex;
282
+ flex-direction: column;
283
+ gap: 12px;
284
+ margin: 20px 0;
285
+ }
286
+
287
+ .info-box {
288
+ padding: 16px 20px;
289
+ border-radius: 10px;
290
+ font-size: 14px;
291
+ line-height: 1.6;
292
+ }
293
+
294
+ .info-label {
295
+ font-size: 11px;
296
+ font-weight: 700;
297
+ text-transform: uppercase;
298
+ letter-spacing: 1px;
299
+ margin-bottom: 8px;
300
+ display: block;
301
+ }
302
+
303
+ .info-problem {
304
+ background: #fef2f2;
305
+ border-left: 3px solid var(--critical);
306
+ }
307
+ .info-problem .info-label { color: var(--critical); }
308
+
309
+ .info-why {
310
+ background: #fff7ed;
311
+ border-left: 3px solid var(--high);
312
+ }
313
+ .info-why .info-label { color: var(--high); }
314
+
315
+ .info-context {
316
+ background: #f0f9ff;
317
+ border-left: 3px solid var(--low);
318
+ }
319
+ .info-context .info-label { color: var(--low); }
320
+
321
+ .info-check {
322
+ background: #fefce8;
323
+ border-left: 3px solid var(--medium);
324
+ }
325
+ .info-check .info-label { color: #a16207; }
326
+
327
+ .info-fix {
328
+ background: #f0fdf4;
329
+ border-left: 3px solid var(--success);
330
+ }
331
+ .info-fix .info-label { color: var(--success); }
332
+
333
+ /* Code Block */
334
+ .code-block {
335
+ background: #18181b;
336
+ border-radius: 10px;
337
+ padding: 16px 20px;
338
+ margin: 16px 0;
339
+ overflow-x: auto;
340
+ }
341
+
342
+ .code-block code {
343
+ font-family: var(--mono);
344
+ font-size: 13px;
345
+ color: #e4e4e7;
346
+ line-height: 1.6;
347
+ }
348
+
349
+ /* Claude Prompt */
350
+ .prompt-box {
351
+ margin-top: 20px;
352
+ padding: 16px 20px;
353
+ background: #18181b;
354
+ border-radius: 10px;
355
+ }
356
+
357
+ .prompt-header {
358
+ display: flex;
359
+ justify-content: space-between;
360
+ align-items: center;
361
+ margin-bottom: 12px;
362
+ }
363
+
364
+ .prompt-label {
365
+ font-size: 11px;
366
+ font-weight: 600;
367
+ text-transform: uppercase;
368
+ letter-spacing: 1px;
369
+ color: #71717a;
370
+ }
371
+
372
+ .prompt-text {
373
+ font-family: var(--mono);
374
+ font-size: 12px;
375
+ color: #a1a1aa;
376
+ line-height: 1.7;
377
+ white-space: pre-wrap;
378
+ }
379
+
380
+ /* Positive Section */
381
+ .positive-list {
382
+ list-style: none;
383
+ }
384
+
385
+ .positive-item {
386
+ display: flex;
387
+ align-items: flex-start;
388
+ gap: 14px;
389
+ padding: 14px 0;
390
+ border-bottom: 1px solid var(--border);
391
+ }
392
+
393
+ .positive-item:last-child { border-bottom: none; }
394
+
395
+ .check-icon {
396
+ width: 22px;
397
+ height: 22px;
398
+ background: #dcfce7;
399
+ color: var(--success);
400
+ border-radius: 50%;
401
+ display: flex;
402
+ align-items: center;
403
+ justify-content: center;
404
+ font-size: 12px;
405
+ flex-shrink: 0;
406
+ }
407
+
408
+ /* Summary */
409
+ .summary-box {
410
+ background: var(--card);
411
+ border-radius: 12px;
412
+ padding: 24px;
413
+ margin-bottom: 20px;
414
+ border: 1px solid var(--border);
415
+ }
416
+
417
+ .summary-text {
418
+ color: var(--text-muted);
419
+ line-height: 1.8;
420
+ }
421
+
422
+ /* Footer */
423
+ footer {
424
+ margin-top: 80px;
425
+ padding-top: 30px;
426
+ border-top: 1px solid var(--border);
427
+ text-align: center;
428
+ font-size: 13px;
429
+ color: var(--text-light);
430
+ }
431
+
432
+ footer a {
433
+ color: var(--text-muted);
434
+ text-decoration: none;
435
+ }
436
+
437
+ /* Print */
438
+ @media print {
439
+ body { background: white; }
440
+ .container { padding: 20px; max-width: 100%; }
441
+ .finding-body { display: block !important; }
442
+ .copy-all, .copy-btn, .prompt-box { display: none; }
443
+ .finding { break-inside: avoid; }
444
+ }
445
+ </style>
446
+ </head>
447
+ <body>
448
+ <div class="container">
449
+ <header>
450
+ <div class="logo">Security Report</div>
451
+ <h1>{{projectName}}</h1>
452
+ <p class="date">{{scanDate}}</p>
453
+ </header>
454
+
455
+ <!-- Score Section -->
456
+ <div class="score-section">
457
+ <div class="score-circle grade-{{scoreGrade}}">{{scoreGrade}}</div>
458
+ <div class="stats">
459
+ <div class="stat stat-critical">
460
+ <div class="stat-num">{{criticalCount}}</div>
461
+ <div class="stat-label">Critical</div>
462
+ </div>
463
+ <div class="stat stat-high">
464
+ <div class="stat-num">{{highCount}}</div>
465
+ <div class="stat-label">High</div>
466
+ </div>
467
+ <div class="stat stat-medium">
468
+ <div class="stat-num">{{mediumCount}}</div>
469
+ <div class="stat-label">Medium</div>
470
+ </div>
471
+ <div class="stat stat-low">
472
+ <div class="stat-num">{{lowCount}}</div>
473
+ <div class="stat-label">Low</div>
474
+ </div>
475
+ </div>
476
+ </div>
477
+
478
+ <!-- Summary -->
479
+ <div class="summary-box">
480
+ <p class="summary-text">{{executiveSummary}}</p>
481
+ </div>
482
+
483
+ <!-- Copy All -->
484
+ <div class="copy-all">
485
+ <button class="copy-all-btn" onclick="copyAllFixes(this)">
486
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
487
+ <rect x="9" y="9" width="13" height="13" rx="2"></rect>
488
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
489
+ </svg>
490
+ Copy All Fixes for Claude Code
491
+ </button>
492
+ <p class="copy-hint">Paste directly into Claude Code to fix everything</p>
493
+ </div>
494
+
495
+ <!-- Critical Findings -->
496
+ {{#if criticalFindings}}
497
+ <section>
498
+ <h2 class="section-title">Critical Issues</h2>
499
+ {{#each criticalFindings}}
500
+ <div class="finding" data-finding-id="{{id}}" data-file="{{file}}" data-line="{{line}}" data-description="{{description}}" data-recommendation="{{recommendation}}" data-severity="critical" data-why="{{why}}" data-context="{{context}}" data-checkbefore="{{checkBefore}}">
501
+ <div class="finding-header" onclick="this.parentElement.classList.toggle('open')">
502
+ <span class="severity-badge sev-critical">Critical</span>
503
+ <span class="finding-title">{{title}}</span>
504
+ <span class="finding-file">{{file}}</span>
505
+ <svg class="expand-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
506
+ </div>
507
+ <div class="finding-body">
508
+ <div class="location">
509
+ <span class="location-file">{{file}}</span>
510
+ <span class="location-line">:{{line}}</span>
511
+ <button class="copy-btn" onclick="event.stopPropagation(); copyText('{{file}}:{{line}}', this)">Copy</button>
512
+ </div>
513
+
514
+ <div class="info-grid">
515
+ <div class="info-box info-problem">
516
+ <span class="info-label">Problem</span>
517
+ <p>{{description}}</p>
518
+ </div>
519
+
520
+ {{#if why}}
521
+ <div class="info-box info-why">
522
+ <span class="info-label">Why This Matters</span>
523
+ <p>{{why}}</p>
524
+ </div>
525
+ {{/if}}
526
+
527
+ {{#if context}}
528
+ <div class="info-box info-context">
529
+ <span class="info-label">Code Context</span>
530
+ <p>{{context}}</p>
531
+ </div>
532
+ {{/if}}
533
+
534
+ {{#if checkBefore}}
535
+ <div class="info-box info-check">
536
+ <span class="info-label">Check Before Fixing</span>
537
+ <p>{{checkBefore}}</p>
538
+ </div>
539
+ {{/if}}
540
+
541
+ {{#if code}}
542
+ <div class="code-block"><code>{{code}}</code></div>
543
+ {{/if}}
544
+
545
+ <div class="info-box info-fix">
546
+ <span class="info-label">How to Fix</span>
547
+ <p>{{recommendation}}</p>
548
+ </div>
549
+ </div>
550
+
551
+ <div class="prompt-box">
552
+ <div class="prompt-header">
553
+ <span class="prompt-label">Claude Code Prompt</span>
554
+ <button class="copy-btn" onclick="event.stopPropagation(); copyPrompt(this.closest('.finding'))">Copy</button>
555
+ </div>
556
+ <div class="prompt-text">Fix {{id}} in {{file}}:{{line}}
557
+
558
+ Problem: {{description}}
559
+
560
+ Why: {{why}}
561
+
562
+ Context: {{context}}
563
+
564
+ Check first: {{checkBefore}}
565
+
566
+ Solution: {{recommendation}}</div>
567
+ </div>
568
+ </div>
569
+ </div>
570
+ {{/each}}
571
+ </section>
572
+ {{/if}}
573
+
574
+ <!-- High Findings -->
575
+ {{#if highFindings}}
576
+ <section>
577
+ <h2 class="section-title">High Priority</h2>
578
+ {{#each highFindings}}
579
+ <div class="finding" data-finding-id="{{id}}" data-file="{{file}}" data-line="{{line}}" data-description="{{description}}" data-recommendation="{{recommendation}}" data-severity="high" data-why="{{why}}" data-context="{{context}}" data-checkbefore="{{checkBefore}}">
580
+ <div class="finding-header" onclick="this.parentElement.classList.toggle('open')">
581
+ <span class="severity-badge sev-high">High</span>
582
+ <span class="finding-title">{{title}}</span>
583
+ <span class="finding-file">{{file}}</span>
584
+ <svg class="expand-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
585
+ </div>
586
+ <div class="finding-body">
587
+ <div class="location">
588
+ <span class="location-file">{{file}}</span>
589
+ <span class="location-line">:{{line}}</span>
590
+ <button class="copy-btn" onclick="event.stopPropagation(); copyText('{{file}}:{{line}}', this)">Copy</button>
591
+ </div>
592
+
593
+ <div class="info-grid">
594
+ <div class="info-box info-problem">
595
+ <span class="info-label">Problem</span>
596
+ <p>{{description}}</p>
597
+ </div>
598
+
599
+ {{#if why}}
600
+ <div class="info-box info-why">
601
+ <span class="info-label">Why This Matters</span>
602
+ <p>{{why}}</p>
603
+ </div>
604
+ {{/if}}
605
+
606
+ {{#if context}}
607
+ <div class="info-box info-context">
608
+ <span class="info-label">Code Context</span>
609
+ <p>{{context}}</p>
610
+ </div>
611
+ {{/if}}
612
+
613
+ {{#if checkBefore}}
614
+ <div class="info-box info-check">
615
+ <span class="info-label">Check Before Fixing</span>
616
+ <p>{{checkBefore}}</p>
617
+ </div>
618
+ {{/if}}
619
+
620
+ {{#if code}}
621
+ <div class="code-block"><code>{{code}}</code></div>
622
+ {{/if}}
623
+
624
+ <div class="info-box info-fix">
625
+ <span class="info-label">How to Fix</span>
626
+ <p>{{recommendation}}</p>
627
+ </div>
628
+ </div>
629
+
630
+ <div class="prompt-box">
631
+ <div class="prompt-header">
632
+ <span class="prompt-label">Claude Code Prompt</span>
633
+ <button class="copy-btn" onclick="event.stopPropagation(); copyPrompt(this.closest('.finding'))">Copy</button>
634
+ </div>
635
+ <div class="prompt-text">Fix {{id}} in {{file}}:{{line}}
636
+
637
+ Problem: {{description}}
638
+
639
+ Why: {{why}}
640
+
641
+ Context: {{context}}
642
+
643
+ Check first: {{checkBefore}}
644
+
645
+ Solution: {{recommendation}}</div>
646
+ </div>
647
+ </div>
648
+ </div>
649
+ {{/each}}
650
+ </section>
651
+ {{/if}}
652
+
653
+ <!-- Medium Findings -->
654
+ {{#if mediumFindings}}
655
+ <section>
656
+ <h2 class="section-title">Medium Priority</h2>
657
+ {{#each mediumFindings}}
658
+ <div class="finding" data-finding-id="{{id}}" data-file="{{file}}" data-line="{{line}}" data-description="{{description}}" data-recommendation="{{recommendation}}" data-severity="medium" data-why="{{why}}" data-context="{{context}}" data-checkbefore="{{checkBefore}}">
659
+ <div class="finding-header" onclick="this.parentElement.classList.toggle('open')">
660
+ <span class="severity-badge sev-medium">Medium</span>
661
+ <span class="finding-title">{{title}}</span>
662
+ <span class="finding-file">{{file}}</span>
663
+ <svg class="expand-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
664
+ </div>
665
+ <div class="finding-body">
666
+ <div class="location">
667
+ <span class="location-file">{{file}}</span>
668
+ <span class="location-line">:{{line}}</span>
669
+ <button class="copy-btn" onclick="event.stopPropagation(); copyText('{{file}}:{{line}}', this)">Copy</button>
670
+ </div>
671
+
672
+ <div class="info-grid">
673
+ <div class="info-box info-problem">
674
+ <span class="info-label">Problem</span>
675
+ <p>{{description}}</p>
676
+ </div>
677
+
678
+ {{#if why}}
679
+ <div class="info-box info-why">
680
+ <span class="info-label">Why This Matters</span>
681
+ <p>{{why}}</p>
682
+ </div>
683
+ {{/if}}
684
+
685
+ {{#if checkBefore}}
686
+ <div class="info-box info-check">
687
+ <span class="info-label">Check Before Fixing</span>
688
+ <p>{{checkBefore}}</p>
689
+ </div>
690
+ {{/if}}
691
+
692
+ <div class="info-box info-fix">
693
+ <span class="info-label">How to Fix</span>
694
+ <p>{{recommendation}}</p>
695
+ </div>
696
+ </div>
697
+
698
+ <div class="prompt-box">
699
+ <div class="prompt-header">
700
+ <span class="prompt-label">Claude Code Prompt</span>
701
+ <button class="copy-btn" onclick="event.stopPropagation(); copyPrompt(this.closest('.finding'))">Copy</button>
702
+ </div>
703
+ <div class="prompt-text">Fix {{id}} in {{file}}:{{line}}
704
+
705
+ Problem: {{description}}
706
+
707
+ Solution: {{recommendation}}</div>
708
+ </div>
709
+ </div>
710
+ </div>
711
+ {{/each}}
712
+ </section>
713
+ {{/if}}
714
+
715
+ <!-- Low Findings -->
716
+ {{#if lowFindings}}
717
+ <section>
718
+ <h2 class="section-title">Low Priority</h2>
719
+ {{#each lowFindings}}
720
+ <div class="finding">
721
+ <div class="finding-header" onclick="this.parentElement.classList.toggle('open')">
722
+ <span class="severity-badge sev-low">Low</span>
723
+ <span class="finding-title">{{title}}</span>
724
+ <span class="finding-file">{{file}}</span>
725
+ <svg class="expand-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
726
+ </div>
727
+ <div class="finding-body">
728
+ <p style="color: var(--text-muted);">{{description}}</p>
729
+ </div>
730
+ </div>
731
+ {{/each}}
732
+ </section>
733
+ {{/if}}
734
+
735
+ <!-- Positive Observations -->
736
+ {{#if positiveObservations}}
737
+ <section>
738
+ <h2 class="section-title">What's Good</h2>
739
+ <ul class="positive-list">
740
+ {{#each positiveObservations}}
741
+ <li class="positive-item">
742
+ <span class="check-icon">&#10003;</span>
743
+ <span>{{this}}</span>
744
+ </li>
745
+ {{/each}}
746
+ </ul>
747
+ </section>
748
+ {{/if}}
749
+
750
+ <footer>
751
+ <p>Generated by CoverMe Scanner</p>
752
+ <p>{{scanDuration}} &middot; {{agentCount}} agents</p>
753
+ </footer>
754
+ </div>
755
+
756
+ <script>
757
+ function copyText(text, btn) {
758
+ navigator.clipboard.writeText(text).then(() => {
759
+ btn.textContent = 'Copied!';
760
+ setTimeout(() => btn.textContent = 'Copy', 1500);
761
+ });
762
+ }
763
+
764
+ function copyPrompt(el) {
765
+ const text = el.querySelector('.prompt-text').textContent;
766
+ const btn = el.querySelector('.prompt-box .copy-btn');
767
+ copyText(text, btn);
768
+ }
769
+
770
+ function copyAllFixes(btn) {
771
+ const findings = document.querySelectorAll('.finding[data-finding-id]');
772
+ let prompt = 'Fix these security issues:\n\n';
773
+
774
+ findings.forEach((f, i) => {
775
+ prompt += `## ${i + 1}. [${f.dataset.severity.toUpperCase()}] ${f.dataset.findingId}\n`;
776
+ prompt += `File: ${f.dataset.file}:${f.dataset.line}\n`;
777
+ prompt += `Problem: ${f.dataset.description}\n`;
778
+ if (f.dataset.why) prompt += `Why: ${f.dataset.why}\n`;
779
+ if (f.dataset.checkbefore) prompt += `Check first: ${f.dataset.checkbefore}\n`;
780
+ prompt += `Fix: ${f.dataset.recommendation}\n\n`;
781
+ });
782
+
783
+ navigator.clipboard.writeText(prompt).then(() => {
784
+ btn.classList.add('copied');
785
+ btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M20 6L9 17l-5-5"/></svg> Copied!';
786
+ setTimeout(() => {
787
+ btn.classList.remove('copied');
788
+ btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><rect x="9" y="9" width="13" height="13" rx="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> Copy All Fixes for Claude Code';
789
+ }, 2500);
790
+ });
791
+ }
792
+
793
+ window.onbeforeprint = () => document.querySelectorAll('.finding').forEach(f => f.classList.add('open'));
794
+ </script>
795
+ </body>
796
+ </html>