mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-28 07:16:53 +05:00
feat(guardian): add guardian package - code quality analyzer
Add @puaros/guardian package v0.1.0 - code quality guardian for vibe coders and enterprise teams. Features: - Hardcode detection (magic numbers, magic strings) - Circular dependency detection - Naming convention enforcement (Clean Architecture) - Architecture violation detection - CLI tool with comprehensive reporting - 159 tests with 80%+ coverage - Smart suggestions for fixes - Built for AI-assisted development Built with Clean Architecture and DDD principles. Works with Claude, GPT, Copilot, Cursor, and any AI coding assistant.
This commit is contained in:
159
packages/guardian/src/cli/index.ts
Normal file
159
packages/guardian/src/cli/index.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env node
|
||||
import { Command } from "commander"
|
||||
import { analyzeProject } from "../api"
|
||||
import { version } from "../../package.json"
|
||||
import {
|
||||
CLI_COMMANDS,
|
||||
CLI_DESCRIPTIONS,
|
||||
CLI_OPTIONS,
|
||||
CLI_ARGUMENTS,
|
||||
DEFAULT_EXCLUDES,
|
||||
CLI_MESSAGES,
|
||||
CLI_LABELS,
|
||||
} from "./constants"
|
||||
|
||||
const program = new Command()
|
||||
|
||||
program.name(CLI_COMMANDS.NAME).description(CLI_DESCRIPTIONS.MAIN).version(version)
|
||||
|
||||
program
|
||||
.command(CLI_COMMANDS.CHECK)
|
||||
.description(CLI_DESCRIPTIONS.CHECK)
|
||||
.argument(CLI_ARGUMENTS.PATH, CLI_DESCRIPTIONS.PATH_ARG)
|
||||
.option(CLI_OPTIONS.EXCLUDE, CLI_DESCRIPTIONS.EXCLUDE_OPTION, [...DEFAULT_EXCLUDES])
|
||||
.option(CLI_OPTIONS.VERBOSE, CLI_DESCRIPTIONS.VERBOSE_OPTION, false)
|
||||
.option(CLI_OPTIONS.NO_HARDCODE, CLI_DESCRIPTIONS.NO_HARDCODE_OPTION)
|
||||
.option(CLI_OPTIONS.NO_ARCHITECTURE, CLI_DESCRIPTIONS.NO_ARCHITECTURE_OPTION)
|
||||
.action(async (path: string, options) => {
|
||||
try {
|
||||
console.log(CLI_MESSAGES.ANALYZING)
|
||||
|
||||
const result = await analyzeProject({
|
||||
rootDir: path,
|
||||
exclude: options.exclude,
|
||||
})
|
||||
|
||||
const {
|
||||
hardcodeViolations,
|
||||
violations,
|
||||
circularDependencyViolations,
|
||||
namingViolations,
|
||||
metrics,
|
||||
} = result
|
||||
|
||||
// Display metrics
|
||||
console.log(CLI_MESSAGES.METRICS_HEADER)
|
||||
console.log(` ${CLI_LABELS.FILES_ANALYZED} ${String(metrics.totalFiles)}`)
|
||||
console.log(` ${CLI_LABELS.TOTAL_FUNCTIONS} ${String(metrics.totalFunctions)}`)
|
||||
console.log(` ${CLI_LABELS.TOTAL_IMPORTS} ${String(metrics.totalImports)}`)
|
||||
|
||||
if (Object.keys(metrics.layerDistribution).length > 0) {
|
||||
console.log(CLI_MESSAGES.LAYER_DISTRIBUTION_HEADER)
|
||||
for (const [layer, count] of Object.entries(metrics.layerDistribution)) {
|
||||
console.log(` ${layer}: ${String(count)} ${CLI_LABELS.FILES}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Architecture violations
|
||||
if (options.architecture && violations.length > 0) {
|
||||
console.log(
|
||||
`${CLI_MESSAGES.VIOLATIONS_HEADER} ${String(violations.length)} ${CLI_LABELS.ARCHITECTURE_VIOLATIONS}\n`,
|
||||
)
|
||||
|
||||
violations.forEach((v, index) => {
|
||||
console.log(`${String(index + 1)}. ${v.file}`)
|
||||
console.log(` Rule: ${v.rule}`)
|
||||
console.log(` ${v.message}`)
|
||||
console.log("")
|
||||
})
|
||||
}
|
||||
|
||||
// Circular dependency violations
|
||||
if (options.architecture && circularDependencyViolations.length > 0) {
|
||||
console.log(
|
||||
`${CLI_MESSAGES.CIRCULAR_DEPS_HEADER} ${String(circularDependencyViolations.length)} ${CLI_LABELS.CIRCULAR_DEPENDENCIES}\n`,
|
||||
)
|
||||
|
||||
circularDependencyViolations.forEach((cd, index) => {
|
||||
console.log(`${String(index + 1)}. ${cd.message}`)
|
||||
console.log(` Severity: ${cd.severity}`)
|
||||
console.log(` Cycle path:`)
|
||||
cd.cycle.forEach((file, i) => {
|
||||
console.log(` ${String(i + 1)}. ${file}`)
|
||||
})
|
||||
console.log(
|
||||
` ${String(cd.cycle.length + 1)}. ${cd.cycle[0]} (back to start)`,
|
||||
)
|
||||
console.log("")
|
||||
})
|
||||
}
|
||||
|
||||
// Naming convention violations
|
||||
if (options.architecture && namingViolations.length > 0) {
|
||||
console.log(
|
||||
`${CLI_MESSAGES.NAMING_VIOLATIONS_HEADER} ${String(namingViolations.length)} ${CLI_LABELS.NAMING_VIOLATIONS}\n`,
|
||||
)
|
||||
|
||||
namingViolations.forEach((nc, index) => {
|
||||
console.log(`${String(index + 1)}. ${nc.file}`)
|
||||
console.log(` File: ${nc.fileName}`)
|
||||
console.log(` Layer: ${nc.layer}`)
|
||||
console.log(` Type: ${nc.type}`)
|
||||
console.log(` Message: ${nc.message}`)
|
||||
if (nc.suggestion) {
|
||||
console.log(` 💡 Suggestion: ${nc.suggestion}`)
|
||||
}
|
||||
console.log("")
|
||||
})
|
||||
}
|
||||
|
||||
// Hardcode violations
|
||||
if (options.hardcode && hardcodeViolations.length > 0) {
|
||||
console.log(
|
||||
`${CLI_MESSAGES.HARDCODE_VIOLATIONS_HEADER} ${String(hardcodeViolations.length)} ${CLI_LABELS.HARDCODE_VIOLATIONS}\n`,
|
||||
)
|
||||
|
||||
hardcodeViolations.forEach((hc, index) => {
|
||||
console.log(
|
||||
`${String(index + 1)}. ${hc.file}:${String(hc.line)}:${String(hc.column)}`,
|
||||
)
|
||||
console.log(` Type: ${hc.type}`)
|
||||
console.log(` Value: ${JSON.stringify(hc.value)}`)
|
||||
console.log(` Context: ${hc.context.trim()}`)
|
||||
console.log(` 💡 Suggested: ${hc.suggestion.constantName}`)
|
||||
console.log(` 📁 Location: ${hc.suggestion.location}`)
|
||||
console.log("")
|
||||
})
|
||||
}
|
||||
|
||||
// Summary
|
||||
const totalIssues =
|
||||
violations.length +
|
||||
hardcodeViolations.length +
|
||||
circularDependencyViolations.length +
|
||||
namingViolations.length
|
||||
|
||||
if (totalIssues === 0) {
|
||||
console.log(CLI_MESSAGES.NO_ISSUES)
|
||||
process.exit(0)
|
||||
} else {
|
||||
console.log(
|
||||
`${CLI_MESSAGES.ISSUES_TOTAL} ${String(totalIssues)} ${CLI_LABELS.ISSUES_TOTAL}`,
|
||||
)
|
||||
console.log(CLI_MESSAGES.TIP)
|
||||
|
||||
if (options.verbose) {
|
||||
console.log(CLI_MESSAGES.HELP_FOOTER)
|
||||
}
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`\n❌ ${CLI_MESSAGES.ERROR_PREFIX}`)
|
||||
console.error(error instanceof Error ? error.message : String(error))
|
||||
console.error("")
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program.parse()
|
||||
Reference in New Issue
Block a user