@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,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stack Detector — Automated technology stack detection from package manifests and config files
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - detect(rootPath): Returns languages, runtime, packageManager, frameworks, databases, infrastructure
|
|
6
|
+
* - detectPackageManifests(rootPath): Finds and parses manifest files
|
|
7
|
+
* - detectPackageManager(rootPath): Delegates to PackageManagerDetector
|
|
8
|
+
* - detectFrameworks(dependencies): Maps package names to framework names
|
|
9
|
+
* - detectDatabases(dependencies): Maps ORM/db packages to database names
|
|
10
|
+
* - detectInfrastructure(dependencies): Maps cloud/monitoring/testing packages
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const PackageManagerDetector = require('./package-manager-detector.cjs');
|
|
16
|
+
|
|
17
|
+
class StackDetector {
|
|
18
|
+
constructor(rootPath) {
|
|
19
|
+
this.rootPath = rootPath;
|
|
20
|
+
this.pmDetector = new PackageManagerDetector(rootPath);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Detect technology stack from root path
|
|
25
|
+
* @param {string} rootPath - Root directory to analyze
|
|
26
|
+
* @returns {object} Stack object with language, runtime, packageManager, frameworks, databases, infrastructure
|
|
27
|
+
*/
|
|
28
|
+
detect(rootPath = this.rootPath) {
|
|
29
|
+
const manifests = this.detectPackageManifests(rootPath);
|
|
30
|
+
const packageManager = this.detectPackageManager(rootPath);
|
|
31
|
+
|
|
32
|
+
let result = {
|
|
33
|
+
language: 'unknown',
|
|
34
|
+
runtime: 'unknown',
|
|
35
|
+
packageManager,
|
|
36
|
+
frameworks: [],
|
|
37
|
+
databases: [],
|
|
38
|
+
infrastructure: [],
|
|
39
|
+
manifests
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Parse package.json if exists
|
|
43
|
+
if (manifests.node) {
|
|
44
|
+
const pkgPath = path.join(rootPath, manifests.node);
|
|
45
|
+
try {
|
|
46
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
47
|
+
const dependencies = {
|
|
48
|
+
...pkg.dependencies,
|
|
49
|
+
...pkg.devDependencies,
|
|
50
|
+
...pkg.peerDependencies
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
result.language = 'javascript';
|
|
54
|
+
result.runtime = 'node';
|
|
55
|
+
result.runtimeVersion = pkg.engines?.node || '>=16.0.0';
|
|
56
|
+
result.frameworks = this.detectFrameworks(dependencies);
|
|
57
|
+
result.databases = this.detectDatabases(dependencies);
|
|
58
|
+
result.infrastructure = this.detectInfrastructure(dependencies);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
// Ignore parse errors
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check for other languages
|
|
65
|
+
if (manifests.python) {
|
|
66
|
+
result.language = 'python';
|
|
67
|
+
result.runtime = 'python';
|
|
68
|
+
}
|
|
69
|
+
if (manifests.ruby) {
|
|
70
|
+
result.language = 'ruby';
|
|
71
|
+
result.runtime = 'ruby';
|
|
72
|
+
}
|
|
73
|
+
if (manifests.php) {
|
|
74
|
+
result.language = 'php';
|
|
75
|
+
result.runtime = 'php';
|
|
76
|
+
}
|
|
77
|
+
if (manifests.java) {
|
|
78
|
+
result.language = 'java';
|
|
79
|
+
result.runtime = 'jvm';
|
|
80
|
+
}
|
|
81
|
+
if (manifests.go) {
|
|
82
|
+
result.language = 'go';
|
|
83
|
+
result.runtime = 'go';
|
|
84
|
+
}
|
|
85
|
+
if (manifests.rust) {
|
|
86
|
+
result.language = 'rust';
|
|
87
|
+
result.runtime = 'rust';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Detect package manifests in root path
|
|
95
|
+
* @param {string} rootPath - Root directory to analyze
|
|
96
|
+
* @returns {object} Object with found manifest files
|
|
97
|
+
*/
|
|
98
|
+
detectPackageManifests(rootPath = this.rootPath) {
|
|
99
|
+
const manifests = {};
|
|
100
|
+
|
|
101
|
+
const manifestMap = {
|
|
102
|
+
node: ['package.json'],
|
|
103
|
+
python: ['requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile'],
|
|
104
|
+
ruby: ['Gemfile'],
|
|
105
|
+
php: ['composer.json'],
|
|
106
|
+
java: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
|
|
107
|
+
go: ['go.mod'],
|
|
108
|
+
rust: ['Cargo.toml']
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
for (const [lang, files] of Object.entries(manifestMap)) {
|
|
112
|
+
for (const file of files) {
|
|
113
|
+
const fullPath = path.join(rootPath, file);
|
|
114
|
+
if (fs.existsSync(fullPath)) {
|
|
115
|
+
manifests[lang] = file;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return manifests;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Detect package manager
|
|
126
|
+
* @param {string} rootPath - Root directory to analyze
|
|
127
|
+
* @returns {string} Package manager name (npm, yarn, pnpm)
|
|
128
|
+
*/
|
|
129
|
+
detectPackageManager(rootPath = this.rootPath) {
|
|
130
|
+
const result = this.pmDetector.detect();
|
|
131
|
+
if (result && typeof result === 'object' && result.manager) {
|
|
132
|
+
return result.manager;
|
|
133
|
+
}
|
|
134
|
+
return result || 'npm';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Detect frameworks from dependencies
|
|
139
|
+
* @param {object} dependencies - Dependencies object from package.json
|
|
140
|
+
* @returns {Array} Array of framework names
|
|
141
|
+
*/
|
|
142
|
+
detectFrameworks(dependencies = {}) {
|
|
143
|
+
const frameworkMap = {
|
|
144
|
+
// Frontend
|
|
145
|
+
'react': 'React',
|
|
146
|
+
'next': 'Next.js',
|
|
147
|
+
'vue': 'Vue.js',
|
|
148
|
+
'nuxt': 'Nuxt.js',
|
|
149
|
+
'@angular/core': 'Angular',
|
|
150
|
+
'svelte': 'Svelte',
|
|
151
|
+
'solid-js': 'SolidJS',
|
|
152
|
+
// Backend
|
|
153
|
+
'express': 'Express',
|
|
154
|
+
'fastify': 'Fastify',
|
|
155
|
+
'@nestjs/core': 'NestJS',
|
|
156
|
+
'nest': 'NestJS',
|
|
157
|
+
'koa': 'Koa',
|
|
158
|
+
'hapi': 'Hapi',
|
|
159
|
+
'@hapi/hapi': 'Hapi',
|
|
160
|
+
// Full-stack
|
|
161
|
+
'remix': 'Remix',
|
|
162
|
+
// Mobile
|
|
163
|
+
'react-native': 'React Native',
|
|
164
|
+
// Python
|
|
165
|
+
'django': 'Django',
|
|
166
|
+
'flask': 'Flask',
|
|
167
|
+
'fastapi': 'FastAPI',
|
|
168
|
+
// Ruby
|
|
169
|
+
'rails': 'Ruby on Rails',
|
|
170
|
+
'sinatra': 'Sinatra',
|
|
171
|
+
// PHP
|
|
172
|
+
'laravel/framework': 'Laravel',
|
|
173
|
+
'symfony': 'Symfony',
|
|
174
|
+
// Java
|
|
175
|
+
'spring-boot': 'Spring Boot',
|
|
176
|
+
'quarkus': 'Quarkus',
|
|
177
|
+
// Build/Dev
|
|
178
|
+
'vite': 'Vite',
|
|
179
|
+
'webpack': 'Webpack',
|
|
180
|
+
'rollup': 'Rollup',
|
|
181
|
+
'parcel': 'Parcel',
|
|
182
|
+
// CSS
|
|
183
|
+
'tailwindcss': 'Tailwind CSS',
|
|
184
|
+
'bootstrap': 'Bootstrap',
|
|
185
|
+
'@mui/material': 'Material-UI',
|
|
186
|
+
'@chakra-ui/react': 'Chakra UI',
|
|
187
|
+
'antd': 'Ant Design'
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const detected = [];
|
|
191
|
+
for (const [pkg, name] of Object.entries(frameworkMap)) {
|
|
192
|
+
if (dependencies[pkg]) {
|
|
193
|
+
detected.push(name);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return detected;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Detect databases from dependencies
|
|
202
|
+
* @param {object} dependencies - Dependencies object from package.json
|
|
203
|
+
* @returns {Array} Array of database names
|
|
204
|
+
*/
|
|
205
|
+
detectDatabases(dependencies = {}) {
|
|
206
|
+
const dbMap = {
|
|
207
|
+
'pg': 'PostgreSQL',
|
|
208
|
+
'postgres': 'PostgreSQL',
|
|
209
|
+
'mysql2': 'MySQL',
|
|
210
|
+
'mysql': 'MySQL',
|
|
211
|
+
'sqlite3': 'SQLite',
|
|
212
|
+
'better-sqlite3': 'SQLite',
|
|
213
|
+
'mssql': 'SQL Server',
|
|
214
|
+
'tedious': 'SQL Server',
|
|
215
|
+
'oracledb': 'Oracle',
|
|
216
|
+
'mongoose': 'MongoDB',
|
|
217
|
+
'mongodb': 'MongoDB',
|
|
218
|
+
'redis': 'Redis',
|
|
219
|
+
'ioredis': 'Redis',
|
|
220
|
+
'prisma': 'Prisma (ORM)',
|
|
221
|
+
'@prisma/client': 'Prisma',
|
|
222
|
+
'typeorm': 'TypeORM',
|
|
223
|
+
'sequelize': 'Sequelize',
|
|
224
|
+
'knex': 'Knex.js',
|
|
225
|
+
'drizzle-orm': 'Drizzle ORM',
|
|
226
|
+
'objection': 'Objection.js',
|
|
227
|
+
'bookshelf': 'Bookshelf',
|
|
228
|
+
'waterline': 'Waterline',
|
|
229
|
+
'rethinkdb': 'RethinkDB',
|
|
230
|
+
'cassandra-driver': 'Cassandra',
|
|
231
|
+
'neo4j-driver': 'Neo4j'
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const detected = [];
|
|
235
|
+
for (const [pkg, name] of Object.entries(dbMap)) {
|
|
236
|
+
if (dependencies[pkg]) {
|
|
237
|
+
detected.push(name);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return detected;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Detect infrastructure from dependencies
|
|
246
|
+
* @param {object} dependencies - Dependencies object from package.json
|
|
247
|
+
* @returns {Array} Array of infrastructure names
|
|
248
|
+
*/
|
|
249
|
+
detectInfrastructure(dependencies = {}) {
|
|
250
|
+
const infraMap = {
|
|
251
|
+
// Cloud
|
|
252
|
+
'@aws-sdk/*': 'AWS SDK',
|
|
253
|
+
'@aws-sdk': 'AWS SDK',
|
|
254
|
+
'aws-sdk': 'AWS SDK v2',
|
|
255
|
+
'@azure/*': 'Azure SDK',
|
|
256
|
+
'@azure': 'Azure SDK',
|
|
257
|
+
'@google-cloud/*': 'Google Cloud SDK',
|
|
258
|
+
// Serverless
|
|
259
|
+
'serverless': 'Serverless Framework',
|
|
260
|
+
// Containers
|
|
261
|
+
'dockerode': 'Docker',
|
|
262
|
+
'@docker/cli': 'Docker',
|
|
263
|
+
// Monitoring/Logging
|
|
264
|
+
'winston': 'Winston (logging)',
|
|
265
|
+
'pino': 'Pino (logging)',
|
|
266
|
+
'bunyan': 'Bunyan (logging)',
|
|
267
|
+
'@sentry/node': 'Sentry',
|
|
268
|
+
'@sentry/*': 'Sentry',
|
|
269
|
+
'datadog-metrics': 'Datadog',
|
|
270
|
+
'newrelic': 'New Relic',
|
|
271
|
+
// Testing
|
|
272
|
+
'jest': 'Jest',
|
|
273
|
+
'vitest': 'Vitest',
|
|
274
|
+
'mocha': 'Mocha',
|
|
275
|
+
'chai': 'Chai',
|
|
276
|
+
'cypress': 'Cypress',
|
|
277
|
+
'playwright': 'Playwright',
|
|
278
|
+
'@testing-library/*': 'Testing Library',
|
|
279
|
+
'@testing-library': 'Testing Library',
|
|
280
|
+
'puppeteer': 'Puppeteer',
|
|
281
|
+
// CI/CD
|
|
282
|
+
'semantic-release': 'Semantic Release',
|
|
283
|
+
// Auth
|
|
284
|
+
'next-auth': 'NextAuth.js',
|
|
285
|
+
'@auth/core': 'Auth.js',
|
|
286
|
+
'passport': 'Passport',
|
|
287
|
+
'jsonwebtoken': 'JWT',
|
|
288
|
+
'bcrypt': 'bcrypt',
|
|
289
|
+
'argon2': 'Argon2'
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const detected = [];
|
|
293
|
+
for (const [pkg, name] of Object.entries(infraMap)) {
|
|
294
|
+
const pattern = pkg.replace('*', '.*');
|
|
295
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
296
|
+
if (Object.keys(dependencies).some(k => regex.test(k) || k.startsWith(pkg.replace('/*', '')))) {
|
|
297
|
+
if (!detected.includes(name)) {
|
|
298
|
+
detected.push(name);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return detected;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Detect languages from file structure
|
|
308
|
+
* @param {object} structure - Structure object from CodebaseAnalyzer
|
|
309
|
+
* @returns {object} Object with language counts
|
|
310
|
+
*/
|
|
311
|
+
detectLanguages(structure) {
|
|
312
|
+
const languageDetectors = {
|
|
313
|
+
'.ts': 'typescript',
|
|
314
|
+
'.tsx': 'typescript',
|
|
315
|
+
'.js': 'javascript',
|
|
316
|
+
'.jsx': 'javascript',
|
|
317
|
+
'.py': 'python',
|
|
318
|
+
'.rb': 'ruby',
|
|
319
|
+
'.go': 'go',
|
|
320
|
+
'.java': 'java',
|
|
321
|
+
'.php': 'php',
|
|
322
|
+
'.rs': 'rust',
|
|
323
|
+
'.cs': 'csharp',
|
|
324
|
+
'.swift': 'swift',
|
|
325
|
+
'.kt': 'kotlin',
|
|
326
|
+
'.scala': 'scala',
|
|
327
|
+
'.clj': 'clojure',
|
|
328
|
+
'.ex': 'elixir',
|
|
329
|
+
'.exs': 'elixir',
|
|
330
|
+
'.erl': 'erlang',
|
|
331
|
+
'.hs': 'haskell',
|
|
332
|
+
'.ml': 'ocaml',
|
|
333
|
+
'.r': 'r',
|
|
334
|
+
'.R': 'r',
|
|
335
|
+
'.sql': 'sql',
|
|
336
|
+
'.sh': 'shell',
|
|
337
|
+
'.bash': 'shell'
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const languages = {};
|
|
341
|
+
const files = structure?.files || [];
|
|
342
|
+
|
|
343
|
+
for (const file of files) {
|
|
344
|
+
const ext = path.extname(file);
|
|
345
|
+
const lang = languageDetectors[ext] || 'unknown';
|
|
346
|
+
languages[lang] = (languages[lang] || 0) + 1;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return languages;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Detect config files in root path
|
|
354
|
+
* @param {string} rootPath - Root directory to analyze
|
|
355
|
+
* @returns {object} Object with found config files
|
|
356
|
+
*/
|
|
357
|
+
detectConfigFiles(rootPath = this.rootPath) {
|
|
358
|
+
const configPatterns = {
|
|
359
|
+
typescript: 'tsconfig.json',
|
|
360
|
+
jsconfig: 'jsconfig.json',
|
|
361
|
+
vite: 'vite.config.js',
|
|
362
|
+
webpack: 'webpack.config.js',
|
|
363
|
+
rollup: 'rollup.config.js',
|
|
364
|
+
eslint: '.eslintrc.js',
|
|
365
|
+
prettier: '.prettierrc',
|
|
366
|
+
next: 'next.config.js',
|
|
367
|
+
nuxt: 'nuxt.config.js',
|
|
368
|
+
remix: 'remix.config.js',
|
|
369
|
+
prisma: 'prisma/schema.prisma',
|
|
370
|
+
jest: 'jest.config.js',
|
|
371
|
+
vitest: 'vitest.config.js',
|
|
372
|
+
cypress: 'cypress.config.js',
|
|
373
|
+
docker: 'Dockerfile',
|
|
374
|
+
dockerCompose: 'docker-compose.yml',
|
|
375
|
+
serverless: 'serverless.yml',
|
|
376
|
+
vercel: 'vercel.json',
|
|
377
|
+
netlify: 'netlify.toml',
|
|
378
|
+
heroku: 'Procfile',
|
|
379
|
+
github: '.github/workflows',
|
|
380
|
+
gitlab: '.gitlab-ci.yml',
|
|
381
|
+
circle: '.circleci/config.yml'
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const configs = {};
|
|
385
|
+
for (const [key, pattern] of Object.entries(configPatterns)) {
|
|
386
|
+
const fullPath = path.join(rootPath, pattern);
|
|
387
|
+
if (fs.existsSync(fullPath)) {
|
|
388
|
+
configs[key] = {
|
|
389
|
+
path: fullPath,
|
|
390
|
+
exists: true
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return configs;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
module.exports = { StackDetector };
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tech Debt Analyzer — Automated tech debt hotspot identification
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - detectDebtMarkers(rootPath): Finds TODO/FIXME/HACK/XXX/BUG/DEPRECATED comments with severity scores
|
|
6
|
+
* - analyzeDependencyRisk(rootPath): npm audit parsing for vulnerabilities
|
|
7
|
+
* - aggregateFindings(debtMarkers, complexityIssues, largeFiles, duplicates): Combines all findings with severity scores
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { execSync } = require('child_process');
|
|
13
|
+
|
|
14
|
+
class TechDebtAnalyzer {
|
|
15
|
+
constructor(rootPath) {
|
|
16
|
+
this.rootPath = rootPath;
|
|
17
|
+
this.patterns = [
|
|
18
|
+
{ marker: 'TODO', severity: 'Low', weight: 1 },
|
|
19
|
+
{ marker: 'FIXME', severity: 'Medium', weight: 2 },
|
|
20
|
+
{ marker: 'HACK', severity: 'Medium', weight: 2 },
|
|
21
|
+
{ marker: 'XXX', severity: 'High', weight: 3 },
|
|
22
|
+
{ marker: 'BUG', severity: 'High', weight: 3 },
|
|
23
|
+
{ marker: 'DEPRECATED', severity: 'Critical', weight: 4 },
|
|
24
|
+
{ marker: 'OPTIMIZE', severity: 'Low', weight: 1 },
|
|
25
|
+
{ marker: 'REFACTOR', severity: 'Medium', weight: 2 }
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Detect debt markers in codebase
|
|
31
|
+
* @param {string} rootPath - Root directory to analyze
|
|
32
|
+
* @returns {Array} Array of debt marker objects with file, line, marker, severity, weight, content
|
|
33
|
+
*/
|
|
34
|
+
detectDebtMarkers(rootPath = this.rootPath) {
|
|
35
|
+
const results = [];
|
|
36
|
+
const srcDirs = ['src', 'app', 'lib', 'commands', 'bin', 'agents', 'hooks'];
|
|
37
|
+
|
|
38
|
+
// Try grep first (Unix-like systems), fallback to pure JS for Windows
|
|
39
|
+
for (const pattern of this.patterns) {
|
|
40
|
+
for (const srcDir of srcDirs) {
|
|
41
|
+
const searchPath = path.join(rootPath, srcDir);
|
|
42
|
+
if (!fs.existsSync(searchPath)) continue;
|
|
43
|
+
|
|
44
|
+
// Try grep on Unix-like systems
|
|
45
|
+
try {
|
|
46
|
+
const output = execSync(
|
|
47
|
+
`grep -rn "${pattern.marker}" "${searchPath}" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.cjs" --include="*.mjs" 2>nul`,
|
|
48
|
+
{ cwd: rootPath, encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const lines = output.trim().split('\n');
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
if (!line.trim()) continue;
|
|
54
|
+
|
|
55
|
+
const parts = line.split(':');
|
|
56
|
+
if (parts.length < 3) continue;
|
|
57
|
+
|
|
58
|
+
const filePath = parts[0];
|
|
59
|
+
const lineNum = parseInt(parts[1], 10);
|
|
60
|
+
const content = parts.slice(2).join(':').trim();
|
|
61
|
+
|
|
62
|
+
results.push({
|
|
63
|
+
file: filePath,
|
|
64
|
+
line: lineNum,
|
|
65
|
+
marker: pattern.marker,
|
|
66
|
+
severity: pattern.severity,
|
|
67
|
+
weight: pattern.weight,
|
|
68
|
+
content: content.substring(0, 200),
|
|
69
|
+
age: null
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {
|
|
73
|
+
// grep not available or no matches, use fallback
|
|
74
|
+
if (err.code === 'ENOENT' || err.status === 1) {
|
|
75
|
+
// Use pure JavaScript fallback
|
|
76
|
+
const fallbackResults = this._detectDebtMarkersJS(searchPath, pattern);
|
|
77
|
+
results.push(...fallbackResults);
|
|
78
|
+
}
|
|
79
|
+
// Other errors are ignored (grep returns non-zero if no matches)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Sort by severity (Critical first)
|
|
85
|
+
results.sort((a, b) => b.weight - a.weight || a.file.localeCompare(b.file));
|
|
86
|
+
|
|
87
|
+
return results;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Pure JavaScript fallback for debt marker detection (Windows compatibility)
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
_detectDebtMarkersJS(dir, pattern) {
|
|
95
|
+
const results = [];
|
|
96
|
+
const fileExtensions = ['.ts', '.tsx', '.js', '.cjs', '.mjs'];
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
const fullPath = path.join(dir, entry.name);
|
|
102
|
+
|
|
103
|
+
if (entry.isDirectory()) {
|
|
104
|
+
// Recurse into subdirectory
|
|
105
|
+
results.push(...this._detectDebtMarkersJS(fullPath, pattern));
|
|
106
|
+
} else if (entry.isFile() && fileExtensions.includes(path.extname(entry.name))) {
|
|
107
|
+
try {
|
|
108
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
109
|
+
const lines = content.split('\n');
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < lines.length; i++) {
|
|
112
|
+
const line = lines[i];
|
|
113
|
+
if (line.includes(pattern.marker)) {
|
|
114
|
+
results.push({
|
|
115
|
+
file: fullPath,
|
|
116
|
+
line: i + 1,
|
|
117
|
+
marker: pattern.marker,
|
|
118
|
+
severity: pattern.severity,
|
|
119
|
+
weight: pattern.weight,
|
|
120
|
+
content: line.trim().substring(0, 200),
|
|
121
|
+
age: null
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (err) {
|
|
126
|
+
// Ignore read errors
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
// Ignore directory read errors
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Analyze dependency risk from npm audit
|
|
139
|
+
* @param {string} rootPath - Root directory to analyze
|
|
140
|
+
* @returns {Array} Array of vulnerability objects with type, package, severity, message, score
|
|
141
|
+
*/
|
|
142
|
+
analyzeDependencyRisk(rootPath = this.rootPath) {
|
|
143
|
+
const risks = [];
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const auditOutput = execSync('npm audit --json', {
|
|
147
|
+
cwd: rootPath,
|
|
148
|
+
encoding: 'utf8',
|
|
149
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const audit = JSON.parse(auditOutput);
|
|
153
|
+
const vulnerabilities = audit.vulnerabilities || {};
|
|
154
|
+
|
|
155
|
+
for (const [pkgName, vuln] of Object.entries(vulnerabilities)) {
|
|
156
|
+
const severity = vuln.severity || 'medium';
|
|
157
|
+
const score = severity === 'critical' ? 4 : severity === 'high' ? 3 : severity === 'medium' ? 2 : 1;
|
|
158
|
+
|
|
159
|
+
risks.push({
|
|
160
|
+
type: 'security',
|
|
161
|
+
package: pkgName,
|
|
162
|
+
severity: severity.charAt(0).toUpperCase() + severity.slice(1),
|
|
163
|
+
message: `${pkgName}@${vuln.vulnerable_versions || 'unknown'} has ${severity} vulnerability`,
|
|
164
|
+
score,
|
|
165
|
+
via: vuln.via || [],
|
|
166
|
+
recommendation: vuln.recommendation || 'Update to latest version'
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
} catch (err) {
|
|
170
|
+
// npm audit may fail or return no vulnerabilities
|
|
171
|
+
if (err.status !== 1) {
|
|
172
|
+
console.warn(`Warning: npm audit failed: ${err.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Sort by severity
|
|
177
|
+
risks.sort((a, b) => b.score - a.score);
|
|
178
|
+
|
|
179
|
+
return risks;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Aggregate all findings into a single report
|
|
184
|
+
* @param {Array} debtMarkers - Debt markers from detectDebtMarkers
|
|
185
|
+
* @param {Array} complexityIssues - Complexity issues from CodeComplexityAnalyzer
|
|
186
|
+
* @param {Array} largeFiles - Large files from CodeComplexityAnalyzer
|
|
187
|
+
* @param {Array} duplicates - Duplicate code from CodeComplexityAnalyzer
|
|
188
|
+
* @param {Array} dependencyRisks - Dependency risks from analyzeDependencyRisk
|
|
189
|
+
* @returns {Array} Aggregated findings sorted by score
|
|
190
|
+
*/
|
|
191
|
+
aggregateFindings(debtMarkers = [], complexityIssues = [], largeFiles = [], duplicates = [], dependencyRisks = []) {
|
|
192
|
+
const allFindings = [];
|
|
193
|
+
|
|
194
|
+
// Add debt markers
|
|
195
|
+
for (const marker of debtMarkers) {
|
|
196
|
+
allFindings.push({
|
|
197
|
+
...marker,
|
|
198
|
+
type: 'debt_marker',
|
|
199
|
+
score: marker.weight,
|
|
200
|
+
description: `${marker.marker}: ${marker.content}`
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Add complexity issues
|
|
205
|
+
for (const issue of complexityIssues) {
|
|
206
|
+
allFindings.push({
|
|
207
|
+
...issue,
|
|
208
|
+
type: 'complexity',
|
|
209
|
+
description: issue.message
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Add large files
|
|
214
|
+
for (const file of largeFiles) {
|
|
215
|
+
allFindings.push({
|
|
216
|
+
...file,
|
|
217
|
+
type: 'large_file',
|
|
218
|
+
description: `Large file: ${file.lines} lines, ${file.sizeKB}KB`
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Add duplicates
|
|
223
|
+
for (const dup of duplicates) {
|
|
224
|
+
allFindings.push({
|
|
225
|
+
...dup,
|
|
226
|
+
type: 'duplicate',
|
|
227
|
+
description: `Duplicate code in ${dup.fileCount} files`
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Add dependency risks
|
|
232
|
+
for (const risk of dependencyRisks) {
|
|
233
|
+
allFindings.push({
|
|
234
|
+
...risk,
|
|
235
|
+
type: 'dependency',
|
|
236
|
+
description: risk.message
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Sort by score descending
|
|
241
|
+
allFindings.sort((a, b) => b.score - a.score || a.file?.localeCompare(b.file) || 0);
|
|
242
|
+
|
|
243
|
+
return allFindings;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get summary of tech debt
|
|
248
|
+
* @param {Array} findings - Aggregated findings
|
|
249
|
+
* @returns {object} Summary object
|
|
250
|
+
*/
|
|
251
|
+
getSummary(findings = []) {
|
|
252
|
+
const summary = {
|
|
253
|
+
total: findings.length,
|
|
254
|
+
byType: {},
|
|
255
|
+
bySeverity: {
|
|
256
|
+
Critical: 0,
|
|
257
|
+
High: 0,
|
|
258
|
+
Medium: 0,
|
|
259
|
+
Low: 0
|
|
260
|
+
},
|
|
261
|
+
topIssues: []
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
for (const finding of findings) {
|
|
265
|
+
// Count by type
|
|
266
|
+
summary.byType[finding.type] = (summary.byType[finding.type] || 0) + 1;
|
|
267
|
+
|
|
268
|
+
// Count by severity
|
|
269
|
+
const severity = finding.severity || 'Medium';
|
|
270
|
+
if (summary.bySeverity[severity] !== undefined) {
|
|
271
|
+
summary.bySeverity[severity]++;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Get top 10 issues
|
|
276
|
+
summary.topIssues = findings.slice(0, 10);
|
|
277
|
+
|
|
278
|
+
return summary;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get tech debt by file
|
|
283
|
+
* @param {Array} findings - Aggregated findings
|
|
284
|
+
* @returns {object} Object mapping file paths to their issues
|
|
285
|
+
*/
|
|
286
|
+
getByFile(findings = []) {
|
|
287
|
+
const byFile = {};
|
|
288
|
+
|
|
289
|
+
for (const finding of findings) {
|
|
290
|
+
if (!finding.file) continue;
|
|
291
|
+
|
|
292
|
+
if (!byFile[finding.file]) {
|
|
293
|
+
byFile[finding.file] = {
|
|
294
|
+
file: finding.file,
|
|
295
|
+
issues: [],
|
|
296
|
+
totalScore: 0
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
byFile[finding.file].issues.push(finding);
|
|
301
|
+
byFile[finding.file].totalScore += finding.score || 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Sort by total score
|
|
305
|
+
return Object.values(byFile).sort((a, b) => b.totalScore - a.totalScore);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
module.exports = { TechDebtAnalyzer };
|