@howlil/ez-agents 3.4.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +735 -462
- package/agents/ez-architect-agent.md +267 -0
- package/agents/ez-backend-agent.md +303 -0
- package/agents/ez-chief-strategist.md +271 -0
- package/agents/ez-codebase-mapper.md +770 -770
- package/agents/ez-context-manager.md +319 -0
- package/agents/ez-debugger.md +1255 -1255
- package/agents/ez-design-expert.md +347 -0
- package/agents/ez-devops-agent.md +331 -0
- package/agents/ez-executor.md +487 -487
- package/agents/ez-frontend-agent.md +322 -0
- package/agents/ez-phase-researcher.md +553 -553
- package/agents/ez-planner.md +1307 -1307
- package/agents/ez-product-engineer.md +435 -0
- package/agents/ez-project-researcher.md +629 -629
- package/agents/ez-qa-agent.md +320 -0
- package/agents/ez-release-agent.md +333 -0
- package/agents/ez-requirements-agent.md +377 -0
- package/agents/ez-roadmapper.md +650 -650
- package/agents/ez-technical-writer.md +551 -0
- package/agents/ez-ux-expert.md +393 -0
- package/agents/ez-verifier.md +579 -579
- package/bin/guards/autonomy-guard.cjs +346 -0
- package/bin/guards/context-budget-guard.cjs +278 -0
- package/bin/guards/hallucination-guard.cjs +380 -0
- package/bin/guards/hidden-state-guard.cjs +182 -0
- package/bin/guards/team-overhead-guard.cjs +266 -0
- package/bin/guards/tool-sprawl-guard.cjs +271 -0
- package/bin/install.js +3221 -3272
- package/bin/lib/analytics/analytics-collector.cjs +86 -0
- package/bin/lib/analytics/analytics-reporter.cjs +130 -0
- package/bin/lib/analytics/cohort-analyzer.cjs +138 -0
- package/bin/lib/analytics/funnel-analyzer.cjs +147 -0
- package/bin/lib/analytics/nps-tracker.cjs +147 -0
- package/bin/lib/archetype-detector.cjs +289 -0
- package/bin/lib/assistant-adapter.cjs +361 -0
- package/bin/lib/audit-exec.cjs +175 -0
- package/bin/lib/auth.cjs +176 -0
- package/bin/lib/backup-service.cjs +422 -0
- package/bin/lib/bdd-validator.cjs +622 -0
- package/bin/lib/business-flow-mapper.cjs +429 -0
- package/bin/lib/circuit-breaker.cjs +276 -0
- package/bin/lib/code-complexity-analyzer.cjs +360 -0
- package/bin/lib/codebase-analyzer.cjs +241 -0
- package/bin/lib/commands.cjs +691 -0
- package/bin/lib/config.cjs +236 -0
- package/bin/lib/constraint-extractor.cjs +526 -0
- package/bin/lib/content-scanner.cjs +238 -0
- package/bin/lib/context-cache.cjs +154 -0
- package/bin/lib/context-compressor.cjs +102 -0
- package/bin/lib/context-deduplicator.cjs +105 -0
- package/bin/lib/context-errors.cjs +78 -0
- package/bin/lib/context-manager.cjs +338 -0
- package/bin/lib/context-metadata-tracker.cjs +140 -0
- package/bin/lib/context-relevance-scorer.cjs +99 -0
- package/bin/lib/core.cjs +507 -0
- package/bin/lib/cost-alerts.cjs +174 -0
- package/bin/lib/cost-tracker.cjs +275 -0
- package/bin/lib/crash-recovery.cjs +220 -0
- package/bin/lib/dependency-graph.cjs +319 -0
- package/bin/lib/deploy/deploy-audit-log.cjs +76 -0
- package/bin/lib/deploy/deploy-detector.cjs +69 -0
- package/bin/lib/deploy/deploy-env-manager.cjs +109 -0
- package/bin/lib/deploy/deploy-health-check.cjs +88 -0
- package/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
- package/bin/lib/deploy/deploy-rollback.cjs +72 -0
- package/bin/lib/deploy/deploy-runner.cjs +97 -0
- package/bin/lib/deploy/deploy-status.cjs +74 -0
- package/bin/lib/discussion-synthesizer.cjs +439 -0
- package/bin/lib/error-cache.cjs +114 -0
- package/bin/lib/error-registry.cjs +177 -0
- package/bin/lib/file-access.cjs +207 -0
- package/bin/lib/file-lock.cjs +236 -0
- package/bin/lib/finops/budget-enforcer.cjs +126 -0
- package/bin/lib/finops/cost-reporter.cjs +132 -0
- package/bin/lib/finops/finops-analyzer.cjs +112 -0
- package/bin/lib/finops/spot-manager.cjs +118 -0
- package/bin/lib/framework-detector.cjs +396 -0
- package/bin/lib/frontmatter.cjs +313 -0
- package/bin/lib/fs-utils.cjs +153 -0
- package/bin/lib/gate-executor.cjs +272 -0
- package/bin/lib/gates/README.md +374 -0
- package/bin/lib/gates/gate-01-requirement.cjs +303 -0
- package/bin/lib/gates/gate-02-architecture.cjs +555 -0
- package/bin/lib/gates/gate-03-code.cjs +635 -0
- package/bin/lib/gates/gate-04-security.cjs +829 -0
- package/bin/lib/git-errors.cjs +83 -0
- package/bin/lib/git-utils.cjs +321 -0
- package/bin/lib/git-workflow-engine.cjs +1157 -0
- package/bin/lib/health-check.cjs +227 -0
- package/bin/lib/index.cjs +279 -0
- package/bin/lib/init.cjs +725 -0
- package/bin/lib/lock-logger.cjs +194 -0
- package/bin/lib/lock-state.cjs +263 -0
- package/bin/lib/lockfile-validator.cjs +227 -0
- package/bin/lib/log-rotation.cjs +71 -0
- package/bin/lib/logger.cjs +125 -0
- package/bin/lib/memory-compression.cjs +256 -0
- package/bin/lib/milestone.cjs +247 -0
- package/bin/lib/model-provider.cjs +241 -0
- package/bin/lib/package-manager-detector.cjs +203 -0
- package/bin/lib/package-manager-executor.cjs +385 -0
- package/bin/lib/package-manager-service.cjs +216 -0
- package/bin/lib/perf/api-monitor.cjs +88 -0
- package/bin/lib/perf/db-optimizer.cjs +78 -0
- package/bin/lib/perf/frontend-performance.cjs +56 -0
- package/bin/lib/perf/perf-analyzer.cjs +77 -0
- package/bin/lib/perf/perf-baseline.cjs +102 -0
- package/bin/lib/perf/perf-reporter.cjs +117 -0
- package/bin/lib/perf/regression-detector.cjs +92 -0
- package/bin/lib/phase.cjs +963 -0
- package/bin/lib/planning-write.cjs +123 -0
- package/bin/lib/project-reporter.cjs +565 -0
- package/bin/lib/quality-gate.cjs +332 -0
- package/bin/lib/quality-metrics.cjs +324 -0
- package/bin/lib/recovery-manager.cjs +98 -0
- package/bin/lib/release-validator.cjs +617 -0
- package/bin/lib/retry.cjs +119 -0
- package/bin/lib/roadmap.cjs +309 -0
- package/bin/lib/safe-exec.cjs +173 -0
- package/bin/lib/safe-path.cjs +130 -0
- package/bin/lib/security-errors.cjs +62 -0
- package/bin/lib/session-chain.cjs +304 -0
- package/bin/lib/session-errors.cjs +81 -0
- package/bin/lib/session-export.cjs +251 -0
- package/bin/lib/session-import.cjs +262 -0
- package/bin/lib/session-manager.cjs +280 -0
- package/bin/lib/skill-context.cjs +148 -0
- package/bin/lib/skill-matcher.cjs +236 -0
- package/bin/lib/skill-registry.cjs +360 -0
- package/bin/lib/skill-resolver.cjs +449 -0
- package/bin/lib/skill-triggers.cjs +90 -0
- package/bin/lib/skill-validator.cjs +270 -0
- package/bin/lib/skill-versioning.cjs +355 -0
- package/bin/lib/stack-detector.cjs +399 -0
- package/bin/lib/state.cjs +736 -0
- package/bin/lib/tech-debt-analyzer.cjs +309 -0
- package/bin/lib/temp-file.cjs +239 -0
- package/bin/lib/template.cjs +223 -0
- package/bin/lib/test-file-lock.cjs +112 -0
- package/bin/lib/test-graceful.cjs +93 -0
- package/bin/lib/test-logger.cjs +60 -0
- package/bin/lib/test-safe-exec.cjs +38 -0
- package/bin/lib/test-safe-path.cjs +33 -0
- package/bin/lib/test-temp-file.cjs +125 -0
- package/bin/lib/tier-manager.cjs +428 -0
- package/bin/lib/timeout-exec.cjs +63 -0
- package/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/bin/lib/url-fetch.cjs +170 -0
- package/bin/lib/verify.cjs +863 -0
- package/bin/update.js +217 -214
- package/commands/deploy.cjs +53 -0
- package/commands/ez/add-tests.md +41 -41
- package/commands/ez/audit-milestone.md +36 -36
- package/commands/ez/complete-milestone.md +136 -136
- package/commands/ez/discuss-phase.md +90 -90
- package/commands/ez/execute-phase.md +52 -41
- package/commands/ez/help.md +22 -22
- package/commands/ez/map-codebase.md +71 -71
- package/commands/ez/new-milestone.md +44 -44
- package/commands/ez/new-project.md +51 -42
- package/commands/ez/plan-phase.md +53 -45
- package/commands/ez/progress.md +36 -24
- package/commands/ez/quick.md +45 -45
- package/commands/ez/resume-work.md +40 -40
- package/commands/ez/run-phase.md +580 -0
- package/commands/ez/settings.md +36 -36
- package/commands/ez/update.md +37 -37
- package/commands/ez/verify-work.md +402 -38
- package/commands/health-check.cjs +44 -0
- package/commands/rollback.cjs +47 -0
- package/ez-agents/bin/ez-tools.cjs +1692 -716
- package/ez-agents/bin/guards/autonomy-guard.cjs +346 -0
- package/ez-agents/bin/guards/context-budget-guard.cjs +247 -0
- package/ez-agents/bin/guards/hallucination-guard.cjs +271 -0
- package/ez-agents/bin/guards/hidden-state-guard.cjs +182 -0
- package/ez-agents/bin/guards/team-overhead-guard.cjs +266 -0
- package/ez-agents/bin/guards/tool-sprawl-guard.cjs +271 -0
- package/ez-agents/bin/lib/analytics/analytics-collector.cjs +86 -0
- package/ez-agents/bin/lib/analytics/analytics-reporter.cjs +130 -0
- package/ez-agents/bin/lib/analytics/cohort-analyzer.cjs +138 -0
- package/ez-agents/bin/lib/analytics/funnel-analyzer.cjs +147 -0
- package/ez-agents/bin/lib/analytics/nps-tracker.cjs +147 -0
- package/ez-agents/bin/lib/archetype-detector.cjs +289 -0
- package/ez-agents/bin/lib/audit-exec.cjs +166 -167
- package/ez-agents/bin/lib/auth.cjs +176 -176
- package/ez-agents/bin/lib/backup-service.cjs +422 -0
- package/ez-agents/bin/lib/bdd-validator.cjs +622 -0
- package/ez-agents/bin/lib/business-flow-mapper.cjs +429 -0
- package/ez-agents/bin/lib/code-complexity-analyzer.cjs +360 -0
- package/ez-agents/bin/lib/codebase-analyzer.cjs +241 -0
- package/ez-agents/bin/lib/commands.cjs +685 -685
- package/ez-agents/bin/lib/config.cjs +41 -1
- package/ez-agents/bin/lib/constraint-extractor.cjs +526 -0
- package/ez-agents/bin/lib/content-scanner.cjs +238 -0
- package/ez-agents/bin/lib/context-cache.cjs +154 -0
- package/ez-agents/bin/lib/context-errors.cjs +71 -0
- package/ez-agents/bin/lib/context-manager.cjs +220 -0
- package/ez-agents/bin/lib/core.cjs +507 -512
- package/ez-agents/bin/lib/cost-tracker.cjs +243 -0
- package/ez-agents/bin/lib/crash-recovery.cjs +172 -0
- package/ez-agents/bin/lib/dependency-graph.cjs +319 -0
- package/ez-agents/bin/lib/deploy/deploy-audit-log.cjs +76 -0
- package/ez-agents/bin/lib/deploy/deploy-detector.cjs +69 -0
- package/ez-agents/bin/lib/deploy/deploy-env-manager.cjs +109 -0
- package/ez-agents/bin/lib/deploy/deploy-health-check.cjs +88 -0
- package/ez-agents/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
- package/ez-agents/bin/lib/deploy/deploy-rollback.cjs +72 -0
- package/ez-agents/bin/lib/deploy/deploy-runner.cjs +97 -0
- package/ez-agents/bin/lib/deploy/deploy-status.cjs +74 -0
- package/ez-agents/bin/lib/discussion-synthesizer.cjs +458 -0
- package/ez-agents/bin/lib/file-access.cjs +207 -0
- package/ez-agents/bin/lib/finops/budget-enforcer.cjs +126 -0
- package/ez-agents/bin/lib/finops/cost-reporter.cjs +132 -0
- package/ez-agents/bin/lib/finops/finops-analyzer.cjs +112 -0
- package/ez-agents/bin/lib/finops/spot-manager.cjs +118 -0
- package/ez-agents/bin/lib/framework-detector.cjs +396 -0
- package/ez-agents/bin/lib/frontmatter.cjs +3 -1
- package/ez-agents/bin/lib/gates/README.md +374 -0
- package/ez-agents/bin/lib/gates/gate-01-requirement.cjs +303 -0
- package/ez-agents/bin/lib/gates/gate-02-architecture.cjs +555 -0
- package/ez-agents/bin/lib/gates/gate-03-code.cjs +635 -0
- package/ez-agents/bin/lib/gates/gate-04-security.cjs +829 -0
- package/ez-agents/bin/lib/git-errors.cjs +83 -0
- package/ez-agents/bin/lib/git-utils.cjs +118 -0
- package/ez-agents/bin/lib/git-workflow-engine.cjs +1157 -0
- package/ez-agents/bin/lib/health-check.cjs +162 -162
- package/ez-agents/bin/lib/index.cjs +40 -2
- package/ez-agents/bin/lib/init.cjs +0 -2
- package/ez-agents/bin/lib/lockfile-validator.cjs +227 -0
- package/ez-agents/bin/lib/log-rotation.cjs +71 -0
- package/ez-agents/bin/lib/logger.cjs +99 -154
- package/ez-agents/bin/lib/memory-compression.cjs +256 -0
- package/ez-agents/bin/lib/package-manager-detector.cjs +203 -0
- package/ez-agents/bin/lib/package-manager-executor.cjs +385 -0
- package/ez-agents/bin/lib/package-manager-service.cjs +216 -0
- package/ez-agents/bin/lib/perf/api-monitor.cjs +88 -0
- package/ez-agents/bin/lib/perf/db-optimizer.cjs +78 -0
- package/ez-agents/bin/lib/perf/frontend-performance.cjs +56 -0
- package/ez-agents/bin/lib/perf/perf-analyzer.cjs +77 -0
- package/ez-agents/bin/lib/perf/perf-baseline.cjs +102 -0
- package/ez-agents/bin/lib/perf/perf-reporter.cjs +117 -0
- package/ez-agents/bin/lib/perf/regression-detector.cjs +92 -0
- package/ez-agents/bin/lib/project-reporter.cjs +502 -0
- package/ez-agents/bin/lib/quality-gate.cjs +332 -0
- package/ez-agents/bin/lib/recovery-manager.cjs +98 -0
- package/ez-agents/bin/lib/release-validator.cjs +617 -0
- package/ez-agents/bin/lib/safe-exec.cjs +128 -214
- package/ez-agents/bin/lib/security-errors.cjs +62 -0
- package/ez-agents/bin/lib/session-chain.cjs +304 -0
- package/ez-agents/bin/lib/session-errors.cjs +81 -0
- package/ez-agents/bin/lib/session-export.cjs +251 -0
- package/ez-agents/bin/lib/session-import.cjs +262 -0
- package/ez-agents/bin/lib/session-manager.cjs +280 -0
- package/ez-agents/bin/lib/skill-context.cjs +148 -0
- package/ez-agents/bin/lib/skill-matcher.cjs +236 -0
- package/ez-agents/bin/lib/skill-registry.cjs +341 -0
- package/ez-agents/bin/lib/skill-resolver.cjs +449 -0
- package/ez-agents/bin/lib/skill-triggers.cjs +90 -0
- package/ez-agents/bin/lib/skill-validator.cjs +270 -0
- package/ez-agents/bin/lib/skill-versioning.cjs +355 -0
- package/ez-agents/bin/lib/stack-detector.cjs +399 -0
- package/ez-agents/bin/lib/tech-debt-analyzer.cjs +309 -0
- package/ez-agents/bin/lib/tier-manager.cjs +428 -0
- package/ez-agents/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/ez-agents/bin/lib/url-fetch.cjs +170 -0
- package/ez-agents/bin/lib/verify.cjs +863 -863
- package/ez-agents/references/decimal-phase-calculation.md +65 -65
- package/ez-agents/references/git-integration.md +248 -248
- package/ez-agents/references/git-planning-commit.md +38 -38
- package/ez-agents/references/metrics-schema.md +118 -0
- package/ez-agents/references/model-profile-resolution.md +34 -34
- package/ez-agents/references/model-profiles.md +93 -93
- package/ez-agents/references/phase-argument-parsing.md +61 -61
- package/ez-agents/references/planning-config.md +340 -200
- package/ez-agents/references/tier-strategy.md +103 -0
- package/ez-agents/references/ui-brand.md +160 -160
- package/ez-agents/references/verification-patterns.md +612 -612
- package/ez-agents/templates/DEBUG.md +164 -164
- package/ez-agents/templates/UAT.md +247 -247
- package/ez-agents/templates/agent-output-format.md +404 -0
- package/ez-agents/templates/bdd-feature.md +173 -0
- package/ez-agents/templates/codebase/architecture.md +255 -255
- package/ez-agents/templates/codebase/structure.md +285 -285
- package/ez-agents/templates/copilot-instructions.md +7 -7
- package/ez-agents/templates/debug-subagent-prompt.md +91 -91
- package/ez-agents/templates/discovery.md +146 -146
- package/ez-agents/templates/discussion.md +68 -0
- package/ez-agents/templates/handoff-protocol.md +294 -0
- package/ez-agents/templates/incident-runbook.md +205 -0
- package/ez-agents/templates/mode-workflow-templates.md +301 -0
- package/ez-agents/templates/phase-prompt.md +610 -610
- package/ez-agents/templates/planner-subagent-prompt.md +117 -117
- package/ez-agents/templates/project.md +184 -184
- package/ez-agents/templates/release-checklist.md +136 -0
- package/ez-agents/templates/research.md +552 -552
- package/ez-agents/templates/rollback-plan.md +201 -0
- package/ez-agents/templates/security-user-setup.md +244 -0
- package/ez-agents/templates/skill-validation-rules.md +476 -0
- package/ez-agents/templates/state.md +180 -176
- package/ez-agents/templates/summary-complex.md +59 -59
- package/ez-agents/tests/gates/gate-01-02.test.cjs +812 -0
- package/ez-agents/tests/gates/gate-03-04.test.cjs +762 -0
- package/ez-agents/tests/gates/gate-05-validator.test.cjs +145 -0
- package/ez-agents/tests/gates/gate-06-docs-validator.test.cjs +244 -0
- package/ez-agents/tests/gates/gate-07-release-validator.test.cjs +219 -0
- package/ez-agents/tests/guards/context-budget-guard.test.cjs +145 -0
- package/ez-agents/tests/guards/edge-case-guards.test.cjs +238 -0
- package/ez-agents/tests/guards/hallucination-guard.test.cjs +124 -0
- package/ez-agents/workflows/audit-milestone.md +1 -1
- package/ez-agents/workflows/autonomous.md +131 -30
- package/ez-agents/workflows/complete-milestone.md +1 -1
- package/ez-agents/workflows/discuss-phase.md +1 -1
- package/ez-agents/workflows/execute-phase.md +169 -3
- package/ez-agents/workflows/help.md +86 -133
- package/ez-agents/workflows/hotfix.md +291 -0
- package/ez-agents/workflows/new-milestone.md +340 -11
- package/ez-agents/workflows/new-project.md +294 -318
- package/ez-agents/workflows/plan-phase.md +22 -40
- package/ez-agents/workflows/progress.md +15 -25
- package/ez-agents/workflows/release.md +253 -0
- package/ez-agents/workflows/resume-session.md +215 -0
- package/ez-agents/workflows/run-phase.md +531 -0
- package/ez-agents/workflows/settings.md +2 -35
- package/hooks/dist/ez-check-update.js +81 -81
- package/hooks/dist/ez-context-monitor.js +148 -141
- package/hooks/dist/ez-statusline.js +115 -115
- package/package.json +78 -64
- package/scripts/fix-qwen-installation.js +144 -144
- package/agents/ez-integration-checker.md +0 -443
- package/agents/ez-nyquist-auditor.md +0 -176
- package/agents/ez-plan-checker.md +0 -706
- package/agents/ez-research-synthesizer.md +0 -247
- package/agents/ez-ui-auditor.md +0 -439
- package/agents/ez-ui-checker.md +0 -300
- package/agents/ez-ui-researcher.md +0 -353
- package/commands/ez/add-phase.md +0 -43
- package/commands/ez/add-todo.md +0 -47
- package/commands/ez/auth.md +0 -87
- package/commands/ez/autonomous.md +0 -41
- package/commands/ez/check-todos.md +0 -45
- package/commands/ez/cleanup.md +0 -18
- package/commands/ez/debug.md +0 -168
- package/commands/ez/health.md +0 -22
- package/commands/ez/insert-phase.md +0 -32
- package/commands/ez/join-discord.md +0 -18
- package/commands/ez/list-phase-assumptions.md +0 -46
- package/commands/ez/pause-work.md +0 -38
- package/commands/ez/plan-milestone-gaps.md +0 -34
- package/commands/ez/reapply-patches.md +0 -124
- package/commands/ez/remove-phase.md +0 -31
- package/commands/ez/research-phase.md +0 -190
- package/commands/ez/set-profile.md +0 -34
- package/commands/ez/stats.md +0 -18
- package/commands/ez/ui-phase.md +0 -34
- package/commands/ez/ui-review.md +0 -32
- package/commands/ez/validate-phase.md +0 -35
- package/ez-agents/templates/UI-SPEC.md +0 -100
- package/ez-agents/templates/VALIDATION.md +0 -76
- package/ez-agents/templates/context.md +0 -352
- package/ez-agents/templates/verification-report.md +0 -322
- package/ez-agents/workflows/research-phase.md +0 -74
- package/ez-agents/workflows/ui-phase.md +0 -290
- package/ez-agents/workflows/ui-review.md +0 -157
- package/ez-agents/workflows/validate-phase.md +0 -167
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Complexity Analyzer — Automated code complexity analysis
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - analyzeComplexity(rootPath): ESLint-based cyclomatic complexity analysis
|
|
6
|
+
* - detectLargeFiles(rootPath, thresholds): Finds files exceeding line/size thresholds
|
|
7
|
+
* - detectDuplicateCode(rootPath): Chunk-based hash comparison for duplicate detection
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const crypto = require('crypto');
|
|
13
|
+
|
|
14
|
+
class CodeComplexityAnalyzer {
|
|
15
|
+
constructor(rootPath) {
|
|
16
|
+
this.rootPath = rootPath;
|
|
17
|
+
this.defaultThresholds = {
|
|
18
|
+
lines: 500,
|
|
19
|
+
sizeKB: 100
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Analyze code complexity using ESLint rules
|
|
25
|
+
* @param {string} rootPath - Root directory to analyze
|
|
26
|
+
* @returns {Promise<Array>} Array of complexity issues with file, line, rule, severity, message, score
|
|
27
|
+
*/
|
|
28
|
+
async analyzeComplexity(rootPath = this.rootPath) {
|
|
29
|
+
const issues = [];
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// Try to use ESLint if available
|
|
33
|
+
const eslintPath = path.join(rootPath, 'node_modules', 'eslint');
|
|
34
|
+
if (fs.existsSync(eslintPath)) {
|
|
35
|
+
const { ESLint } = require(eslintPath);
|
|
36
|
+
|
|
37
|
+
const eslint = new ESLint({
|
|
38
|
+
useEslintrc: false,
|
|
39
|
+
overrideConfig: {
|
|
40
|
+
parserOptions: {
|
|
41
|
+
ecmaVersion: 2022,
|
|
42
|
+
sourceType: 'module'
|
|
43
|
+
},
|
|
44
|
+
rules: {
|
|
45
|
+
complexity: ['error', { max: 10 }],
|
|
46
|
+
'max-depth': ['error', { max: 4 }],
|
|
47
|
+
'max-lines': ['error', { max: 300 }],
|
|
48
|
+
'max-params': ['error', { max: 4 }],
|
|
49
|
+
'max-statements': ['error', { max: 30 }]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const srcDir = path.join(rootPath, 'src');
|
|
55
|
+
if (fs.existsSync(srcDir)) {
|
|
56
|
+
const results = await eslint.lintFiles([`${srcDir}/**/*.ts`, `${srcDir}/**/*.tsx`, `${srcDir}/**/*.js`]);
|
|
57
|
+
|
|
58
|
+
for (const result of results) {
|
|
59
|
+
for (const msg of result.messages) {
|
|
60
|
+
if (msg.ruleId && (msg.ruleId.includes('complexity') || msg.ruleId.includes('max-'))) {
|
|
61
|
+
issues.push({
|
|
62
|
+
file: result.filePath,
|
|
63
|
+
line: msg.line,
|
|
64
|
+
rule: msg.ruleId,
|
|
65
|
+
severity: msg.severity === 2 ? 'High' : 'Medium',
|
|
66
|
+
message: msg.message,
|
|
67
|
+
score: msg.severity === 2 ? 3 : 2
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
// Fallback: basic complexity estimation
|
|
75
|
+
return this._analyzeComplexityFallback(rootPath);
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
// Fallback if ESLint analysis fails
|
|
79
|
+
console.warn(`Warning: ESLint analysis failed: ${err.message}`);
|
|
80
|
+
return this._analyzeComplexityFallback(rootPath);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return issues;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Fallback complexity analysis without ESLint
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
_analyzeComplexityFallback(rootPath) {
|
|
91
|
+
const issues = [];
|
|
92
|
+
const srcDir = path.join(rootPath, 'src');
|
|
93
|
+
|
|
94
|
+
if (!fs.existsSync(srcDir)) return issues;
|
|
95
|
+
|
|
96
|
+
const files = this._getSourceFiles(srcDir);
|
|
97
|
+
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
try {
|
|
100
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
101
|
+
const lines = content.split('\n');
|
|
102
|
+
|
|
103
|
+
// Estimate cyclomatic complexity
|
|
104
|
+
let complexity = 1;
|
|
105
|
+
const complexityKeywords = [
|
|
106
|
+
/\bif\s*\(/g,
|
|
107
|
+
/\belse\s+if\s*\(/g,
|
|
108
|
+
/\bfor\s*\(/g,
|
|
109
|
+
/\bwhile\s*\(/g,
|
|
110
|
+
/\bcase\s+/g,
|
|
111
|
+
/\bcatch\s*\(/g,
|
|
112
|
+
/\?\s*/g,
|
|
113
|
+
/\&\&/g,
|
|
114
|
+
/\|\|/g
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
for (const regex of complexityKeywords) {
|
|
118
|
+
const matches = content.match(regex);
|
|
119
|
+
if (matches) {
|
|
120
|
+
complexity += matches.length;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check function complexity
|
|
125
|
+
const functionRegex = /(function\s+\w+|const\s+\w+\s*=\s*(async\s+)?\(.*\)|\w+\s*:\s*(async\s+)?\(.*\))/g;
|
|
126
|
+
let funcMatch;
|
|
127
|
+
while ((funcMatch = functionRegex.exec(content)) !== null) {
|
|
128
|
+
// Count complexity within function
|
|
129
|
+
const funcStart = funcMatch.index;
|
|
130
|
+
let braceCount = 0;
|
|
131
|
+
let funcEnd = funcStart;
|
|
132
|
+
let inFunction = false;
|
|
133
|
+
|
|
134
|
+
for (let i = funcStart; i < content.length; i++) {
|
|
135
|
+
if (content[i] === '{') {
|
|
136
|
+
braceCount++;
|
|
137
|
+
inFunction = true;
|
|
138
|
+
} else if (content[i] === '}') {
|
|
139
|
+
braceCount--;
|
|
140
|
+
if (inFunction && braceCount === 0) {
|
|
141
|
+
funcEnd = i;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const funcContent = content.substring(funcStart, funcEnd);
|
|
148
|
+
let funcComplexity = 1;
|
|
149
|
+
|
|
150
|
+
for (const regex of complexityKeywords) {
|
|
151
|
+
const matches = funcContent.match(regex);
|
|
152
|
+
if (matches) {
|
|
153
|
+
funcComplexity += matches.length;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (funcComplexity > 10) {
|
|
158
|
+
const lineNum = content.substring(0, funcStart).split('\n').length;
|
|
159
|
+
issues.push({
|
|
160
|
+
file,
|
|
161
|
+
line: lineNum,
|
|
162
|
+
rule: 'complexity',
|
|
163
|
+
severity: funcComplexity > 20 ? 'High' : 'Medium',
|
|
164
|
+
message: `Function complexity (${funcComplexity}) exceeds recommended maximum (10)`,
|
|
165
|
+
score: funcComplexity > 20 ? 3 : 2
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check for high overall complexity
|
|
171
|
+
if (complexity > 50) {
|
|
172
|
+
issues.push({
|
|
173
|
+
file,
|
|
174
|
+
line: 1,
|
|
175
|
+
rule: 'file-complexity',
|
|
176
|
+
severity: 'High',
|
|
177
|
+
message: `File complexity (${complexity}) is very high`,
|
|
178
|
+
score: 3
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
} catch (err) {
|
|
182
|
+
// Ignore read errors
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return issues;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Detect large files exceeding thresholds
|
|
191
|
+
* @param {string} rootPath - Root directory to analyze
|
|
192
|
+
* @param {object} thresholds - Thresholds object with lines and sizeKB
|
|
193
|
+
* @returns {Array} Array of large file objects with file, lines, sizeKB, severity, score
|
|
194
|
+
*/
|
|
195
|
+
detectLargeFiles(rootPath = this.rootPath, thresholds = this.defaultThresholds) {
|
|
196
|
+
const results = [];
|
|
197
|
+
const srcDir = path.join(rootPath, 'src');
|
|
198
|
+
|
|
199
|
+
if (!fs.existsSync(srcDir)) return results;
|
|
200
|
+
|
|
201
|
+
const files = this._getSourceFiles(srcDir);
|
|
202
|
+
|
|
203
|
+
for (const file of files) {
|
|
204
|
+
try {
|
|
205
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
206
|
+
const lines = content.split('\n').length;
|
|
207
|
+
const sizeKB = Buffer.byteLength(content, 'utf8') / 1024;
|
|
208
|
+
|
|
209
|
+
if (lines > thresholds.lines || sizeKB > thresholds.sizeKB) {
|
|
210
|
+
results.push({
|
|
211
|
+
file,
|
|
212
|
+
lines,
|
|
213
|
+
sizeKB: Math.round(sizeKB * 100) / 100,
|
|
214
|
+
severity: lines > 1000 ? 'High' : 'Medium',
|
|
215
|
+
score: lines > 1000 ? 3 : 2
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
} catch (err) {
|
|
219
|
+
// Ignore read errors
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Sort by lines descending
|
|
224
|
+
results.sort((a, b) => b.lines - a.lines);
|
|
225
|
+
|
|
226
|
+
return results;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Detect duplicate code using chunk hashing
|
|
231
|
+
* @param {string} rootPath - Root directory to analyze
|
|
232
|
+
* @returns {Array} Array of duplicate objects with hash, occurrences, fileCount, severity, score
|
|
233
|
+
*/
|
|
234
|
+
detectDuplicateCode(rootPath = this.rootPath) {
|
|
235
|
+
const srcDir = path.join(rootPath, 'src');
|
|
236
|
+
if (!fs.existsSync(srcDir)) return [];
|
|
237
|
+
|
|
238
|
+
const files = this._getSourceFiles(srcDir);
|
|
239
|
+
const chunks = [];
|
|
240
|
+
const chunkSize = 10; // lines
|
|
241
|
+
const overlap = 5; // lines
|
|
242
|
+
|
|
243
|
+
// Break files into chunks
|
|
244
|
+
for (const file of files) {
|
|
245
|
+
try {
|
|
246
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
247
|
+
const lines = content.split('\n');
|
|
248
|
+
|
|
249
|
+
for (let i = 0; i < lines.length - chunkSize; i += overlap) {
|
|
250
|
+
const chunk = lines.slice(i, i + chunkSize).join('\n').trim();
|
|
251
|
+
|
|
252
|
+
// Skip chunks that are mostly empty or comments
|
|
253
|
+
if (chunk.length < 50 || chunk.split('\n').filter(l => l.trim() && !l.trim().startsWith('//')).length < 3) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const hash = crypto.createHash('md5').update(chunk).digest('hex');
|
|
258
|
+
|
|
259
|
+
chunks.push({
|
|
260
|
+
file,
|
|
261
|
+
startLine: i + 1,
|
|
262
|
+
endLine: i + chunkSize,
|
|
263
|
+
hash,
|
|
264
|
+
content: chunk.substring(0, 100) // Truncate for storage
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
// Ignore read errors
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Group by hash
|
|
273
|
+
const byHash = {};
|
|
274
|
+
for (const chunk of chunks) {
|
|
275
|
+
if (!byHash[chunk.hash]) {
|
|
276
|
+
byHash[chunk.hash] = [];
|
|
277
|
+
}
|
|
278
|
+
byHash[chunk.hash].push(chunk);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Find duplicates (same hash in different files)
|
|
282
|
+
const duplicates = [];
|
|
283
|
+
for (const [hash, items] of Object.entries(byHash)) {
|
|
284
|
+
const uniqueFiles = new Set(items.map(i => i.file));
|
|
285
|
+
if (uniqueFiles.size > 1) {
|
|
286
|
+
duplicates.push({
|
|
287
|
+
hash,
|
|
288
|
+
occurrences: items,
|
|
289
|
+
fileCount: uniqueFiles.size,
|
|
290
|
+
severity: uniqueFiles.size > 3 ? 'High' : 'Medium',
|
|
291
|
+
score: uniqueFiles.size > 3 ? 3 : 2,
|
|
292
|
+
files: Array.from(uniqueFiles)
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Sort by file count descending
|
|
298
|
+
duplicates.sort((a, b) => b.fileCount - a.fileCount);
|
|
299
|
+
|
|
300
|
+
return duplicates;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Get source files from directory
|
|
305
|
+
* @private
|
|
306
|
+
*/
|
|
307
|
+
_getSourceFiles(dir, files = [], depth = 0) {
|
|
308
|
+
if (depth > 5 || dir.includes('node_modules') || dir.includes('dist') || dir.includes('build')) {
|
|
309
|
+
return files;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
314
|
+
for (const entry of entries) {
|
|
315
|
+
const fullPath = path.join(dir, entry.name);
|
|
316
|
+
if (entry.isDirectory()) {
|
|
317
|
+
this._getSourceFiles(fullPath, files, depth + 1);
|
|
318
|
+
} else if (entry.isFile() && /\.(ts|tsx|js|jsx)$/.test(entry.name)) {
|
|
319
|
+
files.push(fullPath);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} catch (err) {
|
|
323
|
+
// Ignore errors
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return files;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get complexity summary
|
|
331
|
+
* @param {Array} issues - Complexity issues
|
|
332
|
+
* @returns {object} Summary object
|
|
333
|
+
*/
|
|
334
|
+
getSummary(issues = []) {
|
|
335
|
+
const summary = {
|
|
336
|
+
total: issues.length,
|
|
337
|
+
bySeverity: {
|
|
338
|
+
High: 0,
|
|
339
|
+
Medium: 0,
|
|
340
|
+
Low: 0
|
|
341
|
+
},
|
|
342
|
+
byRule: {},
|
|
343
|
+
affectedFiles: new Set()
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
for (const issue of issues) {
|
|
347
|
+
summary.bySeverity[issue.severity] = (summary.bySeverity[issue.severity] || 0) + 1;
|
|
348
|
+
summary.byRule[issue.rule] = (summary.byRule[issue.rule] || 0) + 1;
|
|
349
|
+
if (issue.file) {
|
|
350
|
+
summary.affectedFiles.add(issue.file);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
summary.affectedFiles = summary.affectedFiles.size;
|
|
355
|
+
|
|
356
|
+
return summary;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
module.exports = { CodeComplexityAnalyzer };
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codebase Analyzer — Automated file structure analysis and module boundary detection
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - analyzeStructure(rootPath): File structure with directories, entry points, config files, source dirs
|
|
6
|
+
* - detectModuleBoundaries(structure): Identifies module patterns (components, services, controllers, routes, models)
|
|
7
|
+
* - classifyFile(fullPath, fileName): Categorizes files as entry points, configs, source, tests
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const micromatch = require('micromatch');
|
|
13
|
+
|
|
14
|
+
class CodebaseAnalyzer {
|
|
15
|
+
constructor(rootPath) {
|
|
16
|
+
this.rootPath = rootPath;
|
|
17
|
+
this.ignorePatterns = [
|
|
18
|
+
'node_modules/**',
|
|
19
|
+
'.git/**',
|
|
20
|
+
'dist/**',
|
|
21
|
+
'build/**',
|
|
22
|
+
'.next/**',
|
|
23
|
+
'coverage/**',
|
|
24
|
+
'*.log',
|
|
25
|
+
'.env*',
|
|
26
|
+
'.planning/**',
|
|
27
|
+
'vendor/**',
|
|
28
|
+
'target/**',
|
|
29
|
+
'.idea/**',
|
|
30
|
+
'.vscode/**'
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Analyze the structure of a codebase
|
|
36
|
+
* @param {string} rootPath - Root directory to analyze
|
|
37
|
+
* @returns {object} Structure object with directories, entryPoints, configFiles, sourceDirs, testDirs, files
|
|
38
|
+
*/
|
|
39
|
+
analyzeStructure(rootPath = this.rootPath) {
|
|
40
|
+
const structure = {
|
|
41
|
+
root: rootPath,
|
|
42
|
+
directories: [],
|
|
43
|
+
entryPoints: [],
|
|
44
|
+
configFiles: [],
|
|
45
|
+
sourceDirs: [],
|
|
46
|
+
testDirs: [],
|
|
47
|
+
files: []
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
this._traverse(rootPath, structure, 0);
|
|
51
|
+
|
|
52
|
+
return structure;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Detect module boundaries from structure
|
|
57
|
+
* @param {object} structure - Structure object from analyzeStructure
|
|
58
|
+
* @returns {Array} Array of module objects with name, path, type, fileCount
|
|
59
|
+
*/
|
|
60
|
+
detectModuleBoundaries(structure) {
|
|
61
|
+
const modules = [];
|
|
62
|
+
const modulePattern = /^(components|services|controllers|routes|models|utils|helpers|lib|middleware|stores|hooks|composables|directives)$/;
|
|
63
|
+
|
|
64
|
+
for (const dir of structure.directories) {
|
|
65
|
+
const dirName = path.basename(dir.path);
|
|
66
|
+
const parentDir = path.basename(path.dirname(dir.path));
|
|
67
|
+
|
|
68
|
+
if (modulePattern.test(dirName)) {
|
|
69
|
+
const fileCount = this._countFiles(dir.path);
|
|
70
|
+
modules.push({
|
|
71
|
+
name: dirName,
|
|
72
|
+
path: dir.path,
|
|
73
|
+
type: parentDir !== path.basename(structure.root) ? parentDir : 'root',
|
|
74
|
+
fileCount,
|
|
75
|
+
depth: dir.depth
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Sort by depth first, then by name
|
|
81
|
+
modules.sort((a, b) => a.depth - b.depth || a.name.localeCompare(b.name));
|
|
82
|
+
|
|
83
|
+
return modules;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Classify a file based on its name and path
|
|
88
|
+
* @param {string} fullPath - Full path to the file
|
|
89
|
+
* @param {string} fileName - File name
|
|
90
|
+
* @param {object} structure - Optional structure object for context
|
|
91
|
+
* @returns {object} Classification with type, category, isEntry, isConfig, isSource, isTest
|
|
92
|
+
*/
|
|
93
|
+
classifyFile(fullPath, fileName, structure = null) {
|
|
94
|
+
const entryPoints = ['index.js', 'index.ts', 'index.tsx', 'index.jsx', 'main.js', 'main.ts', 'app.js', 'app.ts', 'server.js', 'server.ts'];
|
|
95
|
+
const configFiles = ['package.json', 'tsconfig.json', 'jsconfig.json', '.eslintrc', '.eslintrc.js', '.eslintrc.json', '.prettierrc', 'vite.config.js', 'vite.config.ts', 'webpack.config.js', 'rollup.config.js', 'jest.config.js', 'vitest.config.js', 'next.config.js', 'nuxt.config.js', 'tailwind.config.js', 'postcss.config.js'];
|
|
96
|
+
const testExtensions = ['.test.js', '.test.ts', '.test.tsx', '.spec.js', '.spec.ts', '.spec.tsx'];
|
|
97
|
+
const sourceExtensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.rb', '.go', '.java', '.php', '.rs'];
|
|
98
|
+
|
|
99
|
+
const classification = {
|
|
100
|
+
path: fullPath,
|
|
101
|
+
name: fileName,
|
|
102
|
+
type: 'other',
|
|
103
|
+
category: 'other',
|
|
104
|
+
isEntry: false,
|
|
105
|
+
isConfig: false,
|
|
106
|
+
isSource: false,
|
|
107
|
+
isTest: false,
|
|
108
|
+
extension: path.extname(fileName)
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Check entry points
|
|
112
|
+
if (entryPoints.includes(fileName)) {
|
|
113
|
+
classification.isEntry = true;
|
|
114
|
+
classification.type = 'entry';
|
|
115
|
+
classification.category = 'entry';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check config files
|
|
119
|
+
if (configFiles.some(config => fileName === config || fileName.startsWith(config))) {
|
|
120
|
+
classification.isConfig = true;
|
|
121
|
+
classification.type = 'config';
|
|
122
|
+
classification.category = 'config';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Check test files
|
|
126
|
+
if (testExtensions.some(ext => fileName.endsWith(ext)) || fullPath.includes('/test/') || fullPath.includes('/spec/') || fullPath.includes('/tests/')) {
|
|
127
|
+
classification.isTest = true;
|
|
128
|
+
classification.type = 'test';
|
|
129
|
+
classification.category = 'test';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check source files
|
|
133
|
+
if (sourceExtensions.includes(classification.extension) && !classification.isTest && !classification.isConfig) {
|
|
134
|
+
classification.isSource = true;
|
|
135
|
+
classification.type = 'source';
|
|
136
|
+
classification.category = 'source';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return classification;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Internal: Traverse directory tree
|
|
144
|
+
* @private
|
|
145
|
+
*/
|
|
146
|
+
_traverse(dir, structure, depth = 0) {
|
|
147
|
+
if (depth > 10 || micromatch.isMatch(dir, this.ignorePatterns)) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true, withFileTypes: true });
|
|
153
|
+
|
|
154
|
+
for (const entry of entries) {
|
|
155
|
+
const fullPath = path.join(dir, entry.name);
|
|
156
|
+
|
|
157
|
+
if (micromatch.isMatch(fullPath, this.ignorePatterns)) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (entry.isDirectory()) {
|
|
162
|
+
structure.directories.push({
|
|
163
|
+
path: fullPath,
|
|
164
|
+
name: entry.name,
|
|
165
|
+
depth,
|
|
166
|
+
hasSource: false,
|
|
167
|
+
hasTests: false
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Check if directory contains source or tests
|
|
171
|
+
const dirContents = fs.readdirSync(fullPath, { withFileTypes: true });
|
|
172
|
+
for (const content of dirContents) {
|
|
173
|
+
if (content.isFile()) {
|
|
174
|
+
const ext = path.extname(content.name);
|
|
175
|
+
if (['.js', '.ts', '.jsx', '.tsx'].includes(ext)) {
|
|
176
|
+
structure.directories[structure.directories.length - 1].hasSource = true;
|
|
177
|
+
}
|
|
178
|
+
if (content.name.includes('.test.') || content.name.includes('.spec.')) {
|
|
179
|
+
structure.directories[structure.directories.length - 1].hasTests = true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this._traverse(fullPath, structure, depth + 1);
|
|
185
|
+
} else if (entry.isFile()) {
|
|
186
|
+
structure.files.push(fullPath);
|
|
187
|
+
|
|
188
|
+
const classification = this.classifyFile(fullPath, entry.name);
|
|
189
|
+
|
|
190
|
+
if (classification.isEntry) {
|
|
191
|
+
structure.entryPoints.push(fullPath);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (classification.isConfig) {
|
|
195
|
+
structure.configFiles.push(fullPath);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (classification.isSource && fullPath.includes('/src/')) {
|
|
199
|
+
if (!structure.sourceDirs.includes(dir)) {
|
|
200
|
+
structure.sourceDirs.push(dir);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (classification.isTest) {
|
|
205
|
+
if (!structure.testDirs.includes(dir)) {
|
|
206
|
+
structure.testDirs.push(dir);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
// Ignore permission errors and continue
|
|
213
|
+
if (err.code !== 'EPERM' && err.code !== 'EACCES') {
|
|
214
|
+
console.warn(`Warning: Error traversing ${dir}: ${err.message}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Count files in a directory
|
|
221
|
+
* @private
|
|
222
|
+
*/
|
|
223
|
+
_countFiles(dir) {
|
|
224
|
+
let count = 0;
|
|
225
|
+
try {
|
|
226
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
227
|
+
for (const entry of entries) {
|
|
228
|
+
if (entry.isFile() && !micromatch.isMatch(path.join(dir, entry.name), this.ignorePatterns)) {
|
|
229
|
+
count++;
|
|
230
|
+
} else if (entry.isDirectory()) {
|
|
231
|
+
count += this._countFiles(path.join(dir, entry.name));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
} catch (err) {
|
|
235
|
+
// Ignore errors
|
|
236
|
+
}
|
|
237
|
+
return count;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = { CodebaseAnalyzer };
|