03

Architecture

Layers, module map, the 7-step pipeline, data flow between modules

5 min read6 sections

Layers

CleanLens is a TypeScript project with one engine and two thin front ends.

text
┌──────────────────────────────┐   ┌──────────────────────────────┐
│  VS Code extension           │   │  CLI                         │
│  src/extension.ts            │   │  src/cli/index.ts            │
│  src/views/*                 │   │  src/cli/format.ts           │
└──────────────┬───────────────┘   └──────────────┬───────────────┘
               │   analyzeRepository(root, opts)  │
               └───────────────┬──────────────────┘
                               ▼
┌────────────────────────────────────────────────────────────────┐
│  Core pipeline — src/core/analyzeRepository.ts (no VS Code API) │
├──────────────┬──────────────┬───────────────┬──────────────────┤
│ git/         │ configuration│ analyzers/    │ attribution/     │
│ git commands │ config, rules│ rule checks   │ commit replay    │
│ + identities │ presets,     │ per file      │ + fingerprints   │
│              │ excludes     │               │                  │
├──────────────┴──────────────┴───────────────┼──────────────────┤
│ cache/ — per-commit results on disk         │ scoring/ — score │
└─────────────────────────────────────────────┴──────────────────┘
  • Only src/extension.ts and src/views/* import vscode. Everything else is plain Node.js, which is why the CLI and the tests can run it.
  • There are no runtime dependencies except typescript (used as a parser by the JS analyzer, bundled by esbuild). All Git access is execFile("git", …).

Module map

FolderFileResponsibility
src/extension.tsActivation, command registration, progress UI, reading VS Code settings, live-diagnostics wiring
src/core/analyzeRepository.tsThe shared pipeline; merges settings over config; builds the cache; returns ProjectReport
src/cli/index.tsArgument parsing, exit codes, stdout/stderr handling
format.tsReport → text / Markdown; the --fail-on check
src/git/gitService.ts"Is this a repo?", "Has commits?", git log for contributor stats
contributors.tsGroups commits into developers; merges duplicate identities
commitHistory.tsLists non-merge commits (newest N / since date), oldest first
diffService.tsOne git diff per commit → changed files, line ranges, blob ids; git cat-file --batch to read blobs
blameService.tsgit blame --line-porcelain parser. Legacy (from the old blame engine); still tested, not used by the pipeline
src/configuration/types.tsConfig types, the 14 rule ids, titles and categories, file names
configValidator.tsSchema validation of .clean-code-tracker.json
configLoader.tsLoad config (or default preset), merge the local override, write a new config
presets.tsThe four presets; preset auto-detection
excludes.tsBuilt-in excludes; .gitignore / .gitattributes reading; generated-header detection
src/analyzers/fileScanner.tsDirectory walk for the HEAD scan
glob.tsMinimal glob → RegExp and ExcludeMatcher
jsAnalyzer.tsAST-based rules for JS/TS (TypeScript compiler API)
pyAnalyzer.tsLine/indentation-based rules for Python
duplicateCode.tsSliding-window duplicate block detection
secrets.ts, names.tsShared heuristics for secrets and unclear names
model.tsRawFinding → Violation; rule enabled/limit/option helpers
runAnalyzers.tsHEAD scan orchestration; picks the analyzer by extension
analyzeRevision.tsAnalyze one file revision (blob) and fingerprint each violation
src/attribution/commitAttribution.tsWalks commits, compares before/after, classifies each violation
attributeReport.tsMerges the HEAD violations with the commit results; scores developers
fingerprint.tsFNV-1a hash; stable violation fingerprint
ownership.tsEmail → developer id map (+ legacy blame helpers)
src/scoring/scoringConfig.tsEvery tunable constant: weights, scales, prior, thresholds, ENGINE_VERSION
cleanCodeScore.tsThe score formula
src/cache/analysisCache.tsPer-commit JSON cache + config hash
src/types/index.tsSeverity, Violation, Developer, ProjectReport, statuses
src/views/treeProviders.tsSidebar trees + ReportStore
dashboardPanel.tsWebview panel lifecycle and messages
dashboardHtml.tsPure HTML/CSS/JS generation for the dashboard
diagnostics.tsEditor diagnostics: full-report feed and live feed

The pipeline, step by step

analyzeRepository(root, opts) in src/core/analyzeRepository.ts:

text
 1. Git checks        git rev-parse --is-inside-work-tree, git rev-parse HEAD
 2. Config            loadConfig()  →  applySettings(VS Code / CLI overrides)
                      readRepoAttributes() (.gitignore, .gitattributes)
                      mergedExcludeGlobs(), resolveScoring(), analysisConfigHash()
 3. Developers        git log --no-merges → aggregateContributors() → toDevelopers()
 4. HEAD scan         analyzeProject(): walk files → analyzer per file → duplicates
                      → sorted Violation[]
 5. Commit walk       attributeCommits(): newest N commits, oldest first,
                      12 in parallel, each: diff → blobs → analyze before/after
                      → introduced / fixed / existing
 6. Merge + score     attributeReport(): fingerprint every HEAD violation, look up
                      its origin, set status/confidence/developer; compute the
                      project prior; score every developer
 7. Display           extension: tree views, dashboard, diagnostics
                      CLI: text / Markdown / JSON + exit code

The stages:

  1. Git checks: is the folder a repository, and does it have a commit?
  2. Config: load the config file, merge VS Code settings or CLI options on top, read .gitignore and .gitattributes, and build the exclude list, the scoring constants and the config hash.
  3. Developers: read the Git history and group commits into developers.
  4. HEAD scan: analyze the current files and produce the sorted violation list.
  5. Commit walk: the newest N commits, oldest first, 12 in parallel; for each: diff → blobs → analyze before/after → introduced / fixed / existing.
  6. Merge and score: fingerprint every HEAD violation, find its origin, set its status, confidence and developer; then compute the project average and score every developer.
  7. Display: in the extension, the trees, dashboard and diagnostics; in the CLI, text / Markdown / JSON plus the exit code.

Steps 1–6 are identical for both front ends. Only the options differ:

OptionExtensionCLI
configloaded from the repoloaded from the repo, or --preset
settingsfrom VS Code settings (cleanlens.*)from --max-commits, --full-history, --since
cacheDircontext.globalStorageUri<os tmpdir>/cleanlens-cache
cachecleanlens.cache.enabledoff with --no-cache
signalthe progress notification's Cancel buttonnone

Expected failures

AnalysisError is thrown for problems the user can fix: not a Git repository, no commits, invalid config. The extension shows it as an error message; the CLI prints error: … and exits with code 2. Any other exception is a bug.

Data flow and types

text
RawFinding  ──toViolation()──▶  Violation (severity, category, weight, id)
                                   │
       analyzeRevision()           │  fingerprintFor()
                                   ▼
RevisionViolation (+fingerprint) ─▶ CommitContribution (per commit, cached)
                                   │
                    attributeCommits() aggregates
                                   ▼
            DeveloperAttribution (per developer) + origins (fingerprint → commit)
                                   │
                        attributeReport()
                                   ▼
                  ProjectReport { developers[], violations[] }

The type definitions live in src/types/index.ts and src/configuration/types.ts. The report format is documented field by field in 14 — Report schema.

Two analyses, two purposes

This is the most important design point to understand:

HEAD scan (step 4)Commit walk (step 5)
Inputfiles on disk right nowblobs from Git history
Outputthe list of violations shown to youwho introduced / fixed what, and analyzed lines
Duplicate detectionacross all fileswithin each file only
Scopeevery analyzable fileonly files changed by the analyzed commits

Step 6 joins them with fingerprints. A HEAD violation whose fingerprint was introduced in the walk gets that commit's author. A HEAD violation that never appeared in the walk predates the analyzed window → existing.

Build outputs

esbuild bundles two independent files:

  • dist/extension.js — entry src/extension.ts, vscode kept external.
  • dist/cli.js — entry src/cli/index.ts, with a #!/usr/bin/env node banner; exposed as the cleanlens bin.

See 15 — Development guide for the build commands.