@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,428 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tier Manager — Multi-tier release strategy management
|
|
5
|
+
*
|
|
6
|
+
* Manages MVP / Medium / Enterprise tier definitions, validation,
|
|
7
|
+
* and promotion logic for /ez:release workflows.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// ─────────────────────────────────────────────
|
|
16
|
+
// Tier Definitions
|
|
17
|
+
// ─────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const TIER_DEFINITIONS = {
|
|
20
|
+
mvp: {
|
|
21
|
+
name: 'MVP',
|
|
22
|
+
label: 'Minimum Viable Product',
|
|
23
|
+
moscow_scope: ['must'],
|
|
24
|
+
coverage_threshold: 60,
|
|
25
|
+
git_strategy: 'trunk',
|
|
26
|
+
checklist_count: 6,
|
|
27
|
+
rollback_window_minutes: 30,
|
|
28
|
+
description: 'Core @must features only. Ship in hours.'
|
|
29
|
+
},
|
|
30
|
+
medium: {
|
|
31
|
+
name: 'Medium',
|
|
32
|
+
label: 'Production Ready',
|
|
33
|
+
moscow_scope: ['must', 'should'],
|
|
34
|
+
coverage_threshold: 80,
|
|
35
|
+
git_strategy: 'github-flow',
|
|
36
|
+
checklist_count: 18,
|
|
37
|
+
rollback_window_minutes: 15,
|
|
38
|
+
description: 'Must + Should features. Real users, proper testing.'
|
|
39
|
+
},
|
|
40
|
+
enterprise: {
|
|
41
|
+
name: 'Enterprise',
|
|
42
|
+
label: 'Compliance Grade',
|
|
43
|
+
moscow_scope: ['must', 'should', 'could'],
|
|
44
|
+
coverage_threshold: 95,
|
|
45
|
+
git_strategy: 'gitflow',
|
|
46
|
+
checklist_count: 30,
|
|
47
|
+
rollback_window_minutes: 5,
|
|
48
|
+
description: 'All MoSCoW priorities. Regulated industries, enterprise customers.'
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const TIER_ORDER = ['mvp', 'medium', 'enterprise'];
|
|
53
|
+
|
|
54
|
+
// ─────────────────────────────────────────────
|
|
55
|
+
// Tier Accessors
|
|
56
|
+
// ─────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get tier definition
|
|
60
|
+
* @param {string} tier - 'mvp' | 'medium' | 'enterprise'
|
|
61
|
+
* @returns {object}
|
|
62
|
+
*/
|
|
63
|
+
function getTier(tier) {
|
|
64
|
+
const def = TIER_DEFINITIONS[tier.toLowerCase()];
|
|
65
|
+
if (!def) {
|
|
66
|
+
throw new Error(`Unknown tier: ${tier}. Must be one of: ${TIER_ORDER.join(', ')}`);
|
|
67
|
+
}
|
|
68
|
+
return { ...def, id: tier.toLowerCase() };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get all tier definitions
|
|
73
|
+
* @returns {object[]}
|
|
74
|
+
*/
|
|
75
|
+
function getAllTiers() {
|
|
76
|
+
return TIER_ORDER.map(t => ({ id: t, ...TIER_DEFINITIONS[t] }));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if a tier is valid
|
|
81
|
+
* @param {string} tier
|
|
82
|
+
* @returns {boolean}
|
|
83
|
+
*/
|
|
84
|
+
function isValidTier(tier) {
|
|
85
|
+
return TIER_ORDER.includes(tier.toLowerCase());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the tier index (0=mvp, 1=medium, 2=enterprise)
|
|
90
|
+
* @param {string} tier
|
|
91
|
+
* @returns {number}
|
|
92
|
+
*/
|
|
93
|
+
function getTierIndex(tier) {
|
|
94
|
+
return TIER_ORDER.indexOf(tier.toLowerCase());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if target tier is a promotion from current tier
|
|
99
|
+
* @param {string} current
|
|
100
|
+
* @param {string} target
|
|
101
|
+
* @returns {boolean}
|
|
102
|
+
*/
|
|
103
|
+
function isPromotion(current, target) {
|
|
104
|
+
return getTierIndex(target) > getTierIndex(current);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get the tier below (for prerequisite checking)
|
|
109
|
+
* @param {string} tier
|
|
110
|
+
* @returns {string|null}
|
|
111
|
+
*/
|
|
112
|
+
function getPreviousTier(tier) {
|
|
113
|
+
const idx = getTierIndex(tier);
|
|
114
|
+
return idx > 0 ? TIER_ORDER[idx - 1] : null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ─────────────────────────────────────────────
|
|
118
|
+
// Git Strategy
|
|
119
|
+
// ─────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get git strategy for a tier
|
|
123
|
+
* @param {string} tier
|
|
124
|
+
* @returns {{ strategy: string, releaseBranchPrefix: string, targetBranch: string, syncBranch: string|null }}
|
|
125
|
+
*/
|
|
126
|
+
function getGitStrategy(tier) {
|
|
127
|
+
const def = getTier(tier);
|
|
128
|
+
|
|
129
|
+
const strategies = {
|
|
130
|
+
trunk: {
|
|
131
|
+
strategy: 'trunk',
|
|
132
|
+
releaseBranchPrefix: null,
|
|
133
|
+
targetBranch: 'main',
|
|
134
|
+
syncBranch: null,
|
|
135
|
+
description: 'Tag directly on main. No release branch.'
|
|
136
|
+
},
|
|
137
|
+
'github-flow': {
|
|
138
|
+
strategy: 'github-flow',
|
|
139
|
+
releaseBranchPrefix: 'release',
|
|
140
|
+
targetBranch: 'main',
|
|
141
|
+
syncBranch: null,
|
|
142
|
+
description: 'release/vX.Y.Z branch → PR → main'
|
|
143
|
+
},
|
|
144
|
+
gitflow: {
|
|
145
|
+
strategy: 'gitflow',
|
|
146
|
+
releaseBranchPrefix: 'release',
|
|
147
|
+
targetBranch: 'main',
|
|
148
|
+
syncBranch: 'develop',
|
|
149
|
+
description: 'release/vX.Y.Z from develop → main → tag → sync develop'
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
return strategies[def.git_strategy];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Generate release branch name for version
|
|
158
|
+
* @param {string} tier
|
|
159
|
+
* @param {string} version - semver without 'v' prefix
|
|
160
|
+
* @returns {string|null}
|
|
161
|
+
*/
|
|
162
|
+
function getReleaseBranchName(tier, version) {
|
|
163
|
+
const strategy = getGitStrategy(tier);
|
|
164
|
+
if (!strategy.releaseBranchPrefix) return null; // trunk-based
|
|
165
|
+
return `${strategy.releaseBranchPrefix}/v${version}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Generate hotfix branch name
|
|
170
|
+
* @param {string} name - slug for the fix
|
|
171
|
+
* @returns {string}
|
|
172
|
+
*/
|
|
173
|
+
function getHotfixBranchName(name) {
|
|
174
|
+
const slug = name.replace(/[^a-zA-Z0-9-_]/g, '-').replace(/-+/g, '-').toLowerCase();
|
|
175
|
+
return `hotfix/${slug}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ─────────────────────────────────────────────
|
|
179
|
+
// Coverage Validation
|
|
180
|
+
// ─────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Check if coverage meets tier threshold
|
|
184
|
+
* @param {string} tier
|
|
185
|
+
* @param {number} coveragePct
|
|
186
|
+
* @returns {{ passes: boolean, threshold: number, actual: number, gap: number }}
|
|
187
|
+
*/
|
|
188
|
+
function checkCoverage(tier, coveragePct) {
|
|
189
|
+
const def = getTier(tier);
|
|
190
|
+
const passes = coveragePct >= def.coverage_threshold;
|
|
191
|
+
return {
|
|
192
|
+
passes,
|
|
193
|
+
threshold: def.coverage_threshold,
|
|
194
|
+
actual: coveragePct,
|
|
195
|
+
gap: passes ? 0 : def.coverage_threshold - coveragePct
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ─────────────────────────────────────────────
|
|
200
|
+
// Feature Flag Helpers
|
|
201
|
+
// ─────────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get feature flags that should be enabled for a tier
|
|
205
|
+
* MVP: only must features (all other flags = false)
|
|
206
|
+
* Medium: must + should
|
|
207
|
+
* Enterprise: all
|
|
208
|
+
*
|
|
209
|
+
* @param {string} tier
|
|
210
|
+
* @returns {{ enabled_moscow: string[], disabled_moscow: string[] }}
|
|
211
|
+
*/
|
|
212
|
+
function getFeatureFlagConfig(tier) {
|
|
213
|
+
const def = getTier(tier);
|
|
214
|
+
const all = ['must', 'should', 'could'];
|
|
215
|
+
const enabled = def.moscow_scope;
|
|
216
|
+
const disabled = all.filter(m => !enabled.includes(m));
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
enabled_moscow: enabled,
|
|
220
|
+
disabled_moscow: disabled,
|
|
221
|
+
flag_config: {
|
|
222
|
+
ENABLE_SHOULD_FEATURES: enabled.includes('should'),
|
|
223
|
+
ENABLE_COULD_FEATURES: enabled.includes('could')
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ─────────────────────────────────────────────
|
|
229
|
+
// Config Integration
|
|
230
|
+
// ─────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Build release config section for .planning/config.json
|
|
234
|
+
* @param {string} currentTier
|
|
235
|
+
* @returns {object}
|
|
236
|
+
*/
|
|
237
|
+
function buildReleaseConfig(currentTier = 'mvp') {
|
|
238
|
+
return {
|
|
239
|
+
tier: currentTier,
|
|
240
|
+
tiers: {
|
|
241
|
+
mvp: {
|
|
242
|
+
moscow_scope: TIER_DEFINITIONS.mvp.moscow_scope,
|
|
243
|
+
coverage: TIER_DEFINITIONS.mvp.coverage_threshold,
|
|
244
|
+
git: TIER_DEFINITIONS.mvp.git_strategy,
|
|
245
|
+
checklist_items: TIER_DEFINITIONS.mvp.checklist_count
|
|
246
|
+
},
|
|
247
|
+
medium: {
|
|
248
|
+
moscow_scope: TIER_DEFINITIONS.medium.moscow_scope,
|
|
249
|
+
coverage: TIER_DEFINITIONS.medium.coverage_threshold,
|
|
250
|
+
git: TIER_DEFINITIONS.medium.git_strategy,
|
|
251
|
+
checklist_items: TIER_DEFINITIONS.medium.checklist_count
|
|
252
|
+
},
|
|
253
|
+
enterprise: {
|
|
254
|
+
moscow_scope: TIER_DEFINITIONS.enterprise.moscow_scope,
|
|
255
|
+
coverage: TIER_DEFINITIONS.enterprise.coverage_threshold,
|
|
256
|
+
git: TIER_DEFINITIONS.enterprise.git_strategy,
|
|
257
|
+
checklist_items: TIER_DEFINITIONS.enterprise.checklist_count
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Load current tier from config.json
|
|
265
|
+
* @param {string} configPath - Path to .planning/config.json
|
|
266
|
+
* @returns {string}
|
|
267
|
+
*/
|
|
268
|
+
function loadCurrentTier(configPath) {
|
|
269
|
+
try {
|
|
270
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
271
|
+
return (config.release && config.release.tier) || 'mvp';
|
|
272
|
+
} catch {
|
|
273
|
+
return 'mvp';
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Save current tier to config.json
|
|
279
|
+
* @param {string} configPath
|
|
280
|
+
* @param {string} tier
|
|
281
|
+
*/
|
|
282
|
+
function saveCurrentTier(configPath, tier) {
|
|
283
|
+
if (!isValidTier(tier)) throw new Error(`Invalid tier: ${tier}`);
|
|
284
|
+
|
|
285
|
+
let config = {};
|
|
286
|
+
try {
|
|
287
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
288
|
+
} catch {
|
|
289
|
+
// file doesn't exist yet
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!config.release) config.release = {};
|
|
293
|
+
config.release.tier = tier.toLowerCase();
|
|
294
|
+
if (!config.release.tiers) {
|
|
295
|
+
config.release = { ...config.release, ...buildReleaseConfig(tier) };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ─────────────────────────────────────────────
|
|
302
|
+
// Validation Summary
|
|
303
|
+
// ─────────────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Generate a tier validation summary
|
|
307
|
+
* @param {string} tier
|
|
308
|
+
* @param {object} checks - { coverage: number, secretsFound: number, auditPassed: boolean }
|
|
309
|
+
* @returns {{ valid: boolean, tier: string, blockers: string[], warnings: string[], summary: string }}
|
|
310
|
+
*/
|
|
311
|
+
function validateRelease(tier, checks = {}) {
|
|
312
|
+
const def = getTier(tier);
|
|
313
|
+
const blockers = [];
|
|
314
|
+
const warnings = [];
|
|
315
|
+
|
|
316
|
+
// Coverage check
|
|
317
|
+
if (checks.coverage !== undefined) {
|
|
318
|
+
const cov = checkCoverage(tier, checks.coverage);
|
|
319
|
+
if (!cov.passes) {
|
|
320
|
+
warnings.push(`Coverage ${checks.coverage}% is below ${tier} threshold (${def.coverage_threshold}%)`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Security checks
|
|
325
|
+
if (checks.secretsFound > 0) {
|
|
326
|
+
blockers.push(`${checks.secretsFound} potential secret(s) found in committed files`);
|
|
327
|
+
}
|
|
328
|
+
if (checks.auditPassed === false) {
|
|
329
|
+
blockers.push('npm audit found critical vulnerabilities');
|
|
330
|
+
}
|
|
331
|
+
if (checks.hasProdTodos) {
|
|
332
|
+
warnings.push('Production TODO/FIXME comments found in src/');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const valid = blockers.length === 0;
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
valid,
|
|
339
|
+
tier,
|
|
340
|
+
tierDef: def,
|
|
341
|
+
blockers,
|
|
342
|
+
warnings,
|
|
343
|
+
summary: valid
|
|
344
|
+
? `${def.name} release validated (${warnings.length} warnings)`
|
|
345
|
+
: `${def.name} release BLOCKED (${blockers.length} blockers)`
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ─────────────────────────────────────────────
|
|
350
|
+
// CLI Interface
|
|
351
|
+
// ─────────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
if (require.main === module) {
|
|
354
|
+
const args = process.argv.slice(2);
|
|
355
|
+
const cmd = args[0];
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
if (cmd === 'get') {
|
|
359
|
+
const tier = args[1];
|
|
360
|
+
if (!tier) { console.error('Usage: tier-manager.cjs get <tier>'); process.exit(1); }
|
|
361
|
+
console.log(JSON.stringify(getTier(tier), null, 2));
|
|
362
|
+
} else if (cmd === 'all') {
|
|
363
|
+
console.log(JSON.stringify(getAllTiers(), null, 2));
|
|
364
|
+
} else if (cmd === 'git-strategy') {
|
|
365
|
+
const tier = args[1];
|
|
366
|
+
if (!tier) { console.error('Usage: tier-manager.cjs git-strategy <tier>'); process.exit(1); }
|
|
367
|
+
console.log(JSON.stringify(getGitStrategy(tier), null, 2));
|
|
368
|
+
} else if (cmd === 'release-branch') {
|
|
369
|
+
const tier = args[1];
|
|
370
|
+
const version = args[2];
|
|
371
|
+
if (!tier || !version) { console.error('Usage: tier-manager.cjs release-branch <tier> <version>'); process.exit(1); }
|
|
372
|
+
const branch = getReleaseBranchName(tier, version);
|
|
373
|
+
console.log(JSON.stringify({ branch }));
|
|
374
|
+
} else if (cmd === 'check-coverage') {
|
|
375
|
+
const tier = args[1];
|
|
376
|
+
const coverage = parseFloat(args[2]);
|
|
377
|
+
if (!tier || isNaN(coverage)) { console.error('Usage: tier-manager.cjs check-coverage <tier> <pct>'); process.exit(1); }
|
|
378
|
+
console.log(JSON.stringify(checkCoverage(tier, coverage), null, 2));
|
|
379
|
+
} else if (cmd === 'build-config') {
|
|
380
|
+
const tier = args[1] || 'mvp';
|
|
381
|
+
console.log(JSON.stringify(buildReleaseConfig(tier), null, 2));
|
|
382
|
+
} else if (cmd === 'load-tier') {
|
|
383
|
+
const configPath = args[1] || '.planning/config.json';
|
|
384
|
+
console.log(JSON.stringify({ tier: loadCurrentTier(configPath) }));
|
|
385
|
+
} else if (cmd === 'save-tier') {
|
|
386
|
+
const tier = args[1];
|
|
387
|
+
const configPath = args[2] || '.planning/config.json';
|
|
388
|
+
if (!tier) { console.error('Usage: tier-manager.cjs save-tier <tier> [config-path]'); process.exit(1); }
|
|
389
|
+
saveCurrentTier(configPath, tier);
|
|
390
|
+
console.log(JSON.stringify({ saved: true, tier }));
|
|
391
|
+
} else if (cmd === 'validate') {
|
|
392
|
+
const tier = args[1];
|
|
393
|
+
if (!tier) { console.error('Usage: tier-manager.cjs validate <tier> [--coverage N]'); process.exit(1); }
|
|
394
|
+
const coverageIdx = args.indexOf('--coverage');
|
|
395
|
+
const checks = {
|
|
396
|
+
coverage: coverageIdx !== -1 ? parseFloat(args[coverageIdx + 1]) : undefined
|
|
397
|
+
};
|
|
398
|
+
console.log(JSON.stringify(validateRelease(tier, checks), null, 2));
|
|
399
|
+
} else {
|
|
400
|
+
console.error(`Unknown command: ${cmd}`);
|
|
401
|
+
console.error('Commands: get, all, git-strategy, release-branch, check-coverage, build-config, load-tier, save-tier, validate');
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
} catch (err) {
|
|
405
|
+
console.error(`Error: ${err.message}`);
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
module.exports = {
|
|
411
|
+
getTier,
|
|
412
|
+
getAllTiers,
|
|
413
|
+
isValidTier,
|
|
414
|
+
getTierIndex,
|
|
415
|
+
isPromotion,
|
|
416
|
+
getPreviousTier,
|
|
417
|
+
getGitStrategy,
|
|
418
|
+
getReleaseBranchName,
|
|
419
|
+
getHotfixBranchName,
|
|
420
|
+
checkCoverage,
|
|
421
|
+
getFeatureFlagConfig,
|
|
422
|
+
buildReleaseConfig,
|
|
423
|
+
loadCurrentTier,
|
|
424
|
+
saveCurrentTier,
|
|
425
|
+
validateRelease,
|
|
426
|
+
TIER_DEFINITIONS,
|
|
427
|
+
TIER_ORDER
|
|
428
|
+
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trade-off Analyzer — Generate trade-off analysis for architecture decisions
|
|
5
|
+
*
|
|
6
|
+
* Provides structured trade-off analysis with:
|
|
7
|
+
* - Options comparison (pros/cons)
|
|
8
|
+
* - Reversibility assessment
|
|
9
|
+
* - Long-term implications
|
|
10
|
+
* - Decision templates
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const { TradeOffAnalyzer } = require('./tradeoff-analyzer');
|
|
14
|
+
* const analyzer = new TradeOffAnalyzer();
|
|
15
|
+
* const analysis = analyzer.generateAnalysis(options, context);
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const Logger = require('./logger.cjs');
|
|
19
|
+
const logger = new Logger();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Trade-off analysis template
|
|
23
|
+
*/
|
|
24
|
+
const TRADEOFF_TEMPLATE = `## Trade-off Analysis
|
|
25
|
+
|
|
26
|
+
**Decision:** {decision}
|
|
27
|
+
|
|
28
|
+
**Context:**
|
|
29
|
+
- Project Phase: {project_phase}
|
|
30
|
+
- Team Size: {team_size}
|
|
31
|
+
- Deadline: {deadline}
|
|
32
|
+
- User Count: {user_count}
|
|
33
|
+
- Compliance: {compliance}
|
|
34
|
+
|
|
35
|
+
**Options Considered:**
|
|
36
|
+
|
|
37
|
+
{options_section}
|
|
38
|
+
|
|
39
|
+
**Decision:** {selected_option}
|
|
40
|
+
|
|
41
|
+
**Rationale:**
|
|
42
|
+
{rationale}
|
|
43
|
+
|
|
44
|
+
**Trade-offs Accepted:**
|
|
45
|
+
{tradeoffs_accepted}
|
|
46
|
+
|
|
47
|
+
**Reversibility:** {reversibility}
|
|
48
|
+
- {reversal_details}
|
|
49
|
+
|
|
50
|
+
**Review Date:** {review_date}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Trade-off Analyzer class
|
|
55
|
+
*/
|
|
56
|
+
class TradeOffAnalyzer {
|
|
57
|
+
constructor(options = {}) {
|
|
58
|
+
this.logger = logger;
|
|
59
|
+
this.template = options.template || TRADEOFF_TEMPLATE;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate trade-off analysis
|
|
64
|
+
* @param {Array} options - Options to analyze
|
|
65
|
+
* @param {Object} context - Project context
|
|
66
|
+
* @returns {string} Markdown analysis
|
|
67
|
+
*/
|
|
68
|
+
generateAnalysis(options, context = {}) {
|
|
69
|
+
const ctx = {
|
|
70
|
+
decision: context.decision || 'Architecture Decision',
|
|
71
|
+
project_phase: context.project_phase || 'Not specified',
|
|
72
|
+
team_size: context.team_size || 'Not specified',
|
|
73
|
+
deadline: context.deadline || 'Not specified',
|
|
74
|
+
user_count: context.user_count || 'Not specified',
|
|
75
|
+
compliance: context.compliance || 'Not specified'
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const optionsSection = this._generateOptionsSection(options);
|
|
79
|
+
const selectedOption = this._selectOption(options, context);
|
|
80
|
+
const rationale = this._generateRationale(selectedOption, context);
|
|
81
|
+
const tradeoffsAccepted = this._generateTradeoffs(selectedOption, options);
|
|
82
|
+
const reversibility = this.assessReversibility(selectedOption, context);
|
|
83
|
+
const reviewDate = this._calculateReviewDate(reversibility.level);
|
|
84
|
+
|
|
85
|
+
return this.template
|
|
86
|
+
.replace('{decision}', ctx.decision)
|
|
87
|
+
.replace('{project_phase}', ctx.project_phase)
|
|
88
|
+
.replace('{team_size}', ctx.team_size)
|
|
89
|
+
.replace('{deadline}', ctx.deadline)
|
|
90
|
+
.replace('{user_count}', ctx.user_count)
|
|
91
|
+
.replace('{compliance}', ctx.compliance)
|
|
92
|
+
.replace('{options_section}', optionsSection)
|
|
93
|
+
.replace('{selected_option}', selectedOption.name)
|
|
94
|
+
.replace('{rationale}', rationale)
|
|
95
|
+
.replace('{tradeoffs_accepted}', tradeoffsAccepted)
|
|
96
|
+
.replace('{reversibility}', reversibility.level)
|
|
97
|
+
.replace('{reversal_details}', reversibility.details)
|
|
98
|
+
.replace('{review_date}', reviewDate);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Assess reversibility of a decision
|
|
103
|
+
* @param {Object} option - Selected option
|
|
104
|
+
* @param {Object} context - Project context
|
|
105
|
+
* @returns {Object} { level, details, estimated_effort }
|
|
106
|
+
*/
|
|
107
|
+
assessReversibility(option, context = {}) {
|
|
108
|
+
const reversibilityFactors = {
|
|
109
|
+
'monolith': { level: 'Easy', effort: 'Low - refactor to modules' },
|
|
110
|
+
'modular-monolith': { level: 'Medium', effort: 'Medium - extract modules to services' },
|
|
111
|
+
'microservices': { level: 'Hard', effort: 'High - merge services, consolidate databases' },
|
|
112
|
+
'event-driven': { level: 'Medium', effort: 'Medium - revert to synchronous calls' },
|
|
113
|
+
'queue-based': { level: 'Easy', effort: 'Low - revert to synchronous processing' },
|
|
114
|
+
'caching': { level: 'Easy', effort: 'Low - disable cache layers' },
|
|
115
|
+
'api-gateway': { level: 'Medium', effort: 'Medium - route directly to services' }
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const patternKey = option.name?.toLowerCase().replace(/\s+/g, '-');
|
|
119
|
+
const factor = reversibilityFactors[patternKey] || {
|
|
120
|
+
level: 'Medium',
|
|
121
|
+
effort: 'Unknown - depends on implementation'
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Adjust based on implementation depth
|
|
125
|
+
if (context.implementation_stage === 'production') {
|
|
126
|
+
factor.level = factor.level === 'Easy' ? 'Medium' : 'Hard';
|
|
127
|
+
factor.effort = `${factor.effort} (production data migration needed)`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
level: factor.level,
|
|
132
|
+
details: factor.effort,
|
|
133
|
+
estimated_effort: this._estimateEffort(factor.level)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Generate options section
|
|
139
|
+
* @param {Array} options - Options to format
|
|
140
|
+
* @returns {string} Markdown section
|
|
141
|
+
* @private
|
|
142
|
+
*/
|
|
143
|
+
_generateOptionsSection(options) {
|
|
144
|
+
return options.map((opt, idx) => {
|
|
145
|
+
const pros = opt.pros?.map(p => ` - ${p}`).join('\n') || ' - Not specified';
|
|
146
|
+
const cons = opt.cons?.map(c => ` - ${c}`).join('\n') || ' - Not specified';
|
|
147
|
+
const implications = opt.long_term_implications?.map(i => ` - ${i}`).join('\n') || ' - Not specified';
|
|
148
|
+
|
|
149
|
+
return `### Option ${idx + 1}: ${opt.name}
|
|
150
|
+
|
|
151
|
+
**Pros:**
|
|
152
|
+
${pros}
|
|
153
|
+
|
|
154
|
+
**Cons:**
|
|
155
|
+
${cons}
|
|
156
|
+
|
|
157
|
+
**Long-term Implications:**
|
|
158
|
+
${implications}`;
|
|
159
|
+
}).join('\n\n');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Select best option based on context
|
|
164
|
+
* @param {Array} options - Options to choose from
|
|
165
|
+
* @param {Object} context - Project context
|
|
166
|
+
* @returns {Object} Selected option
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
169
|
+
_selectOption(options, context) {
|
|
170
|
+
if (options.length === 0) {
|
|
171
|
+
return { name: 'No options provided', pros: [], cons: [] };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (options.length === 1) {
|
|
175
|
+
return options[0];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Simple scoring based on context
|
|
179
|
+
const scores = options.map(opt => {
|
|
180
|
+
let score = 0;
|
|
181
|
+
|
|
182
|
+
// More pros = higher score
|
|
183
|
+
score += (opt.pros?.length || 0) * 2;
|
|
184
|
+
|
|
185
|
+
// Fewer cons = higher score
|
|
186
|
+
score -= (opt.cons?.length || 0);
|
|
187
|
+
|
|
188
|
+
// Context alignment
|
|
189
|
+
if (opt.context_fit?.includes(context.project_phase)) {
|
|
190
|
+
score += 10;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return { option: opt, score };
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
scores.sort((a, b) => b.score - a.score);
|
|
197
|
+
return scores[0].option;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Generate rationale for selected option
|
|
202
|
+
* @param {Object} option - Selected option
|
|
203
|
+
* @param {Object} context - Project context
|
|
204
|
+
* @returns {string} Rationale text
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
_generateRationale(option, context) {
|
|
208
|
+
const reasons = [];
|
|
209
|
+
|
|
210
|
+
if (option.pros?.length) {
|
|
211
|
+
reasons.push(`Key advantages: ${option.pros.slice(0, 3).join(', ')}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (option.context_fit?.includes(context.project_phase)) {
|
|
215
|
+
reasons.push(`Well-suited for ${context.project_phase} phase`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (option.alignment?.length) {
|
|
219
|
+
reasons.push(`Aligns with: ${option.alignment.join(', ')}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return reasons.join('. ') || 'Selected based on project requirements and constraints.';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Generate trade-offs accepted
|
|
227
|
+
* @param {Object} selected - Selected option
|
|
228
|
+
* @param {Array} options - All options
|
|
229
|
+
* @returns {string} Trade-offs text
|
|
230
|
+
* @private
|
|
231
|
+
*/
|
|
232
|
+
_generateTradeoffs(selected, options) {
|
|
233
|
+
const rejected = options.filter(o => o !== selected);
|
|
234
|
+
|
|
235
|
+
if (rejected.length === 0) {
|
|
236
|
+
return 'No alternative options were considered.';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return rejected.map(opt => {
|
|
240
|
+
const lostBenefits = opt.pros?.join(', ') || 'None';
|
|
241
|
+
return `- By not choosing ${opt.name}, losing: ${lostBenefits}`;
|
|
242
|
+
}).join('\n');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Calculate review date based on reversibility
|
|
247
|
+
* @param {string} level - Reversibility level
|
|
248
|
+
* @returns {string} Review date
|
|
249
|
+
* @private
|
|
250
|
+
*/
|
|
251
|
+
_calculateReviewDate(level) {
|
|
252
|
+
const months = {
|
|
253
|
+
'Easy': 6,
|
|
254
|
+
'Medium': 3,
|
|
255
|
+
'Hard': 1
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const reviewMonths = months[level] || 3;
|
|
259
|
+
const reviewDate = new Date();
|
|
260
|
+
reviewDate.setMonth(reviewDate.getMonth() + reviewMonths);
|
|
261
|
+
|
|
262
|
+
return reviewDate.toISOString().split('T')[0];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Estimate effort to reverse decision
|
|
267
|
+
* @param {string} level - Reversibility level
|
|
268
|
+
* @returns {string} Effort estimate
|
|
269
|
+
* @private
|
|
270
|
+
*/
|
|
271
|
+
_estimateEffort(level) {
|
|
272
|
+
const efforts = {
|
|
273
|
+
'Easy': '1-2 weeks',
|
|
274
|
+
'Medium': '2-6 weeks',
|
|
275
|
+
'Hard': '6+ weeks'
|
|
276
|
+
};
|
|
277
|
+
return efforts[level] || 'Unknown';
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
module.exports = {
|
|
282
|
+
TradeOffAnalyzer,
|
|
283
|
+
TRADEOFF_TEMPLATE
|
|
284
|
+
};
|