@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,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EZ Lock Logger
|
|
5
|
+
*
|
|
6
|
+
* Dedicated logging for phase lock operations.
|
|
7
|
+
* Logs to console only (no file logging).
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Structured logging
|
|
11
|
+
* - Log levels: INFO, WARN, ERROR
|
|
12
|
+
* - Automatic timestamp and context
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* const LockLogger = require('./lock-logger.cjs');
|
|
16
|
+
* const lockLogger = new LockLogger();
|
|
17
|
+
*
|
|
18
|
+
* lockLogger.log('INFO', 'acquire', { phase: '47', agent_id: 'agent-1', result: 'success' });
|
|
19
|
+
* lockLogger.log('WARN', 'conflict', { phase: '47', holder_agent: 'agent-2' });
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const Logger = require('./logger.cjs');
|
|
23
|
+
const logger = new Logger();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* LockLogger class for lock operation logging.
|
|
27
|
+
*/
|
|
28
|
+
class LockLogger {
|
|
29
|
+
/**
|
|
30
|
+
* Creates a new LockLogger instance.
|
|
31
|
+
*/
|
|
32
|
+
constructor() {
|
|
33
|
+
// Console only - no file logging
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Log a lock operation.
|
|
38
|
+
* @param {string} level - Log level: INFO, WARN, ERROR
|
|
39
|
+
* @param {string} operation - Operation type: acquire, release, heartbeat, conflict, stale
|
|
40
|
+
* @param {Object} data - Operation data
|
|
41
|
+
*/
|
|
42
|
+
log(level, operation, data = {}) {
|
|
43
|
+
const entry = {
|
|
44
|
+
timestamp: new Date().toISOString(),
|
|
45
|
+
level: level.toUpperCase(),
|
|
46
|
+
operation,
|
|
47
|
+
...data
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Log to console
|
|
51
|
+
if (level === 'ERROR') {
|
|
52
|
+
console.error(`[EZ LOCK] ${JSON.stringify(entry)}`);
|
|
53
|
+
} else if (level === 'WARN') {
|
|
54
|
+
console.warn(`[EZ LOCK] ${JSON.stringify(entry)}`);
|
|
55
|
+
} else if (process.env.DEBUG === 'ez-agents') {
|
|
56
|
+
console.log(`[EZ LOCK] ${JSON.stringify(entry)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Log lock acquire.
|
|
62
|
+
* @param {string} phase - Phase number
|
|
63
|
+
* @param {string} agentId - Agent identifier
|
|
64
|
+
* @param {Object} result - Acquire result
|
|
65
|
+
*/
|
|
66
|
+
logAcquire(phase, agentId, result) {
|
|
67
|
+
const data = {
|
|
68
|
+
phase,
|
|
69
|
+
agent_id: agentId,
|
|
70
|
+
result: result.acquired ? 'success' : (result.conflict ? 'conflict' : 'error'),
|
|
71
|
+
message: result.message
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (result.lockInfo) {
|
|
75
|
+
data.holder_agent = result.lockInfo.agent_id;
|
|
76
|
+
data.holder_name = result.lockInfo.agent_name;
|
|
77
|
+
data.expires_at = result.expiresAt;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const level = result.acquired ? 'INFO' : (result.conflict ? 'WARN' : 'ERROR');
|
|
81
|
+
this.log(level, 'acquire', data);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Log lock release.
|
|
86
|
+
* @param {string} phase - Phase number
|
|
87
|
+
* @param {string} agentId - Agent identifier
|
|
88
|
+
* @param {Object} result - Release result
|
|
89
|
+
*/
|
|
90
|
+
logRelease(phase, agentId, result) {
|
|
91
|
+
const data = {
|
|
92
|
+
phase,
|
|
93
|
+
agent_id: agentId,
|
|
94
|
+
result: result.released ? 'success' : 'error',
|
|
95
|
+
message: result.message
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (result.lockInfo) {
|
|
99
|
+
data.held_duration_ms = Date.now() - new Date(result.lockInfo.acquired_at).getTime();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const level = result.released ? 'INFO' : 'ERROR';
|
|
103
|
+
this.log(level, 'release', data);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Log heartbeat.
|
|
108
|
+
* @param {string} phase - Phase number
|
|
109
|
+
* @param {string} agentId - Agent identifier
|
|
110
|
+
* @param {Object} result - Heartbeat result
|
|
111
|
+
*/
|
|
112
|
+
logHeartbeat(phase, agentId, result) {
|
|
113
|
+
const data = {
|
|
114
|
+
phase,
|
|
115
|
+
agent_id: agentId,
|
|
116
|
+
result: result.success ? 'success' : 'error',
|
|
117
|
+
message: result.message
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (result.lockInfo) {
|
|
121
|
+
data.expires_at = result.lockInfo.expires_at;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const level = result.success ? 'INFO' : 'WARN';
|
|
125
|
+
this.log(level, 'heartbeat', data);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Log stale lock detection.
|
|
130
|
+
* @param {string} phase - Phase number
|
|
131
|
+
* @param {Object} lockInfo - Stale lock info
|
|
132
|
+
*/
|
|
133
|
+
logStale(phase, lockInfo) {
|
|
134
|
+
const data = {
|
|
135
|
+
phase,
|
|
136
|
+
previous_agent: lockInfo.agent_id,
|
|
137
|
+
previous_name: lockInfo.agent_name,
|
|
138
|
+
reason: 'expired',
|
|
139
|
+
action: 'auto-release'
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (lockInfo.expires_at) {
|
|
143
|
+
data.expired_at = lockInfo.expires_at;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.log('WARN', 'stale', data);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Log lock status update.
|
|
151
|
+
* @param {number} lockCount - Number of active locks
|
|
152
|
+
* @param {Object} result - Update result
|
|
153
|
+
*/
|
|
154
|
+
logStateUpdate(lockCount, result) {
|
|
155
|
+
const data = {
|
|
156
|
+
lock_count: lockCount,
|
|
157
|
+
result: result.updated ? 'success' : 'error'
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (result.error) {
|
|
161
|
+
data.error = result.error;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const level = result.updated ? 'INFO' : 'WARN';
|
|
165
|
+
this.log(level, 'state_update', data);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get log file path (returns null - no file logging).
|
|
170
|
+
* @returns {null} - Always null
|
|
171
|
+
*/
|
|
172
|
+
getLogPath() {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Read recent log entries (returns empty array - no file logging).
|
|
178
|
+
* @param {number} limit - Number of entries to read
|
|
179
|
+
* @returns {Array<Object>} - Empty array
|
|
180
|
+
*/
|
|
181
|
+
readRecent(limit = 100) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Clear log file (no-op - no file logging).
|
|
187
|
+
* @returns {boolean} - Always true
|
|
188
|
+
*/
|
|
189
|
+
clear() {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = LockLogger;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EZ Lock State
|
|
5
|
+
*
|
|
6
|
+
* Integrates lock state into STATE.md and provides lock status reporting.
|
|
7
|
+
* Updates STATE.md automatically on lock acquire/release.
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Scan all active locks
|
|
11
|
+
* - Format lock status as markdown table
|
|
12
|
+
* - Update STATE.md lock status section
|
|
13
|
+
* - Auto-reconcile stale entries
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* const LockState = require('./lock-state.cjs');
|
|
17
|
+
* const lockState = new LockState();
|
|
18
|
+
*
|
|
19
|
+
* // Get formatted lock status
|
|
20
|
+
* const status = await lockState.getLockStatus();
|
|
21
|
+
* const table = lockState.formatLockStatusTable(status);
|
|
22
|
+
*
|
|
23
|
+
* // Update STATE.md
|
|
24
|
+
* await lockState.updateStateLockSection(table);
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
const Logger = require('./logger.cjs');
|
|
30
|
+
const logger = new Logger();
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default configuration.
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_CONFIG = {
|
|
36
|
+
locksDir: path.join(process.cwd(), '.planning', 'locks'),
|
|
37
|
+
statePath: path.join(process.cwd(), '.planning', 'STATE.md'),
|
|
38
|
+
staleThresholdMinutes: 90
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* LockState class for managing lock state integration.
|
|
43
|
+
*/
|
|
44
|
+
class LockState {
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new LockState instance.
|
|
47
|
+
* @param {Object} options - Configuration options
|
|
48
|
+
* @param {string} options.locksDir - Directory for lock files
|
|
49
|
+
* @param {string} options.statePath - Path to STATE.md
|
|
50
|
+
*/
|
|
51
|
+
constructor(options = {}) {
|
|
52
|
+
this.config = {
|
|
53
|
+
...DEFAULT_CONFIG,
|
|
54
|
+
...options
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get status of all active locks.
|
|
60
|
+
* @returns {Promise<Array<Object>>} - Array of lock status objects
|
|
61
|
+
*/
|
|
62
|
+
async getLockStatus() {
|
|
63
|
+
try {
|
|
64
|
+
if (!fs.existsSync(this.config.locksDir)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const files = fs.readdirSync(this.config.locksDir)
|
|
69
|
+
.filter(f => f.endsWith('.lock.json'));
|
|
70
|
+
|
|
71
|
+
const activeLocks = [];
|
|
72
|
+
const now = new Date();
|
|
73
|
+
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
const filePath = path.join(this.config.locksDir, file);
|
|
76
|
+
try {
|
|
77
|
+
const lockInfo = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
78
|
+
|
|
79
|
+
// Check if lock is stale
|
|
80
|
+
const expiresAt = new Date(lockInfo.expires_at);
|
|
81
|
+
const lastHeartbeat = new Date(lockInfo.last_heartbeat);
|
|
82
|
+
const staleThreshold = new Date(now.getTime() - this.config.staleThresholdMinutes * 60000);
|
|
83
|
+
|
|
84
|
+
const isStale = now > expiresAt || lastHeartbeat < staleThreshold;
|
|
85
|
+
|
|
86
|
+
if (!isStale) {
|
|
87
|
+
const acquiredAt = new Date(lockInfo.acquired_at);
|
|
88
|
+
const durationMs = now - acquiredAt;
|
|
89
|
+
const durationMin = Math.round(durationMs / 60000);
|
|
90
|
+
|
|
91
|
+
activeLocks.push({
|
|
92
|
+
phase: lockInfo.phase,
|
|
93
|
+
agentId: lockInfo.agent_id,
|
|
94
|
+
agentName: lockInfo.agent_name,
|
|
95
|
+
expiresAt: lockInfo.expires_at,
|
|
96
|
+
acquiredAt: lockInfo.acquired_at,
|
|
97
|
+
durationMin,
|
|
98
|
+
sessionId: lockInfo.session_id,
|
|
99
|
+
milestone: lockInfo.milestone
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
logger.warn('Failed to read lock file', { file, error: err.message });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Sort by phase number
|
|
108
|
+
activeLocks.sort((a, b) => parseInt(a.phase) - parseInt(b.phase));
|
|
109
|
+
|
|
110
|
+
return activeLocks;
|
|
111
|
+
|
|
112
|
+
} catch (err) {
|
|
113
|
+
logger.error('Failed to get lock status', { error: err.message });
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Format lock status as markdown table.
|
|
120
|
+
* @param {Array<Object>} locks - Lock status objects
|
|
121
|
+
* @returns {string} - Markdown table string
|
|
122
|
+
*/
|
|
123
|
+
formatLockStatusTable(locks) {
|
|
124
|
+
if (!locks || locks.length === 0) {
|
|
125
|
+
return 'No active locks.\n';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let table = '| Phase | Agent | Expires | Duration |\n';
|
|
129
|
+
table += '|-------|-------|---------|----------|\n';
|
|
130
|
+
|
|
131
|
+
for (const lock of locks) {
|
|
132
|
+
const expiresDate = new Date(lock.expiresAt);
|
|
133
|
+
const expiresStr = expiresDate.toISOString().replace('T', ' ').substring(0, 16);
|
|
134
|
+
const durationStr = this._formatDuration(lock.durationMin);
|
|
135
|
+
|
|
136
|
+
table += `| ${lock.phase} | ${lock.agentId} | ${expiresStr} | ${durationStr} |\n`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return table;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Update STATE.md with lock status section.
|
|
144
|
+
* @param {string} table - Markdown table to insert
|
|
145
|
+
* @returns {Promise<Object>} - Update result
|
|
146
|
+
*/
|
|
147
|
+
async updateStateLockSection(table) {
|
|
148
|
+
try {
|
|
149
|
+
if (!fs.existsSync(this.config.statePath)) {
|
|
150
|
+
logger.warn('STATE.md not found', { path: this.config.statePath });
|
|
151
|
+
return {
|
|
152
|
+
updated: false,
|
|
153
|
+
error: 'STATE.md not found'
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let content = fs.readFileSync(this.config.statePath, 'utf8');
|
|
158
|
+
|
|
159
|
+
// Check if Lock Status section exists
|
|
160
|
+
const lockStatusRegex = /## Lock Status\s*\n([\s\S]*?)(?=\n## |\n# |\s*$)/;
|
|
161
|
+
const hasLockSection = lockStatusRegex.test(content);
|
|
162
|
+
|
|
163
|
+
const newSection = `## Lock Status\n\n${table}\n`;
|
|
164
|
+
|
|
165
|
+
if (hasLockSection) {
|
|
166
|
+
// Replace existing section
|
|
167
|
+
content = content.replace(lockStatusRegex, newSection);
|
|
168
|
+
} else {
|
|
169
|
+
// Add new section after the last existing section
|
|
170
|
+
const lastSectionMatch = content.match(/(## [^\n]+\n[\s\S]*)$/);
|
|
171
|
+
if (lastSectionMatch) {
|
|
172
|
+
content = content.replace(lastSectionMatch[0], lastSectionMatch[0] + '\n' + newSection);
|
|
173
|
+
} else {
|
|
174
|
+
// Append to end
|
|
175
|
+
content += '\n' + newSection;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Atomic write
|
|
180
|
+
const tempFile = this.config.statePath + '.tmp';
|
|
181
|
+
fs.writeFileSync(tempFile, content, 'utf8');
|
|
182
|
+
fs.renameSync(tempFile, this.config.statePath);
|
|
183
|
+
|
|
184
|
+
logger.info('STATE.md lock section updated', {
|
|
185
|
+
lockCount: table.split('\n').length - 3 // Subtract header rows
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
updated: true,
|
|
190
|
+
lockCount: table.split('\n').length - 3
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
} catch (err) {
|
|
194
|
+
logger.error('Failed to update STATE.md lock section', { error: err.message });
|
|
195
|
+
return {
|
|
196
|
+
updated: false,
|
|
197
|
+
error: err.message
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Reconcile STATE.md with actual locks (remove stale entries).
|
|
204
|
+
* @returns {Promise<Object>} - Reconcile result
|
|
205
|
+
*/
|
|
206
|
+
async reconcile() {
|
|
207
|
+
try {
|
|
208
|
+
const activeLocks = await this.getLockStatus();
|
|
209
|
+
const table = this.formatLockStatusTable(activeLocks);
|
|
210
|
+
return await this.updateStateLockSection(table);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
logger.error('Lock state reconcile failed', { error: err.message });
|
|
213
|
+
return {
|
|
214
|
+
reconciled: false,
|
|
215
|
+
error: err.message
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Get lock status summary for quick display.
|
|
222
|
+
* @returns {Promise<string>} - Summary string
|
|
223
|
+
*/
|
|
224
|
+
async getSummary() {
|
|
225
|
+
const locks = await this.getLockStatus();
|
|
226
|
+
|
|
227
|
+
if (locks.length === 0) {
|
|
228
|
+
return 'No active locks';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const summary = locks.map(lock => {
|
|
232
|
+
return `Phase ${lock.phase}: ${lock.agentId} (expires ${new Date(lock.expiresAt).toLocaleString()})`;
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return summary.join('\n');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Format duration in human-readable format.
|
|
240
|
+
* @param {number} minutes - Duration in minutes
|
|
241
|
+
* @returns {string} - Formatted duration
|
|
242
|
+
* @private
|
|
243
|
+
*/
|
|
244
|
+
_formatDuration(minutes) {
|
|
245
|
+
if (minutes < 60) {
|
|
246
|
+
return `${minutes} min`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const hours = Math.floor(minutes / 60);
|
|
250
|
+
const mins = minutes % 60;
|
|
251
|
+
|
|
252
|
+
if (hours < 24) {
|
|
253
|
+
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const days = Math.floor(hours / 24);
|
|
257
|
+
const remainingHours = hours % 24;
|
|
258
|
+
|
|
259
|
+
return remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
module.exports = LockState;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lockfile Validator — Validate package manager lockfiles
|
|
5
|
+
*
|
|
6
|
+
* Validates lockfile integrity for npm, yarn, and pnpm:
|
|
7
|
+
* - npm: package-lock.json (JSON format with lockfileVersion)
|
|
8
|
+
* - yarn: yarn.lock (YAML-like format with metadata header)
|
|
9
|
+
* - pnpm: pnpm-lock.yaml (YAML format with lockfileVersion)
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const LockfileValidator = require('./lockfile-validator.cjs');
|
|
13
|
+
* const validator = new LockfileValidator(cwd);
|
|
14
|
+
* const result = validator.validate('npm');
|
|
15
|
+
* // Returns: { valid, reason, lockfileVersion?, packageCount? }
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const Logger = require('./logger.cjs');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Lockfile Validator class
|
|
24
|
+
* Validates lockfile integrity for different package managers
|
|
25
|
+
*/
|
|
26
|
+
class LockfileValidator {
|
|
27
|
+
/**
|
|
28
|
+
* Create a LockfileValidator instance
|
|
29
|
+
* @param {string} cwd - Working directory (default: process.cwd())
|
|
30
|
+
*/
|
|
31
|
+
constructor(cwd = process.cwd()) {
|
|
32
|
+
this.cwd = cwd;
|
|
33
|
+
this.logger = new Logger();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate lockfile for a specific package manager
|
|
38
|
+
* @param {string} manager - Package manager name ('npm', 'yarn', or 'pnpm')
|
|
39
|
+
* @returns {Object} Validation result
|
|
40
|
+
* - {boolean} valid - Whether lockfile is valid
|
|
41
|
+
* - {string} reason - Reason code if invalid
|
|
42
|
+
* - {string} message - Human-readable message if invalid
|
|
43
|
+
* - {number} lockfileVersion - Lockfile version (if valid)
|
|
44
|
+
* - {number} packageCount - Number of packages (if valid)
|
|
45
|
+
*/
|
|
46
|
+
validate(manager) {
|
|
47
|
+
const lockfilePath = this.getLockfilePath(manager);
|
|
48
|
+
|
|
49
|
+
this.logger.debug('Validating lockfile', {
|
|
50
|
+
manager,
|
|
51
|
+
lockfilePath,
|
|
52
|
+
cwd: this.cwd
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Check file existence
|
|
56
|
+
if (!fs.existsSync(lockfilePath)) {
|
|
57
|
+
this.logger.debug('Lockfile not found', { lockfilePath });
|
|
58
|
+
return {
|
|
59
|
+
valid: false,
|
|
60
|
+
reason: 'lockfile_missing',
|
|
61
|
+
message: `No ${path.basename(lockfilePath)} found`
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const content = fs.readFileSync(lockfilePath, 'utf-8');
|
|
67
|
+
|
|
68
|
+
switch (manager) {
|
|
69
|
+
case 'npm':
|
|
70
|
+
return this.validateNpmLockfile(content);
|
|
71
|
+
case 'yarn':
|
|
72
|
+
return this.validateYarnLockfile(content);
|
|
73
|
+
case 'pnpm':
|
|
74
|
+
return this.validatePnpmLockfile(content);
|
|
75
|
+
default:
|
|
76
|
+
return {
|
|
77
|
+
valid: false,
|
|
78
|
+
reason: 'unknown_manager',
|
|
79
|
+
message: `Unknown package manager: ${manager}`
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
this.logger.error('Lockfile read error', {
|
|
84
|
+
manager,
|
|
85
|
+
lockfilePath,
|
|
86
|
+
error: err.message
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
valid: false,
|
|
90
|
+
reason: 'read_error',
|
|
91
|
+
message: err.message
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Validate npm lockfile (package-lock.json)
|
|
98
|
+
* @param {string} content - Lockfile content
|
|
99
|
+
* @returns {Object} Validation result
|
|
100
|
+
*/
|
|
101
|
+
validateNpmLockfile(content) {
|
|
102
|
+
try {
|
|
103
|
+
const lockfile = JSON.parse(content);
|
|
104
|
+
|
|
105
|
+
// Check required fields
|
|
106
|
+
if (!lockfile.lockfileVersion) {
|
|
107
|
+
return {
|
|
108
|
+
valid: false,
|
|
109
|
+
reason: 'invalid_format',
|
|
110
|
+
message: 'Missing lockfileVersion field'
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!lockfile.packages && !lockfile.dependencies) {
|
|
115
|
+
return {
|
|
116
|
+
valid: false,
|
|
117
|
+
reason: 'empty_lockfile',
|
|
118
|
+
message: 'Lockfile has no dependencies'
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const packageCount = Object.keys(lockfile.packages || lockfile.dependencies || {}).length;
|
|
123
|
+
|
|
124
|
+
this.logger.debug('npm lockfile valid', {
|
|
125
|
+
lockfileVersion: lockfile.lockfileVersion,
|
|
126
|
+
packageCount
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
valid: true,
|
|
131
|
+
lockfileVersion: lockfile.lockfileVersion,
|
|
132
|
+
packageCount
|
|
133
|
+
};
|
|
134
|
+
} catch (err) {
|
|
135
|
+
this.logger.debug('npm lockfile invalid JSON', { error: err.message });
|
|
136
|
+
return {
|
|
137
|
+
valid: false,
|
|
138
|
+
reason: 'invalid_json',
|
|
139
|
+
message: `Invalid JSON: ${err.message}`
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Validate yarn lockfile (yarn.lock)
|
|
146
|
+
* @param {string} content - Lockfile content
|
|
147
|
+
* @returns {Object} Validation result
|
|
148
|
+
*/
|
|
149
|
+
validateYarnLockfile(content) {
|
|
150
|
+
// Yarn lockfile is YAML-like format
|
|
151
|
+
// Check for basic structure: __metadata__ (Yarn 2+) or "# yarn lockfile v" (Yarn 1)
|
|
152
|
+
const hasYarn2Metadata = content.includes('__metadata:');
|
|
153
|
+
const hasYarn1Header = content.match(/^# yarn lockfile v/i);
|
|
154
|
+
|
|
155
|
+
if (!hasYarn2Metadata && !hasYarn1Header) {
|
|
156
|
+
this.logger.debug('yarn lockfile invalid format');
|
|
157
|
+
return {
|
|
158
|
+
valid: false,
|
|
159
|
+
reason: 'invalid_format',
|
|
160
|
+
message: 'Invalid yarn.lock format'
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Count dependency entries (lines starting with package name pattern)
|
|
165
|
+
const entryCount = (content.match(/^"?[^@\s]+@/gm) || []).length;
|
|
166
|
+
|
|
167
|
+
this.logger.debug('yarn lockfile valid', {
|
|
168
|
+
version: hasYarn2Metadata ? '2+' : '1',
|
|
169
|
+
entryCount
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
valid: true,
|
|
174
|
+
entryCount
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Validate pnpm lockfile (pnpm-lock.yaml)
|
|
180
|
+
* @param {string} content - Lockfile content
|
|
181
|
+
* @returns {Object} Validation result
|
|
182
|
+
*/
|
|
183
|
+
validatePnpmLockfile(content) {
|
|
184
|
+
// Check for lockfileVersion
|
|
185
|
+
const versionMatch = content.match(/^lockfileVersion:\s*(\d+)/m);
|
|
186
|
+
if (!versionMatch) {
|
|
187
|
+
this.logger.debug('pnpm lockfile missing lockfileVersion');
|
|
188
|
+
return {
|
|
189
|
+
valid: false,
|
|
190
|
+
reason: 'invalid_format',
|
|
191
|
+
message: 'Missing lockfileVersion in pnpm-lock.yaml'
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const lockfileVersion = parseInt(versionMatch[1], 10);
|
|
196
|
+
|
|
197
|
+
// Count dependency entries (lines starting with " /" which are package specs)
|
|
198
|
+
const entryCount = (content.match(/^ \/[^:]+:/gm) || []).length;
|
|
199
|
+
|
|
200
|
+
this.logger.debug('pnpm lockfile valid', {
|
|
201
|
+
lockfileVersion,
|
|
202
|
+
entryCount
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
valid: true,
|
|
207
|
+
lockfileVersion,
|
|
208
|
+
entryCount
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get the lockfile path for a specific package manager
|
|
214
|
+
* @param {string} manager - Package manager name
|
|
215
|
+
* @returns {string} Full path to lockfile
|
|
216
|
+
*/
|
|
217
|
+
getLockfilePath(manager) {
|
|
218
|
+
const lockfiles = {
|
|
219
|
+
'npm': 'package-lock.json',
|
|
220
|
+
'yarn': 'yarn.lock',
|
|
221
|
+
'pnpm': 'pnpm-lock.yaml'
|
|
222
|
+
};
|
|
223
|
+
return path.join(this.cwd, lockfiles[manager] || 'package-lock.json');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
module.exports = LockfileValidator;
|