@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
|
@@ -1,428 +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
|
-
};
|
|
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
|
+
};
|