Extension: press F5. The Run Extension configuration runs the
npm: build task, then opens an Extension Development Host with this
extension loaded. Open any Git repository in that window and run the commands.
CLI:npm run build:cli && node dist/cli.js /path/to/repo, or use
npx tsx src/cli/index.ts /path/to/repo without building.
Dashboard HTML:renderDashboardDocument(report, { nonce: "x", standalone: true })
returns a page you can open in a browser.
Active days, identity merging (all key types and their guards), labels, explicit identities
test/glob.test.ts
Glob → RegExp, rooted vs. unrooted patterns, base-name matching
test/scoring.test.ts
Score formula properties, confidence levels, ranking threshold, fingerprint stability
test/cliFormat.test.ts
Text and Markdown output, not-ranked section, --fail-on logic
test/attribution.test.ts
Legacy blame parsing and ownership helpers
test/integration.test.ts
End-to-end on real temporary Git repositories: fair attribution, fixes, whitespace reformat, exclusions, root commit, small contributors
Integration tests create repositories with git init in a temp directory and
use an isolated, empty git config, so they do not depend on your global Git
settings. Run one file with:
.github/workflows/ci.yml runs on pushes to
main and on pull requests, on Ubuntu, Windows and macOS × Node 22 and 24:
npm ci → typecheck → lint → test → build → vsce package.
The Ubuntu / Node 24 job uploads the .vsix as an artifact. Windows is included
on purpose: path separators, CRLF output and git.exe resolution have caused
real bugs before (see the changelog).
Bump version in package.json; add a CHANGELOG.md entry.
Make sure CI is green.
npm run build && npx vsce package --no-dependencies → cleanlens-<version>.vsix.
Publish with npx vsce publish (needs a PAT for publisher cleanlens) or
upload the .vsix in the Marketplace publisher portal.
Optionally npm publish for the CLI (same version).
Update EXTENSION_VERSION in website/lib/constants.ts.
The files list in package.json controls what ships (in both the .vsix and
the npm package): the two bundles, the icons, README.md, CHANGELOG.md,
docs/PRIVACY.md, docs/RULES.md, LICENSE.txt. The other docs in this folder
are not shipped; add them to files if you want them in the package.
Existing config files do not list the new rule, so it is off for them until
users add it (a missing rule is disabled). The validator accepts it once it is in
RULE_IDS.
Create src/analyzers/<lang>Analyzer.ts exporting
analyze<Lang>(filePath, text, config): RawFinding[] and its extensions list.
Use ruleEnabled, ruleLimit, ruleOption from model.ts, and reuse
secrets.ts / names.ts.
Register it in runAnalyzers.ts: add the
extensions to ALL_EXTENSIONS and a branch in analyzerFor(). The HEAD scan,
the commit walk and live diagnostics all use analyzerFor(), so nothing else
needs wiring.
Check fingerprint.ts strips that language's line-comment syntax (today //
and #).
Consider a preset in presets.ts and detection in detectPreset().
The docs exist in three languages with the same file names:
text
docs/*.md English — the source of truthdocs/ar/*.md Arabicdocs/tr/*.md Turkish
When you change an English doc, update the two translations in the same change.
Rules that keep the website's docs pages (see 16) working:
Keep the heading structure identical (same number of headings, same
levels, same order). The website gives translated headings the English
anchor ids by position, so links and URLs are the same in every language.
Links to section anchors use the English ids in every language
(e.g. 08-scoring.md#severity-weights).
Links to files outside docs/ go one level further up in translations
(../../src/… instead of ../src/…).
Code blocks, identifiers, rule ids, config keys and CLI output stay in English.
If a translation file is missing, the website shows the English one.
Declare it under contributes.configuration.properties in package.json.
Read it in readSettings() in src/extension.ts.
If it maps to a config key, add it to ExtensionSettings and applySettings()
in src/core/analyzeRepository.ts. Prefer undefined when the user has not
set it, so the config file is not overridden by a default.