@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,280 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Session Manager — Core session state persistence module
|
|
5
|
+
*
|
|
6
|
+
* Manages session lifecycle: create, load, update, end, list
|
|
7
|
+
* Sessions stored in .planning/sessions/session-{timestamp}.json
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { safePlanningWriteSync } = require('./planning-write.cjs');
|
|
13
|
+
const { defaultLogger: logger } = require('./logger.cjs');
|
|
14
|
+
|
|
15
|
+
class SessionManager {
|
|
16
|
+
/**
|
|
17
|
+
* Create a SessionManager instance
|
|
18
|
+
* @param {string} sessionsDir - Directory for session files (default: .planning/sessions)
|
|
19
|
+
*/
|
|
20
|
+
constructor(sessionsDir = '.planning/sessions') {
|
|
21
|
+
this.sessionsDir = sessionsDir;
|
|
22
|
+
this._ensureDir();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Ensure sessions directory exists
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
_ensureDir() {
|
|
30
|
+
if (!fs.existsSync(this.sessionsDir)) {
|
|
31
|
+
fs.mkdirSync(this.sessionsDir, { recursive: true });
|
|
32
|
+
logger.info('Sessions directory created', { dir: this.sessionsDir });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generate session ID from timestamp
|
|
38
|
+
* Format: session-YYYYMMDD-HHMMSS
|
|
39
|
+
* @private
|
|
40
|
+
* @returns {string} Session ID
|
|
41
|
+
*/
|
|
42
|
+
_generateSessionId() {
|
|
43
|
+
const now = new Date();
|
|
44
|
+
const timestamp = now.toISOString()
|
|
45
|
+
.replace(/[:.]/g, '-')
|
|
46
|
+
.replace('T', '-')
|
|
47
|
+
.slice(0, -5); // Remove milliseconds and Z
|
|
48
|
+
return `session-${timestamp}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Deep merge source into target
|
|
53
|
+
* @private
|
|
54
|
+
* @param {Object} target - Target object
|
|
55
|
+
* @param {Object} source - Source object
|
|
56
|
+
* @returns {Object} Merged object
|
|
57
|
+
*/
|
|
58
|
+
_deepMerge(target, source) {
|
|
59
|
+
const result = { ...target };
|
|
60
|
+
for (const key in source) {
|
|
61
|
+
if (source.hasOwnProperty(key)) {
|
|
62
|
+
if (source[key] instanceof Object && key !== null) {
|
|
63
|
+
result[key] = this._deepMerge(result[key] || {}, source[key]);
|
|
64
|
+
} else {
|
|
65
|
+
result[key] = source[key];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create a new session
|
|
74
|
+
* @param {Object} options - Session options
|
|
75
|
+
* @param {string} [options.model] - Model identifier
|
|
76
|
+
* @param {number} [options.phase] - Phase number
|
|
77
|
+
* @param {number} [options.plan] - Plan number
|
|
78
|
+
* @param {string} [options.objective] - Session objective
|
|
79
|
+
* @returns {string} Session ID
|
|
80
|
+
*/
|
|
81
|
+
createSession(options = {}) {
|
|
82
|
+
const sessionId = this._generateSessionId();
|
|
83
|
+
const sessionPath = path.join(this.sessionsDir, `${sessionId}.json`);
|
|
84
|
+
|
|
85
|
+
const session = {
|
|
86
|
+
metadata: {
|
|
87
|
+
session_id: sessionId,
|
|
88
|
+
session_version: '1.0',
|
|
89
|
+
started_at: new Date().toISOString(),
|
|
90
|
+
ended_at: null,
|
|
91
|
+
model: options.model || null,
|
|
92
|
+
phase: options.phase || null,
|
|
93
|
+
plan: options.plan || null,
|
|
94
|
+
status: 'active',
|
|
95
|
+
session_chain: [],
|
|
96
|
+
token_usage: {
|
|
97
|
+
input: 0,
|
|
98
|
+
output: 0,
|
|
99
|
+
total: 0
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
context: {
|
|
103
|
+
transcript: '',
|
|
104
|
+
tasks: [],
|
|
105
|
+
decisions: [],
|
|
106
|
+
file_changes: [],
|
|
107
|
+
open_questions: [],
|
|
108
|
+
blockers: []
|
|
109
|
+
},
|
|
110
|
+
state: {
|
|
111
|
+
current_phase: options.phase || null,
|
|
112
|
+
current_plan: options.plan || null,
|
|
113
|
+
incomplete_tasks: [],
|
|
114
|
+
last_action: null,
|
|
115
|
+
next_recommended_action: options.objective || null
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
safePlanningWriteSync(sessionPath, JSON.stringify(session, null, 2));
|
|
121
|
+
logger.info('Session created', { sessionId, sessionPath });
|
|
122
|
+
return sessionId;
|
|
123
|
+
} catch (err) {
|
|
124
|
+
logger.error('Failed to create session', { sessionId, error: err.message });
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Load a session by ID
|
|
131
|
+
* @param {string} sessionId - Session ID
|
|
132
|
+
* @returns {Object|null} Session object or null if not found
|
|
133
|
+
*/
|
|
134
|
+
loadSession(sessionId) {
|
|
135
|
+
const sessionPath = path.join(this.sessionsDir, `${sessionId}.json`);
|
|
136
|
+
|
|
137
|
+
if (!fs.existsSync(sessionPath)) {
|
|
138
|
+
logger.error('Session not found', { sessionId, sessionPath });
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const content = fs.readFileSync(sessionPath, 'utf-8');
|
|
144
|
+
const session = JSON.parse(content);
|
|
145
|
+
logger.info('Session loaded', { sessionId });
|
|
146
|
+
return session;
|
|
147
|
+
} catch (err) {
|
|
148
|
+
logger.error('Failed to load session', { sessionId, error: err.message });
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get the most recent session
|
|
155
|
+
* @returns {Object|null} Last session or null if none exist
|
|
156
|
+
*/
|
|
157
|
+
getLastSession() {
|
|
158
|
+
const sessionFiles = this._getSessionFiles();
|
|
159
|
+
|
|
160
|
+
if (sessionFiles.length === 0) {
|
|
161
|
+
logger.info('No sessions found');
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Files are sorted, newest first
|
|
166
|
+
const lastFile = sessionFiles[0];
|
|
167
|
+
const sessionId = lastFile.replace('.json', '');
|
|
168
|
+
return this.loadSession(sessionId);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Update a session with new data
|
|
173
|
+
* @param {string} sessionId - Session ID
|
|
174
|
+
* @param {Object} updates - Updates to merge into session
|
|
175
|
+
* @returns {boolean} True on success, false if session not found
|
|
176
|
+
*/
|
|
177
|
+
updateSession(sessionId, updates) {
|
|
178
|
+
const session = this.loadSession(sessionId);
|
|
179
|
+
if (!session) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const updatedSession = this._deepMerge(session, updates);
|
|
184
|
+
const sessionPath = path.join(this.sessionsDir, `${sessionId}.json`);
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
safePlanningWriteSync(sessionPath, JSON.stringify(updatedSession, null, 2));
|
|
188
|
+
logger.info('Session updated', { sessionId });
|
|
189
|
+
return true;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
logger.error('Failed to update session', { sessionId, error: err.message });
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* End a session
|
|
198
|
+
* @param {string} sessionId - Session ID
|
|
199
|
+
* @param {Object} finalState - Final state information
|
|
200
|
+
* @param {string} [finalState.status] - Final status (default: 'completed')
|
|
201
|
+
* @param {Array} [finalState.incomplete_tasks] - Incomplete tasks
|
|
202
|
+
* @param {string} [finalState.next_recommended_action] - Next recommended action
|
|
203
|
+
* @returns {boolean} True on success, false if session not found
|
|
204
|
+
*/
|
|
205
|
+
endSession(sessionId, finalState = {}) {
|
|
206
|
+
const session = this.loadSession(sessionId);
|
|
207
|
+
if (!session) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const updates = {
|
|
212
|
+
metadata: {
|
|
213
|
+
ended_at: new Date().toISOString(),
|
|
214
|
+
status: finalState.status || 'completed'
|
|
215
|
+
},
|
|
216
|
+
state: {}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
if (finalState.incomplete_tasks) {
|
|
220
|
+
updates.state.incomplete_tasks = finalState.incomplete_tasks;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (finalState.next_recommended_action) {
|
|
224
|
+
updates.state.next_recommended_action = finalState.next_recommended_action;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return this.updateSession(sessionId, updates);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* List all sessions
|
|
232
|
+
* @returns {Array} Array of session metadata sorted by date (newest first)
|
|
233
|
+
*/
|
|
234
|
+
listSessions() {
|
|
235
|
+
const sessionFiles = this._getSessionFiles();
|
|
236
|
+
const sessions = [];
|
|
237
|
+
|
|
238
|
+
for (const file of sessionFiles) {
|
|
239
|
+
const sessionId = file.replace('.json', '');
|
|
240
|
+
const session = this.loadSession(sessionId);
|
|
241
|
+
if (session) {
|
|
242
|
+
sessions.push({
|
|
243
|
+
session_id: session.metadata.session_id,
|
|
244
|
+
started_at: session.metadata.started_at,
|
|
245
|
+
ended_at: session.metadata.ended_at,
|
|
246
|
+
model: session.metadata.model,
|
|
247
|
+
phase: session.metadata.phase,
|
|
248
|
+
plan: session.metadata.plan,
|
|
249
|
+
status: session.metadata.status
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return sessions;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get session files sorted by name (newest first)
|
|
259
|
+
* @private
|
|
260
|
+
* @returns {Array} Array of filenames
|
|
261
|
+
*/
|
|
262
|
+
_getSessionFiles() {
|
|
263
|
+
if (!fs.existsSync(this.sessionsDir)) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const files = fs.readdirSync(this.sessionsDir)
|
|
269
|
+
.filter(file => /^session-.*\.json$/.test(file))
|
|
270
|
+
.sort()
|
|
271
|
+
.reverse();
|
|
272
|
+
return files;
|
|
273
|
+
} catch (err) {
|
|
274
|
+
logger.error('Failed to read sessions directory', { error: err.message });
|
|
275
|
+
return [];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = SessionManager;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Skill Context — Context schema validation for skill matcher
|
|
5
|
+
*
|
|
6
|
+
* Validates and normalizes matcher context:
|
|
7
|
+
* - stack: object with language, framework, version
|
|
8
|
+
* - scope: enum [new-feature, bugfix, refactor, migration, maintenance]
|
|
9
|
+
* - projectType: string (optional)
|
|
10
|
+
* - mode: enum [greenfield, existing, rapid-mvp, scale-up, maintenance]
|
|
11
|
+
* - constraints: object with deadline, teamSize, compliance, legacySystems
|
|
12
|
+
* - taskDescription: string (for keyword matching)
|
|
13
|
+
* - codebaseFiles: array of strings (for file pattern matching)
|
|
14
|
+
* - executedCommands: array of strings (for command matching)
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* const { validateContext, CONTEXT_SCHEMA } = require('./skill-context.cjs');
|
|
18
|
+
* const { valid, errors, normalizedContext } = validateContext(context);
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const Logger = require('./logger.cjs');
|
|
22
|
+
const logger = new Logger();
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Context schema definition
|
|
26
|
+
*/
|
|
27
|
+
const CONTEXT_SCHEMA = {
|
|
28
|
+
stack: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
optional: true,
|
|
31
|
+
properties: {
|
|
32
|
+
language: { type: 'string', optional: false },
|
|
33
|
+
framework: { type: 'string', optional: true },
|
|
34
|
+
version: { type: 'string', optional: true }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
scope: {
|
|
38
|
+
type: 'enum',
|
|
39
|
+
values: ['new-feature', 'bugfix', 'refactor', 'migration', 'maintenance'],
|
|
40
|
+
optional: true
|
|
41
|
+
},
|
|
42
|
+
projectType: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
optional: true
|
|
45
|
+
},
|
|
46
|
+
mode: {
|
|
47
|
+
type: 'enum',
|
|
48
|
+
values: ['greenfield', 'existing', 'rapid-mvp', 'scale-up', 'maintenance'],
|
|
49
|
+
optional: true,
|
|
50
|
+
normalize: (value) => {
|
|
51
|
+
const mappings = {
|
|
52
|
+
'mvp': 'rapid-mvp',
|
|
53
|
+
'green-field': 'greenfield',
|
|
54
|
+
'brownfield': 'existing',
|
|
55
|
+
'scale': 'scale-up',
|
|
56
|
+
'maint': 'maintenance'
|
|
57
|
+
};
|
|
58
|
+
return mappings[value] || value;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
constraints: {
|
|
62
|
+
type: 'object',
|
|
63
|
+
optional: true,
|
|
64
|
+
properties: {
|
|
65
|
+
deadline: { type: 'string', optional: true },
|
|
66
|
+
teamSize: { type: 'number', optional: true },
|
|
67
|
+
compliance: { type: 'array', optional: true },
|
|
68
|
+
legacySystems: { type: 'array', optional: true }
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
taskDescription: { type: 'string', optional: true },
|
|
72
|
+
codebaseFiles: { type: 'array', optional: true },
|
|
73
|
+
executedCommands: { type: 'array', optional: true }
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Validate and normalize context object
|
|
78
|
+
* @param {Object} context - Context object to validate
|
|
79
|
+
* @returns {Object} Validation result: { valid, errors, normalizedContext }
|
|
80
|
+
*/
|
|
81
|
+
function validateContext(context) {
|
|
82
|
+
const errors = [];
|
|
83
|
+
const normalized = { ...context };
|
|
84
|
+
|
|
85
|
+
if (!context || typeof context !== 'object') {
|
|
86
|
+
return {
|
|
87
|
+
valid: false,
|
|
88
|
+
errors: ['Context must be an object'],
|
|
89
|
+
normalizedContext: null
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Validate and normalize mode
|
|
94
|
+
if (context.mode) {
|
|
95
|
+
const modeSchema = CONTEXT_SCHEMA.mode;
|
|
96
|
+
if (!modeSchema.values.includes(context.mode)) {
|
|
97
|
+
const normalizedMode = modeSchema.normalize?.(context.mode);
|
|
98
|
+
if (normalizedMode && modeSchema.values.includes(normalizedMode)) {
|
|
99
|
+
normalized.mode = normalizedMode;
|
|
100
|
+
} else {
|
|
101
|
+
errors.push(`Invalid mode: ${context.mode}. Valid: ${modeSchema.values.join(', ')}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Validate scope
|
|
107
|
+
if (context.scope) {
|
|
108
|
+
const scopeSchema = CONTEXT_SCHEMA.scope;
|
|
109
|
+
if (!scopeSchema.values.includes(context.scope)) {
|
|
110
|
+
errors.push(`Invalid scope: ${context.scope}. Valid: ${scopeSchema.values.join(', ')}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Validate stack structure
|
|
115
|
+
if (context.stack) {
|
|
116
|
+
if (!context.stack.language && !context.stack.framework) {
|
|
117
|
+
errors.push('Stack must have at least language or framework');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Validate constraints structure
|
|
122
|
+
if (context.constraints) {
|
|
123
|
+
if (context.constraints.teamSize && typeof context.constraints.teamSize !== 'number') {
|
|
124
|
+
errors.push('teamSize must be a number');
|
|
125
|
+
}
|
|
126
|
+
if (context.constraints.compliance && !Array.isArray(context.constraints.compliance)) {
|
|
127
|
+
errors.push('compliance must be an array');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (errors.length > 0) {
|
|
132
|
+
logger.warn('Context validation failed', {
|
|
133
|
+
errorCount: errors.length,
|
|
134
|
+
errors
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
valid: errors.length === 0,
|
|
140
|
+
errors,
|
|
141
|
+
normalizedContext: errors.length === 0 ? normalized : null
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = {
|
|
146
|
+
validateContext,
|
|
147
|
+
CONTEXT_SCHEMA
|
|
148
|
+
};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Skill Matcher — Rule-based skill matching algorithm
|
|
5
|
+
*
|
|
6
|
+
* Matches context to skills using 5-rule matching:
|
|
7
|
+
* - Rule 1: Stack skills (mandatory, priority 100)
|
|
8
|
+
* - Rule 2: Domain skills (if project type detected, priority 90)
|
|
9
|
+
* - Rule 3: Mode skills (ceremony level, priority 85)
|
|
10
|
+
* - Rule 4: Constraint-based skills (deadline, team, compliance, priority 80)
|
|
11
|
+
* - Rule 5: Universal skills (always activate, priority 50)
|
|
12
|
+
*
|
|
13
|
+
* Enforces 3-7 skill limit per task (EDGE-05 compliance)
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* const { SkillMatcher, SKILL_LIMITS } = require('./skill-matcher.cjs');
|
|
17
|
+
* const matcher = new SkillMatcher(registry);
|
|
18
|
+
* const result = matcher.match(context);
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const Logger = require('./logger.cjs');
|
|
22
|
+
const logger = new Logger();
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Skill activation limits (EDGE-05 compliance)
|
|
26
|
+
*/
|
|
27
|
+
const SKILL_LIMITS = {
|
|
28
|
+
MIN_ACTIVE: 3,
|
|
29
|
+
MAX_ACTIVE: 7,
|
|
30
|
+
CRITICAL_TASK_MAX: 5, // For security-sensitive tasks
|
|
31
|
+
MVP_MODE_MAX: 4, // Rapid MVP mode reduces complexity
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Skill Matcher class for matching context to skills
|
|
36
|
+
*/
|
|
37
|
+
class SkillMatcher {
|
|
38
|
+
/**
|
|
39
|
+
* Create a SkillMatcher instance
|
|
40
|
+
* @param {Object} registry - SkillRegistry instance
|
|
41
|
+
* @param {Object} options - Matcher options
|
|
42
|
+
*/
|
|
43
|
+
constructor(registry, options = {}) {
|
|
44
|
+
this.registry = registry;
|
|
45
|
+
this.logger = options.logger || logger;
|
|
46
|
+
this.rules = this.loadMatchingRules();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Match context to skills
|
|
51
|
+
* @param {Object} context - Matching context
|
|
52
|
+
* @returns {Object} Match result: { activatedSkills, rationale, conflicts, metadata }
|
|
53
|
+
*/
|
|
54
|
+
match(context) {
|
|
55
|
+
const candidates = [];
|
|
56
|
+
|
|
57
|
+
// Rule 1: Stack skills (mandatory)
|
|
58
|
+
if (context.stack?.framework || context.stack?.language) {
|
|
59
|
+
const stackSkills = this.findByStack(context.stack);
|
|
60
|
+
stackSkills.forEach(skill => {
|
|
61
|
+
candidates.push({ skill, priority: 100, rule: 'stack' });
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Rule 2: Domain skills (if project type detected)
|
|
66
|
+
if (context.projectType) {
|
|
67
|
+
const domainSkills = this.findByDomain(context.projectType);
|
|
68
|
+
domainSkills.forEach(skill => {
|
|
69
|
+
candidates.push({ skill, priority: 90, rule: 'domain' });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Rule 3: Mode skills (ceremony level)
|
|
74
|
+
if (context.mode) {
|
|
75
|
+
const modeSkills = this.findByMode(context.mode);
|
|
76
|
+
modeSkills.forEach(skill => {
|
|
77
|
+
candidates.push({ skill, priority: 85, rule: 'mode' });
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Rule 4: Constraint-based skills
|
|
82
|
+
const constraintSkills = this.findByConstraints(context.constraints);
|
|
83
|
+
constraintSkills.forEach(skill => {
|
|
84
|
+
candidates.push({ skill, priority: 80, rule: 'constraint' });
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Rule 5: Universal skills (always activate)
|
|
88
|
+
const universalSkills = this.registry.findByTag('universal');
|
|
89
|
+
universalSkills.forEach(skill => {
|
|
90
|
+
candidates.push({ skill, priority: 50, rule: 'universal' });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Sort by priority and apply limits
|
|
94
|
+
const sorted = candidates.sort((a, b) => b.priority - a.priority);
|
|
95
|
+
const limited = this.enforceSkillLimits(sorted, context);
|
|
96
|
+
const activatedSkills = limited.map(c => c.skill);
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
activatedSkills,
|
|
100
|
+
rationale: this.generateRationale(activatedSkills, context),
|
|
101
|
+
conflicts: [], // Reserved for Phase 36 SKILL-07
|
|
102
|
+
metadata: {
|
|
103
|
+
totalCandidates: candidates.length,
|
|
104
|
+
rulesApplied: [...new Set(candidates.map(c => c.rule))],
|
|
105
|
+
limitEnforced: candidates.length > limited.length
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Enforce skill activation limits
|
|
112
|
+
* @param {Object[]} candidates - Sorted candidate skills
|
|
113
|
+
* @param {Object} context - Matching context
|
|
114
|
+
* @returns {Object[]} Limited candidate array
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
enforceSkillLimits(candidates, context) {
|
|
118
|
+
const maxSkills = context.mode === 'rapid-mvp'
|
|
119
|
+
? SKILL_LIMITS.MVP_MODE_MAX
|
|
120
|
+
: SKILL_LIMITS.MAX_ACTIVE;
|
|
121
|
+
|
|
122
|
+
if (candidates.length > maxSkills) {
|
|
123
|
+
this.logger.warn('Skill limit exceeded', {
|
|
124
|
+
candidateCount: candidates.length,
|
|
125
|
+
maxSkills,
|
|
126
|
+
mode: context.mode,
|
|
127
|
+
skills: candidates.slice(0, maxSkills).map(c => c.skill.name)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return candidates.slice(0, maxSkills);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generate rationale for skill activation
|
|
136
|
+
* @param {Object[]} skills - Activated skills
|
|
137
|
+
* @param {Object} context - Matching context
|
|
138
|
+
* @returns {string} Rationale string
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
generateRationale(skills, context) {
|
|
142
|
+
const categories = {};
|
|
143
|
+
skills.forEach(skill => {
|
|
144
|
+
const cat = skill.category || 'other';
|
|
145
|
+
if (!categories[cat]) categories[cat] = [];
|
|
146
|
+
categories[cat].push(skill.name);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const parts = [];
|
|
150
|
+
if (categories.stack) parts.push(`stack: ${categories.stack.join(', ')}`);
|
|
151
|
+
if (categories.domain) parts.push(`domain: ${categories.domain.join(', ')}`);
|
|
152
|
+
if (categories.mode) parts.push(`mode: ${categories.mode.join(', ')}`);
|
|
153
|
+
if (categories.constraint) parts.push(`constraint: ${categories.constraint.join(', ')}`);
|
|
154
|
+
if (categories.universal) parts.push(`universal: ${categories.universal.join(', ')}`);
|
|
155
|
+
|
|
156
|
+
return `Activated ${skills.length} skills based on ${context.mode || 'default'} mode: ${parts.join('; ')}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Find skills by stack
|
|
161
|
+
* @param {Object|string} stack - Stack identifier
|
|
162
|
+
* @returns {Object[]} Matching skills
|
|
163
|
+
*/
|
|
164
|
+
findByStack(stack) {
|
|
165
|
+
return this.registry.findByStack(stack);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Find skills by domain/project type
|
|
170
|
+
* @param {string} projectType - Project type
|
|
171
|
+
* @returns {Object[]} Matching skills
|
|
172
|
+
*/
|
|
173
|
+
findByDomain(projectType) {
|
|
174
|
+
return this.registry.findByTag(projectType);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Find skills by mode
|
|
179
|
+
* @param {string} mode - Mode identifier
|
|
180
|
+
* @returns {Object[]} Matching skills
|
|
181
|
+
*/
|
|
182
|
+
findByMode(mode) {
|
|
183
|
+
return this.registry.findByTag(mode);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Find skills by constraints
|
|
188
|
+
* @param {Object} constraints - Constraint object
|
|
189
|
+
* @returns {Object[]} Matching skills
|
|
190
|
+
* @private
|
|
191
|
+
*/
|
|
192
|
+
findByConstraints(constraints) {
|
|
193
|
+
const skills = [];
|
|
194
|
+
if (!constraints) return skills;
|
|
195
|
+
|
|
196
|
+
// Deadline-based skills
|
|
197
|
+
if (constraints.deadline === '2-weeks' || constraints.deadline === '1-week') {
|
|
198
|
+
const rapid = this.registry.get('rapid_delivery_skill_v1');
|
|
199
|
+
if (rapid) skills.push(rapid);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Compliance-based skills
|
|
203
|
+
if (constraints.compliance?.includes('hipaa')) {
|
|
204
|
+
const hipaa = this.registry.get('hipaa_compliance_skill_v1');
|
|
205
|
+
if (hipaa) skills.push(hipaa);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Team size skills
|
|
209
|
+
if (constraints.teamSize === 1) {
|
|
210
|
+
const solo = this.registry.get('solo_developer_skill_v1');
|
|
211
|
+
if (solo) skills.push(solo);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return skills;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Load matching rules configuration
|
|
219
|
+
* @returns {Object} Rules configuration
|
|
220
|
+
* @private
|
|
221
|
+
*/
|
|
222
|
+
loadMatchingRules() {
|
|
223
|
+
return {
|
|
224
|
+
stack: { priority: 100, required: true },
|
|
225
|
+
domain: { priority: 90, required: false },
|
|
226
|
+
mode: { priority: 85, required: false },
|
|
227
|
+
constraint: { priority: 80, required: false },
|
|
228
|
+
universal: { priority: 50, required: true }
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
module.exports = {
|
|
234
|
+
SkillMatcher,
|
|
235
|
+
SKILL_LIMITS
|
|
236
|
+
};
|