@howlil/ez-agents 3.5.0 → 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 -537
- 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 -333
- package/agents/ez-requirements-agent.md +377 -377
- 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/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 -52
- 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 -53
- package/commands/ez/progress.md +36 -36
- 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 +599 -2
- 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 -622
- 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 -238
- package/ez-agents/bin/lib/context-cache.cjs +154 -154
- package/ez-agents/bin/lib/context-errors.cjs +71 -71
- package/ez-agents/bin/lib/context-manager.cjs +220 -220
- 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/file-access.cjs +207 -207
- 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 -83
- package/ez-agents/bin/lib/git-utils.cjs +321 -321
- package/ez-agents/bin/lib/git-workflow-engine.cjs +1157 -1157
- package/ez-agents/bin/lib/health-check.cjs +162 -162
- package/ez-agents/bin/lib/index.cjs +2 -8
- package/ez-agents/bin/lib/init.cjs +0 -2
- package/ez-agents/bin/lib/lockfile-validator.cjs +227 -227
- package/ez-agents/bin/lib/log-rotation.cjs +71 -0
- package/ez-agents/bin/lib/logger.cjs +22 -47
- package/ez-agents/bin/lib/memory-compression.cjs +256 -256
- package/ez-agents/bin/lib/package-manager-detector.cjs +203 -203
- package/ez-agents/bin/lib/package-manager-executor.cjs +385 -385
- package/ez-agents/bin/lib/package-manager-service.cjs +216 -216
- 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 -614
- package/ez-agents/bin/lib/security-errors.cjs +62 -0
- package/ez-agents/bin/lib/session-chain.cjs +304 -304
- package/ez-agents/bin/lib/session-errors.cjs +81 -81
- package/ez-agents/bin/lib/session-export.cjs +251 -251
- package/ez-agents/bin/lib/session-import.cjs +262 -262
- package/ez-agents/bin/lib/session-manager.cjs +280 -280
- 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 -428
- package/ez-agents/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/ez-agents/bin/lib/url-fetch.cjs +170 -170
- 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 -118
- 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 -340
- package/ez-agents/references/tier-strategy.md +103 -103
- 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 -173
- 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 -68
- package/ez-agents/templates/handoff-protocol.md +294 -0
- package/ez-agents/templates/incident-runbook.md +205 -205
- 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 -133
- package/ez-agents/templates/research.md +552 -552
- package/ez-agents/templates/rollback-plan.md +201 -201
- 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 +844 -844
- 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 +124 -3
- package/ez-agents/workflows/help.md +42 -181
- package/ez-agents/workflows/hotfix.md +291 -291
- package/ez-agents/workflows/new-milestone.md +713 -713
- package/ez-agents/workflows/new-project.md +1089 -1107
- package/ez-agents/workflows/plan-phase.md +0 -40
- package/ez-agents/workflows/release.md +253 -253
- package/ez-agents/workflows/resume-session.md +215 -215
- 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 -71
- 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-observer-agent.md +0 -260
- package/agents/ez-plan-checker.md +0 -706
- package/agents/ez-research-synthesizer.md +0 -247
- package/agents/ez-scrum-master-agent.md +0 -242
- package/agents/ez-tech-lead-agent.md +0 -267
- 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/arch-review.md +0 -102
- 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/export-session.md +0 -79
- package/commands/ez/gather-requirements.md +0 -117
- package/commands/ez/git-workflow.md +0 -72
- package/commands/ez/health.md +0 -22
- package/commands/ez/hotfix.md +0 -120
- package/commands/ez/import-session.md +0 -82
- 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/list-sessions.md +0 -96
- package/commands/ez/package-manager.md +0 -316
- package/commands/ez/pause-work.md +0 -38
- package/commands/ez/plan-milestone-gaps.md +0 -34
- package/commands/ez/preflight.md +0 -79
- package/commands/ez/reapply-patches.md +0 -124
- package/commands/ez/release.md +0 -153
- package/commands/ez/remove-phase.md +0 -31
- package/commands/ez/research-phase.md +0 -190
- package/commands/ez/resume.md +0 -107
- package/commands/ez/set-profile.md +0 -34
- package/commands/ez/standup.md +0 -85
- 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/bin/lib/metrics-tracker.cjs +0 -406
- 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/arch-review.md +0 -54
- package/ez-agents/workflows/export-session.md +0 -255
- package/ez-agents/workflows/gather-requirements.md +0 -206
- package/ez-agents/workflows/import-session.md +0 -303
- package/ez-agents/workflows/research-phase.md +0 -74
- package/ez-agents/workflows/standup.md +0 -64
- 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,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Reporter — Aggregated analytics report generation
|
|
3
|
+
* Generates reports from events, NPS, funnels, and cohorts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
class AnalyticsReporter {
|
|
10
|
+
constructor(cwd) {
|
|
11
|
+
this.cwd = cwd || process.cwd();
|
|
12
|
+
this.reportsDir = path.join(this.cwd, '.planning', 'analytics', 'reports');
|
|
13
|
+
this.ensureDir();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate aggregated analytics report
|
|
18
|
+
* @param {Object} options - Report options
|
|
19
|
+
* @returns {Object} Analytics report
|
|
20
|
+
*/
|
|
21
|
+
generateReport(options = {}) {
|
|
22
|
+
const { calculateNPS } = require('./nps-tracker.cjs');
|
|
23
|
+
const { analyzeFunnel } = require('./funnel-analyzer.cjs');
|
|
24
|
+
const { AnalyticsCollector } = require('./analytics-collector.cjs');
|
|
25
|
+
|
|
26
|
+
const collector = new AnalyticsCollector(this.cwd);
|
|
27
|
+
const events = collector.getAllEvents();
|
|
28
|
+
const nps = calculateNPS(this.cwd);
|
|
29
|
+
|
|
30
|
+
// Group events by type
|
|
31
|
+
const eventsByType = {};
|
|
32
|
+
for (const event of events) {
|
|
33
|
+
if (!eventsByType[event.eventType]) {
|
|
34
|
+
eventsByType[event.eventType] = 0;
|
|
35
|
+
}
|
|
36
|
+
eventsByType[event.eventType]++;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
summary: {
|
|
42
|
+
totalEvents: events.length,
|
|
43
|
+
uniqueEventTypes: Object.keys(eventsByType).length,
|
|
44
|
+
npsScore: nps.score
|
|
45
|
+
},
|
|
46
|
+
events: {
|
|
47
|
+
byType: eventsByType,
|
|
48
|
+
recent: events.slice(-10)
|
|
49
|
+
},
|
|
50
|
+
nps,
|
|
51
|
+
recommendations: this.generateRecommendations(events, nps)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Save report to file
|
|
57
|
+
* @param {Object} report - Report to save
|
|
58
|
+
* @param {string} filename - Optional filename
|
|
59
|
+
* @returns {string} Path to saved report
|
|
60
|
+
*/
|
|
61
|
+
saveReport(report, filename) {
|
|
62
|
+
const reportFilename = filename || `analytics-${Date.now()}.json`;
|
|
63
|
+
const reportPath = path.join(this.reportsDir, reportFilename);
|
|
64
|
+
|
|
65
|
+
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
|
|
66
|
+
return reportPath;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Generate recommendations from analytics
|
|
71
|
+
* @param {Array} events - Events array
|
|
72
|
+
* @param {Object} nps - NPS data
|
|
73
|
+
* @returns {Array} Recommendations
|
|
74
|
+
*/
|
|
75
|
+
generateRecommendations(events, nps) {
|
|
76
|
+
const recommendations = [];
|
|
77
|
+
|
|
78
|
+
if (nps.score < 0) {
|
|
79
|
+
recommendations.push({
|
|
80
|
+
category: 'nps',
|
|
81
|
+
priority: 'high',
|
|
82
|
+
suggestion: 'NPS is negative — investigate detractor feedback'
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (events.length < 100) {
|
|
87
|
+
recommendations.push({
|
|
88
|
+
category: 'tracking',
|
|
89
|
+
priority: 'medium',
|
|
90
|
+
suggestion: 'Low event volume — ensure all key actions are tracked'
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return recommendations;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Ensure reports directory exists
|
|
99
|
+
*/
|
|
100
|
+
ensureDir() {
|
|
101
|
+
if (!fs.existsSync(this.reportsDir)) {
|
|
102
|
+
fs.mkdirSync(this.reportsDir, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Generate analytics report
|
|
109
|
+
* @param {Object} options - Report options
|
|
110
|
+
* @param {string} cwd - Working directory
|
|
111
|
+
* @returns {Object} Analytics report
|
|
112
|
+
*/
|
|
113
|
+
function generateReport(options = {}, cwd) {
|
|
114
|
+
const reporter = new AnalyticsReporter(cwd);
|
|
115
|
+
return reporter.generateReport(options);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Save analytics report
|
|
120
|
+
* @param {Object} report - Report to save
|
|
121
|
+
* @param {string} filename - Filename
|
|
122
|
+
* @param {string} cwd - Working directory
|
|
123
|
+
* @returns {string} Path to saved report
|
|
124
|
+
*/
|
|
125
|
+
function saveReport(report, filename, cwd) {
|
|
126
|
+
const reporter = new AnalyticsReporter(cwd);
|
|
127
|
+
return reporter.saveReport(report, filename);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = { AnalyticsReporter, generateReport, saveReport };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort Analyzer — Cohort-based usage pattern analysis
|
|
3
|
+
* Groups users by time/behavior and tracks retention
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
class CohortAnalyzer {
|
|
10
|
+
constructor(cwd) {
|
|
11
|
+
this.cwd = cwd || process.cwd();
|
|
12
|
+
this.cohortsPath = path.join(this.cwd, '.planning', 'analytics', 'cohorts.json');
|
|
13
|
+
this.ensureFile();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Define a cohort by date range
|
|
18
|
+
* @param {string} name - Cohort name
|
|
19
|
+
* @param {string} startDate - Start date (ISO)
|
|
20
|
+
* @param {string} endDate - End date (ISO)
|
|
21
|
+
* @param {Array} users - User IDs in cohort
|
|
22
|
+
*/
|
|
23
|
+
defineCohort(name, startDate, endDate, users) {
|
|
24
|
+
const cohorts = this.getCohorts();
|
|
25
|
+
|
|
26
|
+
cohorts[name] = {
|
|
27
|
+
startDate,
|
|
28
|
+
endDate,
|
|
29
|
+
users,
|
|
30
|
+
size: users.length,
|
|
31
|
+
createdAt: new Date().toISOString()
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
this.saveCohorts(cohorts);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Calculate retention for a cohort
|
|
39
|
+
* @param {string} cohortName - Cohort name
|
|
40
|
+
* @param {Array} periods - Time periods to analyze
|
|
41
|
+
* @returns {Object} Retention data
|
|
42
|
+
*/
|
|
43
|
+
calculateRetention(cohortName, periods = []) {
|
|
44
|
+
const cohorts = this.getCohorts();
|
|
45
|
+
const cohort = cohorts[cohortName];
|
|
46
|
+
|
|
47
|
+
if (!cohort) {
|
|
48
|
+
return { error: 'Cohort not found' };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const retention = [];
|
|
52
|
+
const initialSize = cohort.size;
|
|
53
|
+
|
|
54
|
+
for (const period of periods) {
|
|
55
|
+
const activeUsers = this.getActiveUsersInPeriod(cohortName, period);
|
|
56
|
+
retention.push({
|
|
57
|
+
period: period.name,
|
|
58
|
+
activeUsers: activeUsers.length,
|
|
59
|
+
retentionRate: initialSize > 0 ? Math.round((activeUsers.length / initialSize) * 100) : 0
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { cohort: cohortName, retention, initialSize };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get active users in a period
|
|
68
|
+
* @param {string} cohortName - Cohort name
|
|
69
|
+
* @param {Object} period - Period definition
|
|
70
|
+
* @returns {Array} Active user IDs
|
|
71
|
+
*/
|
|
72
|
+
getActiveUsersInPeriod(cohortName, period) {
|
|
73
|
+
// Placeholder - would track actual user activity
|
|
74
|
+
const cohorts = this.getCohorts();
|
|
75
|
+
const cohort = cohorts[cohortName];
|
|
76
|
+
if (!cohort) return [];
|
|
77
|
+
|
|
78
|
+
// Return all users for now (placeholder)
|
|
79
|
+
return cohort.users;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get all cohorts
|
|
84
|
+
* @returns {Object} All cohorts
|
|
85
|
+
*/
|
|
86
|
+
getCohorts() {
|
|
87
|
+
if (!fs.existsSync(this.cohortsPath)) return {};
|
|
88
|
+
return JSON.parse(fs.readFileSync(this.cohortsPath, 'utf8'));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Save cohorts
|
|
93
|
+
* @param {Object} cohorts - Cohorts to save
|
|
94
|
+
*/
|
|
95
|
+
saveCohorts(cohorts) {
|
|
96
|
+
fs.writeFileSync(this.cohortsPath, JSON.stringify(cohorts, null, 2), 'utf8');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Ensure cohorts file exists
|
|
101
|
+
*/
|
|
102
|
+
ensureFile() {
|
|
103
|
+
const dir = path.dirname(this.cohortsPath);
|
|
104
|
+
if (!fs.existsSync(dir)) {
|
|
105
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
if (!fs.existsSync(this.cohortsPath)) {
|
|
108
|
+
fs.writeFileSync(this.cohortsPath, '{}', 'utf8');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Define a cohort
|
|
115
|
+
* @param {string} name - Cohort name
|
|
116
|
+
* @param {string} startDate - Start date
|
|
117
|
+
* @param {string} endDate - End date
|
|
118
|
+
* @param {Array} users - User IDs
|
|
119
|
+
* @param {string} cwd - Working directory
|
|
120
|
+
*/
|
|
121
|
+
function defineCohort(name, startDate, endDate, users, cwd) {
|
|
122
|
+
const analyzer = new CohortAnalyzer(cwd);
|
|
123
|
+
return analyzer.defineCohort(name, startDate, endDate, users);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Calculate cohort retention
|
|
128
|
+
* @param {string} cohortName - Cohort name
|
|
129
|
+
* @param {Array} periods - Periods to analyze
|
|
130
|
+
* @param {string} cwd - Working directory
|
|
131
|
+
* @returns {Object} Retention data
|
|
132
|
+
*/
|
|
133
|
+
function calculateRetention(cohortName, periods, cwd) {
|
|
134
|
+
const analyzer = new CohortAnalyzer(cwd);
|
|
135
|
+
return analyzer.calculateRetention(cohortName, periods);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = { CohortAnalyzer, defineCohort, calculateRetention };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Funnel Analyzer — User funnel step tracking and drop-off analysis
|
|
3
|
+
* Tracks conversion through defined funnel steps
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
class FunnelAnalyzer {
|
|
10
|
+
constructor(cwd) {
|
|
11
|
+
this.cwd = cwd || process.cwd();
|
|
12
|
+
this.funnelPath = path.join(this.cwd, '.planning', 'analytics', 'funnels.json');
|
|
13
|
+
this.ensureFile();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Track user progression through funnel step
|
|
18
|
+
* @param {string} funnelName - Funnel name
|
|
19
|
+
* @param {string} userId - User ID
|
|
20
|
+
* @param {string} step - Current step name
|
|
21
|
+
*/
|
|
22
|
+
trackStep(funnelName, userId, step) {
|
|
23
|
+
const funnels = this.getFunnels();
|
|
24
|
+
|
|
25
|
+
if (!funnels[funnelName]) {
|
|
26
|
+
funnels[funnelName] = { steps: [], users: {} };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const funnel = funnels[funnelName];
|
|
30
|
+
|
|
31
|
+
// Add step if new
|
|
32
|
+
if (!funnel.steps.includes(step)) {
|
|
33
|
+
funnel.steps.push(step);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Track user progression
|
|
37
|
+
if (!funnel.users[userId]) {
|
|
38
|
+
funnel.users[userId] = [];
|
|
39
|
+
}
|
|
40
|
+
funnel.users[userId].push({ step, timestamp: new Date().toISOString() });
|
|
41
|
+
|
|
42
|
+
this.saveFunnels(funnels);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Analyze funnel conversion
|
|
47
|
+
* @param {string} funnelName - Funnel name
|
|
48
|
+
* @returns {Object} Funnel analysis { steps, dropOff }
|
|
49
|
+
*/
|
|
50
|
+
analyzeFunnel(funnelName) {
|
|
51
|
+
const funnels = this.getFunnels();
|
|
52
|
+
const funnel = funnels[funnelName];
|
|
53
|
+
|
|
54
|
+
if (!funnel || funnel.steps.length === 0) {
|
|
55
|
+
return { steps: [], dropOff: [], totalUsers: 0 };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const stepCounts = {};
|
|
59
|
+
const userSteps = {};
|
|
60
|
+
|
|
61
|
+
// Count users at each step
|
|
62
|
+
for (const [userId, steps] of Object.entries(funnel.users)) {
|
|
63
|
+
const uniqueSteps = [...new Set(steps.map(s => s.step))];
|
|
64
|
+
for (const step of uniqueSteps) {
|
|
65
|
+
stepCounts[step] = (stepCounts[step] || 0) + 1;
|
|
66
|
+
}
|
|
67
|
+
userSteps[userId] = uniqueSteps.length;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Calculate drop-off between steps
|
|
71
|
+
const dropOff = [];
|
|
72
|
+
for (let i = 0; i < funnel.steps.length - 1; i++) {
|
|
73
|
+
const current = stepCounts[funnel.steps[i]] || 0;
|
|
74
|
+
const next = stepCounts[funnel.steps[i + 1]] || 0;
|
|
75
|
+
dropOff.push({
|
|
76
|
+
from: funnel.steps[i],
|
|
77
|
+
to: funnel.steps[i + 1],
|
|
78
|
+
dropOff: current - next,
|
|
79
|
+
dropOffRate: current > 0 ? Math.round(((current - next) / current) * 100) : 0
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
steps: funnel.steps.map(step => ({
|
|
85
|
+
name: step,
|
|
86
|
+
users: stepCounts[step] || 0
|
|
87
|
+
})),
|
|
88
|
+
dropOff,
|
|
89
|
+
totalUsers: Object.keys(funnel.users).length
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get all funnels
|
|
95
|
+
* @returns {Object} All funnels
|
|
96
|
+
*/
|
|
97
|
+
getFunnels() {
|
|
98
|
+
if (!fs.existsSync(this.funnelPath)) return {};
|
|
99
|
+
return JSON.parse(fs.readFileSync(this.funnelPath, 'utf8'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Save funnels
|
|
104
|
+
* @param {Object} funnels - Funnels to save
|
|
105
|
+
*/
|
|
106
|
+
saveFunnels(funnels) {
|
|
107
|
+
fs.writeFileSync(this.funnelPath, JSON.stringify(funnels, null, 2), 'utf8');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Ensure funnels file exists
|
|
112
|
+
*/
|
|
113
|
+
ensureFile() {
|
|
114
|
+
const dir = path.dirname(this.funnelPath);
|
|
115
|
+
if (!fs.existsSync(dir)) {
|
|
116
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
if (!fs.existsSync(this.funnelPath)) {
|
|
119
|
+
fs.writeFileSync(this.funnelPath, '{}', 'utf8');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Track funnel step
|
|
126
|
+
* @param {string} funnelName - Funnel name
|
|
127
|
+
* @param {string} userId - User ID
|
|
128
|
+
* @param {string} step - Step name
|
|
129
|
+
* @param {string} cwd - Working directory
|
|
130
|
+
*/
|
|
131
|
+
function trackStep(funnelName, userId, step, cwd) {
|
|
132
|
+
const analyzer = new FunnelAnalyzer(cwd);
|
|
133
|
+
return analyzer.trackStep(funnelName, userId, step);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Analyze funnel
|
|
138
|
+
* @param {string} funnelName - Funnel name
|
|
139
|
+
* @param {string} cwd - Working directory
|
|
140
|
+
* @returns {Object} Funnel analysis
|
|
141
|
+
*/
|
|
142
|
+
function analyzeFunnel(funnelName, cwd) {
|
|
143
|
+
const analyzer = new FunnelAnalyzer(cwd);
|
|
144
|
+
return analyzer.analyzeFunnel(funnelName);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = { FunnelAnalyzer, trackStep, analyzeFunnel };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NPS Tracker — NPS survey prompt and score tracking
|
|
3
|
+
* Calculates Net Promoter Score from user responses
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
class NpsTracker {
|
|
10
|
+
constructor(cwd) {
|
|
11
|
+
this.cwd = cwd || process.cwd();
|
|
12
|
+
this.scoresPath = path.join(this.cwd, '.planning', 'analytics', 'nps-scores.json');
|
|
13
|
+
this.ensureFile();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Record an NPS score
|
|
18
|
+
* @param {number} score - Score 0-10
|
|
19
|
+
* @param {Object} metadata - Optional metadata
|
|
20
|
+
*/
|
|
21
|
+
recordScore(score, metadata = {}) {
|
|
22
|
+
if (score < 0 || score > 10) {
|
|
23
|
+
throw new Error('NPS score must be between 0 and 10');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const data = {
|
|
27
|
+
score,
|
|
28
|
+
category: this.categorizeScore(score),
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
...metadata
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const scores = this.getScores();
|
|
34
|
+
scores.push(data);
|
|
35
|
+
fs.writeFileSync(this.scoresPath, JSON.stringify(scores, null, 2), 'utf8');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Calculate current NPS
|
|
40
|
+
* @returns {Object} NPS result { score, promoters, passives, detractors }
|
|
41
|
+
*/
|
|
42
|
+
calculateNPS() {
|
|
43
|
+
const scores = this.getScores();
|
|
44
|
+
if (scores.length === 0) {
|
|
45
|
+
return { score: 0, promoters: 0, passives: 0, detractors: 0, total: 0 };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const promoters = scores.filter(s => s.score >= 9).length;
|
|
49
|
+
const passives = scores.filter(s => s.score >= 7 && s.score <= 8).length;
|
|
50
|
+
const detractors = scores.filter(s => s.score <= 6).length;
|
|
51
|
+
const total = scores.length;
|
|
52
|
+
|
|
53
|
+
const nps = Math.round(((promoters - detractors) / total) * 100);
|
|
54
|
+
|
|
55
|
+
return { score: nps, promoters, passives, detractors, total };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get NPS trend over time
|
|
60
|
+
* @returns {Array} Trend data
|
|
61
|
+
*/
|
|
62
|
+
getTrend() {
|
|
63
|
+
const scores = this.getScores();
|
|
64
|
+
const trend = [];
|
|
65
|
+
let cumulative = [];
|
|
66
|
+
|
|
67
|
+
for (const score of scores) {
|
|
68
|
+
cumulative.push(score);
|
|
69
|
+
const nps = this.calculateNPSFromScores(cumulative);
|
|
70
|
+
trend.push({
|
|
71
|
+
timestamp: score.timestamp,
|
|
72
|
+
nps: nps.score,
|
|
73
|
+
total: cumulative.length
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return trend;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Categorize score
|
|
82
|
+
* @param {number} score - Score 0-10
|
|
83
|
+
* @returns {string} Category
|
|
84
|
+
*/
|
|
85
|
+
categorizeScore(score) {
|
|
86
|
+
if (score >= 9) return 'promoter';
|
|
87
|
+
if (score >= 7) return 'passive';
|
|
88
|
+
return 'detractor';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Calculate NPS from array of scores
|
|
93
|
+
* @param {Array} scores - Array of score objects
|
|
94
|
+
* @returns {Object} NPS result
|
|
95
|
+
*/
|
|
96
|
+
calculateNPSFromScores(scores) {
|
|
97
|
+
const promoters = scores.filter(s => s.score >= 9).length;
|
|
98
|
+
const detractors = scores.filter(s => s.score <= 6).length;
|
|
99
|
+
const total = scores.length;
|
|
100
|
+
return { score: Math.round(((promoters - detractors) / total) * 100) };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get all scores
|
|
105
|
+
* @returns {Array} All scores
|
|
106
|
+
*/
|
|
107
|
+
getScores() {
|
|
108
|
+
if (!fs.existsSync(this.scoresPath)) return [];
|
|
109
|
+
return JSON.parse(fs.readFileSync(this.scoresPath, 'utf8'));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Ensure scores file exists
|
|
114
|
+
*/
|
|
115
|
+
ensureFile() {
|
|
116
|
+
const dir = path.dirname(this.scoresPath);
|
|
117
|
+
if (!fs.existsSync(dir)) {
|
|
118
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
if (!fs.existsSync(this.scoresPath)) {
|
|
121
|
+
fs.writeFileSync(this.scoresPath, '[]', 'utf8');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Record an NPS score
|
|
128
|
+
* @param {number} score - Score 0-10
|
|
129
|
+
* @param {Object} metadata - Metadata
|
|
130
|
+
* @param {string} cwd - Working directory
|
|
131
|
+
*/
|
|
132
|
+
function recordScore(score, metadata = {}, cwd) {
|
|
133
|
+
const tracker = new NpsTracker(cwd);
|
|
134
|
+
return tracker.recordScore(score, metadata);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Calculate current NPS
|
|
139
|
+
* @param {string} cwd - Working directory
|
|
140
|
+
* @returns {Object} NPS result
|
|
141
|
+
*/
|
|
142
|
+
function calculateNPS(cwd) {
|
|
143
|
+
const tracker = new NpsTracker(cwd);
|
|
144
|
+
return tracker.calculateNPS();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = { NpsTracker, recordScore, calculateNPS };
|