chill-agent 1.6.2 → 1.8.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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.devin-plugin/plugin.json +1 -1
- package/.kimi-plugin/plugin.json +1 -1
- package/.qoder-plugin/plugin.json +1 -1
- package/LICENSE +1 -1
- package/README.md +129 -57
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/plugin.yaml +1 -1
- package/src/auditor.js +557 -116
- package/src/cli.js +42 -17
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chill-agent",
|
|
3
3
|
"displayName": "ChillAgent (Anti-Slop 2.0 + Ponytail Senior Dev)",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.8.0",
|
|
5
5
|
"description": "Master Negative Filter, Anti-Slop, and Pragmatic Senior Dev Standard. Combines ruthless YAGNI minimalism with Linear-grade visual engineering.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "sann"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chill-agent",
|
|
3
3
|
"displayName": "ChillAgent (Anti-Slop 2.0 + Ponytail Senior Dev)",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.8.0",
|
|
5
5
|
"description": "Master Negative Filter, Anti-Slop, and Pragmatic Senior Dev Standard. Combines ruthless YAGNI minimalism with Linear-grade visual engineering.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "sann"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chill-agent",
|
|
3
3
|
"displayName": "ChillAgent (Anti-Slop 2.0 + Ponytail Senior Dev)",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.8.0",
|
|
5
5
|
"description": "Master Negative Filter, Anti-Slop, and Pragmatic Senior Dev Standard. Combines ruthless YAGNI minimalism with Linear-grade visual engineering.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "sann"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chill-agent",
|
|
3
3
|
"displayName": "ChillAgent (Anti-Slop 2.0 + Ponytail Senior Dev)",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.8.0",
|
|
5
5
|
"description": "Master Negative Filter, Anti-Slop, and Pragmatic Senior Dev Standard. Combines ruthless YAGNI minimalism with Linear-grade visual engineering.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "sann"
|
package/.kimi-plugin/plugin.json
CHANGED
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,33 +1,59 @@
|
|
|
1
1
|
# ChillAgent (`chill-agent`)
|
|
2
2
|
|
|
3
|
-
> **The Zero-Bloat Anti-Slop 2.0 Engine & Pragmatic Senior Developer Standard.**
|
|
4
|
-
>
|
|
3
|
+
> **The Zero-Bloat Anti-Slop 2.0 Engine & Pragmatic Senior Developer Standard.**
|
|
4
|
+
> Fuses the visual craftsmanship of [anti-slop](https://github.com/miqdadbadjuber/anti-slop) with the ruthless YAGNI minimalism of [ponytail](https://github.com/dietrichgebert/ponytail).
|
|
5
5
|
|
|
6
|
-
[](https://www.npmjs.com/package/chill-agent)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](package.json)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](AGENTS.md)
|
|
10
11
|
|
|
11
12
|
---
|
|
12
13
|
|
|
13
|
-
##
|
|
14
|
+
## Overview
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Most AI coding assistants suffer from two distinct failure modes:
|
|
17
|
+
1. **Visual & Copywriting Slop:** Unsolicited purple-to-cyan mesh gradients, bubbly 24px border radiuses, floating cards without grid alignment, and marketing cliches (*"supercharge your workflow"*, *"effortless simplicity"*).
|
|
18
|
+
2. **Architectural Over-Engineering:** Writing 200 lines of abstract factory patterns, config wrappers, and unrequested npm dependencies when a 3-line standard library call solves the task.
|
|
19
|
+
|
|
20
|
+
**ChillAgent** fixes both simultaneously. It operates as a dual-core negative filter and pragmatic execution engine across 14 coding agent environments.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
24
|
+
│ CHILLAGENT │
|
|
25
|
+
├───────────────────────────────────┬────────────────────────────────────┤
|
|
26
|
+
│ NEGATIVE FILTER (Anti-Slop) │ PRAGMATIC SENIOR DEV (Ponytail) │
|
|
27
|
+
├───────────────────────────────────┼────────────────────────────────────┤
|
|
28
|
+
│ • Zero AI mesh gradients │ • The shortest working diff wins │
|
|
29
|
+
│ • Zero bubbly floating cards │ • Standard library > dependencies │
|
|
30
|
+
│ • Monochromatic carbon surfaces │ • Reuse existing codebase helpers │
|
|
31
|
+
│ • Tabular numbers (monospace) │ • 1-line "why" comments only │
|
|
32
|
+
│ • Concrete domain copy & metrics │ • Delivery disclosure for skips │
|
|
33
|
+
│ • Strict WCAG 4.5:1 contrast │ • Assert-based self-checking tests │
|
|
34
|
+
└───────────────────────────────────┴────────────────────────────────────┘
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quickstart
|
|
40
|
+
|
|
41
|
+
Run directly in any project directory via `npx` without installation:
|
|
16
42
|
|
|
17
43
|
```bash
|
|
18
|
-
# Launch interactive agent setup wizard (Antigravity,
|
|
44
|
+
# Launch interactive agent setup wizard (Antigravity, Cursor, Claude, Codex, Windsurf)
|
|
19
45
|
npx chill-agent
|
|
20
46
|
|
|
21
|
-
# Scan workspace for AI slop cliches
|
|
47
|
+
# Scan current workspace for AI slop cliches, unrequested bloat, and gradient leaks
|
|
22
48
|
npx chill-agent audit
|
|
23
49
|
|
|
24
|
-
# Automatically remediate
|
|
50
|
+
# Automatically remediate violations (strip emojis, clean redundant comments, normalize radius)
|
|
25
51
|
npx chill-agent audit --fix
|
|
26
52
|
|
|
27
|
-
# Generate a
|
|
28
|
-
npx chill-agent init-design
|
|
53
|
+
# Generate a high-craft DESIGN.md specification
|
|
54
|
+
npx chill-agent init-design linear-carbon
|
|
29
55
|
|
|
30
|
-
# Inspect
|
|
56
|
+
# Inspect environment health, active agent manifests, and rule synchronization
|
|
31
57
|
npx chill-agent doctor
|
|
32
58
|
|
|
33
59
|
# Launch deterministic time-travel debugger & Linear-grade Web Inspector
|
|
@@ -36,65 +62,111 @@ npx chill-agent rewind ui
|
|
|
36
62
|
|
|
37
63
|
---
|
|
38
64
|
|
|
39
|
-
##
|
|
65
|
+
## The 6-Step Minimalist Execution Ladder
|
|
40
66
|
|
|
41
|
-
|
|
67
|
+
When solving any task, your agent evaluates options in strict order and stops at the **first option that fully works**:
|
|
42
68
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
│ ANTI-SLOP (Visuals) │ PONYTAIL (Logic) │
|
|
48
|
-
├─────────────────────────┼────────────────────────┤
|
|
49
|
-
│ • No generic gradients │ • Smallest diff wins │
|
|
50
|
-
│ • No pill bubble cards │ • Native platform 1st │
|
|
51
|
-
│ • Purpose Test verified │ • Zero factory bloat │
|
|
52
|
-
│ • Tabular numbers font │ • 1-line why comments │
|
|
53
|
-
│ • Real copy & metrics │ • Delivery disclosure │
|
|
54
|
-
└─────────────────────────┴────────────────────────┘
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### The 6-Step Minimalist Execution Ladder
|
|
58
|
-
Take the **first option** that solves the whole task:
|
|
59
|
-
1. **Does it need to exist?** Skip features and configurability nobody asked for.
|
|
60
|
-
2. **Already in this codebase?** Reuse existing helpers, patterns, and house components.
|
|
61
|
-
3. **Standard library or native web API?** Reach for `node:*`, `fetch`, CSS Grid, native `<dialog>`.
|
|
62
|
-
4. **Already-installed dependency?** Use what exists in `package.json`. Never install a package for 10 lines of code.
|
|
69
|
+
1. **Does it need to exist?** Skip options, wrappers, and flexibility nobody asked for. Name them in one line.
|
|
70
|
+
2. **Already in this codebase?** Reuse existing helpers, patterns, hooks, and house components.
|
|
71
|
+
3. **Standard library or native platform feature?** Reach for `node:*`, native `fetch`, CSS Grid, native `<dialog>`, `Intl.NumberFormat`.
|
|
72
|
+
4. **Already-installed dependency?** Use what is already declared in `package.json`. Never add a dependency for a few lines of logic.
|
|
63
73
|
5. **Can it be a single readable line?** Write the single line.
|
|
64
|
-
6. **Otherwise:**
|
|
74
|
+
6. **Otherwise:** Minimum working code that preserves validation, error handling, and security boundaries.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Intensity Modes (`/chill` & `/ponytail`)
|
|
79
|
+
|
|
80
|
+
Both `/chill` and `/ponytail` commands are recognized interchangeably across all supported agent interfaces:
|
|
81
|
+
|
|
82
|
+
| Mode | Command | Behavioral Scope |
|
|
83
|
+
| :--- | :--- | :--- |
|
|
84
|
+
| **Lite** | `/chill lite` | Prototype speed. Enforces standard library preference and basic negative filters while permitting exploratory architecture. |
|
|
85
|
+
| **Full** *(Default)* | `/chill full` | Production standard. Strict YAGNI ladder, Linear-grade visual constraints, 1-line delivery disclosure of what was skipped. |
|
|
86
|
+
| **Ultra** | `/chill ultra` | Maximum minimalism. Pushes back on unnecessary requests, questions assumptions, demands single-file implementations where viable. |
|
|
87
|
+
| **Off** | `/chill off` | Temporarily suspends rule injection for the active session. |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Modular Skills Architecture (`skills/`)
|
|
92
|
+
|
|
93
|
+
ChillAgent provides 11 specialized skills that inject targeted domain intelligence into agent contexts:
|
|
94
|
+
|
|
95
|
+
- **`chill-slop` (Master Filter):** Unifies Ponytail YAGNI constraints with Anti-Slop Hard Gates (R-01 through R-40).
|
|
96
|
+
- **`chill-slop-ui`:** Precision layout, CSS Grid, 1px `#26262B` borders, carbon surfaces, tabular numbers.
|
|
97
|
+
- **`chill-slop-code`:** Pragmatic software engineering, native APIs, minimal diff footprints, zero factory bloat.
|
|
98
|
+
- **`chill-slop-copywriting`:** Anti-hype copy rules, banned buzzword dictionaries, concrete telemetry metrics.
|
|
99
|
+
- **`chill-slop-human`:** WCAG 2.2 AAA accessibility, APCA contrast validation, 44px minimum hit targets.
|
|
100
|
+
- **`chill-slop-mobile`:** Responsive design, fluid typography via `clamp()`, zero horizontal blowout.
|
|
101
|
+
- **`chill-audit`:** Repository-wide audit tool scanning for over-engineering and visual cliches.
|
|
102
|
+
- **`chill-review`:** Focused code-review skill analyzing diffs for bloat, unnecessary packages, and missed edge cases.
|
|
103
|
+
- **`chill-debt`:** Harvests `shortcut:` comments into a prioritized engineering ledger.
|
|
104
|
+
- **`chill-gain`:** Measures impact scoreboard (lines deleted, tokens saved, bundle bytes eliminated).
|
|
105
|
+
- **`chill-help`:** Instant reference card for modes, commands, and active rules.
|
|
65
106
|
|
|
66
107
|
---
|
|
67
108
|
|
|
68
|
-
##
|
|
109
|
+
## 5 High-Craft Design System Presets (`npx chill-agent init-design`)
|
|
69
110
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
111
|
+
Prevent coding agents from defaulting to generic blank screens or AI gradients by generating an authoritative `DESIGN.md`:
|
|
112
|
+
|
|
113
|
+
1. **`linear-carbon` (Default):** Deep carbon monochrome (`#09090B`), 1px hairline borders (`#26262B`), `JetBrains Mono` tabular metrics, emerald status indicators (`#10B981`).
|
|
114
|
+
2. **`swiss-brutal`:** High-contrast black and white ink, strict 0px border radius, heavy grotesque typography, dense borders.
|
|
115
|
+
3. **`technical-spec`:** Blueprint telemetry layout, slate/steel panels, data density, monospace key-value registers.
|
|
116
|
+
4. **`editorial-paper`:** Warm ivory background (`#FBFBF9`), charcoal typography, literary serif headings, generous reading rhythm.
|
|
117
|
+
5. **`micro-density`:** Cyberpunk telemetry HUD, compact phosphor-green indicators, minimal margins, high information density.
|
|
73
118
|
|
|
74
119
|
---
|
|
75
120
|
|
|
76
|
-
##
|
|
121
|
+
## Multi-Agent Ecosystem Support (14 Environments)
|
|
122
|
+
|
|
123
|
+
ChillAgent installs native configuration doors for every major agent platform:
|
|
124
|
+
|
|
125
|
+
| Platform | Integration Path | Mechanism |
|
|
126
|
+
| :--- | :--- | :--- |
|
|
127
|
+
| **Google Antigravity** | `plugin.json` + `GEMINI.md` | Native skill bundle + instruction injection |
|
|
128
|
+
| **Cursor IDE** | `.cursor/rules/chill-agent.mdc` | MDC context rules + `.cursor-plugin/` |
|
|
129
|
+
| **Claude Code** | `.claude-plugin/plugin.json` | Plugin manifest + lifecycle hooks |
|
|
130
|
+
| **OpenCode** | `.opencode/plugins/` + `.opencode/command/` | Server plugin + slash commands |
|
|
131
|
+
| **Codex CLI** | `.codex-plugin/plugin.json` | Skill adapter + prompt presets |
|
|
132
|
+
| **Windsurf** | `.windsurf/rules/chill-agent.md` | Workspace rule file |
|
|
133
|
+
| **Cline** | `.clinerules/chill-agent` | System prompt instruction set |
|
|
134
|
+
| **Devin** | `.devin-plugin/plugin.json` | Plugin door |
|
|
135
|
+
| **Grok** | `.grok-plugin/marketplace.json` | Auto-invoking skill manifest |
|
|
136
|
+
| **Kimi Code** | `.kimi-plugin/plugin.json` | Direct AGENTS.md binding |
|
|
137
|
+
| **Qoder** | `.qoder-plugin/plugin.json` | Rules + runtime hooks |
|
|
138
|
+
| **Gemini CLI** | `gemini-extension.json` | Reusable extension adapter |
|
|
139
|
+
| **Hermes Agent** | `__init__.py` + `plugin.yaml` | Python runtime hooks + command gateway |
|
|
140
|
+
| **Pi Package** | `pi-extension/index.js` | Status line indicator + session sync |
|
|
77
141
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Verification & Test Suite
|
|
145
|
+
|
|
146
|
+
The package includes an extensive test suite verifying manifest integrity, hook execution, and rule consistency across all host adapters:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Run full unit and integration test suite
|
|
150
|
+
npm test
|
|
151
|
+
|
|
152
|
+
# Run rule copy parity checks
|
|
153
|
+
node scripts/check-rule-copies.js
|
|
154
|
+
|
|
155
|
+
# Run cross-manifest version alignment check
|
|
156
|
+
node scripts/check-versions.js
|
|
157
|
+
```
|
|
84
158
|
|
|
85
159
|
---
|
|
86
160
|
|
|
87
|
-
##
|
|
161
|
+
## Contributing
|
|
88
162
|
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
- **Warm Analog Editorial:** Warm ivory `#FBFBF9` paper, charcoal ink, literary serif headings.
|
|
94
|
-
- **Cyberpunk Micro-Density:** HUD telemetry panels, emerald phosphor text, compact technical toggles.
|
|
163
|
+
Review [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before submitting pull requests. All contributions must maintain:
|
|
164
|
+
- Zero new runtime npm dependencies.
|
|
165
|
+
- The shortest working diff standard.
|
|
166
|
+
- Parity across all multi-agent plugin doors.
|
|
95
167
|
|
|
96
168
|
---
|
|
97
169
|
|
|
98
|
-
##
|
|
170
|
+
## License
|
|
99
171
|
|
|
100
|
-
MIT © [sann](https://github.com/sann)
|
|
172
|
+
MIT © [sann](https://github.com/sann) (Steaven Galang)
|
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chill-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Zero-bloat Anti-Slop 2.0 Engine & Pragmatic Senior Developer Standard. Fuses Anti-Slop visual craftsmanship with Ponytail YAGNI minimalism across all coding agents.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"chill-agent": "bin/chill-agent.js"
|
package/plugin.yaml
CHANGED
package/src/auditor.js
CHANGED
|
@@ -1,146 +1,432 @@
|
|
|
1
|
-
// src/auditor.js - Automated Anti-Slop &
|
|
1
|
+
// src/auditor.js - Automated Anti-Slop 2.0 & Ponytail Multi-Page Auditor
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { c } from './prompts.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Full 40 R-Rule and Ponytail YAGNI Detection Matrix.
|
|
8
|
+
* Tiers:
|
|
9
|
+
* 1. Hard Gate (Absolute FAIL, Severity: HIGH)
|
|
10
|
+
* 2. Purpose-Gate (Technique allowed only with written reason, Severity: MEDIUM)
|
|
11
|
+
* 3. Quality Locks & Minimalism (Consistency & YAGNI, Severity: LOW/MEDIUM)
|
|
12
|
+
*/
|
|
13
|
+
export const SLOP_PATTERNS = [
|
|
14
|
+
// ─── TIER 1: HARD GATES (Absolute Violations) ───────────────────────────
|
|
8
15
|
{
|
|
9
|
-
id: '
|
|
10
|
-
|
|
16
|
+
id: 'R-02',
|
|
17
|
+
alias: 'COPY-04',
|
|
18
|
+
category: 'Hard Gate: Copywriting',
|
|
11
19
|
severity: 'HIGH',
|
|
12
|
-
|
|
20
|
+
rule: 'R-02',
|
|
21
|
+
regex: /—/u,
|
|
22
|
+
name: 'Em Dash (—) AI Writing Tic',
|
|
23
|
+
fix: 'R-02 rule: replace em dash (—) with a comma, period, colon, or parentheses.'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'R-03',
|
|
27
|
+
alias: 'VISUAL-04',
|
|
28
|
+
category: 'Hard Gate: Mobile Responsiveness',
|
|
29
|
+
severity: 'HIGH',
|
|
30
|
+
rule: 'R-03',
|
|
31
|
+
regex: /body\s*\{[^}]*overflow-x\s*:\s*hidden|width:\s*(?:[4-9][0-9]{2}|[1-9][0-9]{3,})px(?!.*@media)/i,
|
|
32
|
+
name: 'Mobile Breakpoint Violation / Lazy Overflow Masking',
|
|
33
|
+
fix: 'R-03 rule: ensure layout reflows without horizontal overflow or lazy `overflow-x: hidden` masking.'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'R-17',
|
|
37
|
+
alias: 'COPY-03',
|
|
38
|
+
category: 'Hard Gate: Data & Numbers',
|
|
39
|
+
severity: 'HIGH',
|
|
40
|
+
rule: 'R-17',
|
|
41
|
+
regex: /\b(?:10[kK]\+|99\.9%|500[mM]\+|120\+\s*countries|10,000\+\s*users|trusted\s+by\s+[0-9,]+\s+happy\s+customers)\b/i,
|
|
42
|
+
name: 'Hallucinated / Unverified Statistics',
|
|
43
|
+
fix: 'R-17 rule: show only verified metrics or use explicit `[REAL DATA]` placeholder.'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'R-18',
|
|
47
|
+
category: 'Hard Gate: Testimonials',
|
|
48
|
+
severity: 'HIGH',
|
|
49
|
+
rule: 'R-18',
|
|
50
|
+
regex: /(?:fictional\s+reviews?|placeholder\s+avatar|randomuser\.me\/api\/portraits)/i,
|
|
51
|
+
name: 'Synthetic / Fictional Testimonial',
|
|
52
|
+
fix: 'R-18 rule: do not render testimonials without verifiable social proof.'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'R-24',
|
|
56
|
+
category: 'Hard Gate: Navigation',
|
|
57
|
+
severity: 'HIGH',
|
|
58
|
+
rule: 'R-24',
|
|
59
|
+
regex: /<a[^>]+href=["']#(?:features|about|contact|pricing)?["'][^>]*>(?!.*scroll)/i,
|
|
60
|
+
name: 'Dead Navigation / Ghost Anchor Link',
|
|
61
|
+
fix: 'R-24 rule: every navigation item must resolve to a real, existing destination section or route.'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'R-25',
|
|
65
|
+
category: 'Hard Gate: Color Contrast',
|
|
66
|
+
severity: 'HIGH',
|
|
67
|
+
rule: 'R-25',
|
|
68
|
+
regex: /(?:text-(?:gray|zinc|slate)-[34]00\s+bg-white|text-(?:gray|zinc|slate)-500\s+bg-(?:black|zinc-900|slate-950)|color:\s*#(?:888|999|aaa|bbb)\s*;\s*background:\s*#(?:fff|fafafa))/i,
|
|
69
|
+
name: 'Sub-WCAG AA Color Contrast (<4.5:1)',
|
|
70
|
+
fix: 'R-25 rule: text must meet minimum 4.5:1 contrast against its background.'
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'R-26',
|
|
74
|
+
category: 'Hard Gate: Interactive Elements',
|
|
75
|
+
severity: 'HIGH',
|
|
76
|
+
rule: 'R-26',
|
|
77
|
+
regex: /<button[^>]*>\s*<\/button>|<button(?![^>]*(?:onClick|type=|on:click|@click|v-on))[^>]*>/i,
|
|
78
|
+
name: 'Dead Interactive Control / Non-Functional Button',
|
|
79
|
+
fix: 'R-26 rule: every interactive control must have functional logic or a visible `// TODO` notice.'
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 'R-32',
|
|
83
|
+
category: 'Hard Gate: Accessibility',
|
|
84
|
+
severity: 'HIGH',
|
|
85
|
+
rule: 'R-32',
|
|
86
|
+
regex: /outline:\s*(?:none|0)(?!\s*;\s*&:focus-visible)|\boutline-none(?!\s+focus-visible:)/i,
|
|
87
|
+
name: 'Stripped Focus Outline Without Replacement',
|
|
88
|
+
fix: 'R-32 rule: never remove default focus ring without providing a visible custom focus indicator.'
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 'R-33',
|
|
92
|
+
category: 'Hard Gate: Script Patching',
|
|
93
|
+
severity: 'HIGH',
|
|
94
|
+
rule: 'R-33',
|
|
95
|
+
regex: /str\.replace\(.*\.css|rewrite_css|patch_styles/i,
|
|
96
|
+
name: 'External File/CSS Patching via Script',
|
|
97
|
+
fix: 'R-33 rule: build features directly in source code, never through external string-replacement scripts.'
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'R-36',
|
|
101
|
+
category: 'Hard Gate: Trust Claims',
|
|
102
|
+
severity: 'HIGH',
|
|
103
|
+
rule: 'R-36',
|
|
104
|
+
regex: /\b(?:SOC\s*2\s*compliant|ISO\s*27001\s*certified|enterprise-grade\s*security|300%\s*faster\s*than)\b/i,
|
|
105
|
+
name: 'Unsubstantiated Compliance / Performance Claim',
|
|
106
|
+
fix: 'R-36 rule: remove fabricated compliance, security, or performance claims lacking verifiable audit proof.'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'R-39',
|
|
110
|
+
alias: 'A11Y-01',
|
|
111
|
+
category: 'Hard Gate: Semantics & Affordances',
|
|
112
|
+
severity: 'HIGH',
|
|
113
|
+
rule: 'R-39',
|
|
114
|
+
regex: /<button[^>]*>[^<]*<svg[^>]*>[\s\S]*?<\/svg>[^<]*<\/button>(?![^<]*aria-label)|<img(?![^>]*\balt=)[^>]*>|user-scalable\s*=\s*no|maximum-scale\s*=\s*1/i,
|
|
115
|
+
name: 'Missing Accessible Name, Alt Text, or Scalability Block',
|
|
116
|
+
fix: 'R-39 rule: provide aria-label on icon buttons, alt text on images, and do not lock viewport scaling.'
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'R-40',
|
|
120
|
+
category: 'Hard Gate: Media & Performance',
|
|
121
|
+
severity: 'HIGH',
|
|
122
|
+
rule: 'R-40',
|
|
123
|
+
regex: /<(?:img|video|iframe)(?![^>]*(?:width|height|aspect-ratio|style=.*aspect-ratio))[^>]*>|<img[^>]*class=["'][^"']*hero[^"']*["'][^>]*loading=["']lazy["']/i,
|
|
124
|
+
name: 'Unreserved Media Dimensions / Lazy-Loaded Hero',
|
|
125
|
+
fix: 'R-40 rule: declare width/height or aspect-ratio on media elements; hero image must load eagerly.'
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// ─── TIER 2: PURPOSE-GATE (Allowed Only With Stated Purpose) ──────────────
|
|
129
|
+
{
|
|
130
|
+
id: 'R-01',
|
|
131
|
+
alias: 'VISUAL-01',
|
|
132
|
+
category: 'Purpose-Gate: Color & Gradients',
|
|
133
|
+
severity: 'MEDIUM',
|
|
134
|
+
rule: 'R-01',
|
|
135
|
+
regex: /from-purple-[0-9]{3}\s+to-indigo-[0-9]{3}|from-violet-[0-9]{3}\s+to-cyan-[0-9]{3}|from-indigo-[0-9]{3}\s+to-pink-[0-9]{3}|linear-gradient\([^)]*(?:#8b5cf6|#6366f1|#ec4899)[^)]*(?:#06b6d4|#3b82f6)\)/i,
|
|
13
136
|
name: 'Generic AI Purple/Cyan Gradient',
|
|
14
|
-
fix: '
|
|
137
|
+
fix: 'R-01 rule: replace default purple mesh with brand-grounded color or linear-carbon `#09090B`.'
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'R-04',
|
|
141
|
+
category: 'Purpose-Gate: Icons',
|
|
142
|
+
severity: 'MEDIUM',
|
|
143
|
+
rule: 'R-04',
|
|
144
|
+
regex: /(?<![a-zA-Z0-9_-])(?:Sparkles?|MagicWand|Wand2|BotIcon|CpuIcon|AiIcon|SparkleIcon|AiOrb)(?![a-zA-Z0-9_-])(?!\s*\/\/\s*purpose:)/i,
|
|
145
|
+
name: 'Generic AI Glyphs (Sparkle/Robot/Magic)',
|
|
146
|
+
fix: 'R-04 rule: icons must represent actual domain concepts, not generic AI decoration.'
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: 'R-06',
|
|
150
|
+
category: 'Purpose-Gate: Typography',
|
|
151
|
+
severity: 'MEDIUM',
|
|
152
|
+
rule: 'R-06',
|
|
153
|
+
regex: /font-mono\s+text-(?:4xl|5xl|6xl)|tracking-\[(?:0\.[3-9]em|[4-9]px)\]/i,
|
|
154
|
+
name: 'Over-Styled Monospace Heading / Extreme Tracking',
|
|
155
|
+
fix: 'R-06 rule: reserve monospace for tabular numbers/code; do not use it purely for terminal aesthetics.'
|
|
15
156
|
},
|
|
16
157
|
{
|
|
17
|
-
id: '
|
|
18
|
-
category: '
|
|
158
|
+
id: 'R-07',
|
|
159
|
+
category: 'Purpose-Gate: Background',
|
|
19
160
|
severity: 'MEDIUM',
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
161
|
+
rule: 'R-07',
|
|
162
|
+
regex: /background-image:\s*(?:radial-gradient\(#|linear-gradient\(to right, #(?:e5e7eb|26262b) 1px, transparent 1px\))|bg-\[radial-gradient/i,
|
|
163
|
+
name: 'Unanchored Background Grid / Dot Matrix',
|
|
164
|
+
fix: 'R-07 rule: use background patterns only if they directly reinforce verified brand identity.'
|
|
23
165
|
},
|
|
24
166
|
{
|
|
25
|
-
id: '
|
|
26
|
-
category: '
|
|
167
|
+
id: 'R-08',
|
|
168
|
+
category: 'Purpose-Gate: Button Arrows',
|
|
27
169
|
severity: 'LOW',
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
170
|
+
rule: 'R-08',
|
|
171
|
+
regex: /<button[^>]*>.*(?:→|→|→|↗|<ArrowRight|<ChevronRight).*<\/button>/i,
|
|
172
|
+
name: 'Default Arrow Cliché on Every Button',
|
|
173
|
+
fix: 'R-08 rule: arrows are directional cues, not default decoration on every action button.'
|
|
31
174
|
},
|
|
32
175
|
{
|
|
33
|
-
id: '
|
|
34
|
-
category: '
|
|
176
|
+
id: 'R-09',
|
|
177
|
+
category: 'Purpose-Gate: Badges',
|
|
35
178
|
severity: 'MEDIUM',
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
179
|
+
rule: 'R-09',
|
|
180
|
+
regex: /rounded-full[^>]*border[^>]*>\s*(?:AI Powered|Beta|New|Next Gen|v[0-9]\.[0-9]|Intelligent)/i,
|
|
181
|
+
name: 'AI Capsule Pill Badge Above Headline',
|
|
182
|
+
fix: 'R-09 rule: capsule badges must serve functional status, not redundant hype.'
|
|
39
183
|
},
|
|
40
|
-
|
|
41
|
-
// 2. Over-Engineering & Bloat (Ponytail Minimalism)
|
|
42
184
|
{
|
|
43
|
-
id: '
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
185
|
+
id: 'R-10',
|
|
186
|
+
alias: 'VISUAL-03',
|
|
187
|
+
category: 'Purpose-Gate: Glassmorphism',
|
|
188
|
+
severity: 'LOW',
|
|
189
|
+
rule: 'R-10',
|
|
190
|
+
regex: /backdrop-blur-(?:md|lg|xl|2xl)/i,
|
|
191
|
+
name: 'Excessive Glassmorphic Blur Stacking',
|
|
192
|
+
fix: 'R-10 rule: dose cap blur on maximum 1-2 elements; avoid stacking across cards and navbars.'
|
|
49
193
|
},
|
|
50
194
|
{
|
|
51
|
-
id: '
|
|
52
|
-
category: '
|
|
195
|
+
id: 'R-12',
|
|
196
|
+
category: 'Purpose-Gate: Shadow',
|
|
53
197
|
severity: 'MEDIUM',
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
198
|
+
rule: 'R-12',
|
|
199
|
+
regex: /shadow-(?:2xl|xl)\s+(?!.*hover:)/i,
|
|
200
|
+
name: 'Heavy Soft Floating Shadows',
|
|
201
|
+
fix: 'R-12 rule: use shadows selectively as hierarchy elevation markers, not floating card defaults.'
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'R-13',
|
|
205
|
+
category: 'Purpose-Gate: Glow',
|
|
206
|
+
severity: 'LOW',
|
|
207
|
+
rule: 'R-13',
|
|
208
|
+
regex: /(?:drop-shadow-\[0_0_|box-shadow:\s*0\s*0\s*[1-9][0-9]px|glow-)/i,
|
|
209
|
+
name: 'Pervasive Neon Glow Accent',
|
|
210
|
+
fix: 'R-13 rule: limit glow to at most 1 key focal element; avoid simultaneous glows on cards and buttons.'
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: 'R-14',
|
|
214
|
+
category: 'Purpose-Gate: Feature Cards',
|
|
215
|
+
severity: 'MEDIUM',
|
|
216
|
+
rule: 'R-14',
|
|
217
|
+
regex: /(?:grid-cols-3.*\{features\.map|\{items\.map.*<Card)/i,
|
|
218
|
+
name: 'Cookie-Cutter Uniform Feature Cards',
|
|
219
|
+
fix: 'R-14 rule: vary card layout and hierarchy to reflect the actual importance of each feature.'
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: 'R-19',
|
|
223
|
+
category: 'Purpose-Gate: Animations',
|
|
224
|
+
severity: 'LOW',
|
|
225
|
+
rule: 'R-19',
|
|
226
|
+
regex: /(?:animate-bounce|animate-pulse\s+animate-spin|fade-up.*floating.*scale)/i,
|
|
227
|
+
name: 'Stacked Template Animations',
|
|
228
|
+
fix: 'R-19 rule: use purposeful motion tied to declared MOTION dial; avoid simultaneously stacked animations.'
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: 'R-22',
|
|
232
|
+
category: 'Purpose-Gate: Illustrations',
|
|
233
|
+
severity: 'LOW',
|
|
234
|
+
rule: 'R-22',
|
|
235
|
+
regex: /(?:undraw|storyset|3d-character|blob-scene)/i,
|
|
236
|
+
name: 'Generic 3D Blob / Stock Illustration',
|
|
237
|
+
fix: 'R-22 rule: use real product screenshots, telemetry diagrams, or no illustration at all.'
|
|
57
238
|
},
|
|
58
239
|
|
|
59
|
-
// 3
|
|
240
|
+
// ─── TIER 3: QUALITY LOCKS & PONYTAIL MINIMALISM ────────────────────────
|
|
60
241
|
{
|
|
61
|
-
id: '
|
|
62
|
-
category: '
|
|
242
|
+
id: 'R-05',
|
|
243
|
+
category: 'Quality Lock: Layout Rhythm',
|
|
244
|
+
severity: 'MEDIUM',
|
|
245
|
+
rule: 'R-05',
|
|
246
|
+
regex: /<section[^>]*>.*how-it-works.*step-1.*step-2.*step-3/is,
|
|
247
|
+
name: 'Template "How It Works" 3-Step Rhythm',
|
|
248
|
+
fix: 'R-05 rule: structure page flow around the actual narrative rather than template 3-step sequences.'
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'R-11',
|
|
252
|
+
alias: 'VISUAL-02',
|
|
253
|
+
category: 'Quality Lock: Border Radius',
|
|
254
|
+
severity: 'MEDIUM',
|
|
255
|
+
rule: 'R-11',
|
|
256
|
+
regex: /\brounded-3xl\b|\brounded-full\s+.*(?:card|panel|modal|dialog)\b/i,
|
|
257
|
+
name: 'Pill-Shaped Floating Bubble / Excessive Radius',
|
|
258
|
+
fix: 'R-11 rule: maintain geometric design system radius (4px-8px) with 1px hairlines.'
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
id: 'R-15',
|
|
262
|
+
category: 'Quality Lock: Call to Action',
|
|
263
|
+
severity: 'LOW',
|
|
264
|
+
rule: 'R-15',
|
|
265
|
+
regex: />\s*(?:Get Started|Learn More|Try Now|Explore|Discover)\s*</i,
|
|
266
|
+
name: 'Generic Vague CTA Copy',
|
|
267
|
+
fix: 'R-15 rule: use action-specific CTAs (e.g. "Start Project", "View Source", "Open Dashboard").'
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'R-16',
|
|
271
|
+
alias: 'COPY-01',
|
|
272
|
+
category: 'Quality Lock: Copywriting Buzzwords',
|
|
63
273
|
severity: 'HIGH',
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
274
|
+
rule: 'R-16',
|
|
275
|
+
regex: /\b(?:unleash\s+the\s+power\s+of|supercharge\s+your|seamlessly\s+integrate|next-generation|game-changer|delve\s+into|dive\s+deep\s+into|cutting-edge|revolutionary|effortless)\b/i,
|
|
276
|
+
name: 'Generic AI Marketing Buzzword',
|
|
277
|
+
fix: 'R-16 rule: replace hype words with concrete technical or functional explanations.'
|
|
67
278
|
},
|
|
68
279
|
{
|
|
69
|
-
id: '
|
|
70
|
-
category: '
|
|
280
|
+
id: 'R-29',
|
|
281
|
+
category: 'Quality Lock: Color Palette',
|
|
71
282
|
severity: 'MEDIUM',
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
283
|
+
rule: 'R-29',
|
|
284
|
+
regex: /(?:bg-(?:red|orange|yellow|green|blue|purple|pink)-[0-9]{3}.*){4,}/i,
|
|
285
|
+
name: 'Palette Explosion (>3 Core Colors)',
|
|
286
|
+
fix: 'R-29 rule: limit palette to maximum 2-3 core brand colors + 1 deliberate accent.'
|
|
75
287
|
},
|
|
76
288
|
{
|
|
77
|
-
id: '
|
|
78
|
-
category: '
|
|
289
|
+
id: 'BLOAT-01',
|
|
290
|
+
category: 'Ponytail Minimalism: Architecture',
|
|
79
291
|
severity: 'HIGH',
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
292
|
+
rule: 'YAGNI',
|
|
293
|
+
regex: /class\s+[A-Za-z0-9]+Factory|class\s+[A-Za-z0-9]+AbstractBuilder|class\s+[A-Za-z0-9]+ProviderManager/i,
|
|
294
|
+
name: 'Premature Factory / Builder Abstraction',
|
|
295
|
+
fix: 'Ponytail YAGNI ladder: replace speculative class wrappers with a direct function or object literal.'
|
|
83
296
|
},
|
|
84
297
|
{
|
|
85
|
-
id: '
|
|
86
|
-
category: '
|
|
298
|
+
id: 'BLOAT-02',
|
|
299
|
+
category: 'Ponytail Minimalism: Dependencies',
|
|
87
300
|
severity: 'MEDIUM',
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
301
|
+
rule: 'STDLIB',
|
|
302
|
+
regex: /import\s+.*lodash\/(?:cloneDeep|isEqual|merge)|import\s+is-number\s+from/i,
|
|
303
|
+
name: 'Unnecessary Micro-Dependency',
|
|
304
|
+
fix: 'Use native JavaScript built-ins (`structuredClone`, standard operators) instead of external packages.'
|
|
91
305
|
},
|
|
92
|
-
|
|
93
|
-
// 4. Code Hygiene Slop
|
|
94
306
|
{
|
|
95
307
|
id: 'CODE-01',
|
|
96
|
-
category: 'Code
|
|
97
|
-
severity: '
|
|
98
|
-
|
|
308
|
+
category: 'Ponytail Hygiene: Code Comments',
|
|
309
|
+
severity: 'LOW',
|
|
310
|
+
rule: 'HYGIENE',
|
|
311
|
+
regex: /\/\/\s*(?:this\s+function\s+calculates|this\s+is\s+the\s+render\s+method|define\s+variables?|initialize\s+state)/i,
|
|
99
312
|
name: 'Redundant Obvious AI Code Comment',
|
|
100
|
-
fix: 'Remove comments that
|
|
313
|
+
fix: 'Remove comments that restate the code. Document the "WHY", not the "WHAT".'
|
|
101
314
|
},
|
|
102
315
|
{
|
|
103
316
|
id: 'CODE-02',
|
|
104
|
-
category: '
|
|
317
|
+
category: 'Ponytail Hygiene: Stubbed Logic',
|
|
105
318
|
severity: 'LOW',
|
|
106
|
-
|
|
319
|
+
rule: 'HYGIENE',
|
|
320
|
+
regex: /\/\/\s*TODO:\s*(?:implement\s+this|add\s+logic\s+later|finish\s+this)/i,
|
|
107
321
|
name: 'Unimplemented Lazy AI Stub',
|
|
108
|
-
fix: 'Provide
|
|
322
|
+
fix: 'Provide working implementation or raise explicit NotImplementedError with actionable detail.'
|
|
109
323
|
},
|
|
110
|
-
|
|
111
|
-
// 5. Human Factors & Accessibility
|
|
112
324
|
{
|
|
113
|
-
id: '
|
|
114
|
-
category: '
|
|
115
|
-
severity: '
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
325
|
+
id: 'COPY-02',
|
|
326
|
+
category: 'Copywriting Cliché: Emoji Overuse',
|
|
327
|
+
severity: 'MEDIUM',
|
|
328
|
+
rule: 'R-02',
|
|
329
|
+
regex: /[✨🚀🎉💡🔥🤖⚡]/u,
|
|
330
|
+
name: 'AI Sparkle / Rocket Emoji Cliché',
|
|
331
|
+
fix: 'Strip decorative emojis from headlines, buttons, and interface labels.'
|
|
119
332
|
}
|
|
120
333
|
];
|
|
121
334
|
|
|
122
335
|
const SCAN_EXTENSIONS = new Set([
|
|
123
|
-
'.
|
|
336
|
+
'.html', '.htm', '.jsx', '.tsx', '.vue', '.svelte', '.astro',
|
|
337
|
+
'.js', '.ts', '.mjs', '.cjs',
|
|
338
|
+
'.css', '.scss', '.sass', '.less',
|
|
339
|
+
'.md', '.mdx'
|
|
124
340
|
]);
|
|
125
341
|
|
|
126
342
|
const IGNORE_DIRS = new Set([
|
|
127
|
-
'node_modules', '.git', '.chillrewind', 'dist', 'build', '.next', '.cache',
|
|
343
|
+
'node_modules', '.git', '.chillrewind', 'dist', 'build', '.next', '.cache',
|
|
344
|
+
'coverage', '.vercel', '.output', '.openclaw', '.claude-plugin', '.codex-plugin',
|
|
345
|
+
'.cursor', '.cursor-plugin', '.windsurf', '.qoder', '.qoder-plugin', '.kimi-plugin',
|
|
346
|
+
'.devin-plugin', '.grok-plugin', '.omp-plugin', 'i18n', 'rules', 'skills'
|
|
128
347
|
]);
|
|
129
348
|
|
|
349
|
+
const IGNORE_FILES = new Set([
|
|
350
|
+
'antislop.md', 'chillslop.md', 'AGENTS.md', 'CLAUDE.md', 'GEMINI.md', 'GUIDE.md',
|
|
351
|
+
'README.md', 'CODE_OF_CONDUCT.md', 'CONTRIBUTING.md', 'SECURITY.md', 'INSTALL.md',
|
|
352
|
+
'after-install.md'
|
|
353
|
+
]);
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Discover all pages, routes, views, layouts, and components across the project.
|
|
357
|
+
*/
|
|
358
|
+
export function discoverPages(rootDir) {
|
|
359
|
+
const pages = [];
|
|
360
|
+
|
|
361
|
+
function walk(currentDir) {
|
|
362
|
+
let entries = [];
|
|
363
|
+
try {
|
|
364
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
365
|
+
} catch {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
for (const entry of entries) {
|
|
370
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
371
|
+
if (entry.isDirectory()) {
|
|
372
|
+
if (!IGNORE_DIRS.has(entry.name)) {
|
|
373
|
+
walk(fullPath);
|
|
374
|
+
}
|
|
375
|
+
} else if (entry.isFile()) {
|
|
376
|
+
if (IGNORE_FILES.has(entry.name)) continue;
|
|
377
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
378
|
+
if (SCAN_EXTENSIONS.has(ext)) {
|
|
379
|
+
const relPath = path.relative(rootDir, fullPath).replace(/\\/g, '/');
|
|
380
|
+
const isRoute = /\b(pages|app|routes|views)\b/i.test(relPath) || entry.name.startsWith('page.') || entry.name === 'index.html';
|
|
381
|
+
const isComponent = /\b(components|ui|widgets)\b/i.test(relPath);
|
|
382
|
+
const isStyle = ext === '.css' || ext === '.scss';
|
|
383
|
+
const isDoc = ext === '.md' || ext === '.mdx';
|
|
384
|
+
|
|
385
|
+
pages.push({
|
|
386
|
+
file: fullPath,
|
|
387
|
+
relPath,
|
|
388
|
+
name: entry.name,
|
|
389
|
+
ext,
|
|
390
|
+
type: isRoute ? 'PAGE_ROUTE' : isComponent ? 'COMPONENT' : isStyle ? 'STYLESHEET' : isDoc ? 'DOCUMENT' : 'SOURCE'
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
walk(rootDir);
|
|
398
|
+
return pages;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Scan a single file against the full 40 R-rule pattern matrix.
|
|
403
|
+
*/
|
|
130
404
|
export function scanFile(filePath) {
|
|
131
405
|
const findings = [];
|
|
132
406
|
try {
|
|
133
407
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
134
|
-
const lines = content.split(
|
|
408
|
+
const lines = content.split(/\r?\n/);
|
|
135
409
|
|
|
136
410
|
lines.forEach((line, lineIdx) => {
|
|
411
|
+
// Carve-out: skip markdown headings for em-dash check (e.g. `## R-02 — Copywriting`)
|
|
412
|
+
const isHeading = line.trim().startsWith('#') || line.trim().startsWith('//') || line.trim().startsWith('*');
|
|
413
|
+
|
|
137
414
|
for (const pattern of SLOP_PATTERNS) {
|
|
415
|
+
if (pattern.id === 'R-02' && isHeading) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
|
|
138
419
|
if (pattern.regex.test(line)) {
|
|
139
420
|
findings.push({
|
|
140
421
|
file: filePath,
|
|
141
422
|
line: lineIdx + 1,
|
|
142
|
-
snippet: line.trim().slice(0,
|
|
143
|
-
|
|
423
|
+
snippet: line.trim().slice(0, 90),
|
|
424
|
+
id: pattern.alias || pattern.id,
|
|
425
|
+
rule: pattern.rule || pattern.id,
|
|
426
|
+
category: pattern.category,
|
|
427
|
+
severity: pattern.severity,
|
|
428
|
+
name: pattern.name,
|
|
429
|
+
fix: pattern.fix
|
|
144
430
|
});
|
|
145
431
|
}
|
|
146
432
|
}
|
|
@@ -149,32 +435,119 @@ export function scanFile(filePath) {
|
|
|
149
435
|
return findings;
|
|
150
436
|
}
|
|
151
437
|
|
|
438
|
+
/**
|
|
439
|
+
* Audit all pages in repository, grouping results by page and rule tier.
|
|
440
|
+
*/
|
|
441
|
+
export function auditAllPages(rootDir) {
|
|
442
|
+
const pages = discoverPages(rootDir);
|
|
443
|
+
const pageResults = [];
|
|
444
|
+
let totalViolations = 0;
|
|
445
|
+
|
|
446
|
+
for (const page of pages) {
|
|
447
|
+
const findings = scanFile(page.file);
|
|
448
|
+
const hardGateFails = findings.filter((f) => f.severity === 'HIGH').length;
|
|
449
|
+
const purposeGateFails = findings.filter((f) => f.severity === 'MEDIUM').length;
|
|
450
|
+
const qualityLockFails = findings.filter((f) => f.severity === 'LOW').length;
|
|
451
|
+
|
|
452
|
+
// Delivery Gate Score: 100 - (High * 15 + Med * 5 + Low * 2)
|
|
453
|
+
const rawScore = 100 - (hardGateFails * 15 + purposeGateFails * 5 + qualityLockFails * 2);
|
|
454
|
+
const score = Math.max(0, Math.min(100, rawScore));
|
|
455
|
+
const status = hardGateFails === 0 && purposeGateFails === 0 ? 'PASS' : 'FAIL';
|
|
456
|
+
|
|
457
|
+
totalViolations += findings.length;
|
|
458
|
+
|
|
459
|
+
pageResults.push({
|
|
460
|
+
page,
|
|
461
|
+
findings,
|
|
462
|
+
score,
|
|
463
|
+
status,
|
|
464
|
+
counts: {
|
|
465
|
+
total: findings.length,
|
|
466
|
+
high: hardGateFails,
|
|
467
|
+
medium: purposeGateFails,
|
|
468
|
+
low: qualityLockFails
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
rootDir,
|
|
475
|
+
totalPages: pages.length,
|
|
476
|
+
totalViolations,
|
|
477
|
+
pageResults,
|
|
478
|
+
overallStatus: pageResults.every((p) => p.status === 'PASS') ? 'PASS' : 'FAIL'
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Scan directory helper for backward compatibility.
|
|
484
|
+
*/
|
|
485
|
+
export function scanDirectory(dirPath) {
|
|
486
|
+
const audit = auditAllPages(dirPath);
|
|
487
|
+
const all = [];
|
|
488
|
+
for (const res of audit.pageResults) {
|
|
489
|
+
all.push(...res.findings);
|
|
490
|
+
}
|
|
491
|
+
return all;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Automatically remediate fixable slop patterns.
|
|
496
|
+
*/
|
|
152
497
|
export function autoFixFile(filePath) {
|
|
153
498
|
let modified = false;
|
|
154
499
|
try {
|
|
155
500
|
let content = fs.readFileSync(filePath, 'utf8');
|
|
156
501
|
|
|
157
|
-
// 1. Strip
|
|
158
|
-
const strippedEmoji = content.replace(/[
|
|
502
|
+
// 1. Strip emojis (R-02 / COPY-02)
|
|
503
|
+
const strippedEmoji = content.replace(/[✨🚀🎉💡🔥🤖⚡]/gu, '');
|
|
159
504
|
if (strippedEmoji !== content) {
|
|
160
505
|
content = strippedEmoji;
|
|
161
506
|
modified = true;
|
|
162
507
|
}
|
|
163
508
|
|
|
164
|
-
// 2.
|
|
165
|
-
const
|
|
509
|
+
// 2. Replace em dashes with comma/period (R-02) outside headings
|
|
510
|
+
const lines = content.split('\n');
|
|
511
|
+
const fixedLines = lines.map((l) => {
|
|
512
|
+
if (l.trim().startsWith('#') || l.trim().startsWith('//')) return l;
|
|
513
|
+
if (l.includes('—')) {
|
|
514
|
+
modified = true;
|
|
515
|
+
return l.replace(/—/g, ',');
|
|
516
|
+
}
|
|
517
|
+
return l;
|
|
518
|
+
});
|
|
519
|
+
if (modified) content = fixedLines.join('\n');
|
|
520
|
+
|
|
521
|
+
// 3. Fix redundant obvious comments (CODE-01)
|
|
522
|
+
const strippedComments = content.replace(/\/\/\s*(?:this\s+function\s+calculates|this\s+is\s+the\s+render\s+method|define\s+variables?|initialize\s+state).*\n/gi, '');
|
|
166
523
|
if (strippedComments !== content) {
|
|
167
524
|
content = strippedComments;
|
|
168
525
|
modified = true;
|
|
169
526
|
}
|
|
170
527
|
|
|
171
|
-
//
|
|
528
|
+
// 4. Normalize rounded-3xl / rounded-full cards (R-11 / VISUAL-02)
|
|
172
529
|
const fixedRadius = content.replace(/\brounded-3xl\b/g, 'rounded-lg');
|
|
173
530
|
if (fixedRadius !== content) {
|
|
174
531
|
content = fixedRadius;
|
|
175
532
|
modified = true;
|
|
176
533
|
}
|
|
177
534
|
|
|
535
|
+
// 5. Replace generic CTAs (R-15)
|
|
536
|
+
const fixedCtas = content
|
|
537
|
+
.replace(/>\s*Get Started\s*</g, '>Start Project<')
|
|
538
|
+
.replace(/>\s*Learn More\s*</g, '>Documentation<');
|
|
539
|
+
if (fixedCtas !== content) {
|
|
540
|
+
content = fixedCtas;
|
|
541
|
+
modified = true;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// 6. Ensure native button type (R-26)
|
|
545
|
+
const fixedButtons = content.replace(/<button(?!\s+type=)([^>]*)>/g, '<button type="button"$1>');
|
|
546
|
+
if (fixedButtons !== content) {
|
|
547
|
+
content = fixedButtons;
|
|
548
|
+
modified = true;
|
|
549
|
+
}
|
|
550
|
+
|
|
178
551
|
if (modified) {
|
|
179
552
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
180
553
|
}
|
|
@@ -182,42 +555,106 @@ export function autoFixFile(filePath) {
|
|
|
182
555
|
return modified;
|
|
183
556
|
}
|
|
184
557
|
|
|
185
|
-
|
|
186
|
-
|
|
558
|
+
/**
|
|
559
|
+
* Generate standard Mode 2 audit report in `anti-slop/audit-NNN-YYYY-MM-DD.md`.
|
|
560
|
+
*/
|
|
561
|
+
export function generateAuditReport(auditData, rootDir = process.cwd()) {
|
|
562
|
+
const outDir = path.join(rootDir, 'anti-slop');
|
|
563
|
+
if (!fs.existsSync(outDir)) {
|
|
564
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
565
|
+
}
|
|
187
566
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
567
|
+
// Determine increment index
|
|
568
|
+
let maxIndex = 0;
|
|
569
|
+
try {
|
|
570
|
+
const existing = fs.readdirSync(outDir);
|
|
571
|
+
for (const f of existing) {
|
|
572
|
+
const match = f.match(/^audit-(\d{3})-/);
|
|
573
|
+
if (match) {
|
|
574
|
+
maxIndex = Math.max(maxIndex, parseInt(match[1], 10));
|
|
575
|
+
}
|
|
194
576
|
}
|
|
577
|
+
} catch {}
|
|
195
578
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
579
|
+
const nextIndex = String(maxIndex + 1).padStart(3, '0');
|
|
580
|
+
const dateStr = new Date().toISOString().slice(0, 10);
|
|
581
|
+
const fileName = `audit-${nextIndex}-${dateStr}.md`;
|
|
582
|
+
const reportPath = path.join(outDir, fileName);
|
|
583
|
+
|
|
584
|
+
const lines = [
|
|
585
|
+
`# Anti-Slop Audit Report #${nextIndex}`,
|
|
586
|
+
``,
|
|
587
|
+
`> Generated on **${dateStr}** by **ChillAgent Anti-Slop 2.0 Engine**`,
|
|
588
|
+
`> Scope: Full-Page Repository Audit (${auditData.totalPages} pages/components scanned)`,
|
|
589
|
+
``,
|
|
590
|
+
`---`,
|
|
591
|
+
``,
|
|
592
|
+
`## Executive Summary`,
|
|
593
|
+
``,
|
|
594
|
+
`- **Delivery Gate Verdict:** **${auditData.overallStatus}**`,
|
|
595
|
+
`- **Total Pages Audited:** ${auditData.totalPages}`,
|
|
596
|
+
`- **Total Infractions Detected:** ${auditData.totalViolations}`,
|
|
597
|
+
``,
|
|
598
|
+
`### Page-by-Page Audit Ledger`,
|
|
599
|
+
``,
|
|
600
|
+
`| Page / Component Path | Type | Health Score | Gate Status | High | Med | Low |`,
|
|
601
|
+
`| :--- | :--- | :---: | :---: | :---: | :---: | :---: |`
|
|
602
|
+
];
|
|
603
|
+
|
|
604
|
+
for (const pr of auditData.pageResults) {
|
|
605
|
+
if (pr.counts.total > 0 || pr.page.type === 'PAGE_ROUTE') {
|
|
606
|
+
lines.push(
|
|
607
|
+
`| \`${pr.page.relPath}\` | ${pr.page.type} | **${pr.score}/100** | ${pr.status === 'PASS' ? '🟢 PASS' : '🔴 FAIL'} | ${pr.counts.high} | ${pr.counts.medium} | ${pr.counts.low} |`
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
lines.push(``);
|
|
613
|
+
lines.push(`---`);
|
|
614
|
+
lines.push(``);
|
|
615
|
+
lines.push(`## Numbered Findings List (Actionable Remediation)`);
|
|
616
|
+
lines.push(``);
|
|
617
|
+
|
|
618
|
+
let findingCounter = 1;
|
|
619
|
+
for (const pr of auditData.pageResults) {
|
|
620
|
+
if (pr.findings.length === 0) continue;
|
|
621
|
+
|
|
622
|
+
lines.push(`### \`${pr.page.relPath}\``);
|
|
623
|
+
lines.push(``);
|
|
624
|
+
|
|
625
|
+
for (const f of pr.findings) {
|
|
626
|
+
lines.push(`#### Finding #${findingCounter} [${f.severity}] — ${f.name} (${f.rule})`);
|
|
627
|
+
lines.push(`- **Location:** \`${pr.page.relPath}:${f.line}\``);
|
|
628
|
+
lines.push(`- **Category:** ${f.category}`);
|
|
629
|
+
lines.push(`- **Violating Snippet:** \`${f.snippet}\``);
|
|
630
|
+
lines.push(`- **Remediation Advice:** ${f.fix}`);
|
|
631
|
+
lines.push(`- [ ] **Approve fix #${findingCounter}**`);
|
|
632
|
+
lines.push(``);
|
|
633
|
+
findingCounter++;
|
|
208
634
|
}
|
|
209
635
|
}
|
|
210
636
|
|
|
211
|
-
|
|
212
|
-
|
|
637
|
+
lines.push(`---`);
|
|
638
|
+
lines.push(``);
|
|
639
|
+
lines.push(`## Mode 2 Execution Workflow`);
|
|
640
|
+
lines.push(`To automatically remediate approved items:`);
|
|
641
|
+
lines.push(`\`\`\`bash`);
|
|
642
|
+
lines.push(`npx chill-agent audit --fix`);
|
|
643
|
+
lines.push(`\`\`\``);
|
|
644
|
+
|
|
645
|
+
fs.writeFileSync(reportPath, lines.join('\n'), 'utf8');
|
|
646
|
+
return { reportPath, fileName, nextIndex };
|
|
213
647
|
}
|
|
214
648
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
649
|
+
/**
|
|
650
|
+
* Format and print comprehensive audit summary to console.
|
|
651
|
+
*/
|
|
652
|
+
export function printAuditReport(findings, targetDir = process.cwd(), auditData = null) {
|
|
653
|
+
console.log(`\n${c.bold}ChillAgent Anti-Slop 2.0 & Ponytail Comprehensive Audit${c.reset}`);
|
|
654
|
+
console.log(`${c.dim}Target Workspace: ${targetDir}${c.reset}\n`);
|
|
218
655
|
|
|
219
|
-
if (findings.length === 0) {
|
|
220
|
-
console.log(`${c.
|
|
656
|
+
if (!findings || findings.length === 0) {
|
|
657
|
+
console.log(`${c.emerald}${c.bold}✔ Clean Codebase!${c.reset} No AI slop patterns or premature over-engineering found.`);
|
|
221
658
|
return;
|
|
222
659
|
}
|
|
223
660
|
|
|
@@ -226,20 +663,24 @@ export function printAuditReport(findings, targetDir = process.cwd()) {
|
|
|
226
663
|
const lowCount = findings.filter((f) => f.severity === 'LOW').length;
|
|
227
664
|
|
|
228
665
|
console.log(
|
|
229
|
-
`Found ${c.bold}${findings.length} infractions${c.reset} ` +
|
|
230
|
-
`(${c.coral}${highCount} High${c.reset}, ${c.amber}${medCount}
|
|
666
|
+
`Found ${c.bold}${findings.length} infractions${c.reset} across audited files ` +
|
|
667
|
+
`(${c.coral}${highCount} Hard Gate / High${c.reset}, ${c.amber}${medCount} Purpose-Gate / Med${c.reset}, ${c.gray}${lowCount} Quality Locks / Low${c.reset}):\n`
|
|
231
668
|
);
|
|
232
669
|
|
|
233
|
-
findings.forEach((f, idx) => {
|
|
670
|
+
findings.slice(0, 50).forEach((f, idx) => {
|
|
234
671
|
const sevColor = f.severity === 'HIGH' ? c.coral : f.severity === 'MEDIUM' ? c.amber : c.gray;
|
|
235
|
-
const relFile = path.relative(targetDir, f.file);
|
|
672
|
+
const relFile = path.relative(targetDir, f.file).replace(/\\/g, '/');
|
|
236
673
|
|
|
237
674
|
console.log(
|
|
238
675
|
`${c.bold}#${idx + 1}${c.reset} [${sevColor}${f.severity}${c.reset}] ` +
|
|
239
|
-
`${c.cyan}${f.
|
|
676
|
+
`${c.cyan}${f.rule || f.id}${c.reset} — ${c.bold}${f.name}${c.reset}`
|
|
240
677
|
);
|
|
241
|
-
console.log(` ${c.dim}Location:${c.reset} ${relFile}:${
|
|
678
|
+
console.log(` ${c.dim}Location:${c.reset} ${relFile}:${f.line}`);
|
|
242
679
|
console.log(` ${c.dim}Snippet:${c.reset} ${c.italic}"${f.snippet}"${c.reset}`);
|
|
243
680
|
console.log(` ${c.emerald}Fix:${c.reset} ${f.fix}\n`);
|
|
244
681
|
});
|
|
682
|
+
|
|
683
|
+
if (findings.length > 50) {
|
|
684
|
+
console.log(`${c.dim}... and ${findings.length - 50} more findings. Run with --report to generate a markdown audit report.${c.reset}\n`);
|
|
685
|
+
}
|
|
245
686
|
}
|
package/src/cli.js
CHANGED
|
@@ -5,20 +5,32 @@ import { parseArgs } from 'node:util';
|
|
|
5
5
|
import { banner, c, select, multiselect, confirm } from './prompts.js';
|
|
6
6
|
import { detectAgents } from './detector.js';
|
|
7
7
|
import { AVAILABLE_SKILLS, copySkillsToProject, generatePointerBlock, injectPointerIntoFile } from './installer.js';
|
|
8
|
-
import { scanDirectory, printAuditReport } from './auditor.js';
|
|
8
|
+
import { scanDirectory, printAuditReport, auditAllPages, generateAuditReport, autoFixFile } from './auditor.js';
|
|
9
9
|
import { DESIGN_PRESETS } from './presets.js';
|
|
10
|
+
import { configureMode } from '../cli/lib/settings.mjs';
|
|
10
11
|
|
|
11
12
|
export async function runCli() {
|
|
12
13
|
const rawArgs = process.argv.slice(2);
|
|
13
14
|
const command = rawArgs[0] || 'wizard';
|
|
14
15
|
|
|
16
|
+
if (command === 'mode' || rawArgs.includes('--mode')) {
|
|
17
|
+
const modeArgs = command === 'mode' ? rawArgs.slice(1) : rawArgs.slice(rawArgs.indexOf('--mode') + 1);
|
|
18
|
+
try {
|
|
19
|
+
console.log(configureMode(modeArgs));
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error(`antislop: ${err.message}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
if (command === 'help' || command === '--help' || command === '-h') {
|
|
16
28
|
banner();
|
|
17
29
|
console.log(`
|
|
18
30
|
${c.bold}USAGE:${c.reset}
|
|
19
31
|
npx chill-agent Launch interactive setup wizard
|
|
20
32
|
npx chill-agent install [--all] [--agent <name>] Install anti-slop skills non-interactively
|
|
21
|
-
npx chill-agent audit [dir] [--
|
|
33
|
+
npx chill-agent audit [dir] [--fix] [--report] [--all-pages] Scan & audit all pages for AI slop
|
|
22
34
|
npx chill-agent init-design [preset] Generate a high-craft DESIGN.md specification
|
|
23
35
|
npx chill-agent doctor Inspect detected coding agents and rule health
|
|
24
36
|
npx chill-agent rewind [subcommand] Run deterministic time-travel debugger & proxy
|
|
@@ -47,32 +59,45 @@ ${c.bold}PRESETS FOR init-design:${c.reset}
|
|
|
47
59
|
|
|
48
60
|
if (command === 'audit' || command === 'lint' || command === 'scan') {
|
|
49
61
|
const isFix = rawArgs.includes('--fix');
|
|
62
|
+
const isReport = rawArgs.includes('--report');
|
|
63
|
+
const isAllPages = rawArgs.includes('--all-pages') || rawArgs.includes('--pages');
|
|
64
|
+
const isJson = rawArgs.includes('--json');
|
|
50
65
|
const targetDir = rawArgs[1] && !rawArgs[1].startsWith('--')
|
|
51
66
|
? path.resolve(process.cwd(), rawArgs[1])
|
|
52
67
|
: process.cwd();
|
|
53
68
|
|
|
69
|
+
const auditData = auditAllPages(targetDir);
|
|
70
|
+
|
|
54
71
|
if (isFix) {
|
|
55
|
-
const { autoFixFile } = await import('./auditor.js');
|
|
56
72
|
let fixCount = 0;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (e.isDirectory() && !['node_modules', '.git', '.chillrewind', 'dist'].includes(e.name)) {
|
|
61
|
-
walkFix(path.join(dir, e.name));
|
|
62
|
-
} else if (e.isFile() && ['.js', '.jsx', '.ts', '.tsx', '.html', '.css', '.md'].some(ext => e.name.endsWith(ext))) {
|
|
63
|
-
if (autoFixFile(path.join(dir, e.name))) fixCount++;
|
|
64
|
-
}
|
|
73
|
+
for (const pr of auditData.pageResults) {
|
|
74
|
+
if (pr.findings.length > 0 && autoFixFile(pr.page.file)) {
|
|
75
|
+
fixCount++;
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
|
-
|
|
68
|
-
console.log(`\n${c.emerald}✔ Anti-Slop Auto-Fix Completed:${c.reset} Remedied slop patterns in ${c.bold}${fixCount}${c.reset} files.`);
|
|
78
|
+
console.log(`\n${c.emerald}✔ Anti-Slop Auto-Fix Completed:${c.reset} Remedied slop patterns across ${c.bold}${fixCount}${c.reset} audited files.`);
|
|
69
79
|
}
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
console.log(
|
|
81
|
+
if (isReport) {
|
|
82
|
+
const rep = generateAuditReport(auditData, targetDir);
|
|
83
|
+
console.log(`\n${c.emerald}✔ Anti-Slop Mode 2 Report Generated:${c.reset} ${c.bold}${rep.reportPath}${c.reset}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (isJson) {
|
|
87
|
+
console.log(JSON.stringify(auditData, null, 2));
|
|
74
88
|
} else {
|
|
75
|
-
|
|
89
|
+
if (isAllPages) {
|
|
90
|
+
console.log(`\n${c.bold}Discovered ${auditData.totalPages} Pages & Component Entrypoints:${c.reset}`);
|
|
91
|
+
console.log(`--------------------------------------------------------------------------------`);
|
|
92
|
+
for (const pr of auditData.pageResults) {
|
|
93
|
+
const statusIcon = pr.status === 'PASS' ? `${c.green}✔ PASS${c.reset}` : `${c.coral}✖ FAIL${c.reset}`;
|
|
94
|
+
console.log(` ${statusIcon} [${c.bold}${String(pr.score).padStart(3)}/100${c.reset}] \`${pr.page.relPath}\` (${pr.page.type}) — ${pr.counts.high} High, ${pr.counts.medium} Med, ${pr.counts.low} Low`);
|
|
95
|
+
}
|
|
96
|
+
console.log(`--------------------------------------------------------------------------------`);
|
|
97
|
+
}
|
|
98
|
+
const allFindings = [];
|
|
99
|
+
for (const pr of auditData.pageResults) allFindings.push(...pr.findings);
|
|
100
|
+
printAuditReport(allFindings, targetDir, auditData);
|
|
76
101
|
}
|
|
77
102
|
return;
|
|
78
103
|
}
|