hunter-party-ts/solid-hunter-ts/SKILL.md
Audit TypeScript class and interface design for SOLID violations — god classes, rigid extension points, broken substitutability, fat interfaces, and concrete dependency chains. Focuses on responsibility assignment and abstraction fitness. Use when: reviewing class hierarchies, preparing for extension with new variants, reducing coupling between services, or improving testability of class-heavy code.
npx skillsauth add skyosev/agent-skills solid-hunter-tsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Audit TypeScript code for SOLID principle violations at the class and interface level — places where responsibilities are misassigned, extension requires modification, subtypes break contracts, interfaces force unused dependencies, or classes are wired to concrete implementations. The goal: classes have clear responsibilities, extend without modification, substitute safely, expose cohesive interfaces, and depend on abstractions.
Single Responsibility (SRP). A class should have one reason to change — one actor, one domain concern. When a class serves multiple actors (e.g., persistence AND presentation), changes for one actor risk breaking the other. This is about responsibility assignment, not function count.
Open/Closed (OCP). A class should be open for extension but closed for modification. Adding a new variant or behavior should not require editing existing code. Extension points — strategy interfaces, plugin hooks, polymorphic dispatch — allow new behavior without touching proven code.
Liskov Substitution (LSP). A subtype must be usable wherever its base type is expected, without the caller knowing or caring. Overrides must honor the base contract: same preconditions or weaker, same postconditions or stronger. If a subclass throws on an inherited method or silently ignores it, the substitution is broken.
Interface Segregation (ISP). No client should be forced to depend on methods it does not use. Fat interfaces that bundle unrelated capabilities force implementors into stub methods and callers into unnecessary coupling. Split into role-specific interfaces that each describe one capability.
Dependency Inversion (DIP). High-level policy should not depend on low-level detail — both should depend on
abstractions. When a service class news its own dependencies or accepts concrete types in its constructor, it
becomes untestable and unchangeable without editing the class itself.
SOLID principles are guidelines for managing change, not rules to apply universally. Do not flag:
Classes that accumulate responsibilities from multiple domain concerns, becoming the "hub" that everything touches.
Signals:
Manager, Handler, Service, Utils, Helper with broad scopeAction: Identify distinct responsibilities. Extract each into a focused class. The original class becomes a coordinator that delegates, or is dissolved entirely.
Code that must be modified — not extended — when a new variant, strategy, or behavior is added.
Signals:
switch/if-else chains on a type discriminant that appear in 3+ locations across the codebaseif (type === 'email') sendEmail(); else if (type === 'sms') sendSms();)switch statements and no registration mechanismAction: Introduce a strategy/plugin pattern: define an interface for the variant behavior, implement per variant, dispatch polymorphically. The core class should not know about individual variants.
Subclasses or interface implementations that violate the contract of their base type.
Signals:
NotImplementedError, UnsupportedOperationError, or similarinstanceof checks in consumer code to handle a specific subclass differentlyAction: If the subclass genuinely cannot support the base contract, the inheritance is wrong. Extract the shared behavior into a separate interface or use composition instead of inheritance.
Interfaces that bundle unrelated capabilities, forcing implementors to depend on methods they don't use.
Signals:
Action: Split into role-specific interfaces (e.g., Readable, Writable instead of ReadWriteStore). Clients
depend only on the interface slice they use.
High-level classes that directly depend on low-level implementations instead of abstractions.
Signals:
new ConcreteService() inside a class method (not a factory or composition root)Action: Define an interface for the dependency. Accept the interface in the constructor. Wire the concrete implementation at the composition root (app startup, DI container, factory).
Note: Direct instantiation of value objects, data structures, and builders is fine — DIP applies to service dependencies that represent behavior, not data construction.
main/master)BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main)
SCOPE=$(git diff --name-only $(git merge-base HEAD $BASE)...HEAD)
Constrain all subsequent scans to the resolved surface.EXCLUDE='--glob !**/*.test.* --glob !**/*.spec.* --glob !**/node_modules/** --glob !**/dist/**'
# Classes (starting point for SRP, DIP analysis)
rg 'class\s+\w+' --type ts $EXCLUDE
# Interfaces (starting point for ISP analysis)
rg 'interface\s+\w+' --type ts $EXCLUDE
# extends / implements (inheritance and interface relationships)
rg 'extends\s+\w+|implements\s+\w+' --type ts $EXCLUDE
# Direct instantiation in non-factory code (DIP signal)
rg 'new\s+[A-Z]\w+\(' --type ts $EXCLUDE
# instanceof checks (LSP signal — caller sniffing subtypes)
rg 'instanceof\s+\w+' --type ts $EXCLUDE
# switch/if-else chains on discriminants (OCP signal)
rg 'switch\s*\(' --type ts $EXCLUDE
# NotImplemented / Unsupported throws (LSP signal)
rg -i 'not.?implemented|unsupported' --type ts $EXCLUDE
For each class found in Phase 2:
new its own service
dependencies?For each interface:
For each inheritance relationship:
instanceof checks that work around broken
substitution?Save as YYYY-MM-DD-solid-hunter-audit-{$LLM-name}.md in the project's docs folder (or project root if no docs folder exists).
# SOLID Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
- Architecture style: {class-based / mixed / functional (limited findings)}
## SRP Violations — God Classes
| # | Class | Location | Responsibilities | Dependencies | Action |
| - | ----- | -------- | ---------------- | ------------ | ------ |
| 1 | `OrderService` | file:line | order CRUD + email + invoicing | 7 injected | Split into 3 focused services |
## OCP Violations — Rigid Extension Points
| # | Location | Pattern | Occurrences | Action |
| - | -------- | ------- | ----------- | ------ |
| 1 | file:line | `switch(type)` with 5 variants in 4 files | 4 | Extract strategy interface |
## LSP Violations — Broken Substitution
| # | Class | Base | Location | Violation | Action |
| - | ----- | ---- | -------- | --------- | ------ |
| 1 | `ReadOnlyRepo` | `Repository` | file:line | `save()` throws NotImplemented | Use composition, not inheritance |
## ISP Violations — Fat Interfaces
| # | Interface | Location | Methods | Avg Used by Implementors | Action |
| - | --------- | -------- | ------- | ------------------------ | ------ |
| 1 | `DataStore` | file:line | 12 | 5 | Split into `Reader` + `Writer` + `Admin` |
## DIP Violations — Concrete Dependencies
| # | Class | Location | Concrete Dependency | Action |
| - | ----- | -------- | ------------------- | ------ |
| 1 | `UserService` | file:line | `new EmailClient()` | Inject via constructor interface |
## Recommendations (Priority Order)
1. **Must-fix**: {god classes with 5+ responsibilities, broken substitution causing runtime errors}
2. **Should-fix**: {rigid extension points touched by every new variant, fat interfaces with stub methods}
3. **Consider**: {concrete dependency chains limiting testability, speculative interface splits}
file/path.ext:line with the exact code.development
Transforms vague feature ideas into precise, codebase-grounded technical requirements. Use when requirements are ambiguous/incomplete, the user struggles to describe behavior, terminology is unclear, or multiple concepts are mixed. Output is a requirements spec—NOT an implementation plan.
tools
Audit TypeScript type definitions for design debt — duplicated shapes, missing derivations, over-engineered generics, under-constrained type parameters, reinvented utility types, and disorganized type architecture. Type structure and maintainability, not type enforcement. Use when: reviewing type definitions for maintainability, reducing type duplication, simplifying over-engineered type-level logic, or reorganizing type architecture after growth.
development
Audit TypeScript test code for quality gaps — missing coverage on critical paths, brittle tests coupled to implementation, over-mocking, assertion-free tests, missing edge cases, and duplicated test setup. Focuses on test effectiveness, not production code structure. Use when: reviewing TypeScript test suites for reliability, reducing false-positive test failures, improving coverage of critical business logic, or cleaning up test debt.
development
Audit TypeScript code for classic code smells — feature envy, data clumps, shotgun surgery, primitive obsession, temporal coupling, comments as deodorant, temporary fields, callback hell, enum abuse, and class abuse. Use when: reviewing TypeScript code for structural design problems, preparing for a refactor, auditing code after rapid feature development, or hunting for misplaced responsibilities.