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.
@@ -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.6.2",
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.6.2",
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.6.2",
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.6.2",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "chill-agent",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "description": "Lazy senior dev mode. Forces the simplest, shortest solution that actually works: YAGNI, stdlib first, no unrequested abstractions.",
5
5
  "author": {
6
6
  "name": "sann",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ponytail",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "description": "Lazy senior dev mode. Forces the simplest, shortest solution that actually works: YAGNI, stdlib first, no unrequested abstractions.",
5
5
  "author": {
6
6
  "name": "sann",
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 sann
3
+ Copyright (c) 2026 sann (Steaven Galang)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
- > Synthesizing the visual craftsmanship of **[anti-slop](https://github.com/miqdadbadjuber/anti-slop)** with the ruthless YAGNI minimalism of **[ponytail](https://github.com/dietrichgebert/ponytail)**.
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
- [![NPM Version](https://img.shields.io/npm/v/chill-agent.svg?style=flat&color=emerald)](https://www.npmjs.com/package/chill-agent)
7
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
- [![Dependencies](https://img.shields.io/badge/dependencies-0-success.svg)](package.json)
9
- [![Node.js: >=18.0.0](https://img.shields.io/badge/node-%3E%3D18.0.0-emerald.svg)](https://nodejs.org)
6
+ [![NPM Version](https://img.shields.io/npm/v/chill-agent.svg?style=flat&color=10B981)](https://www.npmjs.com/package/chill-agent)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-3B82F6.svg)](LICENSE)
8
+ [![Dependencies: 0](https://img.shields.io/badge/dependencies-0-10B981.svg)](package.json)
9
+ [![Node.js: >=18.0.0](https://img.shields.io/badge/node-%3E%3D18.0.0-6366F1.svg)](https://nodejs.org)
10
+ [![Multi-Agent: 14 Hosts](https://img.shields.io/badge/hosts-14%20agents-EC4899.svg)](AGENTS.md)
10
11
 
11
12
  ---
12
13
 
13
- ## ⚡ Quickstart
14
+ ## Overview
14
15
 
15
- Run directly without installing via `npx`:
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, Claude, Cursor, Codex, Windsurf)
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 & over-engineering bloat
47
+ # Scan current workspace for AI slop cliches, unrequested bloat, and gradient leaks
22
48
  npx chill-agent audit
23
49
 
24
- # Automatically remediate slop (strip emojis, clean redundant comments, fix radius)
50
+ # Automatically remediate violations (strip emojis, clean redundant comments, normalize radius)
25
51
  npx chill-agent audit --fix
26
52
 
27
- # Generate a master high-craft DESIGN.md specification
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 agent environment health & active hooks
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
- ## 💎 The Synthesis: Anti-Slop + Ponytail (Made Better)
65
+ ## The 6-Step Minimalist Execution Ladder
40
66
 
41
- ChillAgent is **not** just a list of bans, nor is it just "write less code". It fuses two core engineering philosophies:
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
- │ CHILLAGENT │
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:** The minimum code that works reliably.
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
- ## 🎯 Intensity Modes (`/chill [lite|full|ultra]`)
109
+ ## 5 High-Craft Design System Presets (`npx chill-agent init-design`)
69
110
 
70
- - **`lite`**: Prototyping mode. Strips obvious slop gradients while allowing exploratory code.
71
- - **`full`** *(Default)*: Everyday production engineering. Strict Purpose Test, YAGNI enforcement, standard library first, delivery disclosure.
72
- - **`ultra`**: High-performance refactoring. Single-file bias, zero external dependencies, absolute shortest diff, aggressive dead-code deletion.
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
- ## 🧩 6 Modular Skills (in `./skills/`)
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
- 1. **`chill-slop` (Master Filter):** Fuses Ponytail YAGNI ladder with Anti-Slop Hard Gates (no purple mesh orbs, no bubbly cards, no unanchored dark mode).
79
- 2. **`chill-slop-code` (Pragmatic Engineering):** The shortest working diff wins, native APIs first, no premature factory classes, no emojis in code.
80
- 3. **`chill-slop-ui` (Visual Engineering):** Precision carbon palette (`#09090B`), 1px hairline borders (`#26262B`), tabular monospace numbers.
81
- 4. **`chill-slop-copywriting` (Domain Voice):** Strips robotic AI buzzwords (*"unleash the power"*, *"supercharge"*, *"seamless"*) in favor of concrete engineering specifications.
82
- 5. **`chill-slop-human` (Ergonomics & A11y):** Enforces 4.5:1 contrast, visible focus rings, 44px touch targets, and accessible ARIA affordances.
83
- 6. **`chill-slop-mobile` (Responsive Systems):** Fluid CSS `clamp()` typography, zero horizontal blowout, and comfortable thumb-reach zone actions.
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
- ## 🎨 5 High-Craft Design System Presets (`npx chill-agent init-design`)
161
+ ## Contributing
88
162
 
89
- Prevent your coding agent from falling into the "sterile blank white" trap:
90
- - **Linear Carbon Geist:** Deep obsidian canvas, 1px `#26262B` borders, JetBrains Mono, emerald state indicators.
91
- - **Swiss Neo-Brutal:** High contrast black/white ink, strict 0px border radius, heavy grotesque typography.
92
- - **Industrial Technical Dossier:** Blueprint data-first layout, slate/steel surfaces, dense telemetry views.
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
- ## 📄 License
170
+ ## License
99
171
 
100
- MIT © [sann](https://github.com/sann)
172
+ MIT © [sann](https://github.com/sann) (Steaven Galang)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chill-agent",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "description": "ChillAgent Anti-Slop 2.0 & Pragmatic Senior Dev Mode. Forces smallest working diff, YAGNI, standard library first, zero AI slop.",
5
5
  "contextFileName": "AGENTS.md"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chill-agent",
3
- "version": "1.6.2",
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
@@ -1,5 +1,5 @@
1
1
  name: ponytail
2
- version: 1.6.2
2
+ version: 1.8.0
3
3
  description: Lazy senior dev mode for Hermes Agent, always-on context, bundled skills, and slash commands.
4
4
  author: sann
5
5
  provides_hooks:
package/src/auditor.js CHANGED
@@ -1,146 +1,432 @@
1
- // src/auditor.js - Automated Anti-Slop & Over-Engineering Scanner
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
- const SLOP_PATTERNS = [
7
- // 1. Visual Slop (Anti-Slop UI)
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: 'VISUAL-01',
10
- category: 'Visual Slop',
16
+ id: 'R-02',
17
+ alias: 'COPY-04',
18
+ category: 'Hard Gate: Copywriting',
11
19
  severity: 'HIGH',
12
- 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}/i,
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: 'Replace with precision monochromatic carbon `#09090B` or a branded domain color palette.'
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: 'VISUAL-02',
18
- category: 'Visual Slop',
158
+ id: 'R-07',
159
+ category: 'Purpose-Gate: Background',
19
160
  severity: 'MEDIUM',
20
- regex: /rounded-3xl|rounded-full\s+.*shadow-2xl/i,
21
- name: 'Pill-Shaped Floating Bubble',
22
- fix: 'Use crisp geometric radius (4px to 8px) with hairline 1px borders instead of rounded-3xl with heavy shadow.'
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: 'VISUAL-03',
26
- category: 'Visual Slop',
167
+ id: 'R-08',
168
+ category: 'Purpose-Gate: Button Arrows',
27
169
  severity: 'LOW',
28
- regex: /backdrop-blur-(md|lg|xl|2xl)/i,
29
- name: 'Excessive Glassmorphic Blur',
30
- fix: 'Ensure backdrop-blur is not stacked across cards, modals, and navbars simultaneously.'
170
+ rule: 'R-08',
171
+ regex: /<button[^>]*>.*(?:&rarr;|&#8594;|→|↗|<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: 'VISUAL-04',
34
- category: 'Visual Slop',
176
+ id: 'R-09',
177
+ category: 'Purpose-Gate: Badges',
35
178
  severity: 'MEDIUM',
36
- regex: /body\s*\{[^}]*overflow-x\s*:\s*hidden/i,
37
- name: 'Lazy Body Overflow Masking',
38
- fix: 'Do not use overflow-x: hidden on body to mask layout bugs. Eliminate the protruding element width directly.'
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: 'BLOAT-01',
44
- category: 'Over-Engineering',
45
- severity: 'HIGH',
46
- regex: /class\s+[A-Za-z0-9]+Factory|class\s+[A-Za-z0-9]+AbstractBuilder|class\s+[A-Za-z0-9]+ProviderManager/i,
47
- name: 'Premature Factory / Builder Abstraction',
48
- fix: 'Ponytail YAGNI rule: replace speculative multi-class factory wrappers with a direct 1-line function or object literal.'
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: 'BLOAT-02',
52
- category: 'Over-Engineering',
195
+ id: 'R-12',
196
+ category: 'Purpose-Gate: Shadow',
53
197
  severity: 'MEDIUM',
54
- regex: /import\s+.*lodash\/(cloneDeep|isEqual|merge)|import\s+is-number\s+from/i,
55
- name: 'Unnecessary Micro-Dependency',
56
- fix: 'Use native JavaScript standard library (`structuredClone`, `Object.assign`, or standard operators) instead of external micro-packages.'
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. Copywriting Slop
240
+ // ─── TIER 3: QUALITY LOCKS & PONYTAIL MINIMALISM ────────────────────────
60
241
  {
61
- id: 'COPY-01',
62
- category: 'Copywriting Slop',
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
- 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)\b/i,
65
- name: 'Generic AI Hype Filler',
66
- fix: 'Replace vague hype words with concrete specifications or direct functional instructions.'
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: 'COPY-02',
70
- category: 'Copywriting Slop',
280
+ id: 'R-29',
281
+ category: 'Quality Lock: Color Palette',
71
282
  severity: 'MEDIUM',
72
- regex: /[✨🚀🎉💡🔥]/u,
73
- name: 'AI Sparkle / Rocket Emoji Cliché',
74
- fix: 'Strip emojis from marketing headlines, documentation, and interface button labels.'
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: 'COPY-03',
78
- category: 'Copywriting Slop',
289
+ id: 'BLOAT-01',
290
+ category: 'Ponytail Minimalism: Architecture',
79
291
  severity: 'HIGH',
80
- regex: /\b(trusted\s+by\s+[0-9,]+\s+happy\s+customers|99\.9%\s+customer\s+satisfaction)\b/i,
81
- name: 'Hallucinated Social Proof',
82
- fix: 'Cite verified customer metrics or remove synthetic testimonials entirely.'
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: 'COPY-04',
86
- category: 'Copywriting Slop',
298
+ id: 'BLOAT-02',
299
+ category: 'Ponytail Minimalism: Dependencies',
87
300
  severity: 'MEDIUM',
88
- regex: /—/u,
89
- name: 'Em Dash (—) AI Writing Tic',
90
- fix: 'R-02 rule: replace em dash with comma, period, colon, or parentheses.'
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 Hygiene',
97
- severity: 'MEDIUM',
98
- regex: /\/\/\s*(this\s+function\s+calculates|this\s+is\s+the\s+render\s+method|define\s+variables?|initialize\s+state)/i,
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 merely restate what the code visibly does. Document the "WHY", not the "WHAT".'
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: 'Code Hygiene',
317
+ category: 'Ponytail Hygiene: Stubbed Logic',
105
318
  severity: 'LOW',
106
- regex: /\/\/\s*TODO:\s*(implement\s+this|add\s+logic\s+later|finish\s+this)/i,
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 full working implementation or throw explicit NotImplementedError with actionable message.'
322
+ fix: 'Provide working implementation or raise explicit NotImplementedError with actionable detail.'
109
323
  },
110
-
111
- // 5. Human Factors & Accessibility
112
324
  {
113
- id: 'A11Y-01',
114
- category: 'Human Factors',
115
- severity: 'HIGH',
116
- regex: /<button[^>]*>[^<]*<svg[^>]*>[\s\S]*?<\/svg>[^<]*<\/button>/i,
117
- name: 'Icon Button Missing Accessible Label',
118
- fix: 'Add aria-label="..." to the button element so screen readers can identify the control action.'
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
- '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.html', '.css', '.md', '.mdx'
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', 'coverage'
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('\n');
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, 80),
143
- ...pattern
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 marketing emojis
158
- const strippedEmoji = content.replace(/[✨🚀🎉💡🔥]/gu, '');
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. Fix redundant obvious comments
165
- 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, '');
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
- // 3. Fix rounded-3xl cards
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
- export function scanDirectory(dirPath) {
186
- const allFindings = [];
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
- function walk(current) {
189
- let entries = [];
190
- try {
191
- entries = fs.readdirSync(current, { withFileTypes: true });
192
- } catch {
193
- return;
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
- for (const entry of entries) {
197
- if (entry.isDirectory()) {
198
- if (!IGNORE_DIRS.has(entry.name)) {
199
- walk(path.join(current, entry.name));
200
- }
201
- } else if (entry.isFile()) {
202
- const ext = path.extname(entry.name);
203
- if (SCAN_EXTENSIONS.has(ext)) {
204
- const res = scanFile(path.join(current, entry.name));
205
- allFindings.push(...res);
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
- walk(dirPath);
212
- return allFindings;
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
- export function printAuditReport(findings, targetDir = process.cwd()) {
216
- console.log(`\n${c.bold}ChillAgent Anti-Slop & Over-Engineering Audit${c.reset}`);
217
- console.log(`${c.dim}Scanned directory: ${targetDir}${c.reset}\n`);
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.green}${c.bold}✔ Clean Codebase!${c.reset} No AI slop patterns or premature over-engineering found.`);
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} Medium${c.reset}, ${c.gray}${lowCount} Low${c.reset}):\n`
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.category}${c.reset} — ${c.bold}${f.name}${c.reset}`
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}:${c.line}`);
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] [--json] Scan code & copy for AI slop cliches
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
- function walkFix(dir) {
58
- const entries = fs.readdirSync(dir, { withFileTypes: true });
59
- for (const e of entries) {
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
- walkFix(targetDir);
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
- const findings = scanDirectory(targetDir);
72
- if (rawArgs.includes('--json')) {
73
- console.log(JSON.stringify(findings, null, 2));
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
- printAuditReport(findings, targetDir);
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
  }