@howlil/ez-agents 3.5.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +735 -537
- package/agents/ez-architect-agent.md +267 -0
- package/agents/ez-backend-agent.md +303 -0
- package/agents/ez-chief-strategist.md +271 -0
- package/agents/ez-codebase-mapper.md +770 -770
- package/agents/ez-context-manager.md +319 -0
- package/agents/ez-debugger.md +1255 -1255
- package/agents/ez-design-expert.md +347 -0
- package/agents/ez-devops-agent.md +331 -0
- package/agents/ez-executor.md +487 -487
- package/agents/ez-frontend-agent.md +322 -0
- package/agents/ez-phase-researcher.md +553 -553
- package/agents/ez-planner.md +1307 -1307
- package/agents/ez-product-engineer.md +435 -0
- package/agents/ez-project-researcher.md +629 -629
- package/agents/ez-qa-agent.md +320 -0
- package/agents/ez-release-agent.md +333 -333
- package/agents/ez-requirements-agent.md +377 -377
- package/agents/ez-roadmapper.md +650 -650
- package/agents/ez-technical-writer.md +551 -0
- package/agents/ez-ux-expert.md +393 -0
- package/agents/ez-verifier.md +579 -579
- package/bin/guards/autonomy-guard.cjs +346 -0
- package/bin/guards/context-budget-guard.cjs +278 -0
- package/bin/guards/hallucination-guard.cjs +380 -0
- package/bin/guards/hidden-state-guard.cjs +182 -0
- package/bin/guards/team-overhead-guard.cjs +266 -0
- package/bin/guards/tool-sprawl-guard.cjs +271 -0
- package/bin/lib/analytics/analytics-collector.cjs +86 -0
- package/bin/lib/analytics/analytics-reporter.cjs +130 -0
- package/bin/lib/analytics/cohort-analyzer.cjs +138 -0
- package/bin/lib/analytics/funnel-analyzer.cjs +147 -0
- package/bin/lib/analytics/nps-tracker.cjs +147 -0
- package/bin/lib/archetype-detector.cjs +289 -0
- package/bin/lib/assistant-adapter.cjs +361 -0
- package/bin/lib/audit-exec.cjs +175 -0
- package/bin/lib/auth.cjs +176 -0
- package/bin/lib/backup-service.cjs +422 -0
- package/bin/lib/bdd-validator.cjs +622 -0
- package/bin/lib/business-flow-mapper.cjs +429 -0
- package/bin/lib/circuit-breaker.cjs +276 -0
- package/bin/lib/code-complexity-analyzer.cjs +360 -0
- package/bin/lib/codebase-analyzer.cjs +241 -0
- package/bin/lib/commands.cjs +691 -0
- package/bin/lib/config.cjs +236 -0
- package/bin/lib/constraint-extractor.cjs +526 -0
- package/bin/lib/content-scanner.cjs +238 -0
- package/bin/lib/context-cache.cjs +154 -0
- package/bin/lib/context-compressor.cjs +102 -0
- package/bin/lib/context-deduplicator.cjs +105 -0
- package/bin/lib/context-errors.cjs +78 -0
- package/bin/lib/context-manager.cjs +338 -0
- package/bin/lib/context-metadata-tracker.cjs +140 -0
- package/bin/lib/context-relevance-scorer.cjs +99 -0
- package/bin/lib/core.cjs +507 -0
- package/bin/lib/cost-alerts.cjs +174 -0
- package/bin/lib/cost-tracker.cjs +275 -0
- package/bin/lib/crash-recovery.cjs +220 -0
- package/bin/lib/dependency-graph.cjs +319 -0
- package/bin/lib/deploy/deploy-audit-log.cjs +76 -0
- package/bin/lib/deploy/deploy-detector.cjs +69 -0
- package/bin/lib/deploy/deploy-env-manager.cjs +109 -0
- package/bin/lib/deploy/deploy-health-check.cjs +88 -0
- package/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
- package/bin/lib/deploy/deploy-rollback.cjs +72 -0
- package/bin/lib/deploy/deploy-runner.cjs +97 -0
- package/bin/lib/deploy/deploy-status.cjs +74 -0
- package/bin/lib/discussion-synthesizer.cjs +439 -0
- package/bin/lib/error-cache.cjs +114 -0
- package/bin/lib/error-registry.cjs +177 -0
- package/bin/lib/file-access.cjs +207 -0
- package/bin/lib/file-lock.cjs +236 -0
- package/bin/lib/finops/budget-enforcer.cjs +126 -0
- package/bin/lib/finops/cost-reporter.cjs +132 -0
- package/bin/lib/finops/finops-analyzer.cjs +112 -0
- package/bin/lib/finops/spot-manager.cjs +118 -0
- package/bin/lib/framework-detector.cjs +396 -0
- package/bin/lib/frontmatter.cjs +313 -0
- package/bin/lib/fs-utils.cjs +153 -0
- package/bin/lib/gate-executor.cjs +272 -0
- package/bin/lib/gates/README.md +374 -0
- package/bin/lib/gates/gate-01-requirement.cjs +303 -0
- package/bin/lib/gates/gate-02-architecture.cjs +555 -0
- package/bin/lib/gates/gate-03-code.cjs +635 -0
- package/bin/lib/gates/gate-04-security.cjs +829 -0
- package/bin/lib/git-errors.cjs +83 -0
- package/bin/lib/git-utils.cjs +321 -0
- package/bin/lib/git-workflow-engine.cjs +1157 -0
- package/bin/lib/health-check.cjs +227 -0
- package/bin/lib/index.cjs +279 -0
- package/bin/lib/init.cjs +725 -0
- package/bin/lib/lock-logger.cjs +194 -0
- package/bin/lib/lock-state.cjs +263 -0
- package/bin/lib/lockfile-validator.cjs +227 -0
- package/bin/lib/log-rotation.cjs +71 -0
- package/bin/lib/logger.cjs +125 -0
- package/bin/lib/memory-compression.cjs +256 -0
- package/bin/lib/milestone.cjs +247 -0
- package/bin/lib/model-provider.cjs +241 -0
- package/bin/lib/package-manager-detector.cjs +203 -0
- package/bin/lib/package-manager-executor.cjs +385 -0
- package/bin/lib/package-manager-service.cjs +216 -0
- package/bin/lib/perf/api-monitor.cjs +88 -0
- package/bin/lib/perf/db-optimizer.cjs +78 -0
- package/bin/lib/perf/frontend-performance.cjs +56 -0
- package/bin/lib/perf/perf-analyzer.cjs +77 -0
- package/bin/lib/perf/perf-baseline.cjs +102 -0
- package/bin/lib/perf/perf-reporter.cjs +117 -0
- package/bin/lib/perf/regression-detector.cjs +92 -0
- package/bin/lib/phase.cjs +963 -0
- package/bin/lib/planning-write.cjs +123 -0
- package/bin/lib/project-reporter.cjs +565 -0
- package/bin/lib/quality-gate.cjs +332 -0
- package/bin/lib/quality-metrics.cjs +324 -0
- package/bin/lib/recovery-manager.cjs +98 -0
- package/bin/lib/release-validator.cjs +617 -0
- package/bin/lib/retry.cjs +119 -0
- package/bin/lib/roadmap.cjs +309 -0
- package/bin/lib/safe-exec.cjs +173 -0
- package/bin/lib/safe-path.cjs +130 -0
- package/bin/lib/security-errors.cjs +62 -0
- package/bin/lib/session-chain.cjs +304 -0
- package/bin/lib/session-errors.cjs +81 -0
- package/bin/lib/session-export.cjs +251 -0
- package/bin/lib/session-import.cjs +262 -0
- package/bin/lib/session-manager.cjs +280 -0
- package/bin/lib/skill-context.cjs +148 -0
- package/bin/lib/skill-matcher.cjs +236 -0
- package/bin/lib/skill-registry.cjs +360 -0
- package/bin/lib/skill-resolver.cjs +449 -0
- package/bin/lib/skill-triggers.cjs +90 -0
- package/bin/lib/skill-validator.cjs +270 -0
- package/bin/lib/skill-versioning.cjs +355 -0
- package/bin/lib/stack-detector.cjs +399 -0
- package/bin/lib/state.cjs +736 -0
- package/bin/lib/tech-debt-analyzer.cjs +309 -0
- package/bin/lib/temp-file.cjs +239 -0
- package/bin/lib/template.cjs +223 -0
- package/bin/lib/test-file-lock.cjs +112 -0
- package/bin/lib/test-graceful.cjs +93 -0
- package/bin/lib/test-logger.cjs +60 -0
- package/bin/lib/test-safe-exec.cjs +38 -0
- package/bin/lib/test-safe-path.cjs +33 -0
- package/bin/lib/test-temp-file.cjs +125 -0
- package/bin/lib/tier-manager.cjs +428 -0
- package/bin/lib/timeout-exec.cjs +63 -0
- package/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/bin/lib/url-fetch.cjs +170 -0
- package/bin/lib/verify.cjs +863 -0
- package/bin/update.js +217 -214
- package/commands/deploy.cjs +53 -0
- package/commands/ez/add-tests.md +41 -41
- package/commands/ez/audit-milestone.md +36 -36
- package/commands/ez/complete-milestone.md +136 -136
- package/commands/ez/discuss-phase.md +90 -90
- package/commands/ez/execute-phase.md +52 -52
- package/commands/ez/help.md +22 -22
- package/commands/ez/map-codebase.md +71 -71
- package/commands/ez/new-milestone.md +44 -44
- package/commands/ez/new-project.md +51 -42
- package/commands/ez/plan-phase.md +53 -53
- package/commands/ez/progress.md +36 -36
- package/commands/ez/quick.md +45 -45
- package/commands/ez/resume-work.md +40 -40
- package/commands/ez/run-phase.md +580 -0
- package/commands/ez/settings.md +36 -36
- package/commands/ez/update.md +37 -37
- package/commands/ez/verify-work.md +402 -38
- package/commands/health-check.cjs +44 -0
- package/commands/rollback.cjs +47 -0
- package/ez-agents/bin/ez-tools.cjs +599 -2
- package/ez-agents/bin/guards/autonomy-guard.cjs +346 -0
- package/ez-agents/bin/guards/context-budget-guard.cjs +247 -0
- package/ez-agents/bin/guards/hallucination-guard.cjs +271 -0
- package/ez-agents/bin/guards/hidden-state-guard.cjs +182 -0
- package/ez-agents/bin/guards/team-overhead-guard.cjs +266 -0
- package/ez-agents/bin/guards/tool-sprawl-guard.cjs +271 -0
- package/ez-agents/bin/lib/analytics/analytics-collector.cjs +86 -0
- package/ez-agents/bin/lib/analytics/analytics-reporter.cjs +130 -0
- package/ez-agents/bin/lib/analytics/cohort-analyzer.cjs +138 -0
- package/ez-agents/bin/lib/analytics/funnel-analyzer.cjs +147 -0
- package/ez-agents/bin/lib/analytics/nps-tracker.cjs +147 -0
- package/ez-agents/bin/lib/archetype-detector.cjs +289 -0
- package/ez-agents/bin/lib/audit-exec.cjs +166 -167
- package/ez-agents/bin/lib/auth.cjs +176 -176
- package/ez-agents/bin/lib/backup-service.cjs +422 -0
- package/ez-agents/bin/lib/bdd-validator.cjs +622 -622
- package/ez-agents/bin/lib/business-flow-mapper.cjs +429 -0
- package/ez-agents/bin/lib/code-complexity-analyzer.cjs +360 -0
- package/ez-agents/bin/lib/codebase-analyzer.cjs +241 -0
- package/ez-agents/bin/lib/commands.cjs +685 -685
- package/ez-agents/bin/lib/config.cjs +41 -1
- package/ez-agents/bin/lib/constraint-extractor.cjs +526 -0
- package/ez-agents/bin/lib/content-scanner.cjs +238 -238
- package/ez-agents/bin/lib/context-cache.cjs +154 -154
- package/ez-agents/bin/lib/context-errors.cjs +71 -71
- package/ez-agents/bin/lib/context-manager.cjs +220 -220
- package/ez-agents/bin/lib/core.cjs +507 -512
- package/ez-agents/bin/lib/cost-tracker.cjs +243 -0
- package/ez-agents/bin/lib/crash-recovery.cjs +172 -0
- package/ez-agents/bin/lib/dependency-graph.cjs +319 -0
- package/ez-agents/bin/lib/deploy/deploy-audit-log.cjs +76 -0
- package/ez-agents/bin/lib/deploy/deploy-detector.cjs +69 -0
- package/ez-agents/bin/lib/deploy/deploy-env-manager.cjs +109 -0
- package/ez-agents/bin/lib/deploy/deploy-health-check.cjs +88 -0
- package/ez-agents/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
- package/ez-agents/bin/lib/deploy/deploy-rollback.cjs +72 -0
- package/ez-agents/bin/lib/deploy/deploy-runner.cjs +97 -0
- package/ez-agents/bin/lib/deploy/deploy-status.cjs +74 -0
- package/ez-agents/bin/lib/file-access.cjs +207 -207
- package/ez-agents/bin/lib/finops/budget-enforcer.cjs +126 -0
- package/ez-agents/bin/lib/finops/cost-reporter.cjs +132 -0
- package/ez-agents/bin/lib/finops/finops-analyzer.cjs +112 -0
- package/ez-agents/bin/lib/finops/spot-manager.cjs +118 -0
- package/ez-agents/bin/lib/framework-detector.cjs +396 -0
- package/ez-agents/bin/lib/frontmatter.cjs +3 -1
- package/ez-agents/bin/lib/gates/README.md +374 -0
- package/ez-agents/bin/lib/gates/gate-01-requirement.cjs +303 -0
- package/ez-agents/bin/lib/gates/gate-02-architecture.cjs +555 -0
- package/ez-agents/bin/lib/gates/gate-03-code.cjs +635 -0
- package/ez-agents/bin/lib/gates/gate-04-security.cjs +829 -0
- package/ez-agents/bin/lib/git-errors.cjs +83 -83
- package/ez-agents/bin/lib/git-utils.cjs +321 -321
- package/ez-agents/bin/lib/git-workflow-engine.cjs +1157 -1157
- package/ez-agents/bin/lib/health-check.cjs +162 -162
- package/ez-agents/bin/lib/index.cjs +2 -8
- package/ez-agents/bin/lib/init.cjs +0 -2
- package/ez-agents/bin/lib/lockfile-validator.cjs +227 -227
- package/ez-agents/bin/lib/log-rotation.cjs +71 -0
- package/ez-agents/bin/lib/logger.cjs +22 -47
- package/ez-agents/bin/lib/memory-compression.cjs +256 -256
- package/ez-agents/bin/lib/package-manager-detector.cjs +203 -203
- package/ez-agents/bin/lib/package-manager-executor.cjs +385 -385
- package/ez-agents/bin/lib/package-manager-service.cjs +216 -216
- package/ez-agents/bin/lib/perf/api-monitor.cjs +88 -0
- package/ez-agents/bin/lib/perf/db-optimizer.cjs +78 -0
- package/ez-agents/bin/lib/perf/frontend-performance.cjs +56 -0
- package/ez-agents/bin/lib/perf/perf-analyzer.cjs +77 -0
- package/ez-agents/bin/lib/perf/perf-baseline.cjs +102 -0
- package/ez-agents/bin/lib/perf/perf-reporter.cjs +117 -0
- package/ez-agents/bin/lib/perf/regression-detector.cjs +92 -0
- package/ez-agents/bin/lib/project-reporter.cjs +502 -0
- package/ez-agents/bin/lib/quality-gate.cjs +332 -0
- package/ez-agents/bin/lib/recovery-manager.cjs +98 -0
- package/ez-agents/bin/lib/release-validator.cjs +617 -614
- package/ez-agents/bin/lib/security-errors.cjs +62 -0
- package/ez-agents/bin/lib/session-chain.cjs +304 -304
- package/ez-agents/bin/lib/session-errors.cjs +81 -81
- package/ez-agents/bin/lib/session-export.cjs +251 -251
- package/ez-agents/bin/lib/session-import.cjs +262 -262
- package/ez-agents/bin/lib/session-manager.cjs +280 -280
- package/ez-agents/bin/lib/skill-context.cjs +148 -0
- package/ez-agents/bin/lib/skill-matcher.cjs +236 -0
- package/ez-agents/bin/lib/skill-registry.cjs +341 -0
- package/ez-agents/bin/lib/skill-resolver.cjs +449 -0
- package/ez-agents/bin/lib/skill-triggers.cjs +90 -0
- package/ez-agents/bin/lib/skill-validator.cjs +270 -0
- package/ez-agents/bin/lib/skill-versioning.cjs +355 -0
- package/ez-agents/bin/lib/stack-detector.cjs +399 -0
- package/ez-agents/bin/lib/tech-debt-analyzer.cjs +309 -0
- package/ez-agents/bin/lib/tier-manager.cjs +428 -428
- package/ez-agents/bin/lib/tradeoff-analyzer.cjs +284 -0
- package/ez-agents/bin/lib/url-fetch.cjs +170 -170
- package/ez-agents/bin/lib/verify.cjs +863 -863
- package/ez-agents/references/decimal-phase-calculation.md +65 -65
- package/ez-agents/references/git-integration.md +248 -248
- package/ez-agents/references/git-planning-commit.md +38 -38
- package/ez-agents/references/metrics-schema.md +118 -118
- package/ez-agents/references/model-profile-resolution.md +34 -34
- package/ez-agents/references/model-profiles.md +93 -93
- package/ez-agents/references/phase-argument-parsing.md +61 -61
- package/ez-agents/references/planning-config.md +340 -340
- package/ez-agents/references/tier-strategy.md +103 -103
- package/ez-agents/references/ui-brand.md +160 -160
- package/ez-agents/references/verification-patterns.md +612 -612
- package/ez-agents/templates/DEBUG.md +164 -164
- package/ez-agents/templates/UAT.md +247 -247
- package/ez-agents/templates/agent-output-format.md +404 -0
- package/ez-agents/templates/bdd-feature.md +173 -173
- package/ez-agents/templates/codebase/architecture.md +255 -255
- package/ez-agents/templates/codebase/structure.md +285 -285
- package/ez-agents/templates/copilot-instructions.md +7 -7
- package/ez-agents/templates/debug-subagent-prompt.md +91 -91
- package/ez-agents/templates/discovery.md +146 -146
- package/ez-agents/templates/discussion.md +68 -68
- package/ez-agents/templates/handoff-protocol.md +294 -0
- package/ez-agents/templates/incident-runbook.md +205 -205
- package/ez-agents/templates/mode-workflow-templates.md +301 -0
- package/ez-agents/templates/phase-prompt.md +610 -610
- package/ez-agents/templates/planner-subagent-prompt.md +117 -117
- package/ez-agents/templates/project.md +184 -184
- package/ez-agents/templates/release-checklist.md +136 -133
- package/ez-agents/templates/research.md +552 -552
- package/ez-agents/templates/rollback-plan.md +201 -201
- package/ez-agents/templates/security-user-setup.md +244 -0
- package/ez-agents/templates/skill-validation-rules.md +476 -0
- package/ez-agents/templates/state.md +180 -176
- package/ez-agents/templates/summary-complex.md +59 -59
- package/ez-agents/tests/gates/gate-01-02.test.cjs +812 -0
- package/ez-agents/tests/gates/gate-03-04.test.cjs +762 -0
- package/ez-agents/tests/gates/gate-05-validator.test.cjs +145 -0
- package/ez-agents/tests/gates/gate-06-docs-validator.test.cjs +244 -0
- package/ez-agents/tests/gates/gate-07-release-validator.test.cjs +219 -0
- package/ez-agents/tests/guards/context-budget-guard.test.cjs +145 -0
- package/ez-agents/tests/guards/edge-case-guards.test.cjs +238 -0
- package/ez-agents/tests/guards/hallucination-guard.test.cjs +124 -0
- package/ez-agents/workflows/audit-milestone.md +1 -1
- package/ez-agents/workflows/autonomous.md +844 -844
- package/ez-agents/workflows/complete-milestone.md +1 -1
- package/ez-agents/workflows/discuss-phase.md +1 -1
- package/ez-agents/workflows/execute-phase.md +124 -3
- package/ez-agents/workflows/help.md +42 -181
- package/ez-agents/workflows/hotfix.md +291 -291
- package/ez-agents/workflows/new-milestone.md +713 -713
- package/ez-agents/workflows/new-project.md +1089 -1107
- package/ez-agents/workflows/plan-phase.md +0 -40
- package/ez-agents/workflows/release.md +253 -253
- package/ez-agents/workflows/resume-session.md +215 -215
- package/ez-agents/workflows/run-phase.md +531 -0
- package/ez-agents/workflows/settings.md +2 -35
- package/hooks/dist/ez-check-update.js +81 -81
- package/hooks/dist/ez-context-monitor.js +148 -141
- package/hooks/dist/ez-statusline.js +115 -115
- package/package.json +78 -71
- package/scripts/fix-qwen-installation.js +144 -144
- package/agents/ez-integration-checker.md +0 -443
- package/agents/ez-nyquist-auditor.md +0 -176
- package/agents/ez-observer-agent.md +0 -260
- package/agents/ez-plan-checker.md +0 -706
- package/agents/ez-research-synthesizer.md +0 -247
- package/agents/ez-scrum-master-agent.md +0 -242
- package/agents/ez-tech-lead-agent.md +0 -267
- package/agents/ez-ui-auditor.md +0 -439
- package/agents/ez-ui-checker.md +0 -300
- package/agents/ez-ui-researcher.md +0 -353
- package/commands/ez/add-phase.md +0 -43
- package/commands/ez/add-todo.md +0 -47
- package/commands/ez/arch-review.md +0 -102
- package/commands/ez/auth.md +0 -87
- package/commands/ez/autonomous.md +0 -41
- package/commands/ez/check-todos.md +0 -45
- package/commands/ez/cleanup.md +0 -18
- package/commands/ez/debug.md +0 -168
- package/commands/ez/export-session.md +0 -79
- package/commands/ez/gather-requirements.md +0 -117
- package/commands/ez/git-workflow.md +0 -72
- package/commands/ez/health.md +0 -22
- package/commands/ez/hotfix.md +0 -120
- package/commands/ez/import-session.md +0 -82
- package/commands/ez/insert-phase.md +0 -32
- package/commands/ez/join-discord.md +0 -18
- package/commands/ez/list-phase-assumptions.md +0 -46
- package/commands/ez/list-sessions.md +0 -96
- package/commands/ez/package-manager.md +0 -316
- package/commands/ez/pause-work.md +0 -38
- package/commands/ez/plan-milestone-gaps.md +0 -34
- package/commands/ez/preflight.md +0 -79
- package/commands/ez/reapply-patches.md +0 -124
- package/commands/ez/release.md +0 -153
- package/commands/ez/remove-phase.md +0 -31
- package/commands/ez/research-phase.md +0 -190
- package/commands/ez/resume.md +0 -107
- package/commands/ez/set-profile.md +0 -34
- package/commands/ez/standup.md +0 -85
- package/commands/ez/stats.md +0 -18
- package/commands/ez/ui-phase.md +0 -34
- package/commands/ez/ui-review.md +0 -32
- package/commands/ez/validate-phase.md +0 -35
- package/ez-agents/bin/lib/metrics-tracker.cjs +0 -406
- package/ez-agents/templates/UI-SPEC.md +0 -100
- package/ez-agents/templates/VALIDATION.md +0 -76
- package/ez-agents/templates/context.md +0 -352
- package/ez-agents/templates/verification-report.md +0 -322
- package/ez-agents/workflows/arch-review.md +0 -54
- package/ez-agents/workflows/export-session.md +0 -255
- package/ez-agents/workflows/gather-requirements.md +0 -206
- package/ez-agents/workflows/import-session.md +0 -303
- package/ez-agents/workflows/research-phase.md +0 -74
- package/ez-agents/workflows/standup.md +0 -64
- package/ez-agents/workflows/ui-phase.md +0 -290
- package/ez-agents/workflows/ui-review.md +0 -157
- package/ez-agents/workflows/validate-phase.md +0 -167
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Log Rotation Utility
|
|
5
|
+
*
|
|
6
|
+
* Automatically deletes EZ Agents logs older than 7 days to prevent git spam.
|
|
7
|
+
* Run this weekly or add to CI/CD cleanup job.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node ez-agents/bin/lib/log-rotation.cjs [--dry-run]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const LOGS_DIR = path.join(process.cwd(), '.planning', 'logs');
|
|
16
|
+
const MAX_AGE_DAYS = 7;
|
|
17
|
+
const DRY_RUN = process.argv.includes('--dry-run');
|
|
18
|
+
|
|
19
|
+
function rotateLogs() {
|
|
20
|
+
if (!fs.existsSync(LOGS_DIR)) {
|
|
21
|
+
console.log('Logs directory not found:', LOGS_DIR);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const cutoff = new Date();
|
|
26
|
+
cutoff.setDate(cutoff.getDate() - MAX_AGE_DAYS);
|
|
27
|
+
|
|
28
|
+
const files = fs.readdirSync(LOGS_DIR);
|
|
29
|
+
const logFiles = files.filter(f => f.endsWith('.log'));
|
|
30
|
+
|
|
31
|
+
let deleted = 0;
|
|
32
|
+
let kept = 0;
|
|
33
|
+
let totalSize = 0;
|
|
34
|
+
|
|
35
|
+
logFiles.forEach(file => {
|
|
36
|
+
const filePath = path.join(LOGS_DIR, file);
|
|
37
|
+
const stats = fs.statSync(filePath);
|
|
38
|
+
|
|
39
|
+
if (stats.mtime < cutoff) {
|
|
40
|
+
if (DRY_RUN) {
|
|
41
|
+
console.log(`[DRY-RUN] Would delete: ${file} (${formatBytes(stats.size)})`);
|
|
42
|
+
} else {
|
|
43
|
+
fs.unlinkSync(filePath);
|
|
44
|
+
console.log(`Deleted: ${file}`);
|
|
45
|
+
}
|
|
46
|
+
deleted++;
|
|
47
|
+
totalSize += stats.size;
|
|
48
|
+
} else {
|
|
49
|
+
kept++;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
console.log(`\n${DRY_RUN ? '[DRY-RUN] ' : ''}Log Rotation Complete`);
|
|
54
|
+
console.log(` Deleted: ${deleted} files (${formatBytes(totalSize)})`);
|
|
55
|
+
console.log(` Kept: ${kept} files (last ${MAX_AGE_DAYS} days)`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatBytes(bytes) {
|
|
59
|
+
if (bytes === 0) return '0 Bytes';
|
|
60
|
+
const k = 1024;
|
|
61
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
62
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
63
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Run if called directly
|
|
67
|
+
if (require.main === module) {
|
|
68
|
+
rotateLogs();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { rotateLogs };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EZ Logger — Centralized logging module for EZ workflow
|
|
5
|
+
*
|
|
6
|
+
* Provides structured logging with levels (ERROR, WARN, INFO, DEBUG)
|
|
7
|
+
* Logs to console only (no file logging)
|
|
8
|
+
* Replaces silent catch {} blocks with proper error logging
|
|
9
|
+
* Integrated with ErrorCache for recurring error detection
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const Logger = require('./logger.cjs');
|
|
13
|
+
* const logger = new Logger();
|
|
14
|
+
* logger.error('Something failed', { context: 'details' });
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Lazy-load ErrorCache to avoid circular dependencies
|
|
18
|
+
let ErrorCacheInstance = null;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get or create ErrorCache singleton
|
|
22
|
+
* @returns {Object} ErrorCache instance
|
|
23
|
+
*/
|
|
24
|
+
function getErrorCache() {
|
|
25
|
+
if (!ErrorCacheInstance) {
|
|
26
|
+
try {
|
|
27
|
+
const { ErrorCache } = require('./error-cache.cjs');
|
|
28
|
+
ErrorCacheInstance = new ErrorCache();
|
|
29
|
+
} catch (err) {
|
|
30
|
+
// ErrorCache not available - continue without it
|
|
31
|
+
ErrorCacheInstance = null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return ErrorCacheInstance;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class Logger {
|
|
38
|
+
/**
|
|
39
|
+
* Create a Logger instance
|
|
40
|
+
*/
|
|
41
|
+
constructor() {
|
|
42
|
+
// No file logging - console only
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Write a log entry to console
|
|
47
|
+
* @param {string} level - Log level (ERROR, WARN, INFO, DEBUG)
|
|
48
|
+
* @param {string} message - Log message
|
|
49
|
+
* @param {Object} context - Additional context data
|
|
50
|
+
*/
|
|
51
|
+
log(level, message, context = {}) {
|
|
52
|
+
const entry = {
|
|
53
|
+
timestamp: new Date().toISOString(),
|
|
54
|
+
level,
|
|
55
|
+
message,
|
|
56
|
+
context,
|
|
57
|
+
pid: process.pid
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Always output to console
|
|
61
|
+
if (level === 'ERROR') {
|
|
62
|
+
console.error(`[EZ ${level}] ${message}`);
|
|
63
|
+
} else if (level === 'WARN') {
|
|
64
|
+
console.warn(`[EZ ${level}] ${message}`);
|
|
65
|
+
} else if (process.env.DEBUG === 'ez-agents') {
|
|
66
|
+
// Only output INFO/DEBUG in debug mode
|
|
67
|
+
console.log(`[EZ ${level}] ${message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Log an ERROR level message
|
|
73
|
+
* Records error to cache for recurring detection
|
|
74
|
+
* @param {string} msg - Error message
|
|
75
|
+
* @param {Object} ctx - Additional context (can include error object)
|
|
76
|
+
*/
|
|
77
|
+
error(msg, ctx) {
|
|
78
|
+
// Record to error cache if error object provided
|
|
79
|
+
if (ctx && ctx.error instanceof Error) {
|
|
80
|
+
const cache = getErrorCache();
|
|
81
|
+
if (cache) {
|
|
82
|
+
const fingerprint = cache.record(ctx.error, ctx);
|
|
83
|
+
if (cache.isRecurring(fingerprint)) {
|
|
84
|
+
const entry = cache.get(fingerprint);
|
|
85
|
+
console.warn(`[EZ RECURRING] (${entry.count}x): ${msg}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.log('ERROR', msg, ctx);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Log a WARN level message
|
|
95
|
+
* @param {string} msg - Warning message
|
|
96
|
+
* @param {Object} ctx - Additional context
|
|
97
|
+
*/
|
|
98
|
+
warn(msg, ctx) {
|
|
99
|
+
this.log('WARN', msg, ctx);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Log an INFO level message
|
|
104
|
+
* @param {string} msg - Info message
|
|
105
|
+
* @param {Object} ctx - Additional context
|
|
106
|
+
*/
|
|
107
|
+
info(msg, ctx) {
|
|
108
|
+
this.log('INFO', msg, ctx);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Log a DEBUG level message
|
|
113
|
+
* @param {string} msg - Debug message
|
|
114
|
+
* @param {Object} ctx - Additional context
|
|
115
|
+
*/
|
|
116
|
+
debug(msg, ctx) {
|
|
117
|
+
this.log('DEBUG', msg, ctx);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Singleton instance for default usage
|
|
122
|
+
const defaultLogger = new Logger();
|
|
123
|
+
|
|
124
|
+
module.exports = Logger;
|
|
125
|
+
module.exports.defaultLogger = defaultLogger;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Memory Compression — Compress long session transcripts
|
|
5
|
+
*
|
|
6
|
+
* Reduces session size by keeping first N and last M messages
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { defaultLogger: logger } = require('./logger.cjs');
|
|
10
|
+
|
|
11
|
+
class MemoryCompression {
|
|
12
|
+
/**
|
|
13
|
+
* Create a MemoryCompression instance
|
|
14
|
+
* @param {Object} sessionManager - SessionManager instance
|
|
15
|
+
*/
|
|
16
|
+
constructor(sessionManager) {
|
|
17
|
+
this.sessionManager = sessionManager;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Compress a session transcript
|
|
22
|
+
* @param {string} sessionId - Session ID
|
|
23
|
+
* @param {Object} options - Compression options
|
|
24
|
+
* @param {number} [options.threshold=50] - Minimum messages before compression
|
|
25
|
+
* @param {number} [options.keepFirst=5] - Messages to keep at start
|
|
26
|
+
* @param {number} [options.keepLast=10] - Messages to keep at end
|
|
27
|
+
* @returns {Object} Compression result
|
|
28
|
+
*/
|
|
29
|
+
compress(sessionId, options = {}) {
|
|
30
|
+
const session = this.sessionManager.loadSession(sessionId);
|
|
31
|
+
if (!session) {
|
|
32
|
+
return { compressed: false, reason: 'Session not found' };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
threshold = 50,
|
|
37
|
+
keepFirst = 5,
|
|
38
|
+
keepLast = 10
|
|
39
|
+
} = options;
|
|
40
|
+
|
|
41
|
+
const transcript = session.context?.transcript || '';
|
|
42
|
+
|
|
43
|
+
// Handle string transcript (split by newlines or messages)
|
|
44
|
+
let messages = [];
|
|
45
|
+
if (typeof transcript === 'string') {
|
|
46
|
+
// Try to parse as JSON array first
|
|
47
|
+
try {
|
|
48
|
+
messages = JSON.parse(transcript);
|
|
49
|
+
} catch {
|
|
50
|
+
// Split by newlines as fallback
|
|
51
|
+
messages = transcript.split('\n').filter(line => line.trim());
|
|
52
|
+
}
|
|
53
|
+
} else if (Array.isArray(transcript)) {
|
|
54
|
+
messages = transcript;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (messages.length <= threshold) {
|
|
58
|
+
logger.info('Session below compression threshold', { sessionId, messageCount: messages.length });
|
|
59
|
+
return {
|
|
60
|
+
compressed: false,
|
|
61
|
+
reason: 'Below threshold',
|
|
62
|
+
messageCount: messages.length,
|
|
63
|
+
threshold
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Create compressed transcript
|
|
68
|
+
const firstMessages = messages.slice(0, keepFirst);
|
|
69
|
+
const lastMessages = messages.slice(-keepLast);
|
|
70
|
+
const compressedCount = messages.length - keepFirst - keepLast;
|
|
71
|
+
|
|
72
|
+
const placeholder = {
|
|
73
|
+
role: 'system',
|
|
74
|
+
content: `... ${compressedCount} messages compressed ...`,
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
compressed: true
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const compressedMessages = [
|
|
80
|
+
...firstMessages,
|
|
81
|
+
placeholder,
|
|
82
|
+
...lastMessages
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
// Update session
|
|
86
|
+
const updates = {
|
|
87
|
+
context: {
|
|
88
|
+
transcript: compressedMessages
|
|
89
|
+
},
|
|
90
|
+
metadata: {
|
|
91
|
+
compressed: true,
|
|
92
|
+
compressed_at: new Date().toISOString(),
|
|
93
|
+
compression_stats: {
|
|
94
|
+
original_count: messages.length,
|
|
95
|
+
compressed_count: compressedMessages.length,
|
|
96
|
+
removed_count: compressedCount
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
this.sessionManager.updateSession(sessionId, updates);
|
|
102
|
+
|
|
103
|
+
const reduction = Math.round((1 - compressedMessages.length / messages.length) * 100);
|
|
104
|
+
|
|
105
|
+
logger.info('Session compressed', {
|
|
106
|
+
sessionId,
|
|
107
|
+
originalLength: messages.length,
|
|
108
|
+
newLength: compressedMessages.length,
|
|
109
|
+
reduction
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
compressed: true,
|
|
114
|
+
originalLength: messages.length,
|
|
115
|
+
newLength: compressedMessages.length,
|
|
116
|
+
reduction
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get compression stats for a session
|
|
122
|
+
* @param {string} sessionId - Session ID
|
|
123
|
+
* @returns {Object} Compression statistics
|
|
124
|
+
*/
|
|
125
|
+
getCompressionStats(sessionId) {
|
|
126
|
+
const session = this.sessionManager.loadSession(sessionId);
|
|
127
|
+
if (!session) {
|
|
128
|
+
return { compressed: false, reason: 'Session not found' };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!session.metadata?.compressed) {
|
|
132
|
+
return { compressed: false };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const originalSize = session.metadata.compression_stats?.original_count || 0;
|
|
136
|
+
const compressedSize = session.metadata.compression_stats?.compressed_count || 0;
|
|
137
|
+
const reduction = session.metadata.compression_stats?.removed_count || 0;
|
|
138
|
+
const reductionPercent = originalSize > 0
|
|
139
|
+
? Math.round((reduction / originalSize) * 100)
|
|
140
|
+
: 0;
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
compressed: true,
|
|
144
|
+
original_size: originalSize,
|
|
145
|
+
compressed_size: compressedSize,
|
|
146
|
+
reduction_percent: reductionPercent,
|
|
147
|
+
compressed_at: session.metadata.compressed_at
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if session should be compressed
|
|
153
|
+
* @param {string} sessionId - Session ID
|
|
154
|
+
* @param {number} threshold - Message threshold
|
|
155
|
+
* @returns {boolean} True if compression recommended
|
|
156
|
+
*/
|
|
157
|
+
shouldCompress(sessionId, threshold = 50) {
|
|
158
|
+
const session = this.sessionManager.loadSession(sessionId);
|
|
159
|
+
if (!session) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const transcript = session.context?.transcript || '';
|
|
164
|
+
let messageCount = 0;
|
|
165
|
+
|
|
166
|
+
if (typeof transcript === 'string') {
|
|
167
|
+
try {
|
|
168
|
+
const messages = JSON.parse(transcript);
|
|
169
|
+
messageCount = Array.isArray(messages) ? messages.length : 0;
|
|
170
|
+
} catch {
|
|
171
|
+
messageCount = transcript.split('\n').filter(line => line.trim()).length;
|
|
172
|
+
}
|
|
173
|
+
} else if (Array.isArray(transcript)) {
|
|
174
|
+
messageCount = transcript.length;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return messageCount > threshold;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Compress all sessions exceeding threshold
|
|
182
|
+
* @param {Object} options - Compression options
|
|
183
|
+
* @returns {Object} Batch compression result
|
|
184
|
+
*/
|
|
185
|
+
compressAll(options = {}) {
|
|
186
|
+
const sessions = this.sessionManager.listSessions();
|
|
187
|
+
const results = {
|
|
188
|
+
total: sessions.length,
|
|
189
|
+
compressed: 0,
|
|
190
|
+
skipped: 0,
|
|
191
|
+
details: []
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
for (const sessionMeta of sessions) {
|
|
195
|
+
const shouldCompress = this.shouldCompress(sessionMeta.session_id, options.threshold);
|
|
196
|
+
|
|
197
|
+
if (shouldCompress) {
|
|
198
|
+
const result = this.compress(sessionMeta.session_id, options);
|
|
199
|
+
if (result.compressed) {
|
|
200
|
+
results.compressed++;
|
|
201
|
+
results.details.push({
|
|
202
|
+
sessionId: sessionMeta.session_id,
|
|
203
|
+
...result
|
|
204
|
+
});
|
|
205
|
+
} else {
|
|
206
|
+
results.skipped++;
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
results.skipped++;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
logger.info('Batch compression complete', {
|
|
214
|
+
total: results.total,
|
|
215
|
+
compressed: results.compressed,
|
|
216
|
+
skipped: results.skipped
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return results;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Decompress a session (restore placeholder info)
|
|
224
|
+
* Note: Cannot restore original messages, only marks as decompressed
|
|
225
|
+
* @param {string} sessionId - Session ID
|
|
226
|
+
* @returns {Object} Decompression result
|
|
227
|
+
*/
|
|
228
|
+
decompress(sessionId) {
|
|
229
|
+
const session = this.sessionManager.loadSession(sessionId);
|
|
230
|
+
if (!session) {
|
|
231
|
+
return { decompressed: false, reason: 'Session not found' };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!session.metadata?.compressed) {
|
|
235
|
+
return { decompressed: false, reason: 'Not compressed' };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Remove compression metadata
|
|
239
|
+
this.sessionManager.updateSession(sessionId, {
|
|
240
|
+
metadata: {
|
|
241
|
+
compressed: false,
|
|
242
|
+
compressed_at: null,
|
|
243
|
+
compression_stats: null
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
logger.info('Session marked as decompressed', { sessionId });
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
decompressed: true,
|
|
251
|
+
note: 'Original messages cannot be restored'
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
module.exports = MemoryCompression;
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Milestone — Milestone and requirements lifecycle operations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { escapeRegex, getMilestonePhaseFilter, output, error } = require('./core.cjs');
|
|
8
|
+
const { extractFrontmatter } = require('./frontmatter.cjs');
|
|
9
|
+
const { writeStateMd } = require('./state.cjs');
|
|
10
|
+
|
|
11
|
+
function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
|
|
12
|
+
if (!reqIdsRaw || reqIdsRaw.length === 0) {
|
|
13
|
+
error('requirement IDs required. Usage: requirements mark-complete REQ-01,REQ-02 or REQ-01 REQ-02');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Accept comma-separated, space-separated, or bracket-wrapped: [REQ-01, REQ-02]
|
|
17
|
+
const reqIds = reqIdsRaw
|
|
18
|
+
.join(' ')
|
|
19
|
+
.replace(/[\[\]]/g, '')
|
|
20
|
+
.split(/[,\s]+/)
|
|
21
|
+
.map(r => r.trim())
|
|
22
|
+
.filter(Boolean);
|
|
23
|
+
|
|
24
|
+
if (reqIds.length === 0) {
|
|
25
|
+
error('no valid requirement IDs found');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
|
|
29
|
+
if (!fs.existsSync(reqPath)) {
|
|
30
|
+
output({ updated: false, reason: 'REQUIREMENTS.md not found', ids: reqIds }, raw, 'no requirements file');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let reqContent = fs.readFileSync(reqPath, 'utf-8');
|
|
35
|
+
const updated = [];
|
|
36
|
+
const notFound = [];
|
|
37
|
+
|
|
38
|
+
for (const reqId of reqIds) {
|
|
39
|
+
let found = false;
|
|
40
|
+
const reqEscaped = escapeRegex(reqId);
|
|
41
|
+
|
|
42
|
+
// Update checkbox: - [ ] **REQ-ID** → - [x] **REQ-ID**
|
|
43
|
+
const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqEscaped}\\*\\*)`, 'gi');
|
|
44
|
+
if (checkboxPattern.test(reqContent)) {
|
|
45
|
+
reqContent = reqContent.replace(checkboxPattern, '$1x$2');
|
|
46
|
+
found = true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Update traceability table: | REQ-ID | Phase N | Pending | → | REQ-ID | Phase N | Complete |
|
|
50
|
+
const tablePattern = new RegExp(`(\\|\\s*${reqEscaped}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, 'gi');
|
|
51
|
+
if (tablePattern.test(reqContent)) {
|
|
52
|
+
// Re-read since test() advances lastIndex for global regex
|
|
53
|
+
reqContent = reqContent.replace(
|
|
54
|
+
new RegExp(`(\\|\\s*${reqEscaped}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, 'gi'),
|
|
55
|
+
'$1 Complete $2'
|
|
56
|
+
);
|
|
57
|
+
found = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (found) {
|
|
61
|
+
updated.push(reqId);
|
|
62
|
+
} else {
|
|
63
|
+
notFound.push(reqId);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (updated.length > 0) {
|
|
68
|
+
fs.writeFileSync(reqPath, reqContent, 'utf-8');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
output({
|
|
72
|
+
updated: updated.length > 0,
|
|
73
|
+
marked_complete: updated,
|
|
74
|
+
not_found: notFound,
|
|
75
|
+
total: reqIds.length,
|
|
76
|
+
}, raw, `${updated.length}/${reqIds.length} requirements marked complete`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
80
|
+
if (!version) {
|
|
81
|
+
error('version required for milestone complete (e.g., v1.0)');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
|
|
85
|
+
const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
|
|
86
|
+
const statePath = path.join(cwd, '.planning', 'STATE.md');
|
|
87
|
+
const milestonesPath = path.join(cwd, '.planning', 'MILESTONES.md');
|
|
88
|
+
const archiveDir = path.join(cwd, '.planning', 'milestones');
|
|
89
|
+
const phasesDir = path.join(cwd, '.planning', 'phases');
|
|
90
|
+
const today = new Date().toISOString().split('T')[0];
|
|
91
|
+
const milestoneName = options.name || version;
|
|
92
|
+
|
|
93
|
+
// Ensure archive directory exists
|
|
94
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
95
|
+
|
|
96
|
+
// Scope stats and accomplishments to only the phases belonging to the
|
|
97
|
+
// current milestone's ROADMAP. Uses the shared filter from core.cjs
|
|
98
|
+
// (same logic used by cmdPhasesList and other callers).
|
|
99
|
+
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
|
100
|
+
|
|
101
|
+
// Gather stats from phases (scoped to current milestone only)
|
|
102
|
+
let phaseCount = 0;
|
|
103
|
+
let totalPlans = 0;
|
|
104
|
+
let totalTasks = 0;
|
|
105
|
+
const accomplishments = [];
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
|
109
|
+
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort();
|
|
110
|
+
|
|
111
|
+
for (const dir of dirs) {
|
|
112
|
+
if (!isDirInMilestone(dir)) continue;
|
|
113
|
+
|
|
114
|
+
phaseCount++;
|
|
115
|
+
const phaseFiles = fs.readdirSync(path.join(phasesDir, dir));
|
|
116
|
+
const plans = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md');
|
|
117
|
+
const summaries = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
|
|
118
|
+
totalPlans += plans.length;
|
|
119
|
+
|
|
120
|
+
// Extract one-liners from summaries
|
|
121
|
+
for (const s of summaries) {
|
|
122
|
+
try {
|
|
123
|
+
const content = fs.readFileSync(path.join(phasesDir, dir, s), 'utf-8');
|
|
124
|
+
const fm = extractFrontmatter(content);
|
|
125
|
+
if (fm['one-liner']) {
|
|
126
|
+
accomplishments.push(fm['one-liner']);
|
|
127
|
+
}
|
|
128
|
+
// Count tasks
|
|
129
|
+
const taskMatches = content.match(/##\s*Task\s*\d+/gi) || [];
|
|
130
|
+
totalTasks += taskMatches.length;
|
|
131
|
+
} catch (err) {
|
|
132
|
+
const { defaultLogger: logger } = require('./logger.cjs');
|
|
133
|
+
logger.warn('Failed to read phase summary', { phase: dir, summary: s, error: err.message });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} catch (err) {
|
|
138
|
+
const { defaultLogger: logger } = require('./logger.cjs');
|
|
139
|
+
logger.warn('Failed to scan phase summaries', { phasesDir, error: err.message });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Archive ROADMAP.md
|
|
143
|
+
if (fs.existsSync(roadmapPath)) {
|
|
144
|
+
const roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
|
|
145
|
+
fs.writeFileSync(path.join(archiveDir, `${version}-ROADMAP.md`), roadmapContent, 'utf-8');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Archive REQUIREMENTS.md
|
|
149
|
+
if (fs.existsSync(reqPath)) {
|
|
150
|
+
const reqContent = fs.readFileSync(reqPath, 'utf-8');
|
|
151
|
+
const archiveHeader = `# Requirements Archive: ${version} ${milestoneName}\n\n**Archived:** ${today}\n**Status:** SHIPPED\n\nFor current requirements, see \`.planning/REQUIREMENTS.md\`.\n\n---\n\n`;
|
|
152
|
+
fs.writeFileSync(path.join(archiveDir, `${version}-REQUIREMENTS.md`), archiveHeader + reqContent, 'utf-8');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Archive audit file if exists
|
|
156
|
+
const auditFile = path.join(cwd, '.planning', `${version}-MILESTONE-AUDIT.md`);
|
|
157
|
+
if (fs.existsSync(auditFile)) {
|
|
158
|
+
fs.renameSync(auditFile, path.join(archiveDir, `${version}-MILESTONE-AUDIT.md`));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Create/append MILESTONES.md entry
|
|
162
|
+
const accomplishmentsList = accomplishments.map(a => `- ${a}`).join('\n');
|
|
163
|
+
const milestoneEntry = `## ${version} ${milestoneName} (Shipped: ${today})\n\n**Phases completed:** ${phaseCount} phases, ${totalPlans} plans, ${totalTasks} tasks\n\n**Key accomplishments:**\n${accomplishmentsList || '- (none recorded)'}\n\n---\n\n`;
|
|
164
|
+
|
|
165
|
+
if (fs.existsSync(milestonesPath)) {
|
|
166
|
+
const existing = fs.readFileSync(milestonesPath, 'utf-8');
|
|
167
|
+
if (!existing.trim()) {
|
|
168
|
+
// Empty file — treat like new
|
|
169
|
+
fs.writeFileSync(milestonesPath, `# Milestones\n\n${milestoneEntry}`, 'utf-8');
|
|
170
|
+
} else {
|
|
171
|
+
// Insert after the header line(s) for reverse chronological order (newest first)
|
|
172
|
+
const headerMatch = existing.match(/^(#{1,3}\s+[^\n]*\n\n?)/);
|
|
173
|
+
if (headerMatch) {
|
|
174
|
+
const header = headerMatch[1];
|
|
175
|
+
const rest = existing.slice(header.length);
|
|
176
|
+
fs.writeFileSync(milestonesPath, header + milestoneEntry + rest, 'utf-8');
|
|
177
|
+
} else {
|
|
178
|
+
// No recognizable header — prepend the entry
|
|
179
|
+
fs.writeFileSync(milestonesPath, milestoneEntry + existing, 'utf-8');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
fs.writeFileSync(milestonesPath, `# Milestones\n\n${milestoneEntry}`, 'utf-8');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Update STATE.md
|
|
187
|
+
if (fs.existsSync(statePath)) {
|
|
188
|
+
let stateContent = fs.readFileSync(statePath, 'utf-8');
|
|
189
|
+
stateContent = stateContent.replace(
|
|
190
|
+
/(\*\*Status:\*\*\s*).*/,
|
|
191
|
+
`$1${version} milestone complete`
|
|
192
|
+
);
|
|
193
|
+
stateContent = stateContent.replace(
|
|
194
|
+
/(\*\*Last Activity:\*\*\s*).*/,
|
|
195
|
+
`$1${today}`
|
|
196
|
+
);
|
|
197
|
+
stateContent = stateContent.replace(
|
|
198
|
+
/(\*\*Last Activity Description:\*\*\s*).*/,
|
|
199
|
+
`$1${version} milestone completed and archived`
|
|
200
|
+
);
|
|
201
|
+
writeStateMd(statePath, stateContent, cwd);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Archive phase directories if requested
|
|
205
|
+
let phasesArchived = false;
|
|
206
|
+
if (options.archivePhases) {
|
|
207
|
+
try {
|
|
208
|
+
const phaseArchiveDir = path.join(archiveDir, `${version}-phases`);
|
|
209
|
+
fs.mkdirSync(phaseArchiveDir, { recursive: true });
|
|
210
|
+
|
|
211
|
+
const phaseEntries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
|
212
|
+
const phaseDirNames = phaseEntries.filter(e => e.isDirectory()).map(e => e.name);
|
|
213
|
+
let archivedCount = 0;
|
|
214
|
+
for (const dir of phaseDirNames) {
|
|
215
|
+
if (!isDirInMilestone(dir)) continue;
|
|
216
|
+
fs.renameSync(path.join(phasesDir, dir), path.join(phaseArchiveDir, dir));
|
|
217
|
+
archivedCount++;
|
|
218
|
+
}
|
|
219
|
+
phasesArchived = archivedCount > 0;
|
|
220
|
+
} catch {}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const result = {
|
|
224
|
+
version,
|
|
225
|
+
name: milestoneName,
|
|
226
|
+
date: today,
|
|
227
|
+
phases: phaseCount,
|
|
228
|
+
plans: totalPlans,
|
|
229
|
+
tasks: totalTasks,
|
|
230
|
+
accomplishments,
|
|
231
|
+
archived: {
|
|
232
|
+
roadmap: fs.existsSync(path.join(archiveDir, `${version}-ROADMAP.md`)),
|
|
233
|
+
requirements: fs.existsSync(path.join(archiveDir, `${version}-REQUIREMENTS.md`)),
|
|
234
|
+
audit: fs.existsSync(path.join(archiveDir, `${version}-MILESTONE-AUDIT.md`)),
|
|
235
|
+
phases: phasesArchived,
|
|
236
|
+
},
|
|
237
|
+
milestones_updated: true,
|
|
238
|
+
state_updated: fs.existsSync(statePath),
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
output(result, raw);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = {
|
|
245
|
+
cmdRequirementsMarkComplete,
|
|
246
|
+
cmdMilestoneComplete,
|
|
247
|
+
};
|