@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,270 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Skill Validator — Schema validation for skill metadata
|
|
5
|
+
*
|
|
6
|
+
* Validates skill objects against SKILL-01 metadata schema:
|
|
7
|
+
* - Required fields: name, description, version, category
|
|
8
|
+
* - Category validation: stack, architecture, domain, operational, governance
|
|
9
|
+
* - Tag validation against ALLOWED_TAGS list (50+ tags)
|
|
10
|
+
* - Triggers structure validation
|
|
11
|
+
* - Workflow and recommended_structure validation
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const { SkillValidator, SKILL_SCHEMA, ALLOWED_TAGS } = require('./skill-validator.cjs');
|
|
15
|
+
* const validator = new SkillValidator();
|
|
16
|
+
* const { valid, errors } = validator.validate(skill);
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const Logger = require('./logger.cjs');
|
|
20
|
+
const logger = new Logger();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Allowed tags for skills (50+ tags from research section 7.1)
|
|
24
|
+
*/
|
|
25
|
+
const ALLOWED_TAGS = [
|
|
26
|
+
// Stack tags (12 frameworks)
|
|
27
|
+
'laravel', 'nextjs', 'nestjs', 'react', 'vue', 'angular', 'flutter', 'django',
|
|
28
|
+
'express', 'svelte', 'fastapi', 'spring-boot',
|
|
29
|
+
// Language tags
|
|
30
|
+
'php', 'javascript', 'typescript', 'python', 'java', 'dart',
|
|
31
|
+
// Architecture tags
|
|
32
|
+
'monolith', 'microservices', 'event-driven', 'queue-based', 'caching', 'rbac', 'api-gateway',
|
|
33
|
+
// Domain tags
|
|
34
|
+
'pos', 'ecommerce', 'saas', 'lms', 'booking', 'fintech', 'inventory', 'dashboard', 'cms', 'erp',
|
|
35
|
+
// Operational tags
|
|
36
|
+
'bug-triage', 'refactor', 'migration', 'release', 'rollback', 'incident', 'testing', 'code-review',
|
|
37
|
+
// Additional tags
|
|
38
|
+
'backend', 'frontend', 'fullstack', 'mobile', 'framework', 'mvc', 'mvt',
|
|
39
|
+
'hooks', 'composition-api', 'standalone', 'async', 'middleware', 'modular',
|
|
40
|
+
'bloc', 'universal'
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Skill metadata schema definition
|
|
45
|
+
*/
|
|
46
|
+
const SKILL_SCHEMA = {
|
|
47
|
+
required: ['name', 'description', 'version', 'category'],
|
|
48
|
+
optional: [
|
|
49
|
+
'tags', 'stack', 'category', 'triggers', 'prerequisites',
|
|
50
|
+
'recommended_structure', 'workflow', 'best_practices', 'anti_patterns',
|
|
51
|
+
'scaling_notes', 'when_not_to_use', 'output_template', 'dependencies'
|
|
52
|
+
],
|
|
53
|
+
categories: ['stack', 'architecture', 'domain', 'operational', 'governance']
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Skill Validator class for validating skill metadata
|
|
58
|
+
*/
|
|
59
|
+
class SkillValidator {
|
|
60
|
+
/**
|
|
61
|
+
* Validate a skill object
|
|
62
|
+
* @param {Object} skill - Skill object to validate
|
|
63
|
+
* @returns {Object} Validation result: { valid: boolean, errors: string[] }
|
|
64
|
+
*/
|
|
65
|
+
validate(skill) {
|
|
66
|
+
const errors = [];
|
|
67
|
+
|
|
68
|
+
if (!skill || typeof skill !== 'object') {
|
|
69
|
+
return { valid: false, errors: ['Skill must be an object'] };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check required fields
|
|
73
|
+
for (const field of SKILL_SCHEMA.required) {
|
|
74
|
+
if (skill[field] === undefined || skill[field] === null || skill[field] === '') {
|
|
75
|
+
errors.push(`Required field missing: ${field}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Validate category
|
|
80
|
+
if (skill.category && !SKILL_SCHEMA.categories.includes(skill.category)) {
|
|
81
|
+
errors.push(`Invalid category: ${skill.category}. Must be one of: ${SKILL_SCHEMA.categories.join(', ')}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Validate tags
|
|
85
|
+
if (skill.tags) {
|
|
86
|
+
const tagErrors = this.validateTags(skill.tags);
|
|
87
|
+
errors.push(...tagErrors);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Validate triggers
|
|
91
|
+
if (skill.triggers) {
|
|
92
|
+
const triggerErrors = this.validateTriggers(skill.triggers);
|
|
93
|
+
errors.push(...triggerErrors);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Validate recommended_structure
|
|
97
|
+
if (skill.recommended_structure) {
|
|
98
|
+
const structureErrors = this.validateStructure(skill.recommended_structure);
|
|
99
|
+
errors.push(...structureErrors);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Validate workflow
|
|
103
|
+
if (skill.workflow) {
|
|
104
|
+
const workflowErrors = this.validateWorkflow(skill.workflow);
|
|
105
|
+
errors.push(...workflowErrors);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Validate prerequisites
|
|
109
|
+
if (skill.prerequisites && !Array.isArray(skill.prerequisites)) {
|
|
110
|
+
errors.push('prerequisites must be an array');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (errors.length > 0) {
|
|
114
|
+
logger.warn('Skill validation failed', {
|
|
115
|
+
skillName: skill.name,
|
|
116
|
+
errorCount: errors.length,
|
|
117
|
+
errors
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
valid: errors.length === 0,
|
|
123
|
+
errors
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Validate tags array
|
|
129
|
+
* @param {string[]} tags - Tags to validate
|
|
130
|
+
* @returns {string[]} Array of error messages
|
|
131
|
+
*/
|
|
132
|
+
validateTags(tags) {
|
|
133
|
+
const errors = [];
|
|
134
|
+
|
|
135
|
+
if (!Array.isArray(tags)) {
|
|
136
|
+
return ['tags must be an array'];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const tag of tags) {
|
|
140
|
+
if (typeof tag !== 'string') {
|
|
141
|
+
errors.push(`Tag must be a string: ${tag}`);
|
|
142
|
+
} else if (!ALLOWED_TAGS.includes(tag)) {
|
|
143
|
+
errors.push(`Invalid tag: ${tag}. Allowed tags: ${ALLOWED_TAGS.slice(0, 10).join(', ')}... (total ${ALLOWED_TAGS.length})`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return errors;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Validate triggers object
|
|
152
|
+
* @param {Object} triggers - Triggers object to validate
|
|
153
|
+
* @returns {string[]} Array of error messages
|
|
154
|
+
*/
|
|
155
|
+
validateTriggers(triggers) {
|
|
156
|
+
const errors = [];
|
|
157
|
+
|
|
158
|
+
if (typeof triggers !== 'object') {
|
|
159
|
+
return ['triggers must be an object'];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Validate keywords
|
|
163
|
+
if (triggers.keywords !== undefined) {
|
|
164
|
+
if (!Array.isArray(triggers.keywords)) {
|
|
165
|
+
errors.push('triggers.keywords must be an array');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Validate filePatterns
|
|
170
|
+
if (triggers.filePatterns !== undefined) {
|
|
171
|
+
if (!Array.isArray(triggers.filePatterns)) {
|
|
172
|
+
errors.push('triggers.filePatterns must be an array');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Validate commands
|
|
177
|
+
if (triggers.commands !== undefined) {
|
|
178
|
+
if (!Array.isArray(triggers.commands)) {
|
|
179
|
+
errors.push('triggers.commands must be an array');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Validate stack
|
|
184
|
+
if (triggers.stack !== undefined) {
|
|
185
|
+
if (typeof triggers.stack !== 'string') {
|
|
186
|
+
errors.push('triggers.stack must be a string');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Validate projectArchetypes
|
|
191
|
+
if (triggers.projectArchetypes !== undefined) {
|
|
192
|
+
if (!Array.isArray(triggers.projectArchetypes)) {
|
|
193
|
+
errors.push('triggers.projectArchetypes must be an array');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Validate modes
|
|
198
|
+
if (triggers.modes !== undefined) {
|
|
199
|
+
if (!Array.isArray(triggers.modes)) {
|
|
200
|
+
errors.push('triggers.modes must be an array');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return errors;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Validate recommended_structure object
|
|
209
|
+
* @param {Object} structure - Structure object to validate
|
|
210
|
+
* @returns {string[]} Array of error messages
|
|
211
|
+
*/
|
|
212
|
+
validateStructure(structure) {
|
|
213
|
+
const errors = [];
|
|
214
|
+
|
|
215
|
+
if (typeof structure !== 'object') {
|
|
216
|
+
return ['recommended_structure must be an object'];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Validate directories
|
|
220
|
+
if (structure.directories !== undefined) {
|
|
221
|
+
if (!Array.isArray(structure.directories)) {
|
|
222
|
+
errors.push('recommended_structure.directories must be an array');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return errors;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Validate workflow object
|
|
231
|
+
* @param {Object} workflow - Workflow object to validate
|
|
232
|
+
* @returns {string[]} Array of error messages
|
|
233
|
+
*/
|
|
234
|
+
validateWorkflow(workflow) {
|
|
235
|
+
const errors = [];
|
|
236
|
+
|
|
237
|
+
if (typeof workflow !== 'object') {
|
|
238
|
+
return ['workflow must be an object'];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Validate setup
|
|
242
|
+
if (workflow.setup !== undefined) {
|
|
243
|
+
if (!Array.isArray(workflow.setup)) {
|
|
244
|
+
errors.push('workflow.setup must be an array');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Validate generate
|
|
249
|
+
if (workflow.generate !== undefined) {
|
|
250
|
+
if (!Array.isArray(workflow.generate)) {
|
|
251
|
+
errors.push('workflow.generate must be an array');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Validate test
|
|
256
|
+
if (workflow.test !== undefined) {
|
|
257
|
+
if (!Array.isArray(workflow.test)) {
|
|
258
|
+
errors.push('workflow.test must be an array');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return errors;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
SkillValidator,
|
|
268
|
+
SKILL_SCHEMA,
|
|
269
|
+
ALLOWED_TAGS
|
|
270
|
+
};
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Skill Versioning — Version management and changelog tracking
|
|
5
|
+
*
|
|
6
|
+
* Provides:
|
|
7
|
+
* - Side-by-side version storage (laravel_skill_v1/, laravel_skill_v2/)
|
|
8
|
+
* - VERSIONS.md changelog tracking
|
|
9
|
+
* - Version resolution strategies (explicit, latest stable, project lock)
|
|
10
|
+
* - Skill update function with changelog append
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const { SkillVersionResolver, updateSkill } = require('./skill-versioning.cjs');
|
|
14
|
+
* const resolver = new SkillVersionResolver(registry);
|
|
15
|
+
* const skill = resolver.resolve('laravel_11_structure_skill');
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const Logger = require('./logger.cjs');
|
|
21
|
+
const logger = new Logger();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Skill Version Resolver class for managing skill versions
|
|
25
|
+
*/
|
|
26
|
+
class SkillVersionResolver {
|
|
27
|
+
/**
|
|
28
|
+
* Create a SkillVersionResolver instance
|
|
29
|
+
* @param {Object} registry - SkillRegistry instance
|
|
30
|
+
* @param {Object} options - Resolver options
|
|
31
|
+
*/
|
|
32
|
+
constructor(registry, options = {}) {
|
|
33
|
+
this.registry = registry;
|
|
34
|
+
this.skillsBase = options.skillsBase || path.join(__dirname, '../../skills');
|
|
35
|
+
this.logger = options.logger || logger;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a skill to a specific version
|
|
40
|
+
* @param {string} skillName - Skill name (with or without version)
|
|
41
|
+
* @param {Object} context - Resolution context
|
|
42
|
+
* @returns {Object} Resolved skill object
|
|
43
|
+
*/
|
|
44
|
+
resolve(skillName, context = {}) {
|
|
45
|
+
// Strategy 1: Explicit version request
|
|
46
|
+
if (skillName.includes('_v')) {
|
|
47
|
+
const skill = this.registry.get(skillName);
|
|
48
|
+
if (skill) {
|
|
49
|
+
this.logger.debug('Resolved explicit version', { skillName });
|
|
50
|
+
return skill;
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`Skill version not found: ${skillName}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Strategy 2: Project-specific version lock
|
|
56
|
+
const lockedVersion = context.projectConfig?.skillVersions?.[skillName];
|
|
57
|
+
if (lockedVersion) {
|
|
58
|
+
const versionedName = `${skillName}_v${lockedVersion}`;
|
|
59
|
+
const skill = this.registry.get(versionedName);
|
|
60
|
+
if (skill) {
|
|
61
|
+
this.logger.info('Using locked version', {
|
|
62
|
+
skillName,
|
|
63
|
+
lockedVersion,
|
|
64
|
+
versionedName
|
|
65
|
+
});
|
|
66
|
+
return skill;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Strategy 3: Latest stable version (default)
|
|
71
|
+
const allVersions = this.getAllVersions(skillName);
|
|
72
|
+
const latest = this.getLatestStable(allVersions);
|
|
73
|
+
if (latest) {
|
|
74
|
+
this.logger.debug('Resolved to latest version', {
|
|
75
|
+
skillName,
|
|
76
|
+
version: latest.version,
|
|
77
|
+
name: latest.name
|
|
78
|
+
});
|
|
79
|
+
return latest;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
throw new Error(`No versions found for skill: ${skillName}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get all versions of a skill
|
|
87
|
+
* @param {string} skillName - Base skill name
|
|
88
|
+
* @returns {Object[]} Array of version objects
|
|
89
|
+
*/
|
|
90
|
+
getAllVersions(skillName) {
|
|
91
|
+
const versions = [];
|
|
92
|
+
const category = this._getCategoryForSkill(skillName);
|
|
93
|
+
const skillDir = path.join(this.skillsBase, category, skillName);
|
|
94
|
+
|
|
95
|
+
if (!fs.existsSync(skillDir)) {
|
|
96
|
+
return versions;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Find all versioned directories
|
|
100
|
+
const entries = fs.readdirSync(skillDir, { withFileTypes: true });
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
if (entry.isDirectory() && entry.name.match(/_v(\d+)$/)) {
|
|
103
|
+
const skillPath = path.join(skillDir, entry.name, 'SKILL.md');
|
|
104
|
+
if (fs.existsSync(skillPath)) {
|
|
105
|
+
const content = fs.readFileSync(skillPath, 'utf8');
|
|
106
|
+
const { extractFrontmatter } = require('./frontmatter.cjs');
|
|
107
|
+
const frontmatter = extractFrontmatter(content);
|
|
108
|
+
versions.push({
|
|
109
|
+
name: entry.name,
|
|
110
|
+
version: frontmatter.version || '1.0.0',
|
|
111
|
+
deprecated: frontmatter.deprecated || false,
|
|
112
|
+
path: skillPath
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return versions;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get latest stable version from versions array
|
|
123
|
+
* @param {Object[]} versions - Array of version objects
|
|
124
|
+
* @returns {Object|null} Latest stable version or null
|
|
125
|
+
*/
|
|
126
|
+
getLatestStable(versions) {
|
|
127
|
+
const stableVersions = versions.filter(v => !v.deprecated);
|
|
128
|
+
|
|
129
|
+
if (stableVersions.length === 0) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Sort by version number (extract from name: _v1, _v2, etc.)
|
|
134
|
+
stableVersions.sort((a, b) => {
|
|
135
|
+
const aMatch = a.name.match(/_v(\d+)$/);
|
|
136
|
+
const bMatch = b.name.match(/_v(\d+)$/);
|
|
137
|
+
const aVersion = aMatch ? parseInt(aMatch[1], 10) : 0;
|
|
138
|
+
const bVersion = bMatch ? parseInt(bMatch[1], 10) : 0;
|
|
139
|
+
return bVersion - aVersion;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return stableVersions[0];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Parse version from skill name
|
|
147
|
+
* @param {string} skillName - Skill name
|
|
148
|
+
* @returns {Object} Parsed version info: { baseName, version, fullName }
|
|
149
|
+
*/
|
|
150
|
+
parseVersionFromName(skillName) {
|
|
151
|
+
const match = skillName.match(/^(.+)_v(\d+)$/);
|
|
152
|
+
if (match) {
|
|
153
|
+
return {
|
|
154
|
+
baseName: match[1],
|
|
155
|
+
version: parseInt(match[2], 10),
|
|
156
|
+
fullName: skillName
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
baseName: skillName,
|
|
161
|
+
version: null,
|
|
162
|
+
fullName: skillName
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Infer category from skill name
|
|
168
|
+
* @param {string} skillName - Skill name
|
|
169
|
+
* @returns {string} Category
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
_getCategoryForSkill(skillName) {
|
|
173
|
+
if (skillName.includes('laravel') || skillName.includes('django') ||
|
|
174
|
+
skillName.includes('nextjs') || skillName.includes('react') ||
|
|
175
|
+
skillName.includes('vue') || skillName.includes('angular') ||
|
|
176
|
+
skillName.includes('nestjs') || skillName.includes('fastapi') ||
|
|
177
|
+
skillName.includes('spring') || skillName.includes('flutter') ||
|
|
178
|
+
skillName.includes('express') || skillName.includes('svelte')) {
|
|
179
|
+
return 'stack';
|
|
180
|
+
}
|
|
181
|
+
if (skillName.includes('monolith') || skillName.includes('microservices')) {
|
|
182
|
+
return 'architecture';
|
|
183
|
+
}
|
|
184
|
+
if (skillName.includes('pos') || skillName.includes('ecommerce')) {
|
|
185
|
+
return 'domain';
|
|
186
|
+
}
|
|
187
|
+
if (skillName.includes('bug') || skillName.includes('rollback')) {
|
|
188
|
+
return 'operational';
|
|
189
|
+
}
|
|
190
|
+
return 'stack'; // Default
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Update a skill to a new version
|
|
196
|
+
* @param {string} skillName - Base skill name
|
|
197
|
+
* @param {number} newVersion - New version number
|
|
198
|
+
* @param {Object} changes - Changelog changes
|
|
199
|
+
* @returns {Promise<Object>} Update result
|
|
200
|
+
*/
|
|
201
|
+
async function updateSkill(skillName, newVersion, changes) {
|
|
202
|
+
const skillsBase = path.join(__dirname, '../../skills');
|
|
203
|
+
const category = inferCategory(skillName);
|
|
204
|
+
const skillBaseDir = path.join(skillsBase, category, skillName);
|
|
205
|
+
|
|
206
|
+
// Get previous version
|
|
207
|
+
const previousVersion = await getPreviousVersion(skillName, category);
|
|
208
|
+
if (!previousVersion) {
|
|
209
|
+
throw new Error(`No previous version found for ${skillName}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Create new version directory
|
|
213
|
+
const newVersionDir = path.join(skillBaseDir, `${skillName}_v${newVersion}`);
|
|
214
|
+
const previousVersionDir = path.join(skillBaseDir, `${skillName}_v${previousVersion}`);
|
|
215
|
+
|
|
216
|
+
if (!fs.existsSync(previousVersionDir)) {
|
|
217
|
+
throw new Error(`Previous version directory not found: ${previousVersionDir}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Copy previous version as base
|
|
221
|
+
fs.mkdirSync(newVersionDir, { recursive: true });
|
|
222
|
+
fs.cpSync(previousVersionDir, newVersionDir, { recursive: true });
|
|
223
|
+
|
|
224
|
+
// Update SKILL.md version field
|
|
225
|
+
const skillPath = path.join(newVersionDir, 'SKILL.md');
|
|
226
|
+
let content = fs.readFileSync(skillPath, 'utf8');
|
|
227
|
+
const { spliceFrontmatter, extractFrontmatter } = require('./frontmatter.cjs');
|
|
228
|
+
const frontmatter = extractFrontmatter(content);
|
|
229
|
+
frontmatter.version = `${newVersion}.0.0`;
|
|
230
|
+
content = spliceFrontmatter(content, frontmatter);
|
|
231
|
+
fs.writeFileSync(skillPath, content);
|
|
232
|
+
|
|
233
|
+
// Append to VERSIONS.md
|
|
234
|
+
const versionsPath = path.join(skillBaseDir, 'VERSIONS.md');
|
|
235
|
+
const changelogEntry = formatChangelogEntry(newVersion, changes);
|
|
236
|
+
if (fs.existsSync(versionsPath)) {
|
|
237
|
+
fs.appendFileSync(versionsPath, '\n' + changelogEntry);
|
|
238
|
+
} else {
|
|
239
|
+
fs.writeFileSync(versionsPath, `# ${skillName} Versions\n\n` + changelogEntry);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Log for audit trail
|
|
243
|
+
logger.info('Skill updated', {
|
|
244
|
+
skillName,
|
|
245
|
+
newVersion,
|
|
246
|
+
changes,
|
|
247
|
+
timestamp: new Date().toISOString()
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
skillName,
|
|
252
|
+
newVersion,
|
|
253
|
+
directory: newVersionDir,
|
|
254
|
+
previousVersion
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Format changelog entry
|
|
260
|
+
* @param {number} version - Version number
|
|
261
|
+
* @param {Object} changes - Changes object
|
|
262
|
+
* @returns {string} Formatted changelog entry
|
|
263
|
+
*/
|
|
264
|
+
function formatChangelogEntry(version, changes) {
|
|
265
|
+
const date = new Date().toISOString().split('T')[0];
|
|
266
|
+
let entry = `## v${version}.0.0 (${date})\n`;
|
|
267
|
+
|
|
268
|
+
if (changes.breaking) {
|
|
269
|
+
entry += '**Breaking Changes:**\n';
|
|
270
|
+
changes.breaking.forEach(change => entry += `- ${change}\n`);
|
|
271
|
+
entry += '\n';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (changes.added) {
|
|
275
|
+
entry += '**Added:**\n';
|
|
276
|
+
changes.added.forEach(change => entry += `- ${change}\n`);
|
|
277
|
+
entry += '\n';
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (changes.changed) {
|
|
281
|
+
entry += '**Changed:**\n';
|
|
282
|
+
changes.changed.forEach(change => entry += `- ${change}\n`);
|
|
283
|
+
entry += '\n';
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (changes.deprecated) {
|
|
287
|
+
entry += '**Deprecated:**\n';
|
|
288
|
+
changes.deprecated.forEach(change => entry += `- ${change}\n`);
|
|
289
|
+
entry += '\n';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (changes.removed) {
|
|
293
|
+
entry += '**Removed:**\n';
|
|
294
|
+
changes.removed.forEach(change => entry += `- ${change}\n`);
|
|
295
|
+
entry += '\n';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (changes.fixed) {
|
|
299
|
+
entry += '**Fixed:**\n';
|
|
300
|
+
changes.fixed.forEach(change => entry += `- ${change}\n`);
|
|
301
|
+
entry += '\n';
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (changes.migration) {
|
|
305
|
+
entry += '**Migration Guide:**\n';
|
|
306
|
+
changes.migration.forEach(step => entry += `${step}\n`);
|
|
307
|
+
entry += '\n';
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return entry;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Infer category from skill name
|
|
315
|
+
* @param {string} skillName - Skill name
|
|
316
|
+
* @returns {string} Category
|
|
317
|
+
*/
|
|
318
|
+
function inferCategory(skillName) {
|
|
319
|
+
if (skillName.includes('laravel') || skillName.includes('django') ||
|
|
320
|
+
skillName.includes('nextjs') || skillName.includes('react')) {
|
|
321
|
+
return 'stack';
|
|
322
|
+
}
|
|
323
|
+
return 'stack';
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Get previous version number
|
|
328
|
+
* @param {string} skillName - Skill name
|
|
329
|
+
* @param {string} category - Category
|
|
330
|
+
* @returns {Promise<number|null>} Previous version number
|
|
331
|
+
*/
|
|
332
|
+
async function getPreviousVersion(skillName, category) {
|
|
333
|
+
const skillsBase = path.join(__dirname, '../../skills');
|
|
334
|
+
const skillBaseDir = path.join(skillsBase, category, skillName);
|
|
335
|
+
|
|
336
|
+
if (!fs.existsSync(skillBaseDir)) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const entries = fs.readdirSync(skillBaseDir, { withFileTypes: true });
|
|
341
|
+
const versions = entries
|
|
342
|
+
.filter(e => e.isDirectory() && e.name.match(/_v(\d+)$/))
|
|
343
|
+
.map(e => parseInt(e.name.match(/_v(\d+)$/)[1], 10))
|
|
344
|
+
.sort((a, b) => b - a);
|
|
345
|
+
|
|
346
|
+
return versions[0] || null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
module.exports = {
|
|
350
|
+
SkillVersionResolver,
|
|
351
|
+
updateSkill,
|
|
352
|
+
formatChangelogEntry,
|
|
353
|
+
inferCategory,
|
|
354
|
+
getPreviousVersion
|
|
355
|
+
};
|