@corrode/cli
v0.3.2
Published
AI code maintainability scorer — catch AI-generated code rot before it ships
Downloads
1,099
Maintainers
Readme
@corrode/cli
Corrode is a static analysis CLI that scores the engineering health of TypeScript and JavaScript projects. It runs a suite of analyzer modules against your codebase and produces an Engineering Health Score (0–100) broken down by dimension: Complexity, Coupling, Coverage, Code Hygiene, and Security. Results are displayed in the terminal and optionally uploaded to corrode.dev for sharing.
License: The analysis engine (this package) is MIT-licensed and open to contributions. The cloud platform at corrode.dev is a separate product.
Install & Usage
# Run against current directory
npx @corrode/cli
# Run against a specific path
npx @corrode/cli ./my-project
# Or install globally
npm install -g @corrode/cli
corrode ./my-projectEnvironment variables:
| Variable | Description |
|---|---|
| CORRODE_API_KEY | Optional API key to associate results with your account |
| CORRODE_API_URL | Override the report upload endpoint (default: corrode.dev) |
How Scoring Works
Each analyzer module returns a quality score from 0–100 (higher = better). The overall Engineering Health Score is a weighted average:
| Dimension | Weight | |---|---| | Complexity | 25% | | Coupling | 20% | | Coverage | 20% | | Code Hygiene | 20% | | Security | 15% |
AI Pattern Analysis is informational only — it is shown in the report but does not affect the score.
Adding a New Module
The analysis engine is designed to be easy to extend. Adding a new check takes 3 steps — no other files need to change.
Step 1 — Create your module
src/modules/your-module/index.tsimport type { AnalyzerModule, AnalyzerContext, ModuleResult } from '../types.js'
export const YourModule: AnalyzerModule = {
name: 'Your Check', // shown in terminal output
dimension: 'hygiene', // which scoring dimension this contributes to
weight: 0.05, // portion of the overall score (all weights must sum to 1.0)
async analyze(ctx: AnalyzerContext): Promise<ModuleResult> {
const issues = []
for (const file of ctx.files) {
// ... your analysis logic ...
issues.push({
type: 'your-issue-type',
dimension: 'hygiene',
severity: 'warning', // 'error' | 'warning' | 'info'
file,
line: 42,
message: 'Description of the problem',
rule: 'your-rule-id',
score_impact: -5,
})
}
// Return a quality score (0–100, higher = better) and the issues list
const score = Math.max(0, 100 - issues.length * 5)
return { score, issues }
},
}Available context:
interface AnalyzerContext {
files: string[] // TS/JS source files (absolute paths, tests excluded)
allFiles: string[] // all files including .env, config files (for security scanning)
targetPath: string // root directory being analyzed
}Shared utilities in src/utils/:
ast.ts—traverse(),parseFile(),parseFileWithComments(),getFunctionName()files.ts—getSourceFiles(),getAllFiles()
Step 2 — Register your module
Open src/modules/registry.ts and add your module:
import { YourModule } from './your-module/index.js'
export const SCORED_MODULES: AnalyzerModule[] = [
ComplexityModule, // 0.25
CouplingModule, // 0.20
CoverageModule, // 0.20
HygieneModule, // 0.20
SecurityModule, // 0.15
// ↓ add yours here — remember to adjust weights so they sum to 1.0
// YourModule, // 0.05
]Weight constraint: All weights in
SCORED_MODULESmust sum to exactly1.0. If you add a new module at 0.05, subtract 0.05 from another module's weight.
Step 3 — Done
Run npm run build to verify, then corrode ./your-project to see it in action.
Module Interface Reference
AnalyzerModule (scored)
interface AnalyzerModule {
name: string // display name
dimension: 'complexity' | 'coupling' | 'coverage' | 'hygiene' | 'security'
weight: number // 0–1, all must sum to 1.0
analyze(ctx: AnalyzerContext): Promise<ModuleResult>
}
interface ModuleResult {
score: number // 0–100, higher = better health
issues: Issue[]
}InformationalModule (not scored)
interface InformationalModule {
name: string
dimension: 'ai-patterns'
analyze(ctx: AnalyzerContext): Promise<InformationalModuleResult>
}
interface InformationalModuleResult {
issues: InformationalIssue[]
}Issue
interface Issue {
type: string // specific rule type (e.g. 'high-cyclomatic-complexity')
dimension: Dimension // which module produced this
severity: 'error' | 'warning' | 'info'
file: string // absolute path
line?: number
message: string
rule: string // stable rule ID for filtering/API
score_impact: number // estimated impact (negative = worse)
}Programmatic API
import { runAnalysis } from '@corrode/cli'
const result = await runAnalysis('/path/to/project')
console.log(result.overall) // overall score 0–100
console.log(result.scores.complexity) // per-dimension scores
console.log(result.issues) // all scored issues
console.log(result.informational['ai-patterns']) // informational findingsProject Structure
src/
├── index.ts # CLI entry point + public API exports
├── runner.ts # orchestrates all modules, returns AnalysisResult
├── score.ts # scoring algorithm documentation + utility
├── reporter.ts # terminal output + API POST
├── modules/
│ ├── types.ts # all shared interfaces
│ ├── registry.ts # register modules here
│ ├── complexity/
│ ├── coupling/
│ ├── coverage/
│ ├── hygiene/
│ ├── security/
│ └── ai-patterns/
└── utils/
├── ast.ts # shared AST parsing utilities
└── files.ts # file discovery utilities