cursordoctrine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/INSTALL.md +113 -0
  2. package/LICENSE +21 -0
  3. package/README.md +86 -0
  4. package/bin/cli.mjs +413 -0
  5. package/linux/USER-RULES.md +12 -0
  6. package/linux/doctrine.md +172 -0
  7. package/linux/hooks/anti-slop-audit.sh +163 -0
  8. package/linux/hooks/anti-slop.md +56 -0
  9. package/linux/hooks/final-review.md +52 -0
  10. package/linux/hooks/final-review.sh +99 -0
  11. package/linux/hooks/hook-common.sh +120 -0
  12. package/linux/hooks/minimal-edit-audit.sh +112 -0
  13. package/linux/hooks/permission-gate.sh +75 -0
  14. package/linux/hooks/post-tool-use.sh +53 -0
  15. package/linux/hooks/self-review-trigger.sh +56 -0
  16. package/linux/hooks/self-review.md +48 -0
  17. package/linux/hooks/subagent-stop-review.sh +93 -0
  18. package/linux/hooks.json +64 -0
  19. package/linux/inject-doctrine.sh +31 -0
  20. package/package.json +40 -0
  21. package/skills/anti-slop/SKILL.md +267 -0
  22. package/skills/anti-slop/scripts/scan_slop.py +986 -0
  23. package/windows/USER-RULES.md +12 -0
  24. package/windows/doctrine.md +172 -0
  25. package/windows/hooks/anti-slop-audit.ps1 +182 -0
  26. package/windows/hooks/anti-slop.md +56 -0
  27. package/windows/hooks/final-review.md +52 -0
  28. package/windows/hooks/final-review.ps1 +105 -0
  29. package/windows/hooks/hook-common.ps1 +84 -0
  30. package/windows/hooks/minimal-edit-audit.ps1 +116 -0
  31. package/windows/hooks/permission-gate.ps1 +98 -0
  32. package/windows/hooks/post-tool-use.ps1 +46 -0
  33. package/windows/hooks/self-review-trigger.ps1 +83 -0
  34. package/windows/hooks/self-review.md +48 -0
  35. package/windows/hooks/subagent-stop-review.ps1 +89 -0
  36. package/windows/hooks.json +64 -0
  37. package/windows/inject-doctrine.ps1 +58 -0
@@ -0,0 +1,267 @@
1
+ ---
2
+ name: anti-slop
3
+ description: >-
4
+ Finds and deletes AI slop from a change OR a whole codebase — premature
5
+ abstractions (Factory/Repository/Mediator/CQRS/Event-Sourcing/DDD), unnecessary
6
+ dependencies, redundant comments, happy-path-only code, superficial tests /
7
+ test theater, type escapes (as any / @ts-ignore / type: ignore), swallowed
8
+ errors (empty catch, except: pass), prompt residue ("in a real app", banner
9
+ comments, emoji in code), cargo-cult patterns, accidental complexity,
10
+ hallucinated requirements (unrequested features / scope creep), framework
11
+ slop (React useEffect hell, Astro hydration bloat, Tailwind class soup,
12
+ SELECT *, ORM include pyramids, boolean traps, guard-chain defensive
13
+ inflation, async wrapper mania), and CLONE PROLIFERATION / duplicate helpers
14
+ (20 copies of isRecord, DRY violations,
15
+ knowledge duplication) — organized as twelve failure classes, while preserving
16
+ the behaviour the user actually asked for. Use when the user says remove AI
17
+ slop, de-slop, clean up the slop, /anti-slop, "this is slop", review the whole
18
+ codebase for slop, find duplicate or clone functions, or asks to strip
19
+ over-engineering, premature abstraction, cargo cult, redundant comments, or
20
+ duplicate utilities.
21
+ metadata:
22
+ layer: active-cleanup
23
+ pairs-with: minimal-editing, anti-slop-hook
24
+ ---
25
+
26
+ # Anti-Slop
27
+
28
+ Active counterpart to the `afterFileEdit` anti-slop **hook**. The hook only
29
+ *advises* after each edit; this skill does a deliberate sweep that **removes**
30
+ the slop. Same detectors — but here you fix, you don't flag.
31
+
32
+ Slop = code that runs but should not ship. Think in **failure classes**, not
33
+ individual smells — every finding belongs to one of twelve:
34
+
35
+ | Class | Signal | Names it goes by |
36
+ |-------|--------|------------------|
37
+ | **Structural** | More architecture than business logic. | Premature Abstraction/Generalization, Abstraction Debt, Overengineering, Architecture Astronautics, Indirection Hell, Wrapperitis, Layer Proliferation, Enterprise FizzBuzz |
38
+ | **Semantic** | Code works but no longer matches the problem. | Semantic/Requirement Drift, Intent Erosion, Spec Divergence, Hidden Assumptions |
39
+ | **Complexity** | Every change feels expensive. | Boilerplate Inflation, Complexity Creep, Configuration Sprawl, Cyclomatic Inflation, Cognitive Complexity |
40
+ | **Duplication** | One bug fix = five edits. | Copy-Paste Programming, Clone Proliferation, Knowledge/Logic Duplication, Divergent Duplication, Helper Hell, Micro-Abstraction Spam, Semantic Density Collapse, Generated-Code Fingerprints |
41
+ | **Dependency** | More package management than product. | Dependency Bloat/Hell, Ghost Dependencies, Transitive Explosion, Supply-Chain Bloat |
42
+ | **Testing** | 90% coverage, 0% confidence. | Test Theater, Snapshot Abuse, Mock Hell, Assertion Poverty, Coverage Worship |
43
+ | **Type-System** | The types exist but nobody trusts them. | Any-Driven Development, Stringly Typed Design, Type Erosion, Type Escapes, Unsafe Casting |
44
+ | **API** | Nobody knows how to use it correctly. | Leaky Abstractions, API Surface Inflation, Parameter Explosion, Boolean Trap |
45
+ | **State** | Bugs appear from unrelated changes. | Mutation Soup, Temporal Coupling, Hidden Side Effects, Shared Mutable State, State Explosion |
46
+ | **Performance** | Works in demos, collapses at scale. | N+1 Queries, Algorithmic Waste, Incidental Allocation, Render Thrashing, Cache Cargo-Culting |
47
+ | **Documentation** | Docs actively mislead. | Comment Debt, Documentation Drift, Generated Doc Noise, Stale Examples |
48
+ | **AI-Specific** | Looks professional; nobody can say why it exists. | Hallucinated Architecture/Dependencies/APIs, Defensive Code Inflation, Scaffold Explosion, Refactor Cascades, Synthetic Abstraction, Prompt Residue, Context Drift |
49
+
50
+ The governing question, asked of every survivor: **every new file, dependency,
51
+ abstraction, layer, pattern, interface, hook, provider, service, context,
52
+ middleware, migration, component, type, and configuration must justify its
53
+ existence with a measurable reduction in complexity elsewhere.** What cannot
54
+ answer is slop — most AI slop survives because nobody asks. The senior-review
55
+ form of the question: *what requirement forced this abstraction into
56
+ existence?* AI rarely has an answer.
57
+
58
+ ## Framework failure modes (the vibe-coding stack)
59
+
60
+ Slop is framework-dependent — a React anti-pattern does not exist in SQL. The
61
+ classes above specialize per stack; *(scanner)* marks mechanically seeded ones:
62
+
63
+ - **TypeScript** — any leakage / `as unknown as` cascades *(scanner)*; fake
64
+ type safety (types exist, runtime validation at boundaries doesn't);
65
+ interface explosion (`UserDTO`/`UserResponseDTO`/`UserViewDTO` for one
66
+ entity); generic abuse (`<T extends Record<string, any>>`) where plain types work.
67
+ - **React** — useEffect hell for state synchronization (compute it, or use the
68
+ right primitive); derived state stored in useState instead of computed;
69
+ prop-drilling chains; everything-in-Context; component fragmentation
70
+ (`Button` + `ButtonIcon` + `ButtonLabel` + `ButtonWrapper` for no reason);
71
+ hooks generating hooks.
72
+ - **Astro** — island explosion (50 where 3 are needed); `client:load`
73
+ everywhere when `client:visible` / `client:idle` / zero JS would do; SSR for
74
+ static content; the page that accidentally became a React SPA.
75
+ - **Node** — Controller/Service/Repository/Manager/Provider stacks for CRUD;
76
+ pointless async wrappers (`await Promise.resolve`, async promise executors)
77
+ *(scanner)*; 30-deep middleware chains; custom error hierarchies nobody
78
+ catches; singletons as globals.
79
+ - **SQL / Postgres** — N+1 queries; `SELECT *` in checked-in SQL *(scanner)*;
80
+ premature AND missing indexes (both are "nobody measured"); 20-table join
81
+ monsters; JSONB as a schema escape hatch (`data JSONB` for everything);
82
+ migration spam; schema drift from business reality.
83
+ - **Supabase** — RLS policies nobody can explain; trusting the client with
84
+ security; RPC proliferation (hundreds of tiny functions); auth state
85
+ duplicated outside supabase-js; storage permissions re-implemented in code.
86
+ - **Tailwind** — 200+ character class soup *(scanner)*; magic values
87
+ (`w-[347px]`) *(scanner)*; the same class string pasted everywhere instead of
88
+ one extracted primitive.
89
+ - **ORM (Prisma/Drizzle)** — `include` nested five levels deep; nobody reads
90
+ the generated SQL; every relationship modeled twice; ORM worship that
91
+ ignores what the database actually does.
92
+ - **API** — boolean traps (`createUser(true, false, true)`) *(scanner)*; god
93
+ endpoints (`/api/data` does everything); more DTOs than entities; v1–v4 all
94
+ alive forever.
95
+ - **AI-specific TS/React** — deepening guard chains (`if (!data) return; if
96
+ (!data.user) return;` — the fix is `?.`) *(scanner)*; fallback hell
97
+ (`a ?? b ?? c ?? ""` everywhere); scaffold explosion (hooks/ utils/ helpers/
98
+ services/ providers/ adapters/ before any business logic exists);
99
+ hallucinated extensibility (`UserFactory`/`UserRegistry`/`UserStrategy` for
100
+ one user type).
101
+
102
+ ## The one rule that outranks everything
103
+
104
+ **Never delete the behaviour the user asked for.** Slop is what got added *on
105
+ top* of the task — speculative layers, drive-by abstractions, filler. If
106
+ removing something would change what the feature does, it is not slop: leave it.
107
+ When unsure whether something is slop or intent, **leave it and say so** — never
108
+ guess-delete.
109
+
110
+ ## Workflow
111
+
112
+ Track these phases (TodoWrite on Cursor, if available):
113
+
114
+ 1. **Scope** — decide what to clean: the **whole codebase** (`--all` — the
115
+ default for a "review/clean the codebase" request), a change in progress (the
116
+ diff), or specific files the user named. State SCOPE in one line. Do not
117
+ wander outside it.
118
+ 2. **Scan** — get the deterministic inventory first. Pick the mode:
119
+ ```
120
+ python scripts/scan_slop.py --all --root . # WHOLE codebase + duplication (recommended)
121
+ python scripts/scan_slop.py --root . # only a change in progress (diff vs HEAD)
122
+ python scripts/scan_slop.py src/foo.ts ... # specific files
123
+ ```
124
+ `--all` is the one that reviews the *entire current codebase* and runs the
125
+ cross-file **duplication analysis** (clones / DRY — see below); it is what you
126
+ want for "check what the codebase needs". Explicit paths audit only those files
127
+ and **suppress dead-helper / single-use analysis** (reference counts need the
128
+ whole tree). Diff mode is **silent on a clean tree** by design (nothing to vet).
129
+ Use `python3` if that is your platform's launcher. Resolve `scripts/` against
130
+ this skill's own directory. Add `--gate` to fail CI when slop is found (works
131
+ with `--format json`; the size-only "substantial change" note never gates). If
132
+ the script cannot run, do the detection by hand — it is only `git` plus the
133
+ regexes below. The scanner is deliberately narrow (high precision): it seeds
134
+ deterministic slop. Whether or not it finds anything, still walk the taxonomy
135
+ — most slop is semantic and never shows up in a regex.
136
+ 3. **Delete** — walk the taxonomy table below. For each hit: fix it, don't
137
+ report it line-by-line.
138
+ 4. **Verify** — re-run the scanner (expect clean), re-read the diff, confirm
139
+ behaviour is unchanged and the diff got *smaller*. Then trace every remaining
140
+ addition back to the user's request — anything you cannot trace is a
141
+ hallucinated requirement: delete it or ask. Run the project's tests if
142
+ behaviour-bearing code moved.
143
+ 5. **Report** — short summary: what slop was removed, what was left and why.
144
+
145
+ ## Taxonomy → fix action
146
+
147
+ Walk every row. Rows tagged *(scanner)* are seeded mechanically by
148
+ `scan_slop.py`; the rest need your judgement.
149
+
150
+ | Slop | How to spot it | Fix |
151
+ |------|----------------|-----|
152
+ | **Unnecessary dependency** *(scanner)* | new entry in package.json / requirements / pyproject / Cargo.toml / go.mod … | Remove it; use the stdlib or an existing dep. Keep only if it clearly earns its place. |
153
+ | **Premature abstraction / Abstraction Debt** *(scanner)* | new `*Factory` / `*Repository` / `*Mediator` / `*Strategy` / `*Builder` / `*Wrapper` / `*Orchestrator` / base class / interface, or CQRS / Event-Sourcing / DDD / Hexagonal layering with fewer than 2–3 real call sites *today* | Delete the layer; inline the direct code. "For future flexibility" is not a present problem. |
154
+ | **Redundant comments** *(scanner)* | a comment that restates the next line (`// increment i`, `# return the result`) | Delete it. Keep only comments that explain WHY. |
155
+ | **Type escapes** *(scanner)* | `as any`, `: any`, `@ts-ignore` / `@ts-nocheck`, `# type: ignore`, unsafe casts that silence the checker | Fix the type, not the checker. If a boundary is truly untypable, isolate ONE typed adapter instead of spraying `any`. |
156
+ | **Swallowed errors / Defensive inflation** *(scanner)* | empty `catch {}`, `.catch(() => {})`, bare/broad `except: pass`; try/catch wrapping that only hides failures | Let it fail loudly or handle it meaningfully. Delete catch-alls that exist to suppress. |
157
+ | **Happy-path only** | no handling for the null / empty / zero / boundary / error inputs the task implies | Add the missing edge-case handling. This is the one row where you ADD code. |
158
+ | **Hallucinated requirements** | features, options, config flags, endpoints, CLI args, or "nice to have" handling that no user message asked for and no existing code requires — walk the diff and trace every addition back to the request | Delete it. If you genuinely believe it's needed, ASK first — never ship unrequested scope. |
159
+ | **AI verbosity residue / Prompt residue** *(scanner)* | placeholder phrases (`in a real app`, `for production use`, `this is a simplified`, `TODO: implement actual`), emoji in code or log output, decorative banner walls (`// ===== HELPERS =====`), leftover debug prints | Delete on sight. None of these survive a human review. |
160
+ | **Duplicated logic** | new code mirrors something already in the repo | Delete the copy; call the existing function. Grep before you keep it. |
161
+ | **Clone proliferation / DRY / Knowledge duplication** | `--all` reports the same function name in ≥2 files, or identical bodies under different names (`isRecord` / `isObject` / `isPlainObject`) | Keep ONE canonical definition; re-point imports; delete the copies. One source of truth per concept. |
162
+ | **Utility explosion / Helper Hell / Fingerprints** | a swarm of tiny `is*` / `assert*` / `safe*` one-liners; fingerprints (`isRecord`, `safeParse`, `sleep`, `retry`, `assertNever`) | Inline single-use micro-helpers; consolidate genuinely shared ones into one module. |
163
+ | **Ignored conventions** | style / naming / structure / error-handling differs from the file's neighbours | Rewrite to match the surrounding code. |
164
+ | **Accidental complexity** | indirection / generics / config a junior can't read in 30s | Flatten to the simplest form that works. |
165
+ | **Superficial tests / Test theater** | the test asserts "it runs", mirrors the implementation, or cannot fail; literal tautologies (`expect(true).toBe(true)`, `assert True`) *(scanner)*; snapshot-everything, mocks of mocks, assertion poverty | Rewrite to assert real outcomes and the edge cases; delete tautological tests. |
166
+ | **API slop** | boolean traps (`fn(true, false)`), 5+ positional params, internals leaking through signatures, three names for one concept | Options object / named params; collapse the surface; one name per concept. |
167
+ | **State slop** | shared mutable module state, side effects hidden in getters, order-dependent calls (temporal coupling) | Localize state, make effects explicit, inject dependencies. |
168
+ | **Performance slop** | queries/IO inside loops (N+1), per-call allocation in hot paths, speculative caches nobody measured | Move IO out of loops; measure before caching; delete speculative caches. |
169
+ | **Documentation drift** | comments/docs describing behavior the code no longer has; stale examples that do not run | Fix or delete. Wrong docs are worse than no docs. |
170
+ | **Cargo cult / Semantic Density** | a pattern copied without its reason; you cannot state WHY it is there | Remove what you cannot justify. A shape you have seen ≠ a shape you need. |
171
+ | **Architectural violation / drift** | reaches across layers, business logic in the wrong place, breaks a project constraint — or **drift**: new top-level dirs, modules, or structural patterns that did not exist before this session | Move it to the right layer or revert to the established pattern. Structure changes need explicit user intent, not model initiative. |
172
+
173
+ ### Detection regexes (for scanning by hand — the scanner automates all of these)
174
+
175
+ - Premature abstraction: `\b(class|interface|struct|trait|protocol)\s+[A-Z]\w*(Factory|Repository|Mediator|Strategy|Singleton|Facade|Builder|Visitor|Decorator|Wrapper|Orchestrator)\b`, plus `\b(CQRS|Event[\s-]?Sourcing|Domain[\s-]?Driven|Aggregate Root|Bounded Context|Hexagonal Architecture|Onion Architecture)\b`.
176
+ - Redundant comment: a `//`, `#`, or `*` line that restates the adjacent code verb-for-verb.
177
+ - New dependency: an added line in a dependency manifest declaring `name → version` (manifest metadata like `"version":` / `"node":` is exempt).
178
+ - Verbosity residue: `\b(in a real (app|application|world|scenario)|for production use|this is a simplified|TODO:? implement actual|replace (this )?with your)\b` (case-insensitive); emoji in source (`[\u2600-\u27BF\U0001F300-\U0001FAFF]`); banner walls `^\s*(//|#|/\*)\s*[=*#]{5,}` (`# ----` dividers are a human convention, not residue).
179
+ - Type escapes: `as any`, `as unknown as`, `[,:<]\s*any\b`, `any[]`, `@ts-(ignore|nocheck)` (`@ts-expect-error` is the sanctioned form — leave it); Python `#\s*type:\s*ignore`.
180
+ - Swallowed errors: `catch {}` / `catch (e) {}` empty on one line, `.catch(() => {})` / `.catch(() => null)`, bare `except:` or `except Exception:` followed by `pass` (`except ImportError: pass` is a legitimate idiom). Multi-line catch blocks holding only a comment need your judgement.
181
+ - Tautological tests: `expect(<literal>).toBe(<same literal>)`, `assert True`, `assertTrue(true|True)`, `assert(true)`.
182
+ - Async wrappers: `await Promise.resolve(`, `new Promise(async`.
183
+ - Guard chains: consecutive `if (!x) return` lines where each test deepens the previous (`!data` → `!data.user`) — the fix is optional chaining.
184
+ - Boolean traps: two adjacent literal booleans in a call argument list (`fn(a, true, false)`); array literals exempt.
185
+ - SELECT star: `\bSELECT\s+\*` in `.sql` files (`--` comment lines exempt).
186
+ - Tailwind: `class=` / `className=` strings ≥200 chars; arbitrary values ≥100px (`w-[347px]`).
187
+ - Hallucinated requirements have no regex — they are the diff-vs-request comparison in Verify. Same for architecture drift: compare the tree/imports against what existed at session start (`git diff --stat`, new dirs). Semantic, State, API, and Performance slop are judgement rows: no regex can see intent drift or temporal coupling without lying about confidence.
188
+
189
+ ### Review lens — score the survivors
190
+
191
+ When judging what the regexes cannot see, score against: **semantic density**
192
+ (business value per line), **change surface area** (files touched per simple
193
+ change), **traceability** (intent → implementation time), **locality**
194
+ (understandable without opening 20 files), **dependency cost**, **abstraction
195
+ ROI** (value created ÷ complexity introduced), **architectural compression**
196
+ (smallest architecture that solves the problem), **blast radius** (what one
197
+ change can break), **cognitive load**, **intent preservation** (implementation
198
+ vs requirements), **file proliferation** (files added per feature),
199
+ **abstraction pressure** (abstractions with fewer than two real
200
+ implementations), **indirection depth** (clicks to find the implementation),
201
+ **state count** (mutable states per feature), **hydration cost** (islands vs
202
+ actual interactivity), **SQL efficiency** (queries per page render). Code that
203
+ scores badly on two or more is slop even though no regex caught it.
204
+
205
+ ## Duplication / clones (whole-codebase)
206
+
207
+ `scan_slop.py --all` runs a cross-file analysis a per-diff view cannot — the core
208
+ of a full-codebase de-slop:
209
+
210
+ - **Clone proliferation** — same function name in ≥2 files (the "20 copies of
211
+ `isRecord`" problem).
212
+ - **Knowledge duplication** — identical bodies under different names
213
+ (`isRecord` / `isObject`); one concept scattered, so one conceptual change
214
+ becomes N edits (Divergent Change).
215
+ - **Generated-code fingerprints** — `isRecord`, `safeParse`, `sleep`, `retry`,
216
+ `assertNever` recurring at statistically abnormal rates.
217
+ - **Micro-abstraction load** — the share of tiny `is*`/`assert*`/`safe*` helpers
218
+ (Helper Hell / Semantic Density Collapse).
219
+
220
+ Fix: for each group pick ONE canonical definition (or inline a single-use
221
+ helper), re-point every import, delete the rest. Optimise for *knowledge
222
+ management*, not token volume — one source of truth per concept.
223
+
224
+ ## Automatic final review
225
+
226
+ The `stop` hook (`anti-slop-final-review.ps1`) fires after the agent finishes
227
+ an implementation that edited files: it returns a `followup_message` Cursor
228
+ auto-submits, so the model re-audits everything it changed this session and
229
+ removes slop it introduced — one bounded pass.
230
+
231
+ ## Hard constraints
232
+
233
+ - Preserve requested behaviour (the rule above) — this outranks every fix.
234
+ - Smallest diff: removing slop should *shrink* the change, not reshape working
235
+ code you never touched.
236
+ - Match the file's existing conventions whenever you rewrite.
237
+ - At most a couple of passes per file, then stop — don't thrash.
238
+ - This is a *quality* sweep, not a bug hunt. Correctness/security is the
239
+ self-review trigger's job; don't duplicate it here.
240
+
241
+ ## Report template
242
+
243
+ ```
244
+ === Anti-Slop Sweep ===
245
+ Scope: {diff | files}
246
+ Removed:
247
+ - {N} premature abstraction(s): {names}
248
+ - {N} unnecessary dependency(ies): {names}
249
+ - {N} redundant comment(s)
250
+ - {duplication inlined / tests hardened / edge cases added / complexity flattened}
251
+ Left (with reason): {intent-bearing items deliberately not touched}
252
+ Diff: {before} → {after} lines. Tests: {pass | n/a}
253
+ ```
254
+
255
+ ## Cursor setup
256
+
257
+ | | |
258
+ |--|--|
259
+ | Install path | `~/.cursor/skills/anti-slop/` |
260
+ | Invoke | `/anti-slop`, or "remove the AI slop" |
261
+ | Scanner | `python scripts/scan_slop.py --all` |
262
+ | Final review | automatic via `stop` hook |
263
+
264
+ The scanner is stdlib-only and needs Python 3.9+. Pairs with the **anti-slop
265
+ hook** (advisory, per edit), the **stop hook** (auto final review), and
266
+ **minimal-editing** (smallest-diff). This skill is the active "delete it now"
267
+ layer those only nudge toward.