@underwoodinc/clad-audit
v0.5.5
Published
Agnostic CLAD tier auditor with deep static analysis and scripted remediation plans.
Maintainers
Readme
@underwoodinc/clad-audit
^^^ pairs with CLAD audit extension for vscode/cursor ^^^
Standalone, repository-agnostic CLAD auditor. Node.js CLI — PowerShell, Linux, macOS.
Repository: https://github.com/Underwood-Inc/clad-audit
npm: https://www.npmjs.com/package/@underwoodinc/clad-audit
CLAD docs (bundled): docs/philosophy/clad.md
Install
npm install -g @underwoodinc/clad-audit
clad-audit audit --root /path/to/your/reponpx @underwoodinc/clad-audit audit --root .
npm install github:Underwood-Inc/clad-auditQuick start
clad-audit audit --root . --depth standard
clad-audit audit --root . --depth exhaustive --verbose --report clad-report.md
clad-audit wizard
clad-audit rules # list rule ids + default advice
clad-audit presets # list audit tier presetsNo config file required — generic CLAD defaults apply. Add .clad-audit.yaml at your repo root when you need import aliases, extra view paths, or project-specific allowlists.
New to CLAD? Read the bundled spec: docs/philosophy/clad.md (full) · docs/README.md (index + rule mapping).
Copy-paste configs
Save any block below as .clad-audit.yaml in the repository you audit (--root). Values deep-merge over generic defaults; omit keys you do not need.
No file needed. Run from your repo root:
clad-audit audit --root .
clad-audit audit --root . --preset structure
clad-audit audit --root . --preset full --verboseExpects tiers under src/: atoms/, molecules/, organisms/, recipes/, views/, apps/, sockets/, plugs/.
Use when your bundler resolves $molecules/ etc. but folder layout matches generic CLAD.
# .clad-audit.yaml
srcRoot: src
importAliases:
$atoms/: atoms
$molecules/: molecules
$organisms/: organisms
$recipes/: recipes
$views/: views
$apps/: apps
$sockets/: sockets
$plugs/: plugs
analysis:
defaultDepth: standardclad-audit audit --root .Typical SvelteKit / Vite CLAD repo with runes and .svelte views.
# .clad-audit.yaml
srcRoot: src
importAliases:
$atoms/: atoms
$molecules/: molecules
$organisms/: organisms
$recipes/: recipes
$views/: views
$apps/: apps
$sockets/: sockets
$plugs/: plugs
analysis:
defaultDepth: standard
useTsMorph: true
svelteProps:
enabled: true
maxProps: 4
severity: warning
ignoreGlobs:
- '**/node_modules/**'
- '**/dist/**'
- '**/.svelte-kit/**'
- '**/coverage/**'clad-audit audit --root . --preset full
clad-audit audit --root . --preset apps --verboseWhen UI components live outside src/views/ (e.g. src/ui/components/).
# .clad-audit.yaml
srcRoot: src
importAliases:
'@atoms/': atoms
'@molecules/': molecules
'@organisms/': organisms
'@recipes/': recipes
'@views/': views
'@apps/': apps
views:
extensions: ['.tsx', '.jsx', '.vue']
extraViewPaths:
- ui/components
analysis:
defaultDepth: standard
ignoreGlobs:
- '**/node_modules/**'
- '**/dist/**'
- '**/build/**'clad-audit audit --root . --preset structureFor refactors: enable file-size warnings, Svelte props, and run exhaustive graph rules in CI.
# .clad-audit.yaml
srcRoot: src
importAliases:
$molecules/: molecules
$organisms/: organisms
$recipes/: recipes
$views/: views
$apps/: apps
analysis:
defaultDepth: exhaustive
useTsMorph: true
couplingHotspotThreshold: 10
svelteProps:
enabled: true
maxProps: 4
fileSize:
apps:
maxLines: 350
severity: warning
molecules:
maxLines: 800
severity: warning
recipes:
maxLines: 600
severity: warning
canonAllowlist:
enabled: trueclad-audit audit --root . --preset exhaustive --report clad-report.md --verboseWhen your repo uses different directory names but the same CLAD semantics.
# .clad-audit.yaml
srcRoot: lib
tiers:
atoms: core/atoms
molecules: core/molecules
organisms: core/organisms
recipes: core/recipes
views: ui/views
apps: ui/apps
sockets: ports
plugs: adapters
scanGlobs:
- '**/*.{ts,tsx,svelte}'
ignoreGlobs:
- '**/node_modules/**'
- '**/dist/**'clad-audit audit --root .Add basenames that are genuinely composition roots but not in generic defaults.
# .clad-audit.yaml
srcRoot: src
apps:
allowedFilenamePatterns:
- '^mount[A-Za-z0-9_-]*\\.ts$'
- '^wire[A-Za-z0-9_-]*\\.ts$'
- 'AppSession\\.svelte\\.ts$'
- '^bootstrap[A-Za-z0-9_-]*\\.ts$'
# project-specific composition roots:
- '^createMyAppShell\\.ts$'
- '^runInitialBoot\\.ts$'
views:
extraViewPaths:
- ui/componentsclad-audit audit --root . --preset apps --verbose{
"scripts": {
"clad:audit": "clad-audit audit --root . --preset structure",
"clad:full": "clad-audit audit --root . --preset full --verbose",
"clad:report": "clad-audit audit --root . --preset exhaustive --report clad-report.md",
"clad:wizard": "clad-audit wizard"
},
"devDependencies": {
"@underwoodinc/clad-audit": "^0.5.1"
}
}Local install (no global):
npm install -D @underwoodinc/clad-audit
npx clad-audit audit --root .
pnpm clad:audit# .github/workflows/clad-audit.yml
name: CLAD audit
on:
pull_request:
push:
branches: [master, main]
jobs:
clad-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
- run: npm ci
- name: Structure audit (errors only)
run: npx clad-audit audit --root . --preset structure --depth standard
- name: Full report artifact (optional)
if: always()
run: npx clad-audit audit --root . --preset full --report clad-report.md
- uses: actions/upload-artifact@v4
if: always()
with:
name: clad-report
path: clad-report.mdPin @underwoodinc/clad-audit in devDependencies for reproducible CI.
Full annotated example: examples/clad-audit.example.yaml.
What to expect
The tool scans your tree (not this repo). It never imports your app code. Each finding includes reasoning, advice, and optional remediation steps (deterministic, no LLM).
| Output | Flag / command |
|--------|----------------|
| Colorized terminal | default in TTY; --no-color / NO_COLOR=1 |
| JSON | --json |
| Markdown report | --report path.md |
| Verbose remediation | --verbose |
| Category | Rule ids | Default severity | Min depth |
|----------|----------|------------------|-----------|
| Apps composition root | app-tier-allowlist, view-in-app-tier, organism-in-app-tier, recipe-in-app-tier | error | quick |
| Import boundaries | import-boundary, import-dynamic-boundary, import-reexport-boundary | error | quick / standard / deep |
| Tier impurity | tier-impurity | error | quick |
| Placement & shape | misplaced-tier-shape, unknown-tier-file | error / warning | standard |
| Canon policy | canon-parallel-allowlist | warning | quick |
| Svelte (optional) | svelte-props-excess | warning | quick (off by default) |
| Quality signals | file-size-tier, barrel-reexport-smell, import-cycle, tier-coupling-hotspot | warning / info | quick / deep / exhaustive |
Expand each category below for detection details and remediation patterns.
Depth gates rules by minDepth. Presets pick rule subsets; --depth gates how deep analysis runs.
| Depth | Analysis | Rules unlocked |
|-------|----------|----------------|
| quick | Static imports, path tiers | Core 9 rules (see below) |
| standard | + comment-aware impurity, import graph (regex), misplaced shape | + unknown-tier-file, misplaced-tier-shape, import-dynamic-boundary |
| deep | + ts-morph TypeScript graph | + import-reexport-boundary, barrel-reexport-smell |
| exhaustive | + cycle + fan-in analysis | + import-cycle, tier-coupling-hotspot |
clad-audit audit --root . --depth exhaustive
clad-audit audit --root . --preset structure # apps + import rules only| Preset | Runs |
|--------|------|
| apps | Apps-tier hygiene (4 rules) |
| imports | import-boundary, tier-impurity |
| structure | Apps + imports (6 rules) |
| quality | Canon, file size, Svelte props |
| standard | Structure (errors only) |
| full | All rules at standard depth default |
| exhaustive | All rules at exhaustive depth |
Files in apps/ should be thin composition roots only.
| Rule id | Detects | Default severity |
|---------|---------|------------------|
| app-tier-allowlist | .ts files whose basename is not mount*, wire*, *AppSession, Bridge, bootstrap, etc. | error |
| view-in-app-tier | View extensions (.svelte, .tsx, .jsx, .vue) in apps/ | error |
| organism-in-app-tier | *Rune.svelte.ts, *Session.svelte.ts (except allowed AppSession) | error |
| recipe-in-app-tier | register*, *Paint.ts, orchestrator-shaped files | error |
Remediation pattern: move to views/, organisms/, or recipes/; wire from mount* / *AppSession.
Enforces inward CLAD dependency flow (outer tiers may depend on inner tiers, not vice versa). Matrix is configurable in .clad-audit.yaml → importBoundary.
| Rule id | Detects | Min depth |
|---------|---------|-----------|
| import-boundary | Static import … from crossing forbidden tier pairs | quick |
| import-dynamic-boundary | import() and require() crossing boundaries | standard |
| import-reexport-boundary | export … from exposing forbidden tiers | deep |
Supports import aliases (e.g. $molecules/) when configured in the consumer repo.
Inner tiers stay testable and framework-agnostic. Generic defaults flag:
| Tier | Banned patterns (configurable) |
|------|--------------------------------|
| molecules | Svelte/React/Vue imports; $state, $derived, from 'svelte' |
| sockets | Direct /plugs/ path imports |
At standard+ depth, matches ignore comments and string literals.
Remediation: extract runes to organisms/; adapters to plugs/ behind sockets/ contracts.
clad-audit is framework-aware but not framework-mandatory. Generic defaults encode common CLAD shapes; consumers override in .clad-audit.yaml.
View markup (all UI frameworks)
| Rule | Framework signal | Detects |
|------|------------------|---------|
| view-in-app-tier | .svelte, .tsx, .jsx, .vue | Markup files in apps/ |
| misplaced-tier-shape | same extensions + extraViewPaths | View-shaped files outside views/ |
Svelte runes & sessions
| Rule | Pattern | Detects |
|------|---------|---------|
| organism-in-app-tier | *Rune.svelte.ts, *Session.svelte.ts | Reactive modules in apps/ (except *AppSession) |
| tier-impurity | $state, $derived, from 'svelte' | Runes / Svelte imports in molecules/ |
| svelte-props-excess | $props() destructuring count | Components with too many props (off by default) |
React / Vue impurity (molecules tier)
| Banned import substrings (default) | Purpose |
|-------------------------------------|---------|
| react, react-dom | Keep molecules free of UI framework hooks |
| vue, @vue/ | Same for Vue SFC/composables |
| /svelte, svelte/ | Same for Svelte |
Import graph (deep+)
| Capability | Framework note |
|------------|----------------|
| parseSvelteImports | Reads <script> blocks in .svelte for boundary analysis |
| ts-morph graph | Parses .ts, .tsx for static/dynamic/re-export edges |
Remediation pattern: markup → views/; runes/sessions → organisms/; orchestrators → recipes/; pure logic → molecules/.
Shipped defaults match generic CLAD layout only — no product filenames, no import aliases.
| Config area | What you override in .clad-audit.yaml |
|-------------|----------------------------------------|
| tiers | Folder names if your repo uses different paths |
| importAliases | $molecules/, @app/ etc. for boundary resolution |
| apps.allowedFilenamePatterns | Extra composition-root basenames |
| views.extraViewPaths | Non-standard view folders (e.g. ui/components/) |
| importBoundary | Tier → allowed downstream tiers matrix |
| tierImpurity | Per-tier banned imports and content regexes |
| svelteProps | Enable svelte-props-excess + maxProps |
| ignoreGlobs / scanGlobs | What files enter the audit |
Deep-merge at audit time: unset keys keep generic defaults.
| Command | Purpose |
|---------|---------|
| clad-audit audit | Run rules against --root |
| clad-audit wizard | Interactive depth/preset/config walkthrough |
| clad-audit rules | List built-in rule ids + default advice |
| clad-audit presets | List named rule bundles |
| Flag | Purpose |
|------|---------|
| --depth quick\|standard\|deep\|exhaustive | Gate rules by analysis depth |
| --preset <name> | Subset of rules (see presets table) |
| --json | Machine-readable findings |
| --report <path> | Markdown report file |
| --verbose | Print remediation steps per finding |
| --no-color | Plain text (also NO_COLOR=1) |
Works in PowerShell, bash, and zsh — no shell scripts required.
The editor extension lives in its own repository: Underwood-Inc/clad-audit-vscode (underwoodinc.clad-audit on the Marketplace). It runs the same runAudit engine in-process and publishes findings as Problems / inline squiggles.
git clone https://github.com/Underwood-Inc/clad-audit-vscode.git
cd clad-audit-vscode
npm install
npm run package:vsix
code --install-extension clad-audit-vscode.vsixThe VSIX bundles the full auditor (no separate CLI install). Cursor: cursor --install-extension clad-audit-vscode.vsix.
Install from the Marketplace or build from the extension repo above. Command palette: CLAD: Audit Workspace.
Settings prefix: cladAudit.* (enable, runOnSave, depth, debounce).
| Rule id | Detects | Severity |
|---------|---------|----------|
| misplaced-tier-shape | View/organism/recipe filename shape in wrong tier folder (anywhere under src/) | error |
| unknown-tier-file | File under src/ not matching any configured tier path | warning |
Complements apps-tier rules: catches misfiled modules outside apps/ too.
| Rule id | Detects |
|---------|---------|
| canon-parallel-allowlist | *_ALLOWLIST constants in molecules outside *Catalog.ts modules |
Encourages one table-driven Canon Catalog per policy domain.
These inform refactors; they do not define CLAD tier law by themselves.
| Rule id | Detects | Min depth | Severity |
|---------|---------|-----------|----------|
| file-size-tier | Lines over tier limit (apps/molecules/recipes) | quick | warning |
| import-cycle | Circular import paths | exhaustive | warning |
| barrel-reexport-smell | index.ts re-exporting multiple tiers | deep | info |
| tier-coupling-hotspot | Modules imported by many others | exhaustive | info |
This package is a library tool. Requirements describe what clad-audit must do for its consumers — not CLAD law for your application.
We group by kind so trace IDs are not one-per-test fiction:
| ID | Kind | What it covers | Test coverage | |----|------|----------------|---------------| | FR-001 | Functional | Apps-tier placement (4 rules) | rules + engine fixture | | FR-002 | Functional | Import boundaries + path resolution | pathTier, importAnalysis, rules | | FR-003 | Functional | Tier impurity patterns | rules | | FR-004 | Functional | Misplaced shape / unknown tier | rules | | FR-005 | Functional | Canon allowlist drift | rules | | FR-006 | Functional | Svelte props when consumer enables | rules | | FR-007 | Functional | File size, cycles, barrels, hotspots | rules (partial — graph rules at exhaustive depth) | | NFR-001 | Non-functional | Agnostic defaults + YAML merge | defaultConfig, loadConfig | | NFR-002 | Non-functional | CLI, presets, colorized/JSON/markdown output | terminalStyle, printAuditReport, auditPresets | | META-001 | Meta | requirements-tracer on this Vitest suite | traceability.test |
Not requirements: individual rule ids, preset names, or Mappy-specific filenames — those are implementation/config details documented above.
Full definitions: requirements-registry.yaml. Tests prefix descriptions with [FR-xxx], [NFR-xxx], or [META-001].
Develop
Node.js 22+.
git clone https://github.com/Underwood-Inc/clad-audit.git
cd clad-audit
npm install
npm run verify # vitest + requirements-tracer audit
npm run buildnpm run trace:audit
npm run trace:report # → traceability-report/index.htmlCI: unit tests + trace audit + requirements-tracer-action on PRs.
Publish
npm run build
npm publish --access publicLicense
MIT — LICENSE.
