@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,439 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Discussion Synthesizer — Reads DISCUSSION.md, extracts consensus and blockers
|
|
5
|
+
*
|
|
6
|
+
* Parses the multi-agent DISCUSSION.md format to extract:
|
|
7
|
+
* - Hard blockers from any agent
|
|
8
|
+
* - Warnings and advisory notes
|
|
9
|
+
* - Consensus status (open | consensus-reached | needs-human)
|
|
10
|
+
* - Go/No-Go recommendation
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const { withLock } = require('./file-lock.cjs');
|
|
18
|
+
const { parseFrontmatter } = require('./frontmatter.cjs');
|
|
19
|
+
|
|
20
|
+
// ─────────────────────────────────────────────
|
|
21
|
+
// Parser
|
|
22
|
+
// ─────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parse DISCUSSION.md file
|
|
26
|
+
* @param {string} filePath
|
|
27
|
+
* @returns {{ frontmatter: object, sections: object[], consensus: object, blockers: string[], warnings: string[] }}
|
|
28
|
+
*/
|
|
29
|
+
function parseDiscussion(filePath) {
|
|
30
|
+
if (!fs.existsSync(filePath)) {
|
|
31
|
+
return {
|
|
32
|
+
found: false,
|
|
33
|
+
filePath,
|
|
34
|
+
frontmatter: {},
|
|
35
|
+
sections: [],
|
|
36
|
+
consensus: { status: 'open', goNoGo: 'GO', rationale: 'No discussion file — proceeding' },
|
|
37
|
+
blockers: [],
|
|
38
|
+
warnings: []
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
43
|
+
const lines = content.split('\n');
|
|
44
|
+
|
|
45
|
+
// Parse YAML frontmatter
|
|
46
|
+
const frontmatter = parseFrontmatter(content);
|
|
47
|
+
|
|
48
|
+
// Parse agent sections
|
|
49
|
+
const sections = parseAgentSections(lines);
|
|
50
|
+
|
|
51
|
+
// Extract blockers and warnings from all sections
|
|
52
|
+
const blockers = [];
|
|
53
|
+
const warnings = [];
|
|
54
|
+
|
|
55
|
+
for (const section of sections) {
|
|
56
|
+
const sectionBlockers = extractBlockers(section.content);
|
|
57
|
+
const sectionWarnings = extractWarnings(section.content);
|
|
58
|
+
blockers.push(...sectionBlockers.map(b => ({ agent: section.agent, text: b })));
|
|
59
|
+
warnings.push(...sectionWarnings.map(w => ({ agent: section.agent, text: w })));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Parse consensus section
|
|
63
|
+
const consensus = parseConsensus(sections);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
found: true,
|
|
67
|
+
filePath,
|
|
68
|
+
frontmatter,
|
|
69
|
+
sections,
|
|
70
|
+
consensus,
|
|
71
|
+
blockers,
|
|
72
|
+
warnings,
|
|
73
|
+
hasBlockers: blockers.length > 0,
|
|
74
|
+
hasWarnings: warnings.length > 0
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parse agent sections from DISCUSSION.md
|
|
80
|
+
* Returns array of { agent, heading, content }
|
|
81
|
+
*/
|
|
82
|
+
function parseAgentSections(lines) {
|
|
83
|
+
const sections = [];
|
|
84
|
+
let currentSection = null;
|
|
85
|
+
let currentContent = [];
|
|
86
|
+
|
|
87
|
+
const agentHeadings = [
|
|
88
|
+
{ pattern: /## Requirements Perspective/, agent: 'requirements' },
|
|
89
|
+
{ pattern: /## Tech Lead Perspective/, agent: 'tech-lead' },
|
|
90
|
+
{ pattern: /## Observer Perspective/, agent: 'observer' },
|
|
91
|
+
{ pattern: /## Scrum Master Perspective/, agent: 'scrum-master' },
|
|
92
|
+
{ pattern: /## Consensus/, agent: 'consensus' }
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < lines.length; i++) {
|
|
96
|
+
const line = lines[i];
|
|
97
|
+
|
|
98
|
+
// Check if line starts a known section
|
|
99
|
+
const matchedHeading = agentHeadings.find(h => h.pattern.test(line));
|
|
100
|
+
|
|
101
|
+
if (matchedHeading) {
|
|
102
|
+
// Save previous section
|
|
103
|
+
if (currentSection) {
|
|
104
|
+
sections.push({
|
|
105
|
+
agent: currentSection.agent,
|
|
106
|
+
heading: currentSection.heading,
|
|
107
|
+
content: currentContent.join('\n').trim()
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
currentSection = { agent: matchedHeading.agent, heading: line };
|
|
111
|
+
currentContent = [];
|
|
112
|
+
} else if (currentSection) {
|
|
113
|
+
currentContent.push(line);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Push last section
|
|
118
|
+
if (currentSection) {
|
|
119
|
+
sections.push({
|
|
120
|
+
agent: currentSection.agent,
|
|
121
|
+
heading: currentSection.heading,
|
|
122
|
+
content: currentContent.join('\n').trim()
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return sections;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Extract blocker statements from section content
|
|
131
|
+
* Looks for BLOCKER markers used by agents
|
|
132
|
+
*/
|
|
133
|
+
function extractBlockers(content) {
|
|
134
|
+
const blockers = [];
|
|
135
|
+
const lines = content.split('\n');
|
|
136
|
+
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
if (line.includes('🛑') || line.includes('BLOCKER') || line.match(/\*\*BLOCKER/i)) {
|
|
139
|
+
// Extract the description after the marker
|
|
140
|
+
const cleaned = line
|
|
141
|
+
.replace(/[🛑*]/g, '')
|
|
142
|
+
.replace(/BLOCKER\s*—?\s*/i, '')
|
|
143
|
+
.trim();
|
|
144
|
+
if (cleaned && cleaned.length > 3) {
|
|
145
|
+
blockers.push(cleaned);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return blockers;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Extract warning statements from section content
|
|
155
|
+
*/
|
|
156
|
+
function extractWarnings(content) {
|
|
157
|
+
const warnings = [];
|
|
158
|
+
const lines = content.split('\n');
|
|
159
|
+
|
|
160
|
+
for (const line of lines) {
|
|
161
|
+
if (line.includes('⚠️') || line.match(/\*\*WARNING/i)) {
|
|
162
|
+
const cleaned = line
|
|
163
|
+
.replace(/[⚠️*]/g, '')
|
|
164
|
+
.replace(/WARNING\s*—?\s*/i, '')
|
|
165
|
+
.trim();
|
|
166
|
+
if (cleaned && cleaned.length > 3) {
|
|
167
|
+
warnings.push(cleaned);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return warnings;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Parse consensus section for Go/No-Go status
|
|
177
|
+
*/
|
|
178
|
+
function parseConsensus(sections) {
|
|
179
|
+
const consensusSection = sections.find(s => s.agent === 'consensus');
|
|
180
|
+
|
|
181
|
+
if (!consensusSection) {
|
|
182
|
+
return { status: 'open', goNoGo: 'GO', rationale: 'No consensus section yet' };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const content = consensusSection.content;
|
|
186
|
+
|
|
187
|
+
// Extract Go/No-Go
|
|
188
|
+
let goNoGo = 'GO';
|
|
189
|
+
if (content.match(/NO-GO/i)) goNoGo = 'NO-GO';
|
|
190
|
+
else if (content.match(/HUMAN-NEEDED/i)) goNoGo = 'HUMAN-NEEDED';
|
|
191
|
+
else if (content.match(/^.*GO.*$/m)) goNoGo = 'GO';
|
|
192
|
+
|
|
193
|
+
// Extract status
|
|
194
|
+
let status = 'open';
|
|
195
|
+
if (content.match(/consensus-reached/i) || goNoGo !== 'open') status = 'consensus-reached';
|
|
196
|
+
if (goNoGo === 'HUMAN-NEEDED') status = 'needs-human';
|
|
197
|
+
|
|
198
|
+
// Extract rationale
|
|
199
|
+
const rationaleMatch = content.match(/### Rationale\n([^\n]+)/);
|
|
200
|
+
const rationale = rationaleMatch ? rationaleMatch[1].trim() : '';
|
|
201
|
+
|
|
202
|
+
return { status, goNoGo, rationale };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ─────────────────────────────────────────────
|
|
206
|
+
// Synthesis
|
|
207
|
+
// ─────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Synthesize discussion into orchestrator-ready decision
|
|
211
|
+
* @param {string} discussionPath - Path to DISCUSSION.md
|
|
212
|
+
* @returns {{ proceed: boolean, reason: string, blockers: object[], warnings: object[], score: object }}
|
|
213
|
+
*/
|
|
214
|
+
function synthesize(discussionPath) {
|
|
215
|
+
const discussion = parseDiscussion(discussionPath);
|
|
216
|
+
|
|
217
|
+
if (!discussion.found) {
|
|
218
|
+
return {
|
|
219
|
+
proceed: true,
|
|
220
|
+
reason: 'No discussion file — no pre-flight concerns',
|
|
221
|
+
blockers: [],
|
|
222
|
+
warnings: [],
|
|
223
|
+
agentsParticipated: []
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const hasBlockers = discussion.blockers.length > 0;
|
|
228
|
+
const consensusGoNoGo = discussion.consensus.goNoGo;
|
|
229
|
+
|
|
230
|
+
// Determine whether to proceed
|
|
231
|
+
let proceed = true;
|
|
232
|
+
let reason = '';
|
|
233
|
+
|
|
234
|
+
if (hasBlockers || consensusGoNoGo === 'NO-GO') {
|
|
235
|
+
proceed = false;
|
|
236
|
+
reason = hasBlockers
|
|
237
|
+
? `${discussion.blockers.length} blocker(s) must be resolved before execution`
|
|
238
|
+
: 'Consensus is NO-GO';
|
|
239
|
+
} else if (consensusGoNoGo === 'HUMAN-NEEDED') {
|
|
240
|
+
proceed = false;
|
|
241
|
+
reason = 'Human input required before proceeding';
|
|
242
|
+
} else {
|
|
243
|
+
proceed = true;
|
|
244
|
+
reason = discussion.warnings.length > 0
|
|
245
|
+
? `${discussion.warnings.length} warning(s) — proceeding with awareness`
|
|
246
|
+
: 'No blockers found — proceeding';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const agentsParticipated = discussion.sections
|
|
250
|
+
.filter(s => s.agent !== 'consensus')
|
|
251
|
+
.filter(s => !s.content.includes('{Populated') && s.content.trim().length > 20)
|
|
252
|
+
.map(s => s.agent);
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
proceed,
|
|
256
|
+
reason,
|
|
257
|
+
blockers: discussion.blockers,
|
|
258
|
+
warnings: discussion.warnings,
|
|
259
|
+
consensus: discussion.consensus,
|
|
260
|
+
agentsParticipated,
|
|
261
|
+
frontmatter: discussion.frontmatter
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Check if a discussion file needs updating (agents haven't written yet)
|
|
267
|
+
* @param {string} discussionPath
|
|
268
|
+
* @returns {{ needsObserver: boolean, needsTechLead: boolean, needsScrumMaster: boolean }}
|
|
269
|
+
*/
|
|
270
|
+
function checkParticipation(discussionPath) {
|
|
271
|
+
const discussion = parseDiscussion(discussionPath);
|
|
272
|
+
|
|
273
|
+
if (!discussion.found) {
|
|
274
|
+
return { needsObserver: true, needsTechLead: true, needsScrumMaster: true };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const populated = (agent) => {
|
|
278
|
+
const section = discussion.sections.find(s => s.agent === agent);
|
|
279
|
+
return section && !section.content.includes('{Populated') && section.content.trim().length > 20;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
needsObserver: !populated('observer'),
|
|
284
|
+
needsTechLead: !populated('tech-lead'),
|
|
285
|
+
needsScrumMaster: !populated('scrum-master'),
|
|
286
|
+
needsRequirements: !populated('requirements')
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Update consensus section in DISCUSSION.md
|
|
292
|
+
* @param {string} discussionPath
|
|
293
|
+
* @param {object} consensusData - { goNoGo, blockers, warnings, rationale }
|
|
294
|
+
*/
|
|
295
|
+
async function updateConsensus(discussionPath, consensusData) {
|
|
296
|
+
if (!fs.existsSync(discussionPath)) return false;
|
|
297
|
+
return withLock(discussionPath, async () => {
|
|
298
|
+
const content = fs.readFileSync(discussionPath, 'utf8');
|
|
299
|
+
|
|
300
|
+
const blockerList = consensusData.blockers.length > 0
|
|
301
|
+
? consensusData.blockers.map(b => `- 🛑 ${b.agent}: ${b.text}`).join('\n')
|
|
302
|
+
: 'None';
|
|
303
|
+
|
|
304
|
+
const warningList = consensusData.warnings.length > 0
|
|
305
|
+
? consensusData.warnings.slice(0, 5).map(w => `- ⚠️ ${w.agent}: ${w.text}`).join('\n')
|
|
306
|
+
: 'None';
|
|
307
|
+
|
|
308
|
+
const now = new Date().toISOString();
|
|
309
|
+
const status = consensusData.goNoGo === 'GO'
|
|
310
|
+
? 'consensus-reached'
|
|
311
|
+
: consensusData.goNoGo === 'HUMAN-NEEDED' ? 'needs-human' : 'consensus-reached';
|
|
312
|
+
|
|
313
|
+
const consensusSection = `## Consensus
|
|
314
|
+
|
|
315
|
+
> *Synthesized by orchestrator from above perspectives*
|
|
316
|
+
|
|
317
|
+
**Status:** ${status}
|
|
318
|
+
|
|
319
|
+
### Blockers
|
|
320
|
+
${blockerList}
|
|
321
|
+
|
|
322
|
+
### Key Warnings
|
|
323
|
+
${warningList}
|
|
324
|
+
|
|
325
|
+
### Go / No-Go
|
|
326
|
+
${consensusData.goNoGo} — ${consensusData.rationale}
|
|
327
|
+
|
|
328
|
+
### Rationale
|
|
329
|
+
${consensusData.rationale}
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
*Discussion opened: {timestamp}*
|
|
334
|
+
*Last updated: ${now}*`;
|
|
335
|
+
|
|
336
|
+
// Replace existing consensus section or append
|
|
337
|
+
let updated;
|
|
338
|
+
if (content.includes('## Consensus')) {
|
|
339
|
+
updated = content.replace(/## Consensus[\s\S]*$/, consensusSection);
|
|
340
|
+
} else {
|
|
341
|
+
updated = content + '\n\n' + consensusSection;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Also update frontmatter status
|
|
345
|
+
updated = updated.replace(/^status: .*$/m, `status: ${status}`);
|
|
346
|
+
|
|
347
|
+
fs.writeFileSync(discussionPath, updated, 'utf8');
|
|
348
|
+
return true;
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Format synthesis result as human-readable text
|
|
354
|
+
* @param {object} result - From synthesize()
|
|
355
|
+
* @returns {string}
|
|
356
|
+
*/
|
|
357
|
+
function formatSynthesis(result) {
|
|
358
|
+
const lines = [];
|
|
359
|
+
|
|
360
|
+
lines.push(`## Pre-Flight Discussion Summary`);
|
|
361
|
+
lines.push(`**Decision:** ${result.proceed ? '✓ GO — proceed to execution' : '✗ ' + (result.consensus && result.consensus.goNoGo === 'HUMAN-NEEDED' ? 'HUMAN-NEEDED' : 'NO-GO')}`);
|
|
362
|
+
lines.push(`**Reason:** ${result.reason}`);
|
|
363
|
+
lines.push(`**Agents participated:** ${result.agentsParticipated.join(', ') || 'none'}`);
|
|
364
|
+
|
|
365
|
+
if (result.blockers.length > 0) {
|
|
366
|
+
lines.push('');
|
|
367
|
+
lines.push('### Blockers (must resolve)');
|
|
368
|
+
for (const b of result.blockers) {
|
|
369
|
+
lines.push(`- 🛑 **${b.agent}:** ${b.text}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (result.warnings.length > 0) {
|
|
374
|
+
lines.push('');
|
|
375
|
+
lines.push('### Warnings (advisory)');
|
|
376
|
+
for (const w of result.warnings.slice(0, 5)) {
|
|
377
|
+
lines.push(`- ⚠️ **${w.agent}:** ${w.text}`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return lines.join('\n');
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ─────────────────────────────────────────────
|
|
385
|
+
// CLI Interface
|
|
386
|
+
// ─────────────────────────────────────────────
|
|
387
|
+
|
|
388
|
+
if (require.main === module) {
|
|
389
|
+
const args = process.argv.slice(2);
|
|
390
|
+
const cmd = args[0];
|
|
391
|
+
const discussionPath = args[1];
|
|
392
|
+
|
|
393
|
+
if (!cmd) {
|
|
394
|
+
console.error('Usage: discussion-synthesizer.cjs <synthesize|check-participation|update-consensus> <path> [options]');
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
(async () => {
|
|
399
|
+
try {
|
|
400
|
+
if (cmd === 'synthesize') {
|
|
401
|
+
if (!discussionPath) { console.error('Path required'); process.exit(1); }
|
|
402
|
+
const result = synthesize(discussionPath);
|
|
403
|
+
if (args.includes('--json')) {
|
|
404
|
+
console.log(JSON.stringify(result, null, 2));
|
|
405
|
+
} else {
|
|
406
|
+
console.log(formatSynthesis(result));
|
|
407
|
+
process.exit(result.proceed ? 0 : 1);
|
|
408
|
+
}
|
|
409
|
+
} else if (cmd === 'check-participation') {
|
|
410
|
+
if (!discussionPath) { console.error('Path required'); process.exit(1); }
|
|
411
|
+
const result = checkParticipation(discussionPath);
|
|
412
|
+
console.log(JSON.stringify(result, null, 2));
|
|
413
|
+
} else if (cmd === 'update-consensus') {
|
|
414
|
+
if (!discussionPath) { console.error('Path required'); process.exit(1); }
|
|
415
|
+
const dataArg = args[2];
|
|
416
|
+
if (!dataArg) { console.error('Consensus data JSON required'); process.exit(1); }
|
|
417
|
+
const data = JSON.parse(dataArg);
|
|
418
|
+
const ok = await updateConsensus(discussionPath, data);
|
|
419
|
+
console.log(JSON.stringify({ updated: ok }));
|
|
420
|
+
} else {
|
|
421
|
+
console.error(`Unknown command: ${cmd}`);
|
|
422
|
+
process.exit(1);
|
|
423
|
+
}
|
|
424
|
+
} catch (err) {
|
|
425
|
+
console.error(`Error: ${err.message}`);
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
})();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
module.exports = {
|
|
432
|
+
parseDiscussion,
|
|
433
|
+
synthesize,
|
|
434
|
+
checkParticipation,
|
|
435
|
+
updateConsensus,
|
|
436
|
+
formatSynthesis,
|
|
437
|
+
extractBlockers,
|
|
438
|
+
extractWarnings
|
|
439
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error Cache — Track recurring errors for deduplication
|
|
5
|
+
*
|
|
6
|
+
* Stores error fingerprints and counts occurrences to identify
|
|
7
|
+
* recurring issues vs. one-time failures.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
class ErrorCache {
|
|
11
|
+
/**
|
|
12
|
+
* Create an ErrorCache instance
|
|
13
|
+
*/
|
|
14
|
+
constructor() {
|
|
15
|
+
this.cache = new Map();
|
|
16
|
+
this.maxSize = 1000;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate a fingerprint for an error
|
|
21
|
+
* @param {Error} error - The error to fingerprint
|
|
22
|
+
* @returns {string} Error fingerprint
|
|
23
|
+
*/
|
|
24
|
+
fingerprint(error) {
|
|
25
|
+
if (!error) return 'unknown';
|
|
26
|
+
const name = error.name || 'Error';
|
|
27
|
+
const message = error.message || '';
|
|
28
|
+
const stack = error.stack || '';
|
|
29
|
+
const firstLine = stack.split('\n')[1] || '';
|
|
30
|
+
return `${name}:${message}:${firstLine}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Record an error occurrence
|
|
35
|
+
* @param {Error} error - The error to record
|
|
36
|
+
* @param {Object} context - Additional context
|
|
37
|
+
* @returns {string} Error fingerprint
|
|
38
|
+
*/
|
|
39
|
+
record(error, context = {}) {
|
|
40
|
+
const fp = this.fingerprint(error);
|
|
41
|
+
const entry = this.cache.get(fp);
|
|
42
|
+
|
|
43
|
+
if (entry) {
|
|
44
|
+
entry.count++;
|
|
45
|
+
entry.lastSeen = Date.now();
|
|
46
|
+
entry.context = context;
|
|
47
|
+
} else {
|
|
48
|
+
if (this.cache.size >= this.maxSize) {
|
|
49
|
+
// Remove oldest entry
|
|
50
|
+
const oldestKey = this.cache.keys().next().value;
|
|
51
|
+
this.cache.delete(oldestKey);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.cache.set(fp, {
|
|
55
|
+
count: 1,
|
|
56
|
+
firstSeen: Date.now(),
|
|
57
|
+
lastSeen: Date.now(),
|
|
58
|
+
error: {
|
|
59
|
+
name: error.name,
|
|
60
|
+
message: error.message,
|
|
61
|
+
stack: error.stack
|
|
62
|
+
},
|
|
63
|
+
context
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return fp;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if an error is recurring (seen more than once)
|
|
72
|
+
* @param {string} fingerprint - Error fingerprint
|
|
73
|
+
* @returns {boolean} True if error has been seen before
|
|
74
|
+
*/
|
|
75
|
+
isRecurring(fingerprint) {
|
|
76
|
+
const entry = this.cache.get(fingerprint);
|
|
77
|
+
return entry && entry.count > 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get error entry by fingerprint
|
|
82
|
+
* @param {string} fingerprint - Error fingerprint
|
|
83
|
+
* @returns {Object|undefined} Error entry
|
|
84
|
+
*/
|
|
85
|
+
get(fingerprint) {
|
|
86
|
+
return this.cache.get(fingerprint);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Clear the error cache
|
|
91
|
+
*/
|
|
92
|
+
clear() {
|
|
93
|
+
this.cache.clear();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get cache statistics
|
|
98
|
+
* @returns {Object} Cache statistics
|
|
99
|
+
*/
|
|
100
|
+
stats() {
|
|
101
|
+
let recurring = 0;
|
|
102
|
+
this.cache.forEach(entry => {
|
|
103
|
+
if (entry.count > 1) recurring++;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
total: this.cache.size,
|
|
108
|
+
recurring,
|
|
109
|
+
unique: this.cache.size - recurring
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = { ErrorCache };
|